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