main.c revision 1.3
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
48struct 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
79struct warns warns = {
80	.endiflabels = true,
81	.nestcomment = false,
82	.undef = false,
83	.unused = false,
84};
85
86////////////////////////////////////////////////////////////
87// commandline macros
88
89struct commandline_macro {
90	struct place where;
91	struct place where2;
92	const char *macro;
93	const char *expansion;
94};
95
96static struct array commandline_macros;
97
98static
99void
100commandline_macros_init(void)
101{
102	array_init(&commandline_macros);
103}
104
105static
106void
107commandline_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
122static
123void
124commandline_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
138static
139void
140commandline_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
165static
166void
167commandline_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
176static
177void
178apply_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
197static
198void
199apply_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
207static
208void
209apply_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
244struct commandline_file {
245	struct place where;
246	char *name;
247	bool suppress_output;
248};
249
250static struct array commandline_files;
251
252static
253void
254commandline_files_init(void)
255{
256	array_init(&commandline_files);
257}
258
259static
260void
261commandline_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
278static
279void
280commandline_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
291static
292void
293commandline_addfile_output(const struct place *p, char *name)
294{
295	commandline_addfile(p, name, false);
296}
297
298static
299void
300commandline_addfile_nooutput(const struct place *p, char *name)
301{
302	commandline_addfile(p, name, true);
303}
304
305static
306void
307read_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
333static struct stringarray incpath_quote;
334static struct stringarray incpath_user;
335static struct stringarray incpath_system;
336static struct stringarray incpath_late;
337static const char *sysroot;
338
339static
340void
341incpath_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
349static
350void
351incpath_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
364static
365void
366commandline_isysroot(const struct place *p, char *dir)
367{
368	(void)p;
369	sysroot = dir;
370}
371
372static
373void
374commandline_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
383static
384void
385commandline_addincpath_quote(const struct place *p, char *dir)
386{
387	(void)p;
388	commandline_addincpath(&incpath_quote, dir);
389}
390
391static
392void
393commandline_addincpath_user(const struct place *p, char *dir)
394{
395	(void)p;
396	commandline_addincpath(&incpath_user, dir);
397}
398
399static
400void
401commandline_addincpath_system(const struct place *p, char *dir)
402{
403	(void)p;
404	commandline_addincpath(&incpath_system, dir);
405}
406
407static
408void
409commandline_addincpath_late(const struct place *p, char *dir)
410{
411	(void)p;
412	commandline_addincpath(&incpath_late, dir);
413}
414
415static
416void
417loadincludepath(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
477static const char *commandline_prefix;
478
479static
480void
481commandline_setprefix(const struct place *p, char *prefix)
482{
483	(void)p;
484	commandline_prefix = prefix;
485}
486
487static
488void
489commandline_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
502static
503void
504commandline_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
517static
518void
519commandline_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
530static
531void
532commandline_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
546DEAD static
547void
548commandline_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
557static
558void
559commandline_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
590static
591void
592commandline_dD(void)
593{
594	mode.do_macrolist = true;
595	mode.macrolist_include_stddef = false;
596	mode.macrolist_include_expansions = true;
597}
598
599static
600void
601commandline_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
609static
610void
611commandline_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
622static
623void
624commandline_dI(void)
625{
626	mode.do_trace = true;
627	mode.trace_namesonly = false;
628	mode.trace_indented = false;
629}
630
631static
632void
633commandline_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
644static
645void
646commandline_setdependtarget(const struct place *p, char *str)
647{
648	(void)p;
649	mode.depend_target = str;
650	mode.depend_quote_target = false;
651}
652
653static
654void
655commandline_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
662static
663void
664commandline_setdependoutput(const struct place *p, char *str)
665{
666	(void)p;
667	mode.depend_file = str;
668}
669
670static
671void
672commandline_M(void)
673{
674	mode.do_depend = true;
675	mode.depend_report_system = true;
676	mode.do_output = false;
677}
678
679static
680void
681commandline_MM(void)
682{
683	mode.do_depend = true;
684	mode.depend_report_system = false;
685	mode.do_output = false;
686}
687
688static
689void
690commandline_MD(void)
691{
692	mode.do_depend = true;
693	mode.depend_report_system = true;
694}
695
696static
697void
698commandline_MMD(void)
699{
700	mode.do_depend = true;
701	mode.depend_report_system = false;
702}
703
704static
705void
706commandline_wall(void)
707{
708	warns.nestcomment = true;
709	warns.undef = true;
710	warns.unused = true;
711}
712
713static
714void
715commandline_wnoall(void)
716{
717	warns.nestcomment = false;
718	warns.undef = false;
719	warns.unused = false;
720}
721
722static
723void
724commandline_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
735struct ignore_option {
736	const char *string;
737};
738
739struct flag_option {
740	const char *string;
741	bool *flag;
742	bool setto;
743};
744
745struct act_option {
746	const char *string;
747	void (*func)(void);
748};
749
750struct prefix_option {
751	const char *string;
752	void (*func)(const struct place *, char *);
753};
754
755struct arg_option {
756	const char *string;
757	void (*func)(const struct place *, char *);
758};
759
760static const struct ignore_option ignore_options[] = {
761	{ "m32" },
762	{ "traditional" },
763};
764static const unsigned num_ignore_options = HOWMANY(ignore_options);
765
766static 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};
787static const unsigned num_flag_options = HOWMANY(flag_options);
788
789static 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};
803static const unsigned num_act_options = HOWMANY(act_options);
804
805static 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};
812static const unsigned num_prefix_options = HOWMANY(prefix_options);
813
814static 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};
830static const unsigned num_arg_options = HOWMANY(arg_options);
831
832static
833bool
834check_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
851static
852bool
853check_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
871static
872bool
873check_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
891static
892bool
893check_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
912static
913bool
914check_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
938DEAD static
939void
940usage(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
971static struct stringarray freestrings;
972
973static
974void
975init(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
989static
990void
991cleanup(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
1012void
1013die(void)
1014{
1015	cleanup();
1016	exit(EXIT_FAILURE);
1017}
1018
1019void
1020freestringlater(char *s)
1021{
1022	stringarray_add(&freestrings, s, NULL);
1023}
1024
1025////////////////////////////////////////////////////////////
1026// main
1027
1028int
1029main(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