1 /*-
2 * Copyright (c) 2010 The NetBSD Foundation, Inc.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to The NetBSD Foundation
6 * by David A. Holland.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include <stdio.h>
31 #include <stdarg.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <errno.h>
36
37 #include "bool.h"
38 #include "version.h"
39 #include "config.h"
40 #include "utils.h"
41 #include "array.h"
42 #include "mode.h"
43 #include "place.h"
44 #include "files.h"
45 #include "directive.h"
46 #include "macro.h"
47
48 struct mode mode = {
49 .werror = false,
50
51 .input_allow_dollars = false,
52 .input_tabstop = 8,
53
54 .do_stdinc = true,
55 .do_stddef = true,
56
57 .do_output = true,
58 .output_linenumbers = true,
59 .output_cheaplinenumbers = false,
60 .output_retain_comments = false,
61 .output_file = NULL,
62
63 .do_depend = false,
64 .depend_report_system = false,
65 .depend_assume_generated = false,
66 .depend_issue_fakerules = false,
67 .depend_quote_target = true,
68 .depend_target = NULL,
69 .depend_file = NULL,
70
71 .do_macrolist = false,
72 .macrolist_include_stddef = false,
73 .macrolist_include_expansions = false,
74
75 .do_trace = false,
76 .trace_namesonly = false,
77 .trace_indented = false,
78 };
79
80 struct warns warns = {
81 .endiflabels = true,
82 .nestcomment = false,
83 .undef = false,
84 .unused = false,
85 };
86
87 ////////////////////////////////////////////////////////////
88 // commandline macros
89
90 struct commandline_macro {
91 struct place where;
92 struct place where2;
93 const char *macro;
94 const char *expansion;
95 };
96
97 static struct array commandline_macros;
98
99 static
100 void
commandline_macros_init(void)101 commandline_macros_init(void)
102 {
103 array_init(&commandline_macros);
104 }
105
106 static
107 void
commandline_macros_cleanup(void)108 commandline_macros_cleanup(void)
109 {
110 unsigned i, num;
111 struct commandline_macro *cm;
112
113 num = array_num(&commandline_macros);
114 for (i=0; i<num; i++) {
115 cm = array_get(&commandline_macros, i);
116 dofree(cm, sizeof(*cm));
117 }
118 array_setsize(&commandline_macros, 0);
119
120 array_cleanup(&commandline_macros);
121 }
122
123 static
124 void
commandline_macro_add(const struct place * p,const char * macro,const struct place * p2,const char * expansion)125 commandline_macro_add(const struct place *p, const char *macro,
126 const struct place *p2, const char *expansion)
127 {
128 struct commandline_macro *cm;
129
130 cm = domalloc(sizeof(*cm));
131 cm->where = *p;
132 cm->where2 = *p2;
133 cm->macro = macro;
134 cm->expansion = expansion;
135
136 array_add(&commandline_macros, cm, NULL);
137 }
138
139 static
140 void
commandline_def(const struct place * p,char * str)141 commandline_def(const struct place *p, char *str)
142 {
143 struct place p2;
144 char *val;
145
146 if (*str == '\0') {
147 complain(NULL, "-D: macro name expected");
148 die();
149 }
150
151 val = strchr(str, '=');
152 if (val != NULL) {
153 *val = '\0';
154 val++;
155 }
156
157 if (val) {
158 p2 = *p;
159 place_addcolumns(&p2, strlen(str));
160 } else {
161 place_setbuiltin(&p2, 1);
162 }
163 commandline_macro_add(p, str, &p2, val ? val : "1");
164 }
165
166 static
167 void
commandline_undef(const struct place * p,char * str)168 commandline_undef(const struct place *p, char *str)
169 {
170 if (*str == '\0') {
171 complain(NULL, "-U: macro name expected");
172 die();
173 }
174 commandline_macro_add(p, str, p, NULL);
175 }
176
177 static
178 void
apply_commandline_macros(void)179 apply_commandline_macros(void)
180 {
181 struct commandline_macro *cm;
182 unsigned i, num;
183
184 num = array_num(&commandline_macros);
185 for (i=0; i<num; i++) {
186 cm = array_get(&commandline_macros, i);
187 if (cm->expansion != NULL) {
188 macro_define_plain(&cm->where, cm->macro,
189 &cm->where2, cm->expansion);
190 } else {
191 macro_undef(cm->macro);
192 }
193 dofree(cm, sizeof(*cm));
194 }
195 array_setsize(&commandline_macros, 0);
196 }
197
198 static
199 void
apply_magic_macro(unsigned num,const char * name)200 apply_magic_macro(unsigned num, const char *name)
201 {
202 struct place p;
203
204 place_setbuiltin(&p, num);
205 macro_define_magic(&p, name);
206 }
207
208 static
209 void
apply_builtin_macro(unsigned num,const char * name,const char * val)210 apply_builtin_macro(unsigned num, const char *name, const char *val)
211 {
212 struct place p;
213
214 place_setbuiltin(&p, num);
215 macro_define_plain(&p, name, &p, val);
216 }
217
218 static
219 void
apply_builtin_macros(void)220 apply_builtin_macros(void)
221 {
222 unsigned n = 1;
223
224 apply_magic_macro(n++, "__FILE__");
225 apply_magic_macro(n++, "__LINE__");
226
227 #ifdef CONFIG_OS
228 apply_builtin_macro(n++, CONFIG_OS, "1");
229 #endif
230 #ifdef CONFIG_OS_2
231 apply_builtin_macro(n++, CONFIG_OS_2, "1");
232 #endif
233
234 #ifdef CONFIG_CPU
235 apply_builtin_macro(n++, CONFIG_CPU, "1");
236 #endif
237 #ifdef CONFIG_CPU_2
238 apply_builtin_macro(n++, CONFIG_CPU_2, "1");
239 #endif
240
241 #ifdef CONFIG_SIZE
242 apply_builtin_macro(n++, CONFIG_SIZE, "1");
243 #endif
244 #ifdef CONFIG_BINFMT
245 apply_builtin_macro(n++, CONFIG_BINFMT, "1");
246 #endif
247
248 #ifdef CONFIG_COMPILER
249 apply_builtin_macro(n++, CONFIG_COMPILER, VERSION_MAJOR);
250 apply_builtin_macro(n++, CONFIG_COMPILER_MINOR, VERSION_MINOR);
251 apply_builtin_macro(n++, "__VERSION__", VERSION_LONG);
252 #endif
253 }
254
255 ////////////////////////////////////////////////////////////
256 // extra included files
257
258 struct commandline_file {
259 struct place where;
260 char *name;
261 bool suppress_output;
262 };
263
264 static struct array commandline_files;
265
266 static
267 void
commandline_files_init(void)268 commandline_files_init(void)
269 {
270 array_init(&commandline_files);
271 }
272
273 static
274 void
commandline_files_cleanup(void)275 commandline_files_cleanup(void)
276 {
277 unsigned i, num;
278 struct commandline_file *cf;
279
280 num = array_num(&commandline_files);
281 for (i=0; i<num; i++) {
282 cf = array_get(&commandline_files, i);
283 if (cf != NULL) {
284 dofree(cf, sizeof(*cf));
285 }
286 }
287 array_setsize(&commandline_files, 0);
288
289 array_cleanup(&commandline_files);
290 }
291
292 static
293 void
commandline_addfile(const struct place * p,char * name,bool suppress_output)294 commandline_addfile(const struct place *p, char *name, bool suppress_output)
295 {
296 struct commandline_file *cf;
297
298 cf = domalloc(sizeof(*cf));
299 cf->where = *p;
300 cf->name = name;
301 cf->suppress_output = suppress_output;
302 array_add(&commandline_files, cf, NULL);
303 }
304
305 static
306 void
commandline_addfile_output(const struct place * p,char * name)307 commandline_addfile_output(const struct place *p, char *name)
308 {
309 commandline_addfile(p, name, false);
310 }
311
312 static
313 void
commandline_addfile_nooutput(const struct place * p,char * name)314 commandline_addfile_nooutput(const struct place *p, char *name)
315 {
316 commandline_addfile(p, name, true);
317 }
318
319 static
320 void
read_commandline_files(void)321 read_commandline_files(void)
322 {
323 struct commandline_file *cf;
324 unsigned i, num;
325 bool save = false;
326
327 num = array_num(&commandline_files);
328 for (i=0; i<num; i++) {
329 cf = array_get(&commandline_files, i);
330 array_set(&commandline_files, i, NULL);
331 if (cf->suppress_output) {
332 save = mode.do_output;
333 mode.do_output = false;
334 file_readquote(&cf->where, cf->name);
335 mode.do_output = save;
336 } else {
337 file_readquote(&cf->where, cf->name);
338 }
339 dofree(cf, sizeof(*cf));
340 }
341 array_setsize(&commandline_files, 0);
342 }
343
344 ////////////////////////////////////////////////////////////
345 // include path accumulation
346
347 static struct stringarray incpath_quote;
348 static struct stringarray incpath_user;
349 static struct stringarray incpath_system;
350 static struct stringarray incpath_late;
351 static const char *sysroot;
352
353 static
354 void
incpath_init(void)355 incpath_init(void)
356 {
357 stringarray_init(&incpath_quote);
358 stringarray_init(&incpath_user);
359 stringarray_init(&incpath_system);
360 stringarray_init(&incpath_late);
361 }
362
363 static
364 void
incpath_cleanup(void)365 incpath_cleanup(void)
366 {
367 stringarray_setsize(&incpath_quote, 0);
368 stringarray_setsize(&incpath_user, 0);
369 stringarray_setsize(&incpath_system, 0);
370 stringarray_setsize(&incpath_late, 0);
371
372 stringarray_cleanup(&incpath_quote);
373 stringarray_cleanup(&incpath_user);
374 stringarray_cleanup(&incpath_system);
375 stringarray_cleanup(&incpath_late);
376 }
377
378 static
379 void
commandline_isysroot(const struct place * p,char * dir)380 commandline_isysroot(const struct place *p, char *dir)
381 {
382 (void)p;
383 sysroot = dir;
384 }
385
386 static
387 void
commandline_addincpath(struct stringarray * arr,char * s)388 commandline_addincpath(struct stringarray *arr, char *s)
389 {
390 if (*s == '\0') {
391 complain(NULL, "Empty include directory");
392 die();
393 }
394 stringarray_add(arr, s, NULL);
395 }
396
397 static
398 void
commandline_addincpath_quote(const struct place * p,char * dir)399 commandline_addincpath_quote(const struct place *p, char *dir)
400 {
401 (void)p;
402 commandline_addincpath(&incpath_quote, dir);
403 }
404
405 static
406 void
commandline_addincpath_user(const struct place * p,char * dir)407 commandline_addincpath_user(const struct place *p, char *dir)
408 {
409 (void)p;
410 commandline_addincpath(&incpath_user, dir);
411 }
412
413 static
414 void
commandline_addincpath_system(const struct place * p,char * dir)415 commandline_addincpath_system(const struct place *p, char *dir)
416 {
417 (void)p;
418 commandline_addincpath(&incpath_system, dir);
419 }
420
421 static
422 void
commandline_addincpath_late(const struct place * p,char * dir)423 commandline_addincpath_late(const struct place *p, char *dir)
424 {
425 (void)p;
426 commandline_addincpath(&incpath_late, dir);
427 }
428
429 static
430 void
loadincludepath(void)431 loadincludepath(void)
432 {
433 unsigned i, num;
434 const char *dir;
435 char *t;
436
437 num = stringarray_num(&incpath_quote);
438 for (i=0; i<num; i++) {
439 dir = stringarray_get(&incpath_quote, i);
440 files_addquotepath(dir, false);
441 }
442 files_addquotepath(NULL, false);
443
444 num = stringarray_num(&incpath_user);
445 for (i=0; i<num; i++) {
446 dir = stringarray_get(&incpath_user, i);
447 files_addquotepath(dir, false);
448 files_addbracketpath(dir, false);
449 }
450
451 if (mode.do_stdinc) {
452 if (sysroot != NULL) {
453 t = dostrdup3(sysroot, "/", CONFIG_LOCALINCLUDE);
454 freestringlater(t);
455 dir = t;
456 } else {
457 dir = CONFIG_LOCALINCLUDE;
458 }
459 files_addquotepath(dir, true);
460 files_addbracketpath(dir, true);
461
462 if (sysroot != NULL) {
463 t = dostrdup3(sysroot, "/", CONFIG_SYSTEMINCLUDE);
464 freestringlater(t);
465 dir = t;
466 } else {
467 dir = CONFIG_SYSTEMINCLUDE;
468 }
469 files_addquotepath(dir, true);
470 files_addbracketpath(dir, true);
471 }
472
473 num = stringarray_num(&incpath_system);
474 for (i=0; i<num; i++) {
475 dir = stringarray_get(&incpath_system, i);
476 files_addquotepath(dir, true);
477 files_addbracketpath(dir, true);
478 }
479
480 num = stringarray_num(&incpath_late);
481 for (i=0; i<num; i++) {
482 dir = stringarray_get(&incpath_late, i);
483 files_addquotepath(dir, false);
484 files_addbracketpath(dir, false);
485 }
486 }
487
488 ////////////////////////////////////////////////////////////
489 // silly commandline stuff
490
491 static const char *commandline_prefix;
492
493 static
494 void
commandline_setprefix(const struct place * p,char * prefix)495 commandline_setprefix(const struct place *p, char *prefix)
496 {
497 (void)p;
498 commandline_prefix = prefix;
499 }
500
501 static
502 void
commandline_addincpath_user_withprefix(const struct place * p,char * dir)503 commandline_addincpath_user_withprefix(const struct place *p, char *dir)
504 {
505 char *s;
506
507 if (commandline_prefix == NULL) {
508 complain(NULL, "-iprefix needed");
509 die();
510 }
511 s = dostrdup3(commandline_prefix, "/", dir);
512 freestringlater(s);
513 commandline_addincpath_user(p, s);
514 }
515
516 static
517 void
commandline_addincpath_late_withprefix(const struct place * p,char * dir)518 commandline_addincpath_late_withprefix(const struct place *p, char *dir)
519 {
520 char *s;
521
522 if (commandline_prefix == NULL) {
523 complain(NULL, "-iprefix needed");
524 die();
525 }
526 s = dostrdup3(commandline_prefix, "/", dir);
527 freestringlater(s);
528 commandline_addincpath_late(p, s);
529 }
530
531 static
532 void
commandline_setstd(const struct place * p,char * std)533 commandline_setstd(const struct place *p, char *std)
534 {
535 (void)p;
536
537 if (!strcmp(std, "krc")) {
538 return;
539 }
540 complain(NULL, "Standard %s not supported by this preprocessor", std);
541 die();
542 }
543
544 static
545 void
commandline_setlang(const struct place * p,char * lang)546 commandline_setlang(const struct place *p, char *lang)
547 {
548 (void)p;
549
550 if (!strcmp(lang, "c") || !strcmp(lang, "assembler-with-cpp")) {
551 return;
552 }
553 complain(NULL, "Language %s not supported by this preprocessor", lang);
554 die();
555 }
556
557 ////////////////////////////////////////////////////////////
558 // complex modes
559
560 DEAD static
561 void
commandline_iremap(const struct place * p,char * str)562 commandline_iremap(const struct place *p, char *str)
563 {
564 (void)p;
565 /* XXX */
566 (void)str;
567 complain(NULL, "-iremap not supported");
568 die();
569 }
570
571 static
572 void
commandline_tabstop(const struct place * p,char * s)573 commandline_tabstop(const struct place *p, char *s)
574 {
575 char *t;
576 unsigned long val;
577
578 (void)p;
579
580 t = strchr(s, '=');
581 if (t == NULL) {
582 /* should not happen */
583 complain(NULL, "Invalid tabstop");
584 die();
585 }
586 t++;
587 errno = 0;
588 val = strtoul(t, &t, 10);
589 if (errno || *t != '\0') {
590 complain(NULL, "Invalid tabstop");
591 die();
592 }
593 if (val > 64) {
594 complain(NULL, "Preposterously large tabstop");
595 die();
596 }
597 mode.input_tabstop = val;
598 }
599
600 /*
601 * macrolist
602 */
603
604 static
605 void
commandline_dD(void)606 commandline_dD(void)
607 {
608 mode.do_macrolist = true;
609 mode.macrolist_include_stddef = false;
610 mode.macrolist_include_expansions = true;
611 }
612
613 static
614 void
commandline_dM(void)615 commandline_dM(void)
616 {
617 mode.do_macrolist = true;
618 mode.macrolist_include_stddef = true;
619 mode.macrolist_include_expansions = true;
620 mode.do_output = false;
621 }
622
623 static
624 void
commandline_dN(void)625 commandline_dN(void)
626 {
627 mode.do_macrolist = true;
628 mode.macrolist_include_stddef = false;
629 mode.macrolist_include_expansions = false;
630 }
631
632 /*
633 * include trace
634 */
635
636 static
637 void
commandline_dI(void)638 commandline_dI(void)
639 {
640 mode.do_trace = true;
641 mode.trace_namesonly = false;
642 mode.trace_indented = false;
643 }
644
645 static
646 void
commandline_H(void)647 commandline_H(void)
648 {
649 mode.do_trace = true;
650 mode.trace_namesonly = true;
651 mode.trace_indented = true;
652 }
653
654 /*
655 * depend
656 */
657
658 static
659 void
commandline_setdependtarget(const struct place * p,char * str)660 commandline_setdependtarget(const struct place *p, char *str)
661 {
662 (void)p;
663 mode.depend_target = str;
664 mode.depend_quote_target = false;
665 }
666
667 static
668 void
commandline_setdependtarget_quoted(const struct place * p,char * str)669 commandline_setdependtarget_quoted(const struct place *p, char *str)
670 {
671 (void)p;
672 mode.depend_target = str;
673 mode.depend_quote_target = true;
674 }
675
676 static
677 void
commandline_setdependoutput(const struct place * p,char * str)678 commandline_setdependoutput(const struct place *p, char *str)
679 {
680 (void)p;
681 mode.depend_file = str;
682 }
683
684 static
685 void
commandline_M(void)686 commandline_M(void)
687 {
688 mode.do_depend = true;
689 mode.depend_report_system = true;
690 mode.do_output = false;
691 }
692
693 static
694 void
commandline_MM(void)695 commandline_MM(void)
696 {
697 mode.do_depend = true;
698 mode.depend_report_system = false;
699 mode.do_output = false;
700 }
701
702 static
703 void
commandline_MD(void)704 commandline_MD(void)
705 {
706 mode.do_depend = true;
707 mode.depend_report_system = true;
708 }
709
710 static
711 void
commandline_MMD(void)712 commandline_MMD(void)
713 {
714 mode.do_depend = true;
715 mode.depend_report_system = false;
716 }
717
718 static
719 void
commandline_wall(void)720 commandline_wall(void)
721 {
722 warns.nestcomment = true;
723 warns.undef = true;
724 warns.unused = true;
725 }
726
727 static
728 void
commandline_wnoall(void)729 commandline_wnoall(void)
730 {
731 warns.nestcomment = false;
732 warns.undef = false;
733 warns.unused = false;
734 }
735
736 static
737 void
commandline_wnone(void)738 commandline_wnone(void)
739 {
740 warns.nestcomment = false;
741 warns.endiflabels = false;
742 warns.undef = false;
743 warns.unused = false;
744 }
745
746 ////////////////////////////////////////////////////////////
747 // options
748
749 struct ignore_option {
750 const char *string;
751 };
752
753 struct flag_option {
754 const char *string;
755 bool *flag;
756 bool setto;
757 };
758
759 struct act_option {
760 const char *string;
761 void (*func)(void);
762 };
763
764 struct prefix_option {
765 const char *string;
766 void (*func)(const struct place *, char *);
767 };
768
769 struct arg_option {
770 const char *string;
771 void (*func)(const struct place *, char *);
772 };
773
774 static const struct ignore_option ignore_options[] = {
775 { "m32" },
776 { "traditional" },
777 };
778 static const unsigned num_ignore_options = HOWMANY(ignore_options);
779
780 static const struct flag_option flag_options[] = {
781 { "C", &mode.output_retain_comments, true },
782 { "CC", &mode.output_retain_comments, true },
783 { "MG", &mode.depend_assume_generated, true },
784 { "MP", &mode.depend_issue_fakerules, true },
785 { "P", &mode.output_linenumbers, false },
786 { "Wcomment", &warns.nestcomment, true },
787 { "Wendif-labels", &warns.endiflabels, true },
788 { "Werror", &mode.werror, true },
789 { "Wno-comment", &warns.nestcomment, false },
790 { "Wno-endif-labels", &warns.endiflabels, false },
791 { "Wno-error", &mode.werror, false },
792 { "Wno-undef", &warns.undef, false },
793 { "Wno-unused-macros", &warns.unused, false },
794 { "Wundef", &warns.undef, true },
795 { "Wunused-macros", &warns.unused, true },
796 { "fdollars-in-identifiers", &mode.input_allow_dollars, true },
797 { "fno-dollars-in-identifiers", &mode.input_allow_dollars, false },
798 { "nostdinc", &mode.do_stdinc, false },
799 { "p", &mode.output_cheaplinenumbers, true },
800 { "undef", &mode.do_stddef, false },
801 };
802 static const unsigned num_flag_options = HOWMANY(flag_options);
803
804 static const struct act_option act_options[] = {
805 { "H", commandline_H },
806 { "M", commandline_M },
807 { "MD", commandline_MD },
808 { "MM", commandline_MM },
809 { "MMD", commandline_MMD },
810 { "Wall", commandline_wall },
811 { "Wno-all", commandline_wnoall },
812 { "dD", commandline_dD },
813 { "dI", commandline_dI },
814 { "dM", commandline_dM },
815 { "dN", commandline_dN },
816 { "w", commandline_wnone },
817 };
818 static const unsigned num_act_options = HOWMANY(act_options);
819
820 static const struct prefix_option prefix_options[] = {
821 { "D", commandline_def },
822 { "I", commandline_addincpath_user },
823 { "U", commandline_undef },
824 { "ftabstop=", commandline_tabstop },
825 { "std=", commandline_setstd },
826 };
827 static const unsigned num_prefix_options = HOWMANY(prefix_options);
828
829 static const struct arg_option arg_options[] = {
830 { "MF", commandline_setdependoutput },
831 { "MQ", commandline_setdependtarget_quoted },
832 { "MT", commandline_setdependtarget },
833 { "debuglog", debuglog_open },
834 { "idirafter", commandline_addincpath_late },
835 { "imacros", commandline_addfile_nooutput },
836 { "include", commandline_addfile_output },
837 { "iprefix", commandline_setprefix },
838 { "iquote", commandline_addincpath_quote },
839 { "iremap", commandline_iremap },
840 { "isysroot", commandline_isysroot },
841 { "isystem", commandline_addincpath_system },
842 { "iwithprefix", commandline_addincpath_late_withprefix },
843 { "iwithprefixbefore", commandline_addincpath_user_withprefix },
844 { "x", commandline_setlang },
845 };
846 static const unsigned num_arg_options = HOWMANY(arg_options);
847
848 static
849 bool
check_ignore_option(const char * opt)850 check_ignore_option(const char *opt)
851 {
852 unsigned i;
853 int r;
854
855 for (i=0; i<num_ignore_options; i++) {
856 r = strcmp(opt, ignore_options[i].string);
857 if (r == 0) {
858 return true;
859 }
860 if (r < 0) {
861 break;
862 }
863 }
864 return false;
865 }
866
867 static
868 bool
check_flag_option(const char * opt)869 check_flag_option(const char *opt)
870 {
871 unsigned i;
872 int r;
873
874 for (i=0; i<num_flag_options; i++) {
875 r = strcmp(opt, flag_options[i].string);
876 if (r == 0) {
877 *flag_options[i].flag = flag_options[i].setto;
878 return true;
879 }
880 if (r < 0) {
881 break;
882 }
883 }
884 return false;
885 }
886
887 static
888 bool
check_act_option(const char * opt)889 check_act_option(const char *opt)
890 {
891 unsigned i;
892 int r;
893
894 for (i=0; i<num_act_options; i++) {
895 r = strcmp(opt, act_options[i].string);
896 if (r == 0) {
897 act_options[i].func();
898 return true;
899 }
900 if (r < 0) {
901 break;
902 }
903 }
904 return false;
905 }
906
907 static
908 bool
check_prefix_option(const struct place * p,char * opt)909 check_prefix_option(const struct place *p, char *opt)
910 {
911 unsigned i, len;
912 int r;
913
914 for (i=0; i<num_prefix_options; i++) {
915 len = strlen(prefix_options[i].string);
916 r = strncmp(opt, prefix_options[i].string, len);
917 if (r == 0) {
918 prefix_options[i].func(p, opt + len);
919 return true;
920 }
921 if (r < 0) {
922 break;
923 }
924 }
925 return false;
926 }
927
928 static
929 bool
check_arg_option(const char * opt,const struct place * argplace,char * arg)930 check_arg_option(const char *opt, const struct place *argplace, char *arg)
931 {
932 unsigned i;
933 int r;
934
935 for (i=0; i<num_arg_options; i++) {
936 r = strcmp(opt, arg_options[i].string);
937 if (r == 0) {
938 if (arg == NULL) {
939 complain(NULL,
940 "Option -%s requires an argument",
941 opt);
942 die();
943 }
944 arg_options[i].func(argplace, arg);
945 return true;
946 }
947 if (r < 0) {
948 break;
949 }
950 }
951 return false;
952 }
953
954 DEAD PF(2, 3) static
955 void
usage(const char * progname,const char * fmt,...)956 usage(const char *progname, const char *fmt, ...)
957 {
958 va_list ap;
959
960 fprintf(stderr, "%s: ", progname);
961 va_start(ap, fmt);
962 vfprintf(stderr, fmt, ap);
963 va_end(ap);
964 fprintf(stderr, "\n");
965
966 fprintf(stderr, "usage: %s [options] [infile [outfile]]\n", progname);
967 fprintf(stderr, "Common options:\n");
968 fprintf(stderr, " -C Retain comments\n");
969 fprintf(stderr, " -Dmacro[=def] Predefine macro\n");
970 fprintf(stderr, " -Idir Add to include path\n");
971 fprintf(stderr, " -M Issue depend info\n");
972 fprintf(stderr, " -MD Issue depend info and output\n");
973 fprintf(stderr, " -MM -M w/o system headers\n");
974 fprintf(stderr, " -MMD -MD w/o system headers\n");
975 fprintf(stderr, " -nostdinc Drop default include path\n");
976 fprintf(stderr, " -Umacro Undefine macro\n");
977 fprintf(stderr, " -undef Undefine everything\n");
978 fprintf(stderr, " -Wall Enable all warnings\n");
979 fprintf(stderr, " -Werror Make warnings into errors\n");
980 fprintf(stderr, " -w Disable all warnings\n");
981 die();
982 }
983
984 ////////////////////////////////////////////////////////////
985 // exit and cleanup
986
987 static struct stringarray freestrings;
988
989 static
990 void
init(void)991 init(void)
992 {
993 stringarray_init(&freestrings);
994
995 incpath_init();
996 commandline_macros_init();
997 commandline_files_init();
998
999 place_init();
1000 files_init();
1001 directive_init();
1002 macros_init();
1003 }
1004
1005 static
1006 void
cleanup(void)1007 cleanup(void)
1008 {
1009 unsigned i, num;
1010
1011 macros_cleanup();
1012 directive_cleanup();
1013 files_cleanup();
1014 place_cleanup();
1015
1016 commandline_files_cleanup();
1017 commandline_macros_cleanup();
1018 incpath_cleanup();
1019 debuglog_close();
1020
1021 num = stringarray_num(&freestrings);
1022 for (i=0; i<num; i++) {
1023 dostrfree(stringarray_get(&freestrings, i));
1024 }
1025 stringarray_setsize(&freestrings, 0);
1026 stringarray_cleanup(&freestrings);
1027 }
1028
1029 void
die(void)1030 die(void)
1031 {
1032 cleanup();
1033 exit(EXIT_FAILURE);
1034 }
1035
1036 void
freestringlater(char * s)1037 freestringlater(char *s)
1038 {
1039 stringarray_add(&freestrings, s, NULL);
1040 }
1041
1042 ////////////////////////////////////////////////////////////
1043 // main
1044
1045 int
main(int argc,char * argv[])1046 main(int argc, char *argv[])
1047 {
1048 const char *progname;
1049 const char *inputfile = NULL;
1050 const char *outputfile = NULL;
1051 struct place cmdplace;
1052 int i;
1053
1054 progname = strrchr(argv[0], '/');
1055 progname = progname == NULL ? argv[0] : progname + 1;
1056 complain_init(progname);
1057
1058 if (pledge("stdio rpath wpath cpath", NULL) == -1) {
1059 fprintf(stderr, "%s: pledge: %s", progname, strerror(errno));
1060 exit(1);
1061 }
1062
1063 init();
1064
1065 for (i=1; i<argc; i++) {
1066 if (argv[i][0] != '-' || argv[i][1] == 0) {
1067 break;
1068 }
1069 place_setcommandline(&cmdplace, i, 1);
1070 if (check_ignore_option(argv[i]+1)) {
1071 continue;
1072 }
1073 if (check_flag_option(argv[i]+1)) {
1074 continue;
1075 }
1076 if (check_act_option(argv[i]+1)) {
1077 continue;
1078 }
1079 if (check_prefix_option(&cmdplace, argv[i]+1)) {
1080 continue;
1081 }
1082 place_setcommandline(&cmdplace, i+1, 1);
1083 if (check_arg_option(argv[i]+1, &cmdplace, argv[i+1])) {
1084 i++;
1085 continue;
1086 }
1087 usage(progname, "Invalid option %s", argv[i]);
1088 }
1089 if (i < argc) {
1090 inputfile = argv[i++];
1091 if (!strcmp(inputfile, "-")) {
1092 inputfile = NULL;
1093 }
1094 }
1095 if (i < argc) {
1096 outputfile = argv[i++];
1097 if (!strcmp(outputfile, "-")) {
1098 outputfile = NULL;
1099 }
1100 }
1101 if (i < argc) {
1102 usage(progname, "Extra non-option argument %s", argv[i]);
1103 }
1104
1105 mode.output_file = outputfile;
1106
1107 loadincludepath();
1108 apply_builtin_macros();
1109 apply_commandline_macros();
1110 read_commandline_files();
1111 place_setnowhere(&cmdplace);
1112 file_readabsolute(&cmdplace, inputfile);
1113
1114 cleanup();
1115 if (complain_failed()) {
1116 return EXIT_FAILURE;
1117 }
1118 return EXIT_SUCCESS;
1119 }
1120