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