]> git.draconx.ca Git - cdecl99.git/blob - src/commands.c
429ba852efff70b67470722e2cd8b012a68c9fde
[cdecl99.git] / src / commands.c
1 /*
2  * Main command implementation routines for cdecl99.
3  * Copyright © 2011-2012, 2020-2021, 2023 Nick Bowler
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17  */
18
19 #include <config.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22
23 #include "cdecl99.h"
24 #include "cdecl.h"
25
26 static struct cdecl *do_parse(const char *s, int input_mode)
27 {
28         struct cdecl *parse;
29
30         if (input_mode == INPUT_C)
31                 parse = cdecl_parse_decl(s);
32         else
33                 parse = cdecl_parse_english(s);
34
35         if (!parse)
36                 print_error("%s", cdecl_get_error()->str);
37
38         return parse;
39 }
40
41 /*
42  * Format a declaration according to the given function and return a pointer
43  * to the formatted string.  The returned pointer remains valid until the
44  * next call, after which it must not be re-used.
45  *
46  * Returns NULL on failure.
47  */
48 static const char *do_render(struct cdecl *decl, int output_mode)
49 {
50         static size_t bufsz;
51         static char *buf;
52         size_t rc;
53
54 retry:
55         if (output_mode == OUTPUT_C)
56                 rc = cdecl_declare(buf, bufsz, decl);
57         else
58                 rc = cdecl_explain(buf, bufsz, decl);
59
60         if (rc >= bufsz) {
61                 char *tmp;
62
63                 tmp = realloc(buf, rc + 1);
64                 if (!tmp) {
65                         print_error("%s", _("failed to allocate memory"));
66                         return NULL;
67                 }
68
69                 buf = tmp;
70                 bufsz = rc + 1;
71                 goto retry;
72         }
73
74         return buf;
75 }
76
77 /*
78  * Parse the given string as either C or English (based on input_mode),
79  * then print the result as either C or English (based on output_mode).
80  */
81 int run_command_cdecl(const char *s, int input_mode, int output_mode)
82 {
83         struct cdecl *i, *parse;
84         int ret = -1;
85
86         if (!(parse = do_parse(s, input_mode)))
87                 goto out;
88
89         for (i = parse; i; i = i->next) {
90                 const char *str;
91                 int no_nl = 0;
92
93                 if (!(str = do_render(i, output_mode)))
94                         goto out;
95
96                 /*
97                  * In C output, only the first declarator needs specifiers
98                  * printed, and only the last declarator needs a newline.
99                  */
100                 if (output_mode == OUTPUT_C && i->next) {
101                         i->next->specifiers = NULL;
102                         no_nl = 1;
103                 }
104
105                 printf(", %s%s" + 2*!!i->specifiers, str, "\n" + no_nl);
106         }
107
108         ret = 0;
109 out:
110         cdecl_free(parse);
111         return ret;
112 }