Home | History | Annotate | Line # | Download | only in dist
      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 
     47 struct 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 
     79 struct warns warns = {
     80 	.endiflabels = true,
     81 	.nestcomment = false,
     82 	.undef = false,
     83 	.unused = false,
     84 };
     85 
     86 ////////////////////////////////////////////////////////////
     87 // commandline macros
     88 
     89 struct commandline_macro {
     90 	struct place where;
     91 	struct place where2;
     92 	const char *macro;
     93 	const char *expansion;
     94 };
     95 
     96 static struct array commandline_macros;
     97 
     98 static
     99 void
    100 commandline_macros_init(void)
    101 {
    102 	array_init(&commandline_macros);
    103 }
    104 
    105 static
    106 void
    107 commandline_macros_cleanup(void)
    108 {
    109 	unsigned i, num;
    110 	struct commandline_macro *cm;
    111 
    112 	num = array_num(&commandline_macros);
    113 	for (i=0; i<num; i++) {
    114 		cm = array_get(&commandline_macros, i);
    115 		dofree(cm, sizeof(*cm));
    116 	}
    117 	array_setsize(&commandline_macros, 0);
    118 
    119 	array_cleanup(&commandline_macros);
    120 }
    121 
    122 static
    123 void
    124 commandline_macro_add(const struct place *p, const char *macro,
    125 		      const struct place *p2, const char *expansion)
    126 {
    127 	struct commandline_macro *cm;
    128 
    129 	cm = domalloc(sizeof(*cm));
    130 	cm->where = *p;
    131 	cm->where2 = *p2;
    132 	cm->macro = macro;
    133 	cm->expansion = expansion;
    134 
    135 	array_add(&commandline_macros, cm, NULL);
    136 }
    137 
    138 static
    139 void
    140 commandline_def(const struct place *p, char *str)
    141 {
    142 	struct place p2;
    143 	char *val;
    144 
    145 	if (*str == '\0') {
    146 		complain(NULL, "-D: macro name expected");
    147 		die();
    148 	}
    149 
    150 	val = strchr(str, '=');
    151 	if (val != NULL) {
    152 		*val = '\0';
    153 		val++;
    154 	}
    155 
    156 	if (val) {
    157 		p2 = *p;
    158 		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 
    165 static
    166 void
    167 commandline_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 
    176 static
    177 void
    178 apply_commandline_macros(void)
    179 {
    180 	struct commandline_macro *cm;
    181 	unsigned i, num;
    182 
    183 	num = array_num(&commandline_macros);
    184 	for (i=0; i<num; i++) {
    185 		cm = array_get(&commandline_macros, i);
    186 		if (cm->expansion != NULL) {
    187 			macro_define_plain(&cm->where, cm->macro,
    188 					   &cm->where2, cm->expansion);
    189 		} else {
    190 			macro_undef(cm->macro);
    191 		}
    192 		dofree(cm, sizeof(*cm));
    193 	}
    194 	array_setsize(&commandline_macros, 0);
    195 }
    196 
    197 static
    198 void
    199 apply_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 
    207 static
    208 void
    209 apply_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 
    217 static
    218 void
    219 apply_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 
    257 struct commandline_file {
    258 	struct place where;
    259 	char *name;
    260 	bool suppress_output;
    261 };
    262 
    263 static struct array commandline_files;
    264 
    265 static
    266 void
    267 commandline_files_init(void)
    268 {
    269 	array_init(&commandline_files);
    270 }
    271 
    272 static
    273 void
    274 commandline_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 
    291 static
    292 void
    293 commandline_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 
    304 static
    305 void
    306 commandline_addfile_output(const struct place *p, char *name)
    307 {
    308 	commandline_addfile(p, name, false);
    309 }
    310 
    311 static
    312 void
    313 commandline_addfile_nooutput(const struct place *p, char *name)
    314 {
    315 	commandline_addfile(p, name, true);
    316 }
    317 
    318 static
    319 void
    320 read_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 
    346 static struct stringarray incpath_quote;
    347 static struct stringarray incpath_user;
    348 static struct stringarray incpath_system;
    349 static struct stringarray incpath_late;
    350 static const char *sysroot;
    351 
    352 static
    353 void
    354 incpath_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 
    362 static
    363 void
    364 incpath_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 
    377 static
    378 void
    379 commandline_isysroot(const struct place *p, char *dir)
    380 {
    381 	(void)p;
    382 	sysroot = dir;
    383 }
    384 
    385 static
    386 void
    387 commandline_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 
    396 static
    397 void
    398 commandline_addincpath_quote(const struct place *p, char *dir)
    399 {
    400 	(void)p;
    401 	commandline_addincpath(&incpath_quote, dir);
    402 }
    403 
    404 static
    405 void
    406 commandline_addincpath_user(const struct place *p, char *dir)
    407 {
    408 	(void)p;
    409 	commandline_addincpath(&incpath_user, dir);
    410 }
    411 
    412 static
    413 void
    414 commandline_addincpath_system(const struct place *p, char *dir)
    415 {
    416 	(void)p;
    417 	commandline_addincpath(&incpath_system, dir);
    418 }
    419 
    420 static
    421 void
    422 commandline_addincpath_late(const struct place *p, char *dir)
    423 {
    424 	(void)p;
    425 	commandline_addincpath(&incpath_late, dir);
    426 }
    427 
    428 static
    429 void
    430 loadincludepath(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 
    490 static const char *commandline_prefix;
    491 
    492 static
    493 void
    494 commandline_setprefix(const struct place *p, char *prefix)
    495 {
    496 	(void)p;
    497 	commandline_prefix = prefix;
    498 }
    499 
    500 static
    501 void
    502 commandline_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 
    515 static
    516 void
    517 commandline_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 
    530 static
    531 void
    532 commandline_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 
    543 static
    544 void
    545 commandline_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 
    559 DEAD static
    560 void
    561 commandline_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 
    570 static
    571 void
    572 commandline_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 
    603 static
    604 void
    605 commandline_dD(void)
    606 {
    607 	mode.do_macrolist = true;
    608 	mode.macrolist_include_stddef = false;
    609 	mode.macrolist_include_expansions = true;
    610 }
    611 
    612 static
    613 void
    614 commandline_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 
    622 static
    623 void
    624 commandline_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 
    635 static
    636 void
    637 commandline_dI(void)
    638 {
    639 	mode.do_trace = true;
    640 	mode.trace_namesonly = false;
    641 	mode.trace_indented = false;
    642 }
    643 
    644 static
    645 void
    646 commandline_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 
    657 static
    658 void
    659 commandline_setdependtarget(const struct place *p, char *str)
    660 {
    661 	(void)p;
    662 	mode.depend_target = str;
    663 	mode.depend_quote_target = false;
    664 }
    665 
    666 static
    667 void
    668 commandline_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 
    675 static
    676 void
    677 commandline_setdependoutput(const struct place *p, char *str)
    678 {
    679 	(void)p;
    680 	mode.depend_file = str;
    681 }
    682 
    683 static
    684 void
    685 commandline_M(void)
    686 {
    687 	mode.do_depend = true;
    688 	mode.depend_report_system = true;
    689 	mode.do_output = false;
    690 }
    691 
    692 static
    693 void
    694 commandline_MM(void)
    695 {
    696 	mode.do_depend = true;
    697 	mode.depend_report_system = false;
    698 	mode.do_output = false;
    699 }
    700 
    701 static
    702 void
    703 commandline_MD(void)
    704 {
    705 	mode.do_depend = true;
    706 	mode.depend_report_system = true;
    707 }
    708 
    709 static
    710 void
    711 commandline_MMD(void)
    712 {
    713 	mode.do_depend = true;
    714 	mode.depend_report_system = false;
    715 }
    716 
    717 static
    718 void
    719 commandline_wall(void)
    720 {
    721 	warns.nestcomment = true;
    722 	warns.undef = true;
    723 	warns.unused = true;
    724 }
    725 
    726 static
    727 void
    728 commandline_wnoall(void)
    729 {
    730 	warns.nestcomment = false;
    731 	warns.undef = false;
    732 	warns.unused = false;
    733 }
    734 
    735 static
    736 void
    737 commandline_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 
    748 struct ignore_option {
    749 	const char *string;
    750 };
    751 
    752 struct flag_option {
    753 	const char *string;
    754 	bool *flag;
    755 	bool setto;
    756 };
    757 
    758 struct act_option {
    759 	const char *string;
    760 	void (*func)(void);
    761 };
    762 
    763 struct prefix_option {
    764 	const char *string;
    765 	void (*func)(const struct place *, char *);
    766 };
    767 
    768 struct arg_option {
    769 	const char *string;
    770 	void (*func)(const struct place *, char *);
    771 };
    772 
    773 static const struct ignore_option ignore_options[] = {
    774 	{ "m32" },
    775 	{ "traditional" },
    776 };
    777 static const unsigned num_ignore_options = HOWMANY(ignore_options);
    778 
    779 static 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 };
    801 static const unsigned num_flag_options = HOWMANY(flag_options);
    802 
    803 static 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 };
    817 static const unsigned num_act_options = HOWMANY(act_options);
    818 
    819 static 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 };
    826 static const unsigned num_prefix_options = HOWMANY(prefix_options);
    827 
    828 static 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 };
    845 static const unsigned num_arg_options = HOWMANY(arg_options);
    846 
    847 static
    848 bool
    849 check_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 
    866 static
    867 bool
    868 check_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 
    886 static
    887 bool
    888 check_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 
    906 static
    907 bool
    908 check_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 
    927 static
    928 bool
    929 check_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 
    953 DEAD PF(2, 3) static
    954 void
    955 usage(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 
    986 static struct stringarray freestrings;
    987 
    988 static
    989 void
    990 init(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 
   1004 static
   1005 void
   1006 cleanup(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 
   1028 void
   1029 die(void)
   1030 {
   1031 	cleanup();
   1032 	exit(EXIT_FAILURE);
   1033 }
   1034 
   1035 void
   1036 freestringlater(char *s)
   1037 {
   1038 	stringarray_add(&freestrings, s, NULL);
   1039 }
   1040 
   1041 ////////////////////////////////////////////////////////////
   1042 // main
   1043 
   1044 int
   1045 main(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