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