]> git.draconx.ca Git - cdecl99.git/blob - src/cdecl99.c
493f64f64391826750b655203c71fac0dd884aa1
[cdecl99.git] / src / cdecl99.c
1 /*
2  * Command line utility for making sense of C declarations.
3  * Copyright © 2011-2012, 2020-2021 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 2021 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 bool is_blank_line(const char *line)
126 {
127         for (size_t i = 0; line[i]; i++) {
128                 if (!isblank((unsigned char)line[i]))
129                         return false;
130         }
131
132         return true;
133 }
134
135 static int repl(void)
136 {
137         char *line;
138
139         for (; (line = readline("> ")); free(line)) {
140                 if (!is_blank_line(line))
141                         add_history(line);
142
143                 if (run_command(line, true) > 0)
144                         break;
145         }
146
147         free(line);
148         return 0;
149 }
150
151 static int repl_cmdline(int argc, char **argv)
152 {
153         int opt, rc, ret = 0;
154
155         optind = 1;
156         while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
157                 if (opt != 'e')
158                         continue;
159
160                 rc = run_command(optarg, false);
161                 if (rc < 0)
162                         ret = -1;
163                 else if (rc > 0)
164                         break;
165         }
166
167         return ret;
168 }
169
170 static int repl_noninteractive(void)
171 {
172         int rc, ret = 0, saved_errno;
173         char *line = NULL;
174         size_t n;
175
176         while (getline(&line, &n, stdin) >= 0) {
177                 char *c = strchr(line, '\n');
178                 if (c)
179                         *c = '\0';
180
181                 rc = run_command(line, false);
182                 if (rc < 0)
183                         ret = -1;
184                 else if (rc > 0)
185                         break;
186         }
187
188         saved_errno = errno;
189         free(line);
190
191         if (ferror(stdin)) {
192                 print_error("%s", strerror(saved_errno));
193                 return -1;
194         }
195
196         return ret;
197 }
198
199 /* Initialize gettext */
200 static void init_i18n(void)
201 {
202         if (!ENABLE_NLS)
203                 return;
204
205         setlocale(LC_ALL, "");
206         bindtextdomain(PACKAGE, LOCALEDIR);
207         textdomain(PACKAGE);
208 }
209
210 int main(int argc, char **argv)
211 {
212         bool show_intro = true, execute = false;
213         const char *filename = NULL;
214         int i, opt, rc;
215
216         if (argc > 0)
217                 progname = argv[0];
218
219         init_i18n();
220
221         while ((opt = getopt_long(argc, argv, sopts, lopts, NULL)) != -1) {
222                 switch (opt) {
223                 case 'q':
224                         show_intro = false;
225                         break;
226                 case 'b':
227                         interactive = false;
228                         break;
229                 case 'i':
230                         interactive = true;
231                         break;
232                 case 'f':
233                         filename = optarg;
234                         break;
235                 case 'e':
236                         execute = true;
237                         break;
238                 case 'V':
239                         print_version();
240                         return EXIT_SUCCESS;
241                 case 'H':
242                         print_help();
243                         return EXIT_SUCCESS;
244                 default:
245                         print_usage(stderr);
246                         return EXIT_FAILURE;
247                 }
248         }
249
250         if (optind < argc) {
251                 fprintf(stderr, "%s: ", progname);
252
253                 fprintf(stderr, _("excess command-line arguments:"),
254                                 progname);
255                 for (i = optind; i < argc; i++) {
256                         fprintf(stderr, " %s", argv[i]);
257                 }
258                 fprintf(stderr, "\n");
259                 print_usage(stderr);
260                 return EXIT_FAILURE;
261         }
262
263         /* --filename and --execute imply --batch. */
264         if (filename || execute)
265                 interactive = false;
266
267         /* --batch implies --quiet */
268         if (interactive && show_intro)
269                 print_version();
270
271         /* --execute supersedes --filename */
272         if (filename && !execute) {
273                 if (!freopen(filename, "r", stdin)) {
274                         print_error("failed to open %s: %s\n", filename,
275                                                         strerror(errno));
276                         return EXIT_FAILURE;
277                 }
278         }
279
280         if (interactive)
281                 rc = repl();
282         else if (execute)
283                 rc = repl_cmdline(argc, argv);
284         else
285                 rc = repl_noninteractive();
286
287         if (rc != 0)
288                 return EXIT_FAILURE;
289         return EXIT_SUCCESS;
290 }