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