parse-options: add precision handling for PARSE_OPT_CMDMODE

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. Call the function that does the raw casting do_get_int_value() to reserve the name get_int_value() for a more friendly wrapper we're going to introduce in one of the next patches. Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

René Scharfe committed Jul 9, 2025 at 11:45 UTC 0d3e045b34f38d23e6160ce8aae363f358bd5cdc
4 files changed +48 -8
builtin/am.c
+1
@@ -2406,6 +2406,7 @@ int cmd_am(int argc,
2406 .type = OPTION_CALLBACK,
2407 .long_name = "show-current-patch",
2408 .value = &resume_mode,
2409 + .precision = sizeof(resume_mode),
2410 .argh = "(diff|raw)",
2411 .help = N_("show the patch being applied"),
2412 .flags = PARSE_OPT_CMDMODE | PARSE_OPT_OPTARG | PARSE_OPT_NONEG | PARSE_OPT_LITERAL_ARGHELP,
parse-options.c
+36 -5
@@ -68,6 +68,26 @@ static char *fix_filename(const char *prefix, const char *file)
68 return prefix_filename_except_for_dash(prefix, file);
69 }
70
71 +static int do_get_int_value(const void *value, size_t precision, intmax_t *ret)
72 +{
73 + switch (precision) {
74 + case sizeof(int8_t):
75 + *ret = *(int8_t *)value;
76 + return 0;
77 + case sizeof(int16_t):
78 + *ret = *(int16_t *)value;
79 + return 0;
80 + case sizeof(int32_t):
81 + *ret = *(int32_t *)value;
82 + return 0;
83 + case sizeof(int64_t):
84 + *ret = *(int64_t *)value;
85 + return 0;
86 + default:
87 + return -1;
88 + }
89 +}
90 +
91 static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
92 const struct option *opt,
93 enum opt_parsed flags,
@@ -266,7 +286,9 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
286 }
287
288 struct parse_opt_cmdmode_list {
269 - int value, *value_ptr;
289 + intmax_t value;
290 + void *value_ptr;
291 + size_t precision;
292 const struct option *opt;
293 const char *arg;
294 enum opt_parsed flags;
@@ -280,7 +302,7 @@ static void build_cmdmode_list(struct parse_opt_ctx_t *ctx,
302
303 for (; opts->type != OPTION_END; opts++) {
304 struct parse_opt_cmdmode_list *elem = ctx->cmdmode_list;
283 - int *value_ptr = opts->value;
305 + void *value_ptr = opts->value;
306
307 if (!(opts->flags & PARSE_OPT_CMDMODE) || !value_ptr)
308 continue;
@@ -292,10 +314,13 @@ static void build_cmdmode_list(struct parse_opt_ctx_t *ctx,
314
315 CALLOC_ARRAY(elem, 1);
316 elem->value_ptr = value_ptr;
295 - elem->value = *value_ptr;
317 + elem->precision = opts->precision;
318 + if (do_get_int_value(value_ptr, opts->precision, &elem->value))
319 + optbug(opts, "has invalid precision");
320 elem->next = ctx->cmdmode_list;
321 ctx->cmdmode_list = elem;
322 }
323 + BUG_if_bug("invalid 'struct option'");
324 }
325
326 static char *optnamearg(const struct option *opt, const char *arg,
@@ -317,7 +342,13 @@ static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
342 char *opt_name, *other_opt_name;
343
344 for (; elem; elem = elem->next) {
320 - if (*elem->value_ptr == elem->value)
345 + intmax_t new_value;
346 +
347 + if (do_get_int_value(elem->value_ptr, elem->precision,
348 + &new_value))
349 + BUG("impossible: invalid precision");
350 +
351 + if (new_value == elem->value)
352 continue;
353
354 if (elem->opt &&
@@ -327,7 +358,7 @@ static enum parse_opt_result get_value(struct parse_opt_ctx_t *p,
358 elem->opt = opt;
359 elem->arg = arg;
360 elem->flags = flags;
330 - elem->value = *elem->value_ptr;
361 + elem->value = new_value;
362 }
363
364 if (result || !elem)
parse-options.h
+1
@@ -269,6 +269,7 @@ struct option {
269 .short_name = (s), \
270 .long_name = (l), \
271 .value = (v), \
272 + .precision = sizeof(*v), \
273 .help = (h), \
274 .flags = PARSE_OPT_CMDMODE|PARSE_OPT_NOARG|PARSE_OPT_NONEG | (f), \
275 .defval = (i), \
t/helper/test-parse-options.c
+10 -3
@@ -148,9 +148,16 @@ int cmd__parse_options(int argc, const char **argv)
148 OPT_SET_INT(0, "set23", &integer, "set integer to 23", 23),
149 OPT_CMDMODE(0, "mode1", &integer, "set integer to 1 (cmdmode option)", 1),
150 OPT_CMDMODE(0, "mode2", &integer, "set integer to 2 (cmdmode option)", 2),
151 - OPT_CALLBACK_F(0, "mode34", &integer, "(3|4)",
152 - "set integer to 3 or 4 (cmdmode option)",
153 - PARSE_OPT_CMDMODE, mode34_callback),
151 + {
152 + .type = OPTION_CALLBACK,
153 + .long_name = "mode34",
154 + .value = &integer,
155 + .precision = sizeof(integer),
156 + .argh = "(3|4)",
157 + .help = "set integer to 3 or 4 (cmdmode option)",
158 + .flags = PARSE_OPT_CMDMODE,
159 + .callback = mode34_callback,
160 + },
161 OPT_CALLBACK('L', "length", &integer, "str",
162 "get length of <str>", length_callback),
163 OPT_FILENAME('F', "file", &file, "set file to <file>"),