]> git.draconx.ca Git - cdecl99.git/blob - src/cdecl99.c
Release 1.3.
[cdecl99.git] / src / cdecl99.c
1 /*
2  * Command line utility for making sense of C declarations.
3  * Copyright © 2011-2012, 2020-2024 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 #include <stdarg.h>
29 #include <inttypes.h>
30
31 #include <getopt.h>
32 #include <gettext.h>
33 #include <mbswidth.h>
34
35 #include "cdecl99.h"
36 #include "cdecl.h"
37 #include "help.h"
38 #include "xtra.h"
39 #include "options.h"
40 #include "version.h"
41 #include "getline.h"
42
43 #if HAVE_READLINE_READLINE_H
44 #  include <readline/readline.h>
45 #endif
46 #if HAVE_RL_ADD_HISTORY && HAVE_READLINE_HISTORY_H
47 #  include <readline/history.h>
48
49 /* call add_history only if the line is non-blank */
50 static void do_add_history(const char *line)
51 {
52         if (line[strspn(line, " \t")])
53                 add_history(line);
54 }
55 #else
56 static void do_add_history(const char *line)
57 {
58 }
59 #endif
60
61 static const char *progname = "cdecl99";
62 static bool batch_mode;
63
64 void print_error(const char *fmt, ...)
65 {
66         va_list(ap);
67
68         fprintf(stderr, batch_mode ? "%s: %s" : "%.0s%s",
69                         progname, _("error: "));
70
71         va_start(ap, fmt);
72         vfprintf(stderr, fmt, ap);
73         va_end(ap);
74
75         fprintf(stderr, "\n");
76 }
77
78 static void print_version(void)
79 {
80         do_print_version(PACKAGE_STRING);
81 }
82
83 static void print_usage(FILE *f)
84 {
85         fprintf(f, _("Usage: %s [options]\n"), progname);
86         if (f != stdout)
87                 fprintf(f, _("Try %s --help for more information.\n"),
88                            progname);
89 }
90
91 static void print_help(const struct option *lopts)
92 {
93         struct lopt_help help = {0};
94         const struct option *opt;
95
96         print_usage(stdout);
97
98         puts(_("This is \"cdecl99\": a command-line tool for parsing and constructing\n"
99                "complicated C declarations."));
100         putchar('\n');
101
102         puts(_("Options:"));
103         for (opt = lopts; opt->name; opt++) {
104                 if (!lopt_get_help(opt, &help))
105                         continue;
106
107                 help_print_option(opt, help.arg, help.desc, 20);
108         }
109
110         printf("\n%s\n\n",
111                _("For more information, see the cdecl99(1) man page."));
112
113         /*
114          * TRANSLATORS: Please add *another line* indicating where users should
115          * report translation bugs.
116          */
117         printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
118 }
119
120 static int do_readline(char **linebuf, size_t *n, bool batch)
121 {
122 #if !HAVE_READLINE
123         if (!batch) {
124                 fputs("> ", stdout);
125                 fflush(stdout);
126         }
127
128         return do_getline(linebuf, n);
129 #else
130         if (batch)
131                 return do_getline(linebuf, n);
132
133         free(*linebuf);
134         if (!(*linebuf = readline("> ")))
135                 return 0;
136
137         do_add_history(*linebuf);
138         return 1;
139 #endif
140 }
141
142 static int repl(bool batch)
143 {
144         char *line = NULL;
145         bool fail = 0;
146         size_t n = 0;
147
148         while (do_readline(&line, &n, batch)) {
149                 int rc = run_command(line, batch);
150                 if (rc > 0)
151                         break;
152                 if (rc < 0)
153                         fail = batch;
154         }
155
156         free(line);
157         return fail ? EXIT_FAILURE : 0;
158 }
159
160 static int repl_cmdline(unsigned count, char **commands)
161 {
162         bool fail = 0;
163         unsigned i;
164
165         for (i = 0; i < count; i++) {
166                 int rc = run_command(commands[i], true);
167                 if (rc > 0)
168                         break;
169                 if (rc < 0)
170                         fail = true;
171         }
172
173         return fail ? EXIT_FAILURE : 0;
174 }
175
176 /* Initialize gettext */
177 static void init_i18n(void)
178 {
179         if (!ENABLE_NLS)
180                 return;
181
182         setlocale(LC_ALL, "");
183         bindtextdomain(PACKAGE, LOCALEDIR);
184         textdomain(PACKAGE);
185 }
186
187 enum {
188         INIT_EXIT_SUCCESS = -1,
189         INIT_EXIT_FAILURE = -2
190 };
191
192 static int initialize(int argc, char **argv)
193 {
194         int i, opt, quiet = 0, execute = 0;
195         const char *filename = NULL;
196
197         XTRA_PACKED_LOPTS(lopts);
198
199         if (argc > 0)
200                 progname = argv[0];
201
202         init_i18n();
203
204         while ((opt = getopt_long(argc, argv, SOPT_STRING, lopts, 0)) != -1) {
205                 switch (opt) {
206                 case 'q':
207                         quiet = 1;
208                         break;
209                 case 'b':
210                         batch_mode = true;
211                         break;
212                 case 'i':
213                         batch_mode = false;
214                         break;
215                 case 'f':
216                         filename = optarg;
217                         break;
218                 case 'e':
219                         argv[execute++] = optarg;
220                         break;
221                 case 'V':
222                         print_version();
223                         return INIT_EXIT_SUCCESS;
224                 case 'H':
225                         print_help(lopts);
226                         return INIT_EXIT_SUCCESS;
227                 default:
228                         print_usage(stderr);
229                         return INIT_EXIT_FAILURE;
230                 }
231         }
232
233         if (optind < argc) {
234                 fprintf(stderr, "%s: %s", progname,
235                                 _("excess command-line arguments:"));
236                 for (i = optind; i < argc; i++) {
237                         fprintf(stderr, " %s", argv[i]);
238                 }
239                 fprintf(stderr, "\n");
240                 print_usage(stderr);
241                 return INIT_EXIT_FAILURE;
242         }
243
244         /* --filename and --execute imply --batch. */
245         if (filename || execute)
246                 batch_mode = true;
247
248         /* --batch implies --quiet */
249         if (!batch_mode && !quiet)
250                 print_version();
251
252         /* --execute supersedes --filename */
253         if (filename && !execute) {
254                 if (!freopen(filename, "r", stdin)) {
255                         print_error("failed to open %s: %s", filename,
256                                                         strerror(errno));
257                         return INIT_EXIT_FAILURE;
258                 }
259         }
260
261         return execute;
262 }
263
264 int main(int argc, char **argv)
265 {
266         int execute;
267
268         switch ((execute = initialize(argc, argv))) {
269         case INIT_EXIT_SUCCESS: return EXIT_SUCCESS;
270         case INIT_EXIT_FAILURE: return EXIT_FAILURE;
271         }
272
273         if (execute)
274                 return repl_cmdline(execute, argv);
275         else
276                 return repl(batch_mode);
277 }