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