]> git.draconx.ca Git - cdecl99.git/blob - src/cdecl99.c
Avoid a couple warnings reported by gcc.
[cdecl99.git] / src / cdecl99.c
1 /*
2  * Command line utility for making sense of C declarations.
3  * Copyright © 2011-2012, 2020-2021 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 #include <stdbool.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include <errno.h>
26 #include <locale.h>
27 #include <assert.h>
28
29 #include <getopt.h>
30 #include <gettext.h>
31 #include <readline.h>
32 #include <striconv.h>
33 #include <localcharset.h>
34 #include <mbswidth.h>
35
36 #include "cdecl99.h"
37 #include "cdecl.h"
38
39 static const char *progname = "cdecl99";
40
41 #include "options.h"
42 static const char sopts[] = SOPT_STRING;
43 static const struct option lopts[] = {
44         LOPTS_INITIALIZER,
45         {0}
46 };
47
48 static void print_version(void)
49 {
50         const char *copysign = "(C)";
51         void *convsign = NULL;
52
53         if (ENABLE_NLS) {
54                 convsign = str_iconv("\xc2\xa9", "UTF-8", locale_charset());
55                 if (convsign)
56                         copysign = convsign;
57         }
58
59         puts(PACKAGE_STRING);
60         printf("Copyright %s 2021 Nick Bowler.\n", copysign);
61         puts("License GPLv3+: GNU GPL version 3 or any later version.");
62         puts("This is free software: you are free to change and redistribute it.");
63         puts("There is NO WARRANTY, to the extent permitted by law.");
64
65         free(convsign);
66 }
67
68 static void print_usage(FILE *f)
69 {
70         fprintf(f, _("Usage: %s [options]\n"), progname);
71         if (f != stdout)
72                 fprintf(f, _("Try %s --help for more information.\n"),
73                            progname);
74 }
75
76 static int
77 print_optstring(const struct option *opt, const struct lopt_help *help)
78 {
79         char optstring[100];
80         int w;
81
82         if (!ENABLE_NLS)
83                 goto no_translate;
84
85         if (opt->has_arg) {
86                 w = snprintf(optstring, sizeof optstring,
87                              _("  -%c, --%s=%s"), opt->val, opt->name,
88                              pgettext_expr(opt->name, help->arg));
89         } else {
90                 w = snprintf(optstring, sizeof optstring,
91                              _("  -%c, --%s"), opt->val, opt->name);
92         }
93
94         if (w < 0)
95                 goto no_translate;
96
97         w = mbsnwidth(optstring, w, 0);
98         printf("%s", optstring);
99         goto out;
100
101 no_translate:
102         if (opt->has_arg) {
103                 w = printf("  -%c, --%s=%s", opt->val, opt->name, help->arg);
104         } else {
105                 w = printf("  -%c, --%s", opt->val, opt->name);
106         }
107 out:
108         if (w < 0 || w > 18) {
109                 putchar('\n');
110                 return 0;
111         }
112
113         return w;
114 }
115
116 /*
117  * Print a string, with each line indented by i spaces.  The first line
118  * will be indented by w fewer spaces (to account for the cursor being in
119  * some other column).
120  */
121 void print_block(const char *s, int i, int w)
122 {
123         for (; *s; w = 0) {
124                 const char *nl = strchr(s, '\n');
125                 int n = (nl ? nl-s : -1);
126
127                 printf("%*s%.*s\n", i-w, "", n, s);
128                 if (!nl)
129                         break;
130
131                 s = nl+1;
132         }
133 }
134
135 static void print_help(void)
136 {
137         const struct option *opt;
138
139         print_usage(stdout);
140
141         puts(_("This is \"cdecl99\": a command-line tool for parsing and constructing\n"
142                "complicated C declarations."));
143         putchar('\n');
144
145         puts(_("Options:"));
146         for (opt = lopts; opt->name; opt++) {
147                 struct lopt_help help;
148                 int w;
149
150                 if (!lopt_get_help(opt, &help))
151                         continue;
152
153                 w = print_optstring(opt, &help);
154
155                 if (ENABLE_NLS)
156                         help.desc = pgettext_expr(opt->name, help.desc);
157
158                 print_block(help.desc, 20, w);
159         }
160         putchar('\n');
161
162         puts(_("For more information, see the cdecl99(1) man page."));
163         putchar('\n');
164
165         /*
166          * TRANSLATORS: Please add *another line* indicating where users should
167          * report translation bugs.
168          */
169         printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
170 }
171
172 static bool is_blank_line(const char *line)
173 {
174         for (size_t i = 0; line[i]; i++) {
175                 if (!isblank((unsigned char)line[i]))
176                         return false;
177         }
178
179         return true;
180 }
181
182 static int repl(void)
183 {
184         char *line;
185
186         for (; (line = readline("> ")); free(line)) {
187                 if (!is_blank_line(line))
188                         add_history(line);
189
190                 if (run_command(line, true) > 0)
191                         break;
192         }
193
194         free(line);
195         return 0;
196 }
197
198 static int repl_cmdline(int argc, char **argv)
199 {
200         int opt, rc, ret = 0;
201
202         optind = 1;
203         while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
204                 if (opt != 'e')
205                         continue;
206
207                 rc = run_command(optarg, false);
208                 if (rc < 0)
209                         ret = -1;
210                 else if (rc > 0)
211                         break;
212         }
213
214         return ret;
215 }
216
217 static int repl_noninteractive(void)
218 {
219         int rc, ret = 0, saved_errno;
220         char *line = NULL;
221         size_t n;
222
223         while (getline(&line, &n, stdin) >= 0) {
224                 char *c = strchr(line, '\n');
225                 if (c)
226                         *c = '\0';
227
228                 rc = run_command(line, false);
229                 if (rc < 0)
230                         ret = -1;
231                 else if (rc > 0)
232                         break;
233         }
234
235         saved_errno = errno;
236         free(line);
237
238         if (ferror(stdin)) {
239                 errno = saved_errno;
240                 perror("read error");
241                 return -1;
242         }
243
244         return ret;
245 }
246
247 /* Initialize gettext */
248 static void init_i18n(void)
249 {
250         if (!ENABLE_NLS)
251                 return;
252
253         setlocale(LC_ALL, "");
254         bindtextdomain(PACKAGE, LOCALEDIR);
255         textdomain(PACKAGE);
256 }
257
258 int main(int argc, char **argv)
259 {
260         bool show_intro = true, interactive = true, execute = false;
261         const char *filename = NULL;
262         int i, opt, rc;
263
264         if (argc > 0)
265                 progname = argv[0];
266
267         init_i18n();
268
269         while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
270                 switch (opt) {
271                 case 'q':
272                         show_intro = false;
273                         break;
274                 case 'b':
275                         interactive = false;
276                         break;
277                 case 'i':
278                         interactive = true;
279                         break;
280                 case 'f':
281                         filename = optarg;
282                         break;
283                 case 'e':
284                         execute = true;
285                         break;
286                 case 'V':
287                         print_version();
288                         return EXIT_SUCCESS;
289                 case 'H':
290                         print_help();
291                         return EXIT_SUCCESS;
292                 default:
293                         print_usage(stderr);
294                         return EXIT_FAILURE;
295                 }
296         }
297
298         if (optind < argc) {
299                 fprintf(stderr, _("%s: excess command-line arguments:"),
300                                 progname);
301                 for (i = optind; i < argc; i++) {
302                         fprintf(stderr, " %s", argv[i]);
303                 }
304                 fprintf(stderr, "\n");
305                 print_usage(stderr);
306                 return EXIT_FAILURE;
307         }
308
309         /* --filename and --execute imply --batch. */
310         if (filename || execute)
311                 interactive = false;
312
313         /* --batch implies --quiet */
314         if (interactive && show_intro)
315                 print_version();
316
317         /* --execute supersedes --filename */
318         if (filename && !execute) {
319                 if (!freopen(filename, "r", stdin)) {
320                         perror(filename);
321                         return EXIT_FAILURE;
322                 }
323         }
324
325         if (interactive)
326                 rc = repl();
327         else if (execute)
328                 rc = repl_cmdline(argc, argv);
329         else
330                 rc = repl_noninteractive();
331
332         if (rc != 0)
333                 return EXIT_FAILURE;
334         return EXIT_SUCCESS;
335 }