parse-options: add precision handling for OPTION_SET_INT
Similar to 09705696f7 (parse-options: introduce precision handling for `OPTION_INTEGER`, 2025-04-17) support value variables of different sizes for OPTION_SET_INT. Do that by requiring their "precision" to be set, casting their "value" pointer accordingly and checking whether the value fits. Factor out the casting code from the part of do_get_value() that handles OPTION_INTEGER to avoid code duplication. We're going to use it in the next patches as well. 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
c898bbc5e4b582c28379bc64b7f9c9ec96106993
4 files changed
+45
-20
builtin/update-index.c
+6
@@ -981,6 +981,7 @@ int cmd_update_index(int argc,
981
.type = OPTION_SET_INT,
982
.long_name = "assume-unchanged",
983
.value = &mark_valid_only,
984
+ .precision = sizeof(mark_valid_only),
985
.help = N_("mark files as \"not changing\""),
986
.flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG,
987
.defval = MARK_FLAG,
@@ -989,6 +990,7 @@ int cmd_update_index(int argc,
990
.type = OPTION_SET_INT,
991
.long_name = "no-assume-unchanged",
992
.value = &mark_valid_only,
993
+ .precision = sizeof(mark_valid_only),
994
.help = N_("clear assumed-unchanged bit"),
995
.flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG,
996
.defval = UNMARK_FLAG,
@@ -997,6 +999,7 @@ int cmd_update_index(int argc,
999
.type = OPTION_SET_INT,
1000
.long_name = "skip-worktree",
1001
.value = &mark_skip_worktree_only,
1002
+ .precision = sizeof(mark_skip_worktree_only),
1003
.help = N_("mark files as \"index-only\""),
1004
.flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG,
1005
.defval = MARK_FLAG,
@@ -1005,6 +1008,7 @@ int cmd_update_index(int argc,
1008
.type = OPTION_SET_INT,
1009
.long_name = "no-skip-worktree",
1010
.value = &mark_skip_worktree_only,
1011
+ .precision = sizeof(mark_skip_worktree_only),
1012
.help = N_("clear skip-worktree bit"),
1013
.flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG,
1014
.defval = UNMARK_FLAG,
@@ -1079,6 +1083,7 @@ int cmd_update_index(int argc,
1083
.type = OPTION_SET_INT,
1084
.long_name = "fsmonitor-valid",
1085
.value = &mark_fsmonitor_only,
1086
+ .precision = sizeof(mark_fsmonitor_only),
1087
.help = N_("mark files as fsmonitor valid"),
1088
.flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG,
1089
.defval = MARK_FLAG,
@@ -1087,6 +1092,7 @@ int cmd_update_index(int argc,
1092
.type = OPTION_SET_INT,
1093
.long_name = "no-fsmonitor-valid",
1094
.value = &mark_fsmonitor_only,
1095
+ .precision = sizeof(mark_fsmonitor_only),
1096
.help = N_("clear fsmonitor valid bit"),
1097
.flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG,
1098
.defval = UNMARK_FLAG,
parse-options.c
+36
-20
@@ -88,6 +88,36 @@ static int do_get_int_value(const void *value, size_t precision, intmax_t *ret)
88
}
89
}
90
91
+static enum parse_opt_result set_int_value(const struct option *opt,
92
+ enum opt_parsed flags,
93
+ intmax_t value)
94
+{
95
+ switch (opt->precision) {
96
+ case sizeof(int8_t):
97
+ *(int8_t *)opt->value = value;
98
+ return 0;
99
+ case sizeof(int16_t):
100
+ *(int16_t *)opt->value = value;
101
+ return 0;
102
+ case sizeof(int32_t):
103
+ *(int32_t *)opt->value = value;
104
+ return 0;
105
+ case sizeof(int64_t):
106
+ *(int64_t *)opt->value = value;
107
+ return 0;
108
+ default:
109
+ BUG("invalid precision for option %s", optname(opt, flags));
110
+ }
111
+}
112
+
113
+static int signed_int_fits(intmax_t value, size_t precision)
114
+{
115
+ size_t bits = precision * CHAR_BIT;
116
+ intmax_t upper_bound = INTMAX_MAX >> (bitsizeof(intmax_t) - bits);
117
+ intmax_t lower_bound = -upper_bound - 1;
118
+ return lower_bound <= value && value <= upper_bound;
119
+}
120
+
121
static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
122
const struct option *opt,
123
enum opt_parsed flags,
@@ -136,8 +166,7 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
166
return 0;
167
168
case OPTION_SET_INT:
139
- *(int *)opt->value = unset ? 0 : opt->defval;
140
- return 0;
169
+ return set_int_value(opt, flags, unset ? 0 : opt->defval);
170
171
case OPTION_STRING:
172
if (unset)
@@ -219,23 +248,7 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
248
return error(_("value %s for %s not in range [%"PRIdMAX",%"PRIdMAX"]"),
249
arg, optname(opt, flags), (intmax_t)lower_bound, (intmax_t)upper_bound);
250
222
- switch (opt->precision) {
223
- case 1:
224
- *(int8_t *)opt->value = value;
225
- return 0;
226
- case 2:
227
- *(int16_t *)opt->value = value;
228
- return 0;
229
- case 4:
230
- *(int32_t *)opt->value = value;
231
- return 0;
232
- case 8:
233
- *(int64_t *)opt->value = value;
234
- return 0;
235
- default:
236
- BUG("invalid precision for option %s",
237
- optname(opt, flags));
238
- }
251
+ return set_int_value(opt, flags, value);
252
}
253
case OPTION_UNSIGNED:
254
{
@@ -617,10 +630,13 @@ static void parse_options_check(const struct option *opts)
630
opts->long_name && !(opts->flags & PARSE_OPT_NONEG))
631
optbug(opts, "OPTION_SET_INT 0 should not be negatable");
632
switch (opts->type) {
633
+ case OPTION_SET_INT:
634
+ if (!signed_int_fits(opts->defval, opts->precision))
635
+ optbug(opts, "has invalid defval");
636
+ /* fallthru */
637
case OPTION_COUNTUP:
638
case OPTION_BIT:
639
case OPTION_NEGBIT:
623
- case OPTION_SET_INT:
640
case OPTION_NUMBER:
641
case OPTION_BITOP:
642
if ((opts->flags & PARSE_OPT_OPTARG) ||
parse-options.h
+2
@@ -190,6 +190,7 @@ struct option {
190
.short_name = (s), \
191
.long_name = (l), \
192
.value = (v), \
193
+ .precision = sizeof(*v), \
194
.help = (h), \
195
.flags = PARSE_OPT_NOARG | (f), \
196
.defval = (i), \
@@ -260,6 +261,7 @@ struct option {
261
.short_name = (s), \
262
.long_name = (l), \
263
.value = (v), \
264
+ .precision = sizeof(*v), \
265
.help = (h), \
266
.flags = PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, \
267
.defval = 1, \
t/helper/test-parse-options.c
+1
@@ -131,6 +131,7 @@ int cmd__parse_options(int argc, const char **argv)
131
.short_name = 'B',
132
.long_name = "no-fear",
133
.value = &boolean,
134
+ .precision = sizeof(boolean),
135
.help = "be brave",
136
.flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG,
137
.defval = 1,