git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 3/5] parse-options: introduce precision handling for `OPTION_MAGNITUDE`

From
Patrick Steinhardt <ps@pks.im>
Date
Apr 1, 2025, 15:01 UTC
Message-ID
<20250401-b4-pks-parse-options-integers-v1-3-a628ad40c3b4@pks.im>
In-Reply-To
<20250401-b4-pks-parse-options-integers-v1-0-a628ad40c3b4@pks.im>

This commit is the equivalent to the preceding commit, but instead of introducing precision handling for `OPTION_INTEGER` we introduce it for `OPTION_MAGNITUDE`.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 parse-options.c               | 50 ++++++++++++++++++++++++++++++++++---------
 parse-options.h               |  1 +
 t/helper/test-parse-options.c |  3 +++
 t/t0040-parse-options.sh      | 18 +++++++++++++++-
 4 files changed, 61 insertions(+), 11 deletions(-)
diff --git a/parse-options.c b/parse-options.c
index dbda9b7cfe7..3954ee0e570 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -217,21 +217,51 @@ static enum parse_opt_result do_get_value(struct parse_opt_ctx_t *p,
 		}
 	}
 	case OPTION_MAGNITUDE:
+	{
+		uintmax_t upper_bound = 0;
+		unsigned long value;
+
+		/*
+		 * It's stupid, but the obvious way of calculating the upper
+		 * bound via `2 ^ n - 1` overflows.
+		 */
+		for (size_t i = 0; i < opt->precision * 8; i++)
+			upper_bound |= ((uintmax_t) 1 << i);
+
 		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 (!git_parse_ulong(arg, &value)) {
 			return error(_("%s expects a non-negative integer value"
 				       " with an optional k/m/g suffix"),
 				     optname(opt, flags));
-		return 0;
+		}
+
+		if (value > upper_bound)
+			return error(_("value %"PRIuMAX" for %s exceeds %"PRIuMAX),
+				     (uintmax_t) value, optname(opt, flags), upper_bound);
+
+		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);
diff --git a/parse-options.h b/parse-options.h
index 8d5f9c95f9c..4b561679581 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -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/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index b1275dfade4..46deb4317ef 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -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 m16 = 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_MAGNITUDE('m', "magnitude", &magnitude, "get a magnitude"),
+		OPT_MAGNITUDE(0, "m16", &m16, "get a 16 bit magnitude"),
 		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, "magnitude: %lu", magnitude);
+	show(&expect, &ret, "m16: %"PRIuMAX, (uintmax_t) m16);
 	show(&expect, &ret, "timestamp: %"PRItime, timestamp);
 	show(&expect, &ret, "string: %s", string ? string : "(not set)");
 	show(&expect, &ret, "abbrev: %d", abbrev);
diff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh
index e3ca7a27738..5f503b26cc8 100755
--- a/t/t0040-parse-options.sh
+++ b/t/t0040-parse-options.sh
@@ -25,6 +25,7 @@ usage: test-tool parse-options <options>
     --[no-]i16 <n>        get a 16 bit integer
     -j <n>                get a integer, too
     -m, --magnitude <n>   get a magnitude
+    --m16 <n>             get a 16 bit magnitude
     --[no-]set23          set integer to 23
     --mode1               set integer to 1 (cmdmode option)
     --mode2               set integer to 2 (cmdmode option)
@@ -139,6 +140,7 @@ boolean: 2
 integer: 1729
 i16: 0
 magnitude: 16384
+m16: 0
 timestamp: 0
 string: 123
 abbrev: 7
@@ -160,6 +162,7 @@ boolean: 2
 integer: 1729
 i16: 9000
 magnitude: 16384
+m16: 32768
 timestamp: 0
 string: 321
 abbrev: 10
@@ -171,7 +174,7 @@ EOF
 
 test_expect_success 'long options' '
 	test-tool parse-options --boolean --integer 1729 --i16 9000 --magnitude 16k \
-		--boolean --string2=321 --verbose --verbose --no-dry-run \
+		--m16 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 &&
@@ -184,6 +187,7 @@ test_expect_success 'abbreviate to something longer than SHA1 length' '
 	integer: 0
 	i16: 0
 	magnitude: 0
+	m16: 0
 	timestamp: 0
 	string: (not set)
 	abbrev: 100
@@ -259,6 +263,7 @@ boolean: 1
 integer: 13
 i16: 0
 magnitude: 0
+m16: 0
 timestamp: 0
 string: 123
 abbrev: 7
@@ -283,6 +288,7 @@ boolean: 0
 integer: 2
 i16: 0
 magnitude: 0
+m16: 0
 timestamp: 0
 string: (not set)
 abbrev: 7
@@ -351,6 +357,7 @@ boolean: 5
 integer: 4
 i16: 0
 magnitude: 0
+m16: 0
 timestamp: 0
 string: (not set)
 abbrev: 7
@@ -377,6 +384,7 @@ boolean: 1
 integer: 23
 i16: 0
 magnitude: 0
+m16: 0
 timestamp: 0
 string: (not set)
 abbrev: 7
@@ -457,6 +465,7 @@ boolean: 0
 integer: 0
 i16: 0
 magnitude: 0
+m16: 0
 timestamp: 0
 string: (not set)
 abbrev: 7
@@ -804,4 +813,11 @@ test_expect_success 'i16 limits range' '
 	test_grep "value -32769 for option .i16. not in range \[-32768,32767\]" err
 '
 
+test_expect_success 'm16 limits range' '
+	test-tool parse-options --m16 65535 >out &&
+	test_grep "m16: 65535" out &&
+	test_must_fail test-tool parse-options --m16 65536 2>err &&
+	test_grep "value 65536 for option .m16. exceeds 65535" err
+'
+
 test_done
-- 
2.49.0.604.gff1f9ca942.dirty
Previous: Patrick SteinhardtNext: Patrick Steinhardt
Message 5 of 37 in “parse-options: harden handling of integer values”
  1. 0/5 parse-options: harden handling of integer valuesPatrick Steinhardt, Apr 1, 2025
  2. 2/5 parse-options: introduce precision handling for `OPTION_INTEGER`Patrick Steinhardt, Apr 1, 2025
  3. René ScharfeApr 1, 2025
  4. Patrick SteinhardtApr 15, 2025
  5. 3/5 parse-options: introduce precision handling for `OPTION_MAGNITUDE`Patrick Steinhardt, Apr 1, 2025
  6. 4/5 parse-options: introduce `OPTION_UNSIGNED`Patrick Steinhardt, Apr 1, 2025
  7. 1/5 global: use designated initializers for optionsPatrick Steinhardt, Apr 1, 2025
  8. 5/5 parse-options: detect mismatches in integer signednessPatrick Steinhardt, Apr 1, 2025
  9. 0/5 parse-options: harden handling of integer valuesPatrick Steinhardt, Apr 15, 2025
  10. 1/5 global: use designated initializers for optionsPatrick Steinhardt, Apr 15, 2025
  11. 2/5 parse-options: introduce precision handling for `OPTION_INTEGER`Patrick Steinhardt, Apr 15, 2025
  12. Phillip WoodApr 15, 2025
  13. Patrick SteinhardtApr 16, 2025
  14. Junio C HamanoApr 15, 2025
  15. Patrick SteinhardtApr 16, 2025
  16. 3/5 parse-options: introduce precision handling for `OPTION_MAGNITUDE`Patrick Steinhardt, Apr 15, 2025
  17. 4/5 parse-options: introduce `OPTION_UNSIGNED`Patrick Steinhardt, Apr 15, 2025
  18. Phillip WoodApr 15, 2025
  19. Patrick SteinhardtApr 16, 2025
  20. phillip.wood123@gmail.comApr 16, 2025
  21. René ScharfeApr 15, 2025
  22. Patrick SteinhardtApr 16, 2025
  23. 5/5 parse-options: detect mismatches in integer signednessPatrick Steinhardt, Apr 15, 2025
  24. Junio C HamanoApr 15, 2025
  25. 0/7 parse-options: harden handling of integer valuesPatrick Steinhardt, Apr 16, 2025
  26. 2/7 parse-options: check for overflow when parsing integersPatrick Steinhardt, Apr 16, 2025
  27. 1/7 global: use designated initializers for optionsPatrick Steinhardt, Apr 16, 2025
  28. 4/7 parse-options: introduce precision handling for `OPTION_MAGNITUDE`Patrick Steinhardt, Apr 16, 2025
  29. 3/7 parse-options: introduce precision handling for `OPTION_INTEGER`Patrick Steinhardt, Apr 16, 2025
  30. Junio C HamanoApr 16, 2025
  31. 5/7 parse-options: introduce `OPTION_UNSIGNED`Patrick Steinhardt, Apr 16, 2025
  32. Junio C HamanoApr 16, 2025
  33. Patrick SteinhardtApr 17, 2025
  34. 7/7 parse-options: introduce bounded integer optionsPatrick Steinhardt, Apr 16, 2025
  35. Junio C HamanoApr 16, 2025
  36. Patrick SteinhardtApr 17, 2025
  37. 6/7 parse-options: detect mismatches in integer signednessPatrick Steinhardt, Apr 16, 2025

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.