// SPDX-License-Identifier: MIT /* * 'sparse' library helper routines. * * Copyright (C) 2003 Transmeta Corp. * 2003-2004 Linus Torvalds * 2017-2020 Luc Van Oostenryck */ #include "options.h" #include "lib.h" #include "machine.h" #include "target.h" #include #include #include #include #ifndef __GNUC__ # define __GNUC__ 2 # define __GNUC_MINOR__ 95 # define __GNUC_PATCHLEVEL__ 0 #endif enum flag_type { FLAG_OFF, FLAG_ON, FLAG_FORCE_OFF }; int die_if_error = 0; int do_output = 1; int gcc_major = __GNUC__; int gcc_minor = __GNUC_MINOR__; int gcc_patchlevel = __GNUC_PATCHLEVEL__; int has_error = 0; int optimize_level; int optimize_size; int preprocess_only; int preprocessing; int verbose; #define CMDLINE_INCLUDE 20 int cmdline_include_nr = 0; char *cmdline_include[CMDLINE_INCLUDE]; const char *base_filename; const char *diag_prefix = ""; const char *gcc_base_dir = GCC_BASE; const char *multiarch_dir = MULTIARCH_TRIPLET; const char *outfile = NULL; enum standard standard = STANDARD_GNU89; int arch_big_endian = ARCH_BIG_ENDIAN; int arch_cmodel = CMODEL_UNKNOWN; int arch_fp_abi = FP_ABI_NATIVE; int arch_m64 = ARCH_M64_DEFAULT; int arch_msize_long = 0; int arch_os = OS_NATIVE; int dbg_compound = 0; int dbg_dead = 0; int dbg_domtree = 0; int dbg_entry = 0; int dbg_ir = 0; int dbg_postorder = 0; int dump_macro_defs = 0; int dump_macros_only = 0; int dissect_show_all_symbols = 0; unsigned long fdump_ir; int fhosted = 1; unsigned int fmax_errors = 100; unsigned int fmax_warnings = 100; int fmem_report = 0; unsigned long long fmemcpy_max_count = 100000; unsigned long fpasses = ~0UL; int fpic = 0; int fpie = 0; int fshort_wchar = 0; int funsigned_bitfields = 0; int funsigned_char = 0; int Waddress = 0; int Waddress_space = 1; int Wbitwise = 1; int Wbitwise_pointer = 0; int Wcast_from_as = 0; int Wcast_to_as = 0; int Wcast_truncate = 1; int Wconstant_suffix = 0; int Wconstexpr_not_const = 0; int Wcontext = 1; int Wdecl = 1; int Wdeclarationafterstatement = -1; int Wdefault_bitfield_sign = 0; int Wdesignated_init = 1; int Wdo_while = 0; int Wenum_mismatch = 1; int Wexternal_function_has_definition = 1; int Wflexible_array_array = 1; int Wflexible_array_nested = 0; int Wflexible_array_sizeof = 0; int Wflexible_array_union = 0; int Wimplicit_int = 1; int Winit_cstring = 0; int Wint_to_pointer_cast = 1; int Wmemcpy_max_count = 1; int Wnewline_eof = 1; int Wnon_pointer_null = 1; int Wold_initializer = 1; int Wold_style_definition = 1; int Wone_bit_signed_bitfield = 1; int Woverride_init = 1; int Woverride_init_all = 0; int Woverride_init_whole_range = 0; int Wparen_string = 0; int Wpast_deep_designator = 0; int Wpedantic = 0; int Wpointer_arith = 0; int Wpointer_to_int_cast = 1; int Wptr_subtraction_blows = 0; int Wreturn_void = 0; int Wshadow = 0; int Wshift_count_negative = 1; int Wshift_count_overflow = 1; int Wsizeof_bool = 0; int Wsparse_error = FLAG_FORCE_OFF; int Wstrict_prototypes = 1; int Wtautological_compare = 0; int Wtransparent_union = 0; int Wtypesign = 0; int Wundef = 0; int Wuninitialized = 1; int Wunion_cast = 0; int Wuniversal_initializer = 0; int Wunknown_attribute = 0; int Wvla = 1; //////////////////////////////////////////////////////////////////////////////// // Helpers for option parsing static const char *match_option(const char *arg, const char *prefix) { unsigned int n = strlen(prefix); if (strncmp(arg, prefix, n) == 0) return arg + n; return NULL; } struct val_map { const char *name; int val; }; static int handle_subopt_val(const char *opt, const char *arg, const struct val_map *map, int *flag) { const char *name; if (*arg++ != '=') die("missing argument for option '%s'", opt); for (;(name = map->name); map++) { if (strcmp(name, arg) == 0 || strcmp(name, "*") == 0) { *flag = map->val; return 1; } if (strcmp(name, "?") == 0) die("invalid argument '%s' in option '%s'", arg, opt); } return 0; } struct mask_map { const char *name; unsigned long mask; }; static int apply_mask(unsigned long *val, const char *str, unsigned len, const struct mask_map *map, int neg) { const char *name; for (;(name = map->name); map++) { if (!strncmp(name, str, len) && !name[len]) { if (neg == 0) *val |= map->mask; else *val &= ~map->mask; return 0; } } return 1; } static int handle_suboption_mask(const char *arg, const char *opt, const struct mask_map *map, unsigned long *flag) { if (*opt == '\0') { apply_mask(flag, "", 0, map, 0); return 1; } if (*opt++ != '=') return 0; while (1) { unsigned int len = strcspn(opt, ",+"); int neg = 0; if (len == 0) goto end; if (!strncmp(opt, "no-", 3)) { opt += 3; len -= 3; neg = 1; } if (apply_mask(flag, opt, len, map, neg)) die("error: wrong option '%.*s' for \'%s\'", len, opt, arg); end: opt += len; if (*opt++ == '\0') break; } return 1; } #define OPT_INVERSE 1 #define OPT_VAL 2 struct flag { const char *name; int *flag; int (*fun)(const char *arg, const char *opt, const struct flag *, int options); unsigned long mask; int val; }; static int handle_switches(const char *ori, const char *opt, const struct flag *flags) { const char *arg = opt; int val = 1; // Prefixe "no-" mean to turn flag off. if (strncmp(arg, "no-", 3) == 0) { arg += 3; val = 0; } for (; flags->name; flags++) { const char *opt = match_option(arg, flags->name); int rc; if (!opt) continue; if (flags->fun) { int options = 0; if (!val) options |= OPT_INVERSE; if ((rc = flags->fun(ori, opt, flags, options))) return rc; } // boolean flag if (opt[0] == '\0' && flags->flag) { if (flags->mask & OPT_VAL) val = flags->val; if (flags->mask & OPT_INVERSE) val = !val; *flags->flag = val; return 1; } } // not handled return 0; } static char **handle_onoff_switch(char *arg, char **next, const struct flag flags[]) { int flag = FLAG_ON; char *p = arg + 1; unsigned i; // Prefixes "no" and "no-" mean to turn warning off. if (p[0] == 'n' && p[1] == 'o') { p += 2; if (p[0] == '-') p++; flag = FLAG_FORCE_OFF; } for (i = 0; flags[i].name; i++) { if (!strcmp(p,flags[i].name)) { *flags[i].flag = flag; return next; } } // Unknown. return NULL; } static void handle_onoff_switch_finalize(const struct flag flags[]) { unsigned i; for (i = 0; flags[i].name; i++) { if (*flags[i].flag == FLAG_FORCE_OFF) *flags[i].flag = FLAG_OFF; } } static int handle_switch_setval(const char *arg, const char *opt, const struct flag *flag, int options) { *(flag->flag) = flag->mask; return 1; } #define OPTNUM_ZERO_IS_INF 1 #define OPTNUM_UNLIMITED 2 #define OPT_NUMERIC(NAME, TYPE, FUNCTION) \ static int opt_##NAME(const char *arg, const char *opt, TYPE *ptr, int flag) \ { \ char *end; \ TYPE val; \ \ val = FUNCTION(opt, &end, 0); \ if (*end != '\0' || end == opt) { \ if ((flag & OPTNUM_UNLIMITED) && !strcmp(opt, "unlimited")) \ val = ~val; \ else \ die("error: wrong argument to \'%s\'", arg); \ } \ if ((flag & OPTNUM_ZERO_IS_INF) && val == 0) \ val = ~val; \ *ptr = val; \ return 1; \ } OPT_NUMERIC(ullong, unsigned long long, strtoull) OPT_NUMERIC(uint, unsigned int, strtoul) //////////////////////////////////////////////////////////////////////////////// // Option parsing static char **handle_switch_a(char *arg, char **next) { if (!strcmp(arg, "ansi")) standard = STANDARD_C89; return next; } static char **handle_switch_D(char *arg, char **next) { const char *name = arg + 1; const char *value = "1"; if (!*name) { arg = *++next; if (!arg) die("argument to `-D' is missing"); name = arg; } for (;;arg++) { char c; c = *arg; if (!c) break; if (c == '=') { *arg = '\0'; value = arg + 1; break; } } add_pre_buffer("#define %s %s\n", name, value); return next; } static char **handle_switch_d(char *arg, char **next) { char *arg_char = arg + 1; /* * -d, where is a sequence of characters, not preceded * by a space. If you specify characters whose behaviour conflicts, * the result is undefined. */ while (*arg_char) { switch (*arg_char) { case 'M': /* dump just the macro definitions */ dump_macros_only = 1; dump_macro_defs = 0; break; case 'D': /* like 'M', but also output pre-processed text */ dump_macro_defs = 1; dump_macros_only = 0; break; case 'N': /* like 'D', but only output macro names not bodies */ break; case 'I': /* like 'D', but also output #include directives */ break; case 'U': /* like 'D', but only output expanded macros */ break; } arg_char++; } return next; } static char **handle_switch_E(char *arg, char **next) { if (arg[1] == '\0') preprocess_only = 1; return next; } static int handle_ftabstop(const char *arg, const char *opt, const struct flag *flag, int options) { unsigned long val; char *end; if (*opt == '\0') die("error: missing argument to \"%s\"", arg); /* we silently ignore silly values */ val = strtoul(opt, &end, 10); if (*end == '\0' && 1 <= val && val <= 100) tabstop = val; return 1; } static int handle_fpasses(const char *arg, const char *opt, const struct flag *flag, int options) { unsigned long mask; mask = flag->mask; if (*opt == '\0') { if (options & OPT_INVERSE) fpasses &= ~mask; else fpasses |= mask; return 1; } if (options & OPT_INVERSE) return 0; if (!strcmp(opt, "-enable")) { fpasses |= mask; return 1; } if (!strcmp(opt, "-disable")) { fpasses &= ~mask; return 1; } if (!strcmp(opt, "=last")) { // clear everything above mask |= mask - 1; fpasses &= mask; return 1; } return 0; } static int handle_fdiagnostic_prefix(const char *arg, const char *opt, const struct flag *flag, int options) { switch (*opt) { case '\0': diag_prefix = "sparse: "; return 1; case '=': diag_prefix = xasprintf("%s: ", opt+1); return 1; default: return 0; } } static int handle_fdump_ir(const char *arg, const char *opt, const struct flag *flag, int options) { static const struct mask_map dump_ir_options[] = { { "", PASS_LINEARIZE }, { "linearize", PASS_LINEARIZE }, { "mem2reg", PASS_MEM2REG }, { "final", PASS_FINAL }, { }, }; return handle_suboption_mask(arg, opt, dump_ir_options, &fdump_ir); } static int handle_fmemcpy_max_count(const char *arg, const char *opt, const struct flag *flag, int options) { opt_ullong(arg, opt, &fmemcpy_max_count, OPTNUM_ZERO_IS_INF|OPTNUM_UNLIMITED); return 1; } static int handle_fmax_errors(const char *arg, const char *opt, const struct flag *flag, int options) { opt_uint(arg, opt, &fmax_errors, OPTNUM_UNLIMITED); return 1; } static int handle_fmax_warnings(const char *arg, const char *opt, const struct flag *flag, int options) { opt_uint(arg, opt, &fmax_warnings, OPTNUM_UNLIMITED); return 1; } static struct flag fflags[] = { { "diagnostic-prefix", NULL, handle_fdiagnostic_prefix }, { "dump-ir", NULL, handle_fdump_ir }, { "freestanding", &fhosted, NULL, OPT_INVERSE }, { "hosted", &fhosted }, { "linearize", NULL, handle_fpasses, PASS_LINEARIZE }, { "max-errors=", NULL, handle_fmax_errors }, { "max-warnings=", NULL, handle_fmax_warnings }, { "mem-report", &fmem_report }, { "memcpy-max-count=", NULL, handle_fmemcpy_max_count }, { "tabstop=", NULL, handle_ftabstop }, { "mem2reg", NULL, handle_fpasses, PASS_MEM2REG }, { "optim", NULL, handle_fpasses, PASS_OPTIM }, { "pic", &fpic, handle_switch_setval, 1 }, { "PIC", &fpic, handle_switch_setval, 2 }, { "pie", &fpie, handle_switch_setval, 1 }, { "PIE", &fpie, handle_switch_setval, 2 }, { "signed-bitfields", &funsigned_bitfields, NULL, OPT_INVERSE }, { "unsigned-bitfields", &funsigned_bitfields, NULL, }, { "signed-char", &funsigned_char, NULL, OPT_INVERSE }, { "short-wchar", &fshort_wchar }, { "unsigned-char", &funsigned_char, NULL, }, { }, }; static char **handle_switch_f(char *arg, char **next) { if (handle_switches(arg-1, arg+1, fflags)) return next; return next; } static char **handle_switch_G(char *arg, char **next) { if (!strcmp(arg, "G") && *next) return next + 1; // "-G 0" else return next; // "-G0" or (bogus) terminal "-G" } static char **handle_base_dir(char *arg, char **next) { gcc_base_dir = *++next; if (!gcc_base_dir) die("missing argument for -gcc-base-dir option"); return next; } static char **handle_switch_g(char *arg, char **next) { if (!strcmp(arg, "gcc-base-dir")) return handle_base_dir(arg, next); return next; } static char **handle_switch_I(char *arg, char **next) { char *path = arg+1; switch (arg[1]) { case '-': add_pre_buffer("#split_include\n"); break; case '\0': /* Plain "-I" */ path = *++next; if (!path) die("missing argument for -I option"); /* Fall through */ default: add_pre_buffer("#add_include \"%s/\"\n", path); } return next; } static void add_cmdline_include(char *filename) { if (cmdline_include_nr >= CMDLINE_INCLUDE) die("too many include files for %s\n", filename); cmdline_include[cmdline_include_nr++] = filename; } static char **handle_switch_i(char *arg, char **next) { if (*next && !strcmp(arg, "include")) add_cmdline_include(*++next); else if (*next && !strcmp(arg, "imacros")) add_cmdline_include(*++next); else if (*next && !strcmp(arg, "isystem")) { char *path = *++next; if (!path) die("missing argument for -isystem option"); add_pre_buffer("#add_isystem \"%s/\"\n", path); } else if (*next && !strcmp(arg, "idirafter")) { char *path = *++next; if (!path) die("missing argument for -idirafter option"); add_pre_buffer("#add_dirafter \"%s/\"\n", path); } return next; } static char **handle_switch_M(char *arg, char **next) { if (!strcmp(arg, "MF") || !strcmp(arg,"MQ") || !strcmp(arg,"MT")) { if (!*next) die("missing argument for -%s option", arg); return next + 1; } return next; } static int handle_march(const char *opt, const char *arg, const struct flag *flag, int options) { if (arch_target->parse_march) arch_target->parse_march(arg); return 1; } static int handle_mcmodel(const char *opt, const char *arg, const struct flag *flag, int options) { static const struct val_map cmodels[] = { { "kernel", CMODEL_KERNEL }, { "large", CMODEL_LARGE }, { "medany", CMODEL_MEDANY }, { "medium", CMODEL_MEDIUM }, { "medlow", CMODEL_MEDLOW }, { "small", CMODEL_SMALL }, { "tiny", CMODEL_TINY }, { }, }; return handle_subopt_val(opt, arg, cmodels, flag->flag); } static int handle_mfloat_abi(const char *opt, const char *arg, const struct flag *flag, int options) { static const struct val_map fp_abis[] = { { "hard", FP_ABI_HARD }, { "soft", FP_ABI_SOFT }, { "softfp", FP_ABI_HYBRID }, { "?" }, }; return handle_subopt_val(opt, arg, fp_abis, flag->flag); } static char **handle_multiarch_dir(char *arg, char **next) { multiarch_dir = *++next; if (!multiarch_dir) die("missing argument for -multiarch-dir option"); return next; } static const struct flag mflags[] = { { "64", &arch_m64, NULL, OPT_VAL, ARCH_LP64 }, { "32", &arch_m64, NULL, OPT_VAL, ARCH_LP32 }, { "31", &arch_m64, NULL, OPT_VAL, ARCH_LP32 }, { "16", &arch_m64, NULL, OPT_VAL, ARCH_LP32 }, { "x32",&arch_m64, NULL, OPT_VAL, ARCH_X32 }, { "size-llp64", &arch_m64, NULL, OPT_VAL, ARCH_LLP64 }, { "size-long", &arch_msize_long }, { "arch=", NULL, handle_march }, { "big-endian", &arch_big_endian, NULL }, { "little-endian", &arch_big_endian, NULL, OPT_INVERSE }, { "cmodel", &arch_cmodel, handle_mcmodel }, { "float-abi", &arch_fp_abi, handle_mfloat_abi }, { "hard-float", &arch_fp_abi, NULL, OPT_VAL, FP_ABI_HARD }, { "soft-float", &arch_fp_abi, NULL, OPT_VAL, FP_ABI_SOFT }, { } }; static char **handle_switch_m(char *arg, char **next) { if (!strcmp(arg, "meabi") && next[1] && next[1][0] != '-') { // clang has such an option with syntax: -meabi // It's used by the kernel for armv7. // GCC has the same option but with no argument. // Parse it here to consume the possible argument. static const char *valid[] = { "gnu", "4", "5", "default", NULL }; int i; for (i = 0; valid[i]; i++) { if (!strcmp(next[1], valid[i])) return ++next; } } if (!strcmp(arg, "multiarch-dir")) { return handle_multiarch_dir(arg, next); } else { handle_switches(arg-1, arg+1, mflags); } return next; } static char **handle_nostdinc(char *arg, char **next) { add_pre_buffer("#nostdinc\n"); return next; } static char **handle_switch_n(char *arg, char **next) { if (!strcmp(arg, "nostdinc")) return handle_nostdinc(arg, next); return next; } static char **handle_switch_O(char *arg, char **next) { int level = 1; if (arg[1] >= '0' && arg[1] <= '9') level = arg[1] - '0'; optimize_level = level; optimize_size = arg[1] == 's'; return next; } static char **handle_switch_o(char *arg, char **next) { if (!strcmp(arg, "o")) { // "-o foo" if (!*++next) die("argument to '-o' is missing"); outfile = *next; } // else "-ofoo" return next; } static const struct flag pflags[] = { { "pedantic", &Wpedantic, NULL, OPT_VAL, FLAG_ON }, { } }; static char **handle_switch_p(char *arg, char **next) { handle_switches(arg-1, arg, pflags); return next; } static char **handle_switch_s(const char *arg, char **next) { if ((arg = match_option(arg, "std="))) { if (!strcmp(arg, "c89") || !strcmp(arg, "iso9899:1990")) standard = STANDARD_C89; else if (!strcmp(arg, "iso9899:199409")) standard = STANDARD_C94; else if (!strcmp(arg, "c99") || !strcmp(arg, "c9x") || !strcmp(arg, "iso9899:1999") || !strcmp(arg, "iso9899:199x")) standard = STANDARD_C99; else if (!strcmp(arg, "gnu89")) standard = STANDARD_GNU89; else if (!strcmp(arg, "gnu99") || !strcmp(arg, "gnu9x")) standard = STANDARD_GNU99; else if (!strcmp(arg, "c11") || !strcmp(arg, "c1x") || !strcmp(arg, "iso9899:2011")) standard = STANDARD_C11; else if (!strcmp(arg, "gnu11")) standard = STANDARD_GNU11; else if (!strcmp(arg, "c17") || !strcmp(arg, "c18") || !strcmp(arg, "iso9899:2017") || !strcmp(arg, "iso9899:2018")) standard = STANDARD_C17; else if (!strcmp(arg, "gnu17") || !strcmp(arg, "gnu18")) standard = STANDARD_GNU17; else die("Unsupported C dialect"); } return next; } static char **handle_switch_U(char *arg, char **next) { const char *name = arg + 1; if (*name == '\0') { name = *++next; if (!name) die("argument to `-U' is missing"); } add_pre_buffer("#undef %s\n", name); return next; } static struct flag debugs[] = { { "compound", &dbg_compound}, { "dead", &dbg_dead}, { "domtree", &dbg_domtree}, { "entry", &dbg_entry}, { "ir", &dbg_ir}, { "postorder", &dbg_postorder}, { } }; static char **handle_switch_v(char *arg, char **next) { char ** ret = handle_onoff_switch(arg, next, debugs); if (ret) return ret; // Unknown. do { verbose++; } while (*++arg == 'v'); return next; } static void handle_switch_v_finalize(void) { handle_onoff_switch_finalize(debugs); } static const struct flag warnings[] = { { "address", &Waddress }, { "address-space", &Waddress_space }, { "bitwise", &Wbitwise }, { "bitwise-pointer", &Wbitwise_pointer}, { "cast-from-as", &Wcast_from_as }, { "cast-to-as", &Wcast_to_as }, { "cast-truncate", &Wcast_truncate }, { "constant-suffix", &Wconstant_suffix }, { "constexpr-not-const", &Wconstexpr_not_const}, { "context", &Wcontext }, { "decl", &Wdecl }, { "declaration-after-statement", &Wdeclarationafterstatement }, { "default-bitfield-sign", &Wdefault_bitfield_sign }, { "designated-init", &Wdesignated_init }, { "do-while", &Wdo_while }, { "enum-mismatch", &Wenum_mismatch }, { "external-function-has-definition", &Wexternal_function_has_definition }, { "flexible-array-array", &Wflexible_array_array }, { "flexible-array-nested", &Wflexible_array_nested }, { "flexible-array-sizeof", &Wflexible_array_sizeof }, { "flexible-array-union", &Wflexible_array_union }, { "implicit-int", &Wimplicit_int }, { "init-cstring", &Winit_cstring }, { "int-to-pointer-cast", &Wint_to_pointer_cast }, { "memcpy-max-count", &Wmemcpy_max_count }, { "non-pointer-null", &Wnon_pointer_null }, { "newline-eof", &Wnewline_eof }, { "old-initializer", &Wold_initializer }, { "old-style-definition", &Wold_style_definition }, { "one-bit-signed-bitfield", &Wone_bit_signed_bitfield }, { "override-init", &Woverride_init }, { "override-init-all", &Woverride_init_all }, { "paren-string", &Wparen_string }, { "past-deep-designator", &Wpast_deep_designator }, { "pedantic", &Wpedantic }, { "pointer-to-int-cast", &Wpointer_to_int_cast }, { "ptr-subtraction-blows", &Wptr_subtraction_blows }, { "return-void", &Wreturn_void }, { "shadow", &Wshadow }, { "shift-count-negative", &Wshift_count_negative }, { "shift-count-overflow", &Wshift_count_overflow }, { "sizeof-bool", &Wsizeof_bool }, { "strict-prototypes", &Wstrict_prototypes }, { "pointer-arith", &Wpointer_arith }, { "sparse-error", &Wsparse_error }, { "tautological-compare", &Wtautological_compare }, { "transparent-union", &Wtransparent_union }, { "typesign", &Wtypesign }, { "undef", &Wundef }, { "uninitialized", &Wuninitialized }, { "union-cast", &Wunion_cast }, { "universal-initializer", &Wuniversal_initializer }, { "unknown-attribute", &Wunknown_attribute }, { "vla", &Wvla }, { } }; static char **handle_switch_W(char *arg, char **next) { char ** ret = handle_onoff_switch(arg, next, warnings); if (ret) return ret; if (!strcmp(arg, "Wsparse-all")) { int i; for (i = 0; warnings[i].name; i++) { if (*warnings[i].flag != FLAG_FORCE_OFF) *warnings[i].flag = FLAG_ON; } } // Unknown. return next; } static void handle_switch_W_finalize(void) { handle_onoff_switch_finalize(warnings); /* default Wdeclarationafterstatement based on the C dialect */ if (-1 == Wdeclarationafterstatement) { switch (standard) { case STANDARD_C89: case STANDARD_C94: Wdeclarationafterstatement = 1; break; default: Wdeclarationafterstatement = 0; break; } } } static char **handle_switch_x(char *arg, char **next) { if (!*++next) die("missing argument for -x option"); return next; } static char **handle_arch(char *arg, char **next) { enum machine mach; if (*arg++ != '=') die("missing argument for --arch option"); mach = target_parse(arg); if (mach != MACH_UNKNOWN) target_config(mach); return next; } static char **handle_param(char *arg, char **next) { char *value = NULL; /* For now just skip any '--param=*' or '--param *' */ if (*arg == '\0') { value = *++next; } else if (isspace((unsigned char)*arg) || *arg == '=') { value = ++arg; } if (!value) die("missing argument for --param option"); if (!strcmp(value, "dissect-show-all-symbols")) dissect_show_all_symbols = 1; return next; } static char **handle_os(char *arg, char **next) { if (*arg++ != '=') die("missing argument for --os option"); target_os(arg); return next; } static char **handle_version(char *arg, char **next) { printf("%s\n", sparse_version); exit(0); } struct switches { const char *name; char **(*fn)(char *, char **); unsigned int prefix:1; }; static char **handle_long_options(char *arg, char **next) { static struct switches cmd[] = { { "arch", handle_arch, 1 }, { "os", handle_os, 1 }, { "param", handle_param, 1 }, { "version", handle_version }, { NULL, NULL } }; struct switches *s = cmd; while (s->name) { int optlen = strlen(s->name); if (!strncmp(s->name, arg, optlen + !s->prefix)) return s->fn(arg + optlen, next); s++; } return next; } char **handle_switch(char *arg, char **next) { switch (*arg) { case 'a': return handle_switch_a(arg, next); case 'D': return handle_switch_D(arg, next); case 'd': return handle_switch_d(arg, next); case 'E': return handle_switch_E(arg, next); case 'f': return handle_switch_f(arg, next); case 'g': return handle_switch_g(arg, next); case 'G': return handle_switch_G(arg, next); case 'I': return handle_switch_I(arg, next); case 'i': return handle_switch_i(arg, next); case 'M': return handle_switch_M(arg, next); case 'm': return handle_switch_m(arg, next); case 'n': return handle_switch_n(arg, next); case 'o': return handle_switch_o(arg, next); case 'O': return handle_switch_O(arg, next); case 'p': return handle_switch_p(arg, next); case 's': return handle_switch_s(arg, next); case 'U': return handle_switch_U(arg, next); case 'v': return handle_switch_v(arg, next); case 'W': return handle_switch_W(arg, next); case 'x': return handle_switch_x(arg, next); case '-': return handle_long_options(arg + 1, next); default: break; } /* * Ignore unknown command line options: * they're probably gcc switches */ return next; } void handle_switch_finalize(void) { handle_switch_v_finalize(); handle_switch_W_finalize(); }