]> git.ipfire.org Git - thirdparty/git.git/commitdiff
parse-options: add precision handling for OPTION_SET_INT
authorRené Scharfe <l.s.r@web.de>
Wed, 9 Jul 2025 09:45:24 +0000 (11:45 +0200)
committerJunio C Hamano <gitster@pobox.com>
Wed, 9 Jul 2025 15:39:26 +0000 (08:39 -0700)
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>
builtin/update-index.c
parse-options.c
parse-options.h
t/helper/test-parse-options.c

index 538b619ba4f3bb9a91befac68176a5ca1b8ac465..0c1d4ed55beb0870061bf6f6e0114c90aa4f033e 100644 (file)
@@ -981,6 +981,7 @@ int cmd_update_index(int argc,
                        .type = OPTION_SET_INT,
                        .long_name = "assume-unchanged",
                        .value = &mark_valid_only,
+                       .precision = sizeof(mark_valid_only),
                        .help = N_("mark files as \"not changing\""),
                        .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG,
                        .defval = MARK_FLAG,
@@ -989,6 +990,7 @@ int cmd_update_index(int argc,
                        .type = OPTION_SET_INT,
                        .long_name = "no-assume-unchanged",
                        .value = &mark_valid_only,
+                       .precision = sizeof(mark_valid_only),
                        .help = N_("clear assumed-unchanged bit"),
                        .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG,
                        .defval = UNMARK_FLAG,
@@ -997,6 +999,7 @@ int cmd_update_index(int argc,
                        .type = OPTION_SET_INT,
                        .long_name = "skip-worktree",
                        .value = &mark_skip_worktree_only,
+                       .precision = sizeof(mark_skip_worktree_only),
                        .help = N_("mark files as \"index-only\""),
                        .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG,
                        .defval = MARK_FLAG,
@@ -1005,6 +1008,7 @@ int cmd_update_index(int argc,
                        .type = OPTION_SET_INT,
                        .long_name = "no-skip-worktree",
                        .value = &mark_skip_worktree_only,
+                       .precision = sizeof(mark_skip_worktree_only),
                        .help = N_("clear skip-worktree bit"),
                        .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG,
                        .defval = UNMARK_FLAG,
@@ -1079,6 +1083,7 @@ int cmd_update_index(int argc,
                        .type = OPTION_SET_INT,
                        .long_name = "fsmonitor-valid",
                        .value = &mark_fsmonitor_only,
+                       .precision = sizeof(mark_fsmonitor_only),
                        .help = N_("mark files as fsmonitor valid"),
                        .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG,
                        .defval = MARK_FLAG,
@@ -1087,6 +1092,7 @@ int cmd_update_index(int argc,
                        .type = OPTION_SET_INT,
                        .long_name = "no-fsmonitor-valid",
                        .value = &mark_fsmonitor_only,
+                       .precision = sizeof(mark_fsmonitor_only),
                        .help = N_("clear fsmonitor valid bit"),
                        .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG,
                        .defval = UNMARK_FLAG,
index ddac008a5ea7a77c4f66b0497c426f7f29379a89..639f41b83b697bb73e726c550da329c28fd8965e 100644 (file)
@@ -88,6 +88,36 @@ static int do_get_int_value(const void *value, size_t precision, intmax_t *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,
@@ -136,8 +166,7 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
                return 0;
 
        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)
@@ -219,23 +248,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:
        {
@@ -617,10 +630,13 @@ 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_SET_INT:
+                       if (!signed_int_fits(opts->defval, opts->precision))
+                               optbug(opts, "has invalid defval");
+                       /* fallthru */
                case OPTION_COUNTUP:
                case OPTION_BIT:
                case OPTION_NEGBIT:
-               case OPTION_SET_INT:
                case OPTION_NUMBER:
                case OPTION_BITOP:
                        if ((opts->flags & PARSE_OPT_OPTARG) ||
index c75a473c9ecdfc0848e8d4dcc89607c117e90fbe..71516e4b5b9fed29a9668bdf1897a36e19f137a1 100644 (file)
@@ -190,6 +190,7 @@ struct option {
        .short_name = (s), \
        .long_name = (l), \
        .value = (v), \
+       .precision = sizeof(*v), \
        .help = (h), \
        .flags = PARSE_OPT_NOARG | (f), \
        .defval = (i), \
@@ -260,6 +261,7 @@ struct option {
        .short_name = (s), \
        .long_name = (l), \
        .value = (v), \
+       .precision = sizeof(*v), \
        .help = (h), \
        .flags = PARSE_OPT_NOARG | PARSE_OPT_HIDDEN, \
        .defval = 1, \
index 1e03ff88f6ff89f23455f4ebb8f547598e148323..2ba2546d70a0e91118fb785ff0061aaba1da7610 100644 (file)
@@ -131,6 +131,7 @@ int cmd__parse_options(int argc, const char **argv)
                        .short_name = 'B',
                        .long_name = "no-fear",
                        .value = &boolean,
+                       .precision = sizeof(boolean),
                        .help = "be brave",
                        .flags = PARSE_OPT_NOARG | PARSE_OPT_NONEG,
                        .defval = 1,