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