diff options
Diffstat (limited to 'parse-options.c')
| -rw-r--r-- | parse-options.c | 216 |
1 files changed, 158 insertions, 58 deletions
diff --git a/parse-options.c b/parse-options.c index a9a39ecaef..c9cafc21b9 100644 --- a/parse-options.c +++ b/parse-options.c @@ -68,6 +68,64 @@ static char *fix_filename(const char *prefix, const char *file) return prefix_filename_except_for_dash(prefix, file); } +static int do_get_int_value(const void *value, size_t precision, intmax_t *ret) +{ + switch (precision) { + case sizeof(int8_t): + *ret = *(int8_t *)value; + return 0; + case sizeof(int16_t): + *ret = *(int16_t *)value; + return 0; + case sizeof(int32_t): + *ret = *(int32_t *)value; + return 0; + case sizeof(int64_t): + *ret = *(int64_t *)value; + return 0; + default: + return -1; + } +} + +static intmax_t get_int_value(const struct option *opt, enum opt_parsed flags) +{ + intmax_t ret; + if (do_get_int_value(opt->value, opt->precision, &ret)) + BUG("invalid precision for option %s", optname(opt, flags)); + return ret; +} + +static enum parse_opt_result set_int_value(const struct option *opt, + enum opt_parsed flags, + intmax_t value) +{ + switch (opt->precision) { + case sizeof(int8_t): + *(int8_t *)opt->value = value; + return 0; + case sizeof(int16_t): + *(int16_t *)opt->value = value; + return 0; + case sizeof(int32_t): + *(int32_t *)opt->value = value; + return 0; + case sizeof(int64_t): + *(int64_t *)opt->value = value; + return 0; + default: + BUG("invalid precision for option %s", optname(opt, flags)); + } +} + +static int signed_int_fits(intmax_t value, size_t precision) +{ + size_t bits = precision * CHAR_BIT; + intmax_t upper_bound = INTMAX_MAX >> (bitsizeof(intmax_t) - bits); + intmax_t lower_bound = -upper_bound - 1; + return lower_bound <= value && value <= upper_bound; +} + static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p, const struct option *opt, enum opt_parsed flags, @@ -75,7 +133,6 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p, { const char *arg; const int unset = flags & OPT_UNSET; - int err; if (unset && p->opt) return error(_("%s takes no value"), optname(opt, flags)); @@ -89,35 +146,55 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p, return opt->ll_callback(p, opt, NULL, unset); case OPTION_BIT: + { + intmax_t value = get_int_value(opt, flags); if (unset) - *(int *)opt->value &= ~opt->defval; + value &= ~opt->defval; else - *(int *)opt->value |= opt->defval; - return 0; + value |= opt->defval; + return set_int_value(opt, flags, value); + } case OPTION_NEGBIT: + { + intmax_t value = get_int_value(opt, flags); if (unset) - *(int *)opt->value |= opt->defval; + value |= opt->defval; else - *(int *)opt->value &= ~opt->defval; - return 0; + value &= ~opt->defval; + return set_int_value(opt, flags, value); + } case OPTION_BITOP: + { + intmax_t value = get_int_value(opt, flags); if (unset) BUG("BITOP can't have unset form"); - *(int *)opt->value &= ~opt->extra; - *(int *)opt->value |= opt->defval; - return 0; + value &= ~opt->extra; + value |= opt->defval; + return set_int_value(opt, flags, value); + } case OPTION_COUNTUP: - if (*(int *)opt->value < 0) - *(int *)opt->value = 0; - *(int *)opt->value = unset ? 0 : *(int *)opt->value + 1; - return 0; + { + size_t bits = CHAR_BIT * opt->precision; + intmax_t upper_bound = INTMAX_MAX >> (bitsizeof(intmax_t) - bits); + intmax_t value = get_int_value(opt, flags); + + if (value < 0) + value = 0; + if (unset) + value = 0; + else if (value < upper_bound) + value++; + else + return error(_("value for %s exceeds %"PRIdMAX), + optname(opt, flags), upper_bound); + return set_int_value(opt, flags, value); + } case OPTION_SET_INT: - *(int *)opt->value = unset ? 0 : opt->defval; - return 0; + return set_int_value(opt, flags, unset ? 0 : opt->defval); case OPTION_STRING: if (unset) @@ -131,21 +208,29 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p, case OPTION_FILENAME: { const char *value; - - FREE_AND_NULL(*(char **)opt->value); - - err = 0; + bool is_optional; if (unset) value = NULL; else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) - value = (const char *) opt->defval; - else - err = get_arg(p, opt, flags, &value); + value = (const char *)opt->defval; + else { + int err = get_arg(p, opt, flags, &value); + if (err) + return err; + } + if (!value) + return 0; - if (!err) - *(char **)opt->value = fix_filename(p->prefix, value); - return err; + is_optional = skip_prefix(value, ":(optional)", &value); + value = fix_filename(p->prefix, value); + if (is_optional && is_missing_file(value)) { + free((char *)value); + } else { + FREE_AND_NULL(*(char **)opt->value); + *(const char **)opt->value = value; + } + return 0; } case OPTION_CALLBACK: { @@ -199,23 +284,7 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p, return error(_("value %s for %s not in range [%"PRIdMAX",%"PRIdMAX"]"), arg, optname(opt, flags), (intmax_t)lower_bound, (intmax_t)upper_bound); - switch (opt->precision) { - case 1: - *(int8_t *)opt->value = value; - return 0; - case 2: - *(int16_t *)opt->value = value; - return 0; - case 4: - *(int32_t *)opt->value = value; - return 0; - case 8: - *(int64_t *)opt->value = value; - return 0; - default: - BUG("invalid precision for option %s", - optname(opt, flags)); - } + return set_int_value(opt, flags, value); } case OPTION_UNSIGNED: { @@ -266,7 +335,9 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p, } struct parse_opt_cmdmode_list { - int value, *value_ptr; + intmax_t value; + void *value_ptr; + size_t precision; const struct option *opt; const char *arg; enum opt_parsed flags; @@ -280,7 +351,7 @@ static void build_cmdmode_list(struct parse_opt_ctx_t *ctx, for (; opts->type != OPTION_END; opts++) { struct parse_opt_cmdmode_list *elem = ctx->cmdmode_list; - int *value_ptr = opts->value; + void *value_ptr = opts->value; if (!(opts->flags & PARSE_OPT_CMDMODE) || !value_ptr) continue; @@ -292,10 +363,13 @@ static void build_cmdmode_list(struct parse_opt_ctx_t *ctx, CALLOC_ARRAY(elem, 1); elem->value_ptr = value_ptr; - elem->value = *value_ptr; + elem->precision = opts->precision; + if (do_get_int_value(value_ptr, opts->precision, &elem->value)) + optbug(opts, "has invalid precision"); elem->next = ctx->cmdmode_list; ctx->cmdmode_list = elem; } + BUG_if_bug("invalid 'struct option'"); } static char *optnamearg(const struct option *opt, const char *arg, @@ -317,7 +391,13 @@ static enum parse_opt_result get_value(struct parse_opt_ctx_t *p, char *opt_name, *other_opt_name; for (; elem; elem = elem->next) { - if (*elem->value_ptr == elem->value) + intmax_t new_value; + + if (do_get_int_value(elem->value_ptr, elem->precision, + &new_value)) + BUG("impossible: invalid precision"); + + if (new_value == elem->value) continue; if (elem->opt && @@ -327,7 +407,7 @@ static enum parse_opt_result get_value(struct parse_opt_ctx_t *p, elem->opt = opt; elem->arg = arg; elem->flags = flags; - elem->value = *elem->value_ptr; + elem->value = new_value; } if (result || !elem) @@ -586,10 +666,14 @@ static void parse_options_check(const struct option *opts) opts->long_name && !(opts->flags & PARSE_OPT_NONEG)) optbug(opts, "OPTION_SET_INT 0 should not be negatable"); switch (opts->type) { - case OPTION_COUNTUP: + case OPTION_SET_INT: case OPTION_BIT: case OPTION_NEGBIT: - case OPTION_SET_INT: + case OPTION_BITOP: + case OPTION_COUNTUP: + if (!signed_int_fits(opts->defval, opts->precision)) + optbug(opts, "has invalid defval"); + /* fallthru */ case OPTION_NUMBER: if ((opts->flags & PARSE_OPT_OPTARG) || !(opts->flags & PARSE_OPT_NOARG)) @@ -876,10 +960,16 @@ static void free_preprocessed_options(struct option *options) free(options); } +#define USAGE_NORMAL 0 +#define USAGE_FULL 1 +#define USAGE_TO_STDOUT 0 +#define USAGE_TO_STDERR 1 + static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t *, const char * const *, const struct option *, - int, int); + int full_usage, + int usage_to_stderr); enum parse_opt_result parse_options_step(struct parse_opt_ctx_t *ctx, const struct option *options, @@ -1011,7 +1101,8 @@ enum parse_opt_result parse_options_step(struct parse_opt_ctx_t *ctx, } if (internal_help && !strcmp(arg + 2, "help-all")) - return usage_with_options_internal(ctx, usagestr, options, 1, 0); + return usage_with_options_internal(ctx, usagestr, options, + USAGE_FULL, USAGE_TO_STDOUT); if (internal_help && !strcmp(arg + 2, "help")) goto show_usage; switch (parse_long_opt(ctx, arg + 2, options)) { @@ -1052,7 +1143,8 @@ unknown: return PARSE_OPT_DONE; show_usage: - return usage_with_options_internal(ctx, usagestr, options, 0, 0); + return usage_with_options_internal(ctx, usagestr, options, + USAGE_NORMAL, USAGE_TO_STDOUT); } int parse_options_end(struct parse_opt_ctx_t *ctx) @@ -1261,7 +1353,7 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t if (!saw_empty_line && !*str) saw_empty_line = 1; - string_list_split(&list, str, '\n', -1); + string_list_split(&list, str, "\n", -1); for (j = 0; j < list.nr; j++) { const char *line = list.items[j].string; @@ -1367,7 +1459,8 @@ static enum parse_opt_result usage_with_options_internal(struct parse_opt_ctx_t void NORETURN usage_with_options(const char * const *usagestr, const struct option *opts) { - usage_with_options_internal(NULL, usagestr, opts, 0, 1); + usage_with_options_internal(NULL, usagestr, opts, + USAGE_NORMAL, USAGE_TO_STDERR); exit(129); } @@ -1375,9 +1468,16 @@ void show_usage_with_options_if_asked(int ac, const char **av, const char * const *usagestr, const struct option *opts) { - if (ac == 2 && !strcmp(av[1], "-h")) { - usage_with_options_internal(NULL, usagestr, opts, 0, 0); - exit(129); + if (ac == 2) { + if (!strcmp(av[1], "-h")) { + usage_with_options_internal(NULL, usagestr, opts, + USAGE_NORMAL, USAGE_TO_STDOUT); + exit(129); + } else if (!strcmp(av[1], "--help-all")) { + usage_with_options_internal(NULL, usagestr, opts, + USAGE_FULL, USAGE_TO_STDOUT); + exit(129); + } } } |
