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