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