xref: /openbsd/libexec/tradcpp/main.c (revision 274d7c50)
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
101 commandline_macros_init(void)
102 {
103 	array_init(&commandline_macros);
104 }
105 
106 static
107 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
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
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
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
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
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
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
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
268 commandline_files_init(void)
269 {
270 	array_init(&commandline_files);
271 }
272 
273 static
274 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
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
307 commandline_addfile_output(const struct place *p, char *name)
308 {
309 	commandline_addfile(p, name, false);
310 }
311 
312 static
313 void
314 commandline_addfile_nooutput(const struct place *p, char *name)
315 {
316 	commandline_addfile(p, name, true);
317 }
318 
319 static
320 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
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
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
380 commandline_isysroot(const struct place *p, char *dir)
381 {
382 	(void)p;
383 	sysroot = dir;
384 }
385 
386 static
387 void
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
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
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
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
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
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
495 commandline_setprefix(const struct place *p, char *prefix)
496 {
497 	(void)p;
498 	commandline_prefix = prefix;
499 }
500 
501 static
502 void
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
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
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
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
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
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
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
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
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
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
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
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
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
678 commandline_setdependoutput(const struct place *p, char *str)
679 {
680 	(void)p;
681 	mode.depend_file = str;
682 }
683 
684 static
685 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
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
704 commandline_MD(void)
705 {
706 	mode.do_depend = true;
707 	mode.depend_report_system = true;
708 }
709 
710 static
711 void
712 commandline_MMD(void)
713 {
714 	mode.do_depend = true;
715 	mode.depend_report_system = false;
716 }
717 
718 static
719 void
720 commandline_wall(void)
721 {
722 	warns.nestcomment = true;
723 	warns.undef = true;
724 	warns.unused = true;
725 }
726 
727 static
728 void
729 commandline_wnoall(void)
730 {
731 	warns.nestcomment = false;
732 	warns.undef = false;
733 	warns.unused = false;
734 }
735 
736 static
737 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
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
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
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
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
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
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
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
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
1030 die(void)
1031 {
1032 	cleanup();
1033 	exit(EXIT_FAILURE);
1034 }
1035 
1036 void
1037 freestringlater(char *s)
1038 {
1039 	stringarray_add(&freestrings, s, NULL);
1040 }
1041 
1042 ////////////////////////////////////////////////////////////
1043 // main
1044 
1045 int
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