Mercurial > ~dholland > hg > tradcpp > index.cgi
view main.c @ 136:59680a727e9d
Improve previous.
Just in case we ever crash and reach cleanup() while processing an
-include foo option, take the array entry for it out of the array to
make sure it doesn't get freed twice. This case shouldn't be
reachable, but it's better to be safe.
author | David A. Holland |
---|---|
date | Tue, 09 Jul 2013 13:38:43 -0400 |
parents | eaae8014a94a |
children | 0816803b22d1 |
line wrap: on
line source
/*- * Copyright (c) 2010 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by David A. Holland. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <err.h> #include "version.h" #include "config.h" #include "utils.h" #include "array.h" #include "mode.h" #include "place.h" #include "files.h" #include "directive.h" #include "macro.h" struct mode mode = { .werror = false, .input_allow_dollars = false, .input_tabstop = 8, .do_stdinc = true, .do_stddef = true, .do_output = true, .output_linenumbers = true, .output_retain_comments = false, .output_file = NULL, .do_depend = false, .depend_report_system = false, .depend_assume_generated = false, .depend_issue_fakerules = false, .depend_quote_target = true, .depend_target = NULL, .depend_file = NULL, .do_macrolist = false, .macrolist_include_stddef = false, .macrolist_include_expansions = false, .do_trace = false, .trace_namesonly = false, .trace_indented = false, }; struct warns warns = { .endiflabels = true, .nestcomment = false, .undef = false, .unused = false, }; /* this is always true, but can be set explicitly with -traditional */ static bool traditional = true; //////////////////////////////////////////////////////////// // commandline macros struct commandline_macro { struct place where; struct place where2; const char *macro; const char *expansion; }; static struct array commandline_macros; static void commandline_macros_init(void) { array_init(&commandline_macros); } static void commandline_macros_cleanup(void) { unsigned i, num; struct commandline_macro *cm; num = array_num(&commandline_macros); for (i=0; i<num; i++) { cm = array_get(&commandline_macros, i); dofree(cm, sizeof(*cm)); } array_setsize(&commandline_macros, 0); array_cleanup(&commandline_macros); } static void commandline_macro_add(const struct place *p, const char *macro, const struct place *p2, const char *expansion) { struct commandline_macro *cm; cm = domalloc(sizeof(*cm)); cm->where = *p; cm->where2 = *p2; cm->macro = macro; cm->expansion = expansion; array_add(&commandline_macros, cm, NULL); } static void commandline_def(const struct place *p, char *str) { struct place p2; char *val; if (*str == '\0') { warnx("-D: macro name expected"); die(); } val = strchr(str, '='); if (val != NULL) { *val = '\0'; val++; } if (val) { p2 = *p; p2.column += strlen(str); } else { place_setbuiltin(&p2, 1); } commandline_macro_add(p, str, &p2, val ? val : "1"); } static void commandline_undef(const struct place *p, char *str) { if (*str == '\0') { warnx("-D: macro name expected"); die(); } commandline_macro_add(p, str, p, NULL); } static void apply_commandline_macros(void) { struct commandline_macro *cm; unsigned i, num; num = array_num(&commandline_macros); for (i=0; i<num; i++) { cm = array_get(&commandline_macros, i); if (cm->expansion != NULL) { macro_define_plain(&cm->where, cm->macro, &cm->where2, cm->expansion); } else { macro_undef(cm->macro); } dofree(cm, sizeof(*cm)); } array_setsize(&commandline_macros, 0); } static void apply_builtin_macro(unsigned num, const char *name, const char *val) { struct place p; place_setbuiltin(&p, num); macro_define_plain(&p, name, &p, val); } static void apply_builtin_macros(void) { unsigned n = 1; #ifdef CONFIG_OS apply_builtin_macro(n++, CONFIG_OS, "1"); #endif #ifdef CONFIG_OS_2 apply_builtin_macro(n++, CONFIG_OS_2, "1"); #endif #ifdef CONFIG_CPU apply_builtin_macro(n++, CONFIG_CPU, "1"); #endif #ifdef CONFIG_CPU_2 apply_builtin_macro(n++, CONFIG_CPU_2, "1"); #endif #ifdef CONFIG_SIZE apply_builtin_macro(n++, CONFIG_SIZE, "1"); #endif #ifdef CONFIG_BINFMT apply_builtin_macro(n++, CONFIG_BINFMT, "1"); #endif #ifdef CONFIG_COMPILER apply_builtin_macro(n++, CONFIG_COMPILER, VERSION_MAJOR); apply_builtin_macro(n++, CONFIG_COMPILER_MINOR, VERSION_MINOR); apply_builtin_macro(n++, "__VERSION__", VERSION_LONG); #endif } //////////////////////////////////////////////////////////// // extra included files struct commandline_file { struct place where; char *name; bool suppress_output; }; static struct array commandline_files; static void commandline_files_init(void) { array_init(&commandline_files); } static void commandline_files_cleanup(void) { unsigned i, num; struct commandline_file *cf; num = array_num(&commandline_files); for (i=0; i<num; i++) { cf = array_get(&commandline_files, i); if (cf != NULL) { dofree(cf, sizeof(*cf)); } } array_setsize(&commandline_files, 0); array_cleanup(&commandline_files); } static void commandline_addfile(const struct place *p, char *name, bool suppress_output) { struct commandline_file *cf; cf = domalloc(sizeof(*cf)); cf->where = *p; cf->name = name; cf->suppress_output = suppress_output; array_add(&commandline_files, cf, NULL); } static void commandline_addfile_output(const struct place *p, char *name) { commandline_addfile(p, name, false); } static void commandline_addfile_nooutput(const struct place *p, char *name) { commandline_addfile(p, name, true); } static void read_commandline_files(void) { struct commandline_file *cf; unsigned i, num; bool save = false; num = array_num(&commandline_files); for (i=0; i<num; i++) { cf = array_get(&commandline_files, i); array_set(&commandline_files, i, NULL); if (cf->suppress_output) { save = mode.do_output; mode.do_output = false; file_readquote(&cf->where, cf->name); mode.do_output = save; } else { file_readquote(&cf->where, cf->name); } dofree(cf, sizeof(*cf)); } array_setsize(&commandline_files, 0); } //////////////////////////////////////////////////////////// // include path accumulation static struct stringarray incpath_quote; static struct stringarray incpath_user; static struct stringarray incpath_system; static struct stringarray incpath_late; static const char *sysroot; static void incpath_init(void) { stringarray_init(&incpath_quote); stringarray_init(&incpath_user); stringarray_init(&incpath_system); stringarray_init(&incpath_late); } static void incpath_cleanup(void) { stringarray_setsize(&incpath_quote, 0); stringarray_setsize(&incpath_user, 0); stringarray_setsize(&incpath_system, 0); stringarray_setsize(&incpath_late, 0); stringarray_cleanup(&incpath_quote); stringarray_cleanup(&incpath_user); stringarray_cleanup(&incpath_system); stringarray_cleanup(&incpath_late); } static void commandline_isysroot(const struct place *p, char *dir) { (void)p; sysroot = dir; } static void commandline_addincpath(struct stringarray *arr, char *s) { if (*s == '\0') { warnx("Empty include path"); die(); } stringarray_add(arr, s, NULL); } static void commandline_addincpath_quote(const struct place *p, char *dir) { (void)p; commandline_addincpath(&incpath_quote, dir); } static void commandline_addincpath_user(const struct place *p, char *dir) { (void)p; commandline_addincpath(&incpath_user, dir); } static void commandline_addincpath_system(const struct place *p, char *dir) { (void)p; commandline_addincpath(&incpath_system, dir); } static void commandline_addincpath_late(const struct place *p, char *dir) { (void)p; commandline_addincpath(&incpath_late, dir); } static void loadincludepath(void) { unsigned i, num; const char *dir; char *t; num = stringarray_num(&incpath_quote); for (i=0; i<num; i++) { dir = stringarray_get(&incpath_quote, i); files_addquotepath(dir, false); } files_addquotepath(NULL, false); num = stringarray_num(&incpath_user); for (i=0; i<num; i++) { dir = stringarray_get(&incpath_user, i); files_addquotepath(dir, false); files_addbracketpath(dir, false); } if (mode.do_stdinc) { if (sysroot != NULL) { t = dostrdup3(sysroot, "/", CONFIG_LOCALINCLUDE); freestringlater(t); dir = t; } else { dir = CONFIG_LOCALINCLUDE; } files_addquotepath(dir, true); files_addbracketpath(dir, true); if (sysroot != NULL) { t = dostrdup3(sysroot, "/", CONFIG_SYSTEMINCLUDE); freestringlater(t); dir = t; } else { dir = CONFIG_SYSTEMINCLUDE; } files_addquotepath(dir, true); files_addbracketpath(dir, true); } num = stringarray_num(&incpath_system); for (i=0; i<num; i++) { dir = stringarray_get(&incpath_system, i); files_addquotepath(dir, true); files_addbracketpath(dir, true); } num = stringarray_num(&incpath_late); for (i=0; i<num; i++) { dir = stringarray_get(&incpath_late, i); files_addquotepath(dir, false); files_addbracketpath(dir, false); } } //////////////////////////////////////////////////////////// // silly commandline stuff static const char *commandline_prefix; static void commandline_setprefix(const struct place *p, char *prefix) { (void)p; commandline_prefix = prefix; } static void commandline_addincpath_user_withprefix(const struct place *p, char *dir) { char *s; if (commandline_prefix == NULL) { warnx("-iprefix needed"); die(); } s = dostrdup3(commandline_prefix, "/", dir); freestringlater(s); commandline_addincpath_user(p, s); } static void commandline_addincpath_late_withprefix(const struct place *p, char *dir) { char *s; if (commandline_prefix == NULL) { warnx("-iprefix needed"); die(); } s = dostrdup3(commandline_prefix, "/", dir); freestringlater(s); commandline_addincpath_late(p, s); } static void commandline_setstd(const struct place *p, char *std) { (void)p; if (!strcmp(std, "krc")) { return; } warnx("Standard %s not supported by this preprocessor", std); die(); } static void commandline_setlang(const struct place *p, char *lang) { (void)p; if (!strcmp(lang, "c") || !strcmp(lang, "assembler-with-cpp")) { return; } warnx("Language %s not supported by this preprocessor", lang); die(); } //////////////////////////////////////////////////////////// // complex modes DEAD static void commandline_iremap(const struct place *p, char *str) { (void)p; /* XXX */ (void)str; warnx("-iremap not supported"); die(); } static void commandline_tabstop(const struct place *p, char *s) { char *t; unsigned long val; (void)p; t = strchr(s, '='); if (t == NULL) { /* should not happen */ warnx("Invalid tabstop"); die(); } t++; errno = 0; val = strtoul(t, &t, 10); if (errno || *t != '\0') { warnx("Invalid tabstop"); die(); } if (val > 64) { warnx("Preposterously large tabstop"); die(); } mode.input_tabstop = val; } /* * macrolist */ static void commandline_dD(void) { mode.do_macrolist = true; mode.macrolist_include_stddef = false; mode.macrolist_include_expansions = true; } static void commandline_dM(void) { mode.do_macrolist = true; mode.macrolist_include_stddef = true; mode.macrolist_include_expansions = true; mode.do_output = false; } static void commandline_dN(void) { mode.do_macrolist = true; mode.macrolist_include_stddef = false; mode.macrolist_include_expansions = false; } /* * include trace */ static void commandline_dI(void) { mode.do_trace = true; mode.trace_namesonly = false; mode.trace_indented = false; } static void commandline_H(void) { mode.do_trace = true; mode.trace_namesonly = true; mode.trace_indented = true; } /* * depend */ static void commandline_setdependtarget(const struct place *p, char *str) { (void)p; mode.depend_target = str; mode.depend_quote_target = false; } static void commandline_setdependtarget_quoted(const struct place *p, char *str) { (void)p; mode.depend_target = str; mode.depend_quote_target = true; } static void commandline_setdependoutput(const struct place *p, char *str) { (void)p; mode.depend_file = str; } static void commandline_M(void) { mode.do_depend = true; mode.depend_report_system = true; mode.do_output = false; } static void commandline_MM(void) { mode.do_depend = true; mode.depend_report_system = false; mode.do_output = false; } static void commandline_MD(void) { mode.do_depend = true; mode.depend_report_system = true; } static void commandline_MMD(void) { mode.do_depend = true; mode.depend_report_system = false; } static void commandline_wall(void) { warns.nestcomment = true; warns.undef = true; warns.unused = true; } static void commandline_wnoall(void) { warns.nestcomment = false; warns.undef = false; warns.unused = false; } static void commandline_wnone(void) { warns.nestcomment = false; warns.endiflabels = false; warns.undef = false; warns.unused = false; } //////////////////////////////////////////////////////////// // options struct flag_option { const char *string; bool *flag; bool setto; }; struct act_option { const char *string; void (*func)(void); }; struct prefix_option { const char *string; void (*func)(const struct place *, char *); }; struct arg_option { const char *string; void (*func)(const struct place *, char *); }; static const struct flag_option flag_options[] = { { "C", &mode.output_retain_comments, true }, { "CC", &mode.output_retain_comments, true }, { "MG", &mode.depend_assume_generated, true }, { "MP", &mode.depend_issue_fakerules, true }, { "P", &mode.output_linenumbers, false }, { "Wcomment", &warns.nestcomment, true }, { "Wendif-labels", &warns.endiflabels, true }, { "Werror", &mode.werror, true }, { "Wno-comment", &warns.nestcomment, false }, { "Wno-endif-labels", &warns.endiflabels, false }, { "Wno-error", &mode.werror, false }, { "Wno-undef", &warns.undef, false }, { "Wno-unused-macros", &warns.unused, false }, { "Wundef", &warns.undef, true }, { "Wunused-macros", &warns.unused, true }, { "fdollars-in-identifiers", &mode.input_allow_dollars, true }, { "fno-dollars-in-identifiers", &mode.input_allow_dollars, false }, { "nostdinc", &mode.do_stdinc, false }, { "traditional", &traditional, true }, { "undef", &mode.do_stddef, false }, }; static const unsigned num_flag_options = HOWMANY(flag_options); static const struct act_option act_options[] = { { "H", commandline_H }, { "M", commandline_M }, { "MD", commandline_MD }, { "MM", commandline_MM }, { "MMD", commandline_MMD }, { "Wall", commandline_wall }, { "Wno-all", commandline_wnoall }, { "dD", commandline_dD }, { "dI", commandline_dI }, { "dM", commandline_dM }, { "dN", commandline_dN }, { "w", commandline_wnone }, }; static const unsigned num_act_options = HOWMANY(act_options); static const struct prefix_option prefix_options[] = { { "D", commandline_def }, { "I", commandline_addincpath_user }, { "U", commandline_undef }, { "ftabstop=", commandline_tabstop }, { "std=", commandline_setstd }, }; static const unsigned num_prefix_options = HOWMANY(prefix_options); static const struct arg_option arg_options[] = { { "MF", commandline_setdependoutput }, { "MQ", commandline_setdependtarget_quoted }, { "MT", commandline_setdependtarget }, { "idirafter", commandline_addincpath_late }, { "imacros", commandline_addfile_nooutput }, { "include", commandline_addfile_output }, { "iprefix", commandline_setprefix }, { "iquote", commandline_addincpath_quote }, { "iremap", commandline_iremap }, { "isysroot", commandline_isysroot }, { "isystem", commandline_addincpath_system }, { "iwithprefix", commandline_addincpath_late_withprefix }, { "iwithprefixbefore", commandline_addincpath_user_withprefix }, { "x", commandline_setlang }, }; static const unsigned num_arg_options = HOWMANY(arg_options); static bool check_flag_option(const char *opt) { unsigned i; int r; for (i=0; i<num_flag_options; i++) { r = strcmp(opt, flag_options[i].string); if (r == 0) { *flag_options[i].flag = flag_options[i].setto; return true; } if (r < 0) { break; } } return false; } static bool check_act_option(const char *opt) { unsigned i; int r; for (i=0; i<num_act_options; i++) { r = strcmp(opt, act_options[i].string); if (r == 0) { act_options[i].func(); return true; } if (r < 0) { break; } } return false; } static bool check_prefix_option(const struct place *p, char *opt) { unsigned i, len; int r; for (i=0; i<num_prefix_options; i++) { len = strlen(prefix_options[i].string); r = strncmp(opt, prefix_options[i].string, len); if (r == 0) { prefix_options[i].func(p, opt + len); return true; } if (r < 0) { break; } } return false; } static bool check_arg_option(const char *opt, const struct place *argplace, char *arg) { unsigned i; int r; for (i=0; i<num_arg_options; i++) { r = strcmp(opt, arg_options[i].string); if (r == 0) { if (arg == NULL) { warnx("Option -%s requires an argument", opt); die(); } arg_options[i].func(argplace, arg); return true; } if (r < 0) { break; } } return false; } DEAD static void usage(const char *argv0) { const char *progname; progname = strrchr(argv0, '/'); progname = progname == NULL ? argv0 : progname + 1; fprintf(stderr, "Usage: %s [options] [infile [outfile]]\n", progname); fprintf(stderr, "Common options:\n"); fprintf(stderr, " -C Retain comments\n"); fprintf(stderr, " -Dmacro[=def] Predefine macro\n"); fprintf(stderr, " -Idir Add to include path\n"); fprintf(stderr, " -M Issue depend info\n"); fprintf(stderr, " -MD Issue depend info and output\n"); fprintf(stderr, " -MM -M w/o system headers\n"); fprintf(stderr, " -MMD -MD w/o system headers\n"); fprintf(stderr, " -nostdinc Drop default include path\n"); fprintf(stderr, " -Umacro Undefine macro\n"); fprintf(stderr, " -undef Undefine everything\n"); fprintf(stderr, " -Wall Enable all warnings\n"); fprintf(stderr, " -Werror Make warnings into errors\n"); fprintf(stderr, " -w Disable all warnings\n"); die(); } //////////////////////////////////////////////////////////// // exit and cleanup static struct stringarray freestrings; static void init(void) { stringarray_init(&freestrings); incpath_init(); commandline_macros_init(); commandline_files_init(); place_init(); files_init(); directive_init(); macros_init(); } static void cleanup(void) { unsigned i, num; macros_cleanup(); directive_cleanup(); files_cleanup(); place_cleanup(); commandline_files_cleanup(); commandline_macros_cleanup(); incpath_cleanup(); num = stringarray_num(&freestrings); for (i=0; i<num; i++) { dostrfree(stringarray_get(&freestrings, i)); } stringarray_setsize(&freestrings, 0); stringarray_cleanup(&freestrings); } void die(void) { cleanup(); exit(EXIT_FAILURE); } void freestringlater(char *s) { stringarray_add(&freestrings, s, NULL); } //////////////////////////////////////////////////////////// // main int main(int argc, char *argv[]) { const char *inputfile = NULL; const char *outputfile = NULL; struct place cmdplace; int i; init(); for (i=1; i<argc; i++) { if (argv[i][0] != '-') { break; } place_setcommandline(&cmdplace, i, 1); if (check_flag_option(argv[i]+1)) { continue; } if (check_act_option(argv[i]+1)) { continue; } if (check_prefix_option(&cmdplace, argv[i]+1)) { continue; } place_setcommandline(&cmdplace, i+1, 1); if (check_arg_option(argv[i]+1, &cmdplace, argv[i+1])) { i++; continue; } usage(argv[0]); } if (i < argc) { inputfile = argv[i++]; } if (i < argc) { outputfile = argv[i++]; } if (i < argc) { usage(argv[0]); } mode.output_file = outputfile; loadincludepath(); apply_builtin_macros(); apply_commandline_macros(); read_commandline_files(); place_setnowhere(&cmdplace); file_readabsolute(&cmdplace, inputfile); cleanup(); if (complain_failed()) { return EXIT_FAILURE; } return EXIT_SUCCESS; }