2 * Command line utility for making sense of C declarations.
3 * Copyright © 2011-2012, 2020-2021 Nick Bowler
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.
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.
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/>.
35 #include <localcharset.h>
38 #define _(x) gettext(x)
40 static const char *progname = "cdecl99";
43 static const char sopts[] = SOPT_STRING;
44 static const struct option lopts[] = {
49 static void print_version(void)
51 const char *copysign = "(C)";
52 void *convsign = NULL;
55 convsign = str_iconv("\xc2\xa9", "UTF-8", locale_charset());
61 printf("Copyright %s 2021 Nick Bowler.\n", copysign);
62 puts("License GPLv3+: GNU GPL version 3 or any later version.");
63 puts("This is free software: you are free to change and redistribute it.");
64 puts("There is NO WARRANTY, to the extent permitted by law.");
69 static void print_usage(FILE *f)
71 fprintf(f, _("Usage: %s [options]\n"), progname);
73 fprintf(f, _("Try %s --help for more information.\n"),
78 print_optstring(const struct option *opt, const struct lopt_help *help)
87 w = snprintf(optstring, sizeof optstring,
88 _(" -%c, --%s=%s"), opt->val, opt->name,
89 pgettext_expr(opt->name, help->arg));
91 w = snprintf(optstring, sizeof optstring,
92 _(" -%c, --%s"), opt->val, opt->name);
98 w = mbsnwidth(optstring, w, 0);
99 printf("%s", optstring);
104 w = printf(" -%c, --%s=%s", opt->val, opt->name, help->arg);
106 w = printf(" -%c, --%s", opt->val, opt->name);
109 if (w < 0 || w > 18) {
117 static void print_help(void)
119 const struct option *opt;
123 puts(_("This is \"cdecl99\": a command-line tool for parsing and constructing\n"
124 "complicated C declarations."));
128 for (opt = lopts; opt->name; opt++) {
129 struct lopt_help help;
133 if (!lopt_get_help(opt, &help))
136 w = print_optstring(opt, &help);
139 help.desc = pgettext_expr(opt->name, help.desc);
141 for (line = help.desc; *line; w = 0) {
142 const char *nl = strchr(line, '\n');
143 int n = (nl ? nl-line : -1);
145 printf("%*s%.*s\n", 20-w, "", n, line);
154 puts(_("For more information, see the cdecl99(1) man page."));
158 * TRANSLATORS: Please add *another line* indicating where users should
159 * report translation bugs.
161 printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
165 * Format a declaration according to the given function and return a pointer
166 * to the formatted string. The returned pointer remains valid until the
167 * next call, after which it must not be re-used.
169 * Returns NULL on failure.
172 do_format(size_t func(char *, size_t, struct cdecl *), struct cdecl *decl)
180 rc = func(buf, bufsz, decl);
184 tmp = realloc(buf, rc + 1);
186 fprintf(stderr, "%s\n", _("failed to allocate memory"));
198 static int cmd_explain(const char *cmd, const char *arg)
200 const struct cdecl_error *err;
205 decl = cdecl_parse_decl(arg);
207 err = cdecl_get_error();
208 fprintf(stderr, "%s\n", err->str);
212 for (struct cdecl *i = decl; i; i = i->next) {
213 str = do_format(cdecl_explain, i);
226 static int cmd_simplify(const char *cmd, const char *arg)
228 const struct cdecl_error *err;
233 decl = cdecl_parse_decl(arg);
235 err = cdecl_get_error();
236 fprintf(stderr, "%s\n", err->str);
240 for (struct cdecl *i = decl; i; i = i->next) {
241 struct cdecl_declspec *s = i->specifiers;
244 i->specifiers = NULL;
248 str = do_format(cdecl_declare, i);
265 static int cmd_declare(const char *cmd, const char *arg)
267 const struct cdecl_error *err;
272 /* The name of the command is significant here. */
273 decl = cdecl_parse_english(cmd);
275 err = cdecl_get_error();
276 fprintf(stderr, "%s\n", err->str);
281 * English parses have at most one full declarator, so no loop is
284 str = do_format(cdecl_declare, decl);
295 static int cmd_quit(const char *cmd, const char *arg)
300 static int cmd_help(const char *cmd, const char *arg);
302 static const struct command {
304 int (*func)(const char *cmd, const char *arg);
307 { "explain", cmd_explain, "Explain a C declaration." },
308 { "simplify", cmd_simplify, "Simplify a C declaration." },
309 { "declare", cmd_declare, "Construct a C declaration." },
310 { "type", cmd_declare, "Construct a C type name." },
311 { "help", cmd_help, "Print this list of commands." },
312 { "quit", cmd_quit, "Quit the program." },
313 { "exit", cmd_quit, NULL }
315 static const size_t ncommands = sizeof commands / sizeof commands[0];
317 static int cmd_help(const char *cmd, const char *arg)
319 for (size_t i = 0; i < ncommands; i++) {
320 if (!commands[i].blurb)
323 printf("%s -- %s\n", commands[i].name, commands[i].blurb);
330 * Ensure that the first n characters of str equal the entire (null-terminated)
333 static int cmd_cmp(const char *str, const char *cmd, size_t n)
335 size_t cmdlen = strlen(cmd);
341 return memcmp(str, cmd, n);
344 static int run_command(const char *line)
346 const char *cmd = line + strspn(line, " \t");
347 const char *arg = cmd + strcspn(cmd, " \t");
352 for (size_t i = 0; i < ncommands; i++) {
353 if (cmd_cmp(cmd, commands[i].name, arg-cmd) != 0)
356 return commands[i].func(cmd, arg);
359 fprintf(stderr, "Undefined command: %.*s\n", (int)(arg-cmd), cmd);
363 static bool is_blank_line(const char *line)
365 for (size_t i = 0; line[i]; i++) {
366 if (!isblank((unsigned char)line[i]))
373 static int repl(void)
377 for (; (line = readline("> ")); free(line)) {
378 if (!is_blank_line(line))
379 cdecl_add_history(line);
381 if (!run_command(line))
389 static int repl_cmdline(int argc, char **argv)
391 int opt, rc, ret = 0;
394 while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
398 rc = run_command(optarg);
408 static int repl_noninteractive(void)
410 int rc, ret = 0, saved_errno;
414 while (getline(&line, &n, stdin) >= 0) {
415 char *c = strchr(line, '\n');
419 rc = run_command(line);
431 perror("read error");
438 /* Initialize gettext */
439 static void init_i18n(void)
444 setlocale(LC_ALL, "");
445 bindtextdomain(PACKAGE, LOCALEDIR);
449 int main(int argc, char **argv)
451 bool show_intro = true, interactive = true, execute = false;
452 const char *filename = NULL;
460 while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
490 fprintf(stderr, _("%s: excess command-line arguments:"),
492 for (i = optind; i < argc; i++) {
493 fprintf(stderr, " %s", argv[i]);
495 fprintf(stderr, "\n");
500 /* --filename and --execute imply --batch. */
501 if (filename || execute)
504 /* --batch implies --quiet */
505 if (interactive && show_intro)
508 /* --execute supersedes --filename */
509 if (filename && !execute) {
510 if (!freopen(filename, "r", stdin)) {
519 rc = repl_cmdline(argc, argv);
521 rc = repl_noninteractive();