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