tradcpp: upgrade to 0.5.3
[oweals/cde.git] / cde / util / tradcpp / main.c
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 }