Build on 09705696f7 (parse-options: introduce precision handling for `OPTION_INTEGER`, 2025-04-17) to support value variables of different sizes for PARSE_OPT_CMDMODE options. Do that by requiring their "precision" to be set and casting their "value" pointer accordingly. get_value() needs to access all PARSE_OPT_CMDMODE values in addition to the actual value it is supposed to get to detect conflicting changes. Give it an example struct option pointer in cmdmode_list instead of just the "value" pointer to allow it to use the proper "precision". Use optbug() in get_int_value() to report options with unsupported "precision" values without requiring enum opt_parsed flags, as we don't have them in build_cmdmode_list(). Use BUG right afterwards to abort for uses outside of build_cmdmode_list() by aborting immediately. Signed-off-by: René Scharfe <l.s.r@xxxxxx> --- builtin/am.c | 1 + parse-options.c | 36 ++++++++++++++++++++++++++--------- parse-options.h | 1 + t/helper/test-parse-options.c | 13 ++++++++++--- 4 files changed, 39 insertions(+), 12 deletions(-) diff --git a/builtin/am.c b/builtin/am.c index a800003340..c9d925f7b9 100644 --- a/builtin/am.c +++ b/builtin/am.c @@ -2406,6 +2406,7 @@ int cmd_am(int argc, .type = OPTION_CALLBACK, .long_name = "show-current-patch", .value = &resume_mode, + .precision = sizeof(resume_mode), .argh = "(diff|raw)", .help = N_("show the patch being applied"), .flags = PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP, diff --git a/parse-options.c b/parse-options.c index a9a39ecaef..da07a000a3 100644 --- a/parse-options.c +++ b/parse-options.c @@ -68,6 +68,23 @@ static char *fix_filename(const char *prefix, const char *file) return prefix_filename_except_for_dash(prefix, file); } +static intmax_t get_int_value(const struct option *opt) +{ + switch (opt->precision) { + case sizeof(int8_t): + return *(int8_t *)opt->value; + case sizeof(int16_t): + return *(int16_t *)opt->value; + case sizeof(int32_t): + return *(int32_t *)opt->value; + case sizeof(int64_t): + return *(int64_t *)opt->value; + default: + optbug(opt, "has invalid precision"); + BUG("invalid 'struct option'"); + } +} + static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p, const struct option *opt, enum opt_parsed flags, @@ -266,8 +283,8 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p, } struct parse_opt_cmdmode_list { - int value, *value_ptr; - const struct option *opt; + intmax_t value; + const struct option *opt, *reference_opt; const char *arg; enum opt_parsed flags; struct parse_opt_cmdmode_list *next; @@ -280,19 +297,18 @@ 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; - if (!(opts->flags & PARSE_OPT_CMDMODE) || !value_ptr) + if (!(opts->flags & PARSE_OPT_CMDMODE) || !opts->value) continue; - while (elem && elem->value_ptr != value_ptr) + while (elem && elem->reference_opt->value != opts->value) elem = elem->next; if (elem) continue; CALLOC_ARRAY(elem, 1); - elem->value_ptr = value_ptr; - elem->value = *value_ptr; + elem->reference_opt = opts; + elem->value = get_int_value(opts); elem->next = ctx->cmdmode_list; ctx->cmdmode_list = elem; } @@ -317,7 +333,9 @@ 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 = get_int_value(elem->reference_opt); + + if (new_value == elem->value) continue; if (elem->opt && @@ -327,7 +345,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) diff --git a/parse-options.h b/parse-options.h index 91c3e3c29b..c75a473c9e 100644 --- a/parse-options.h +++ b/parse-options.h @@ -269,6 +269,7 @@ struct option { .short_name = (s), \ .long_name = (l), \ .value = (v), \ + .precision = sizeof(*v), \ .help = (h), \ .flags = PARSE_OPT_CMDMODE|PARSE_OPT_NOARG|PARSE_OPT_NONEG | (f), \ .defval = (i), \ diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c index f2663dd0c0..1e03ff88f6 100644 --- a/t/helper/test-parse-options.c +++ b/t/helper/test-parse-options.c @@ -148,9 +148,16 @@ int cmd__parse_options(int argc, const char **argv) OPT_SET_INT(0, "set23", &integer, "set integer to 23", 23), OPT_CMDMODE(0, "mode1", &integer, "set integer to 1 (cmdmode option)", 1), OPT_CMDMODE(0, "mode2", &integer, "set integer to 2 (cmdmode option)", 2), - OPT_CALLBACK_F(0, "mode34", &integer, "(3|4)", - "set integer to 3 or 4 (cmdmode option)", - PARSE_OPT_CMDMODE, mode34_callback), + { + .type = OPTION_CALLBACK, + .long_name = "mode34", + .value = &integer, + .precision = sizeof(integer), + .argh = "(3|4)", + .help = "set integer to 3 or 4 (cmdmode option)", + .flags = PARSE_OPT_CMDMODE, + .callback = mode34_callback, + }, OPT_CALLBACK('L', "length", &integer, "str", "get length of <str>", length_callback), OPT_FILENAME('F', "file", &file, "set file to <file>"), -- 2.50.0