]> git.ipfire.org Git - thirdparty/git.git/commitdiff
parse-options: introduce precision handling for `OPTION_UNSIGNED`
authorPatrick Steinhardt <ps@pks.im>
Thu, 17 Apr 2025 10:49:41 +0000 (12:49 +0200)
committerJunio C Hamano <gitster@pobox.com>
Thu, 17 Apr 2025 15:15:16 +0000 (08:15 -0700)
This commit is the equivalent to the preceding commit, but instead of
introducing precision handling for `OPTION_INTEGER` we introduce it for
`OPTION_UNSIGNED`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
parse-options.c
parse-options.h
parse.c
parse.h
t/helper/test-parse-options.c
t/t0040-parse-options.sh

index 768718a397242e90887f72efe4cd7a62842c2633..a9a39ecaef6c36c64c57115a1e552d19039ccbe5 100644 (file)
@@ -197,7 +197,7 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
 
                if (value < lower_bound)
                        return error(_("value %s for %s not in range [%"PRIdMAX",%"PRIdMAX"]"),
-                                    arg, optname(opt, flags), lower_bound, upper_bound);
+                                    arg, optname(opt, flags), (intmax_t)lower_bound, (intmax_t)upper_bound);
 
                switch (opt->precision) {
                case 1:
@@ -218,21 +218,47 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
                }
        }
        case OPTION_UNSIGNED:
+       {
+               uintmax_t upper_bound = UINTMAX_MAX >> (bitsizeof(uintmax_t) - CHAR_BIT * opt->precision);
+               uintmax_t value;
+
                if (unset) {
-                       *(unsigned long *)opt->value = 0;
-                       return 0;
-               }
-               if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
-                       *(unsigned long *)opt->value = opt->defval;
-                       return 0;
-               }
-               if (get_arg(p, opt, flags, &arg))
+                       value = 0;
+               } else if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
+                       value = opt->defval;
+               } else if (get_arg(p, opt, flags, &arg)) {
                        return -1;
-               if (!git_parse_ulong(arg, opt->value))
+               } else if (!*arg) {
+                       return error(_("%s expects a numerical value"),
+                                    optname(opt, flags));
+               } else if (!git_parse_unsigned(arg, &value, upper_bound)) {
+                       if (errno == ERANGE)
+                               return error(_("value %s for %s not in range [%"PRIdMAX",%"PRIdMAX"]"),
+                                            arg, optname(opt, flags), (uintmax_t) 0, upper_bound);
+
                        return error(_("%s expects a non-negative integer value"
                                       " with an optional k/m/g suffix"),
                                     optname(opt, flags));
-               return 0;
+               }
+
+               switch (opt->precision) {
+               case 1:
+                       *(uint8_t *)opt->value = value;
+                       return 0;
+               case 2:
+                       *(uint16_t *)opt->value = value;
+                       return 0;
+               case 4:
+                       *(uint32_t *)opt->value = value;
+                       return 0;
+               case 8:
+                       *(uint64_t *)opt->value = value;
+                       return 0;
+               default:
+                       BUG("invalid precision for option %s",
+                           optname(opt, flags));
+               }
+       }
 
        default:
                BUG("opt->type %d should not happen", opt->type);
index 4c430c7273c13eebfe9fb1855f0d64df3e05f237..dc460a26ff1e4c0379c604d2a5996c23fe9942f8 100644 (file)
@@ -281,6 +281,7 @@ struct option {
        .short_name = (s), \
        .long_name = (l), \
        .value = (v), \
+       .precision = sizeof(*v), \
        .argh = N_("n"), \
        .help = (h), \
        .flags = PARSE_OPT_NONEG, \
diff --git a/parse.c b/parse.c
index 3c47448ca675fbf3d67cb6969c40f602bd99d35d..48313571aab1290924fe2b0019bbeba012898e98 100644 (file)
--- a/parse.c
+++ b/parse.c
@@ -51,7 +51,7 @@ int git_parse_signed(const char *value, intmax_t *ret, intmax_t max)
        return 0;
 }
 
-static int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max)
+int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max)
 {
        if (value && *value) {
                char *end;
diff --git a/parse.h b/parse.h
index 6bb9a54d9ac8f782406fd33e59b9d42c6862d63e..ea32de9a91fbfbc3cbe4894937852e398e24dde6 100644 (file)
--- a/parse.h
+++ b/parse.h
@@ -2,6 +2,7 @@
 #define PARSE_H
 
 int git_parse_signed(const char *value, intmax_t *ret, intmax_t max);
+int git_parse_unsigned(const char *value, uintmax_t *ret, uintmax_t max);
 int git_parse_ssize_t(const char *, ssize_t *);
 int git_parse_ulong(const char *, unsigned long *);
 int git_parse_int(const char *value, int *ret);
index 3689aee831521f34cefd6367f676d95a80e685e0..f2663dd0c07279b26a1f10a966170239c0f34583 100644 (file)
@@ -120,6 +120,7 @@ int cmd__parse_options(int argc, const char **argv)
        };
        struct string_list expect = STRING_LIST_INIT_NODUP;
        struct string_list list = STRING_LIST_INIT_NODUP;
+       uint16_t u16 = 0;
        int16_t i16 = 0;
 
        struct option options[] = {
@@ -143,6 +144,7 @@ int cmd__parse_options(int argc, const char **argv)
                OPT_INTEGER(0, "i16", &i16, "get a 16 bit integer"),
                OPT_INTEGER('j', NULL, &integer, "get a integer, too"),
                OPT_UNSIGNED('u', "unsigned", &unsigned_integer, "get an unsigned integer"),
+               OPT_UNSIGNED(0, "u16", &u16, "get a 16 bit unsigned integer"),
                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),
@@ -214,6 +216,7 @@ int cmd__parse_options(int argc, const char **argv)
        show(&expect, &ret, "integer: %d", integer);
        show(&expect, &ret, "i16: %"PRIdMAX, (intmax_t) i16);
        show(&expect, &ret, "unsigned: %lu", unsigned_integer);
+       show(&expect, &ret, "u16: %"PRIuMAX, (uintmax_t) u16);
        show(&expect, &ret, "timestamp: %"PRItime, timestamp);
        show(&expect, &ret, "string: %s", string ? string : "(not set)");
        show(&expect, &ret, "abbrev: %d", abbrev);
index be785547eaddea3ff53d73ece6cfb51aa3846764..ca55ea8228c3789c5089d9141412224589ac18f7 100755 (executable)
@@ -25,6 +25,7 @@ usage: test-tool parse-options <options>
     --[no-]i16 <n>        get a 16 bit integer
     -j <n>                get a integer, too
     -u, --unsigned <n>    get an unsigned integer
+    --u16 <n>             get a 16 bit unsigned integer
     --[no-]set23          set integer to 23
     --mode1               set integer to 1 (cmdmode option)
     --mode2               set integer to 2 (cmdmode option)
@@ -141,6 +142,7 @@ boolean: 2
 integer: 1729
 i16: 0
 unsigned: 16384
+u16: 0
 timestamp: 0
 string: 123
 abbrev: 7
@@ -162,6 +164,7 @@ boolean: 2
 integer: 1729
 i16: 9000
 unsigned: 16384
+u16: 32768
 timestamp: 0
 string: 321
 abbrev: 10
@@ -173,7 +176,7 @@ EOF
 
 test_expect_success 'long options' '
        test-tool parse-options --boolean --integer 1729 --i16 9000 --unsigned 16k \
-               --boolean --string2=321 --verbose --verbose --no-dry-run \
+               --u16 32k --boolean --string2=321 --verbose --verbose --no-dry-run \
                --abbrev=10 --file fi.le --obsolete \
                >output 2>output.err &&
        test_must_be_empty output.err &&
@@ -186,6 +189,7 @@ test_expect_success 'abbreviate to something longer than SHA1 length' '
        integer: 0
        i16: 0
        unsigned: 0
+       u16: 0
        timestamp: 0
        string: (not set)
        abbrev: 100
@@ -261,6 +265,7 @@ boolean: 1
 integer: 13
 i16: 0
 unsigned: 0
+u16: 0
 timestamp: 0
 string: 123
 abbrev: 7
@@ -285,6 +290,7 @@ boolean: 0
 integer: 2
 i16: 0
 unsigned: 0
+u16: 0
 timestamp: 0
 string: (not set)
 abbrev: 7
@@ -353,6 +359,7 @@ boolean: 5
 integer: 4
 i16: 0
 unsigned: 0
+u16: 0
 timestamp: 0
 string: (not set)
 abbrev: 7
@@ -379,6 +386,7 @@ boolean: 1
 integer: 23
 i16: 0
 unsigned: 0
+u16: 0
 timestamp: 0
 string: (not set)
 abbrev: 7
@@ -459,6 +467,7 @@ boolean: 0
 integer: 0
 i16: 0
 unsigned: 0
+u16: 0
 timestamp: 0
 string: (not set)
 abbrev: 7
@@ -806,4 +815,11 @@ test_expect_success 'i16 limits range' '
        test_grep "value -32769 for option .i16. not in range \[-32768,32767\]" err
 '
 
+test_expect_success 'u16 limits range' '
+       test-tool parse-options --u16 65535 >out &&
+       test_grep "u16: 65535" out &&
+       test_must_fail test-tool parse-options --u16 65536 2>err &&
+       test_grep "value 65536 for option .u16. not in range \[0,65535\]" err
+'
+
 test_done