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