summaryrefslogtreecommitdiff
path: root/parse-options.c
diff options
context:
space:
mode:
authorJunio C Hamano <gitster@pobox.com>2025-04-24 17:25:33 -0700
committerJunio C Hamano <gitster@pobox.com>2025-04-24 17:25:34 -0700
commit2bc5414c411aab33c155b1070b7764ef6a49a02d (patch)
tree3f2b065f7c9c54838ab380ba5d16e7a9f742344b /parse-options.c
parent68e5342e191a2de216dbf712a6dbfa49282429c4 (diff)
parent791aeddfa2fdb9e830e24c50c97bb5e8bf3613e6 (diff)
Merge branch 'ps/parse-options-integers'
Update parse-options API to catch mistakes to pass address of an integral variable of a wrong type/size. * ps/parse-options-integers: parse-options: detect mismatches in integer signedness parse-options: introduce precision handling for `OPTION_UNSIGNED` parse-options: introduce precision handling for `OPTION_INTEGER` parse-options: rename `OPT_MAGNITUDE()` to `OPT_UNSIGNED()` parse-options: support unit factors in `OPT_INTEGER()` global: use designated initializers for options parse: fix off-by-one for minimum signed values
Diffstat (limited to 'parse-options.c')
-rw-r--r--parse-options.c102
1 files changed, 77 insertions, 25 deletions
diff --git a/parse-options.c b/parse-options.c
index 35fbb3b0d6..a9a39ecaef 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -73,7 +73,7 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
enum opt_parsed flags,
const char **argp)
{
- const char *s, *arg;
+ const char *arg;
const int unset = flags & OPT_UNSET;
int err;
@@ -172,41 +172,93 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
return (*opt->ll_callback)(p, opt, p_arg, p_unset);
}
case OPTION_INTEGER:
+ {
+ intmax_t upper_bound = INTMAX_MAX >> (bitsizeof(intmax_t) - CHAR_BIT * opt->precision);
+ intmax_t lower_bound = -upper_bound - 1;
+ intmax_t value;
+
if (unset) {
- *(int *)opt->value = 0;
- return 0;
- }
- if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
- *(int *)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 (!*arg)
+ } else if (!*arg) {
return error(_("%s expects a numerical value"),
optname(opt, flags));
- *(int *)opt->value = strtol(arg, (char **)&s, 10);
- if (*s)
- return error(_("%s expects a numerical value"),
+ } else if (!git_parse_signed(arg, &value, upper_bound)) {
+ if (errno == ERANGE)
+ return error(_("value %s for %s not in range [%"PRIdMAX",%"PRIdMAX"]"),
+ arg, optname(opt, flags), lower_bound, upper_bound);
+
+ return error(_("%s expects an integer value with an optional k/m/g suffix"),
optname(opt, flags));
- return 0;
+ }
- case OPTION_MAGNITUDE:
- if (unset) {
- *(unsigned long *)opt->value = 0;
+ if (value < lower_bound)
+ 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;
- }
- if (opt->flags & PARSE_OPT_OPTARG && !p->opt) {
- *(unsigned long *)opt->value = opt->defval;
+ 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));
}
- if (get_arg(p, opt, flags, &arg))
+ }
+ case OPTION_UNSIGNED:
+ {
+ uintmax_t upper_bound = UINTMAX_MAX >> (bitsizeof(uintmax_t) - CHAR_BIT * opt->precision);
+ uintmax_t value;
+
+ if (unset) {
+ 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);
@@ -656,7 +708,7 @@ static void show_negated_gitcomp(const struct option *opts, int show_all,
case OPTION_STRING:
case OPTION_FILENAME:
case OPTION_INTEGER:
- case OPTION_MAGNITUDE:
+ case OPTION_UNSIGNED:
case OPTION_CALLBACK:
case OPTION_BIT:
case OPTION_NEGBIT:
@@ -708,7 +760,7 @@ static int show_gitcomp(const struct option *opts, int show_all)
case OPTION_STRING:
case OPTION_FILENAME:
case OPTION_INTEGER:
- case OPTION_MAGNITUDE:
+ case OPTION_UNSIGNED:
case OPTION_CALLBACK:
if (opts->flags & PARSE_OPT_NOARG)
break;