From: Paul Eggert Date: Sun, 19 Dec 2021 19:48:14 +0000 (-0500) Subject: Remove arbitrary limits on intcmp integers X-Git-Tag: 4.3.90~128 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=7192d0ec4a51a62bf63c7c411500f88bc36377ad;p=thirdparty%2Fmake.git Remove arbitrary limits on intcmp integers We don't need to parse strings into C integer values to compare them. * src/function.c (parse_textint): Find boundaries of a numeric string. (func_intcmp): Use parse_textint() to compare integers textually. * tests/scripts/functions/intcmp: Test with extra-large numbers. --- diff --git a/src/function.c b/src/function.c index af6ad135..c107a387 100644 --- a/src/function.c +++ b/src/function.c @@ -1276,6 +1276,49 @@ func_sort (char *o, char **argv, const char *funcname UNUSED) return o; } +/* + Traverse NUMBER consisting of optional leading white space, optional + sign, digits, and optional trailing white space. + If number is not of the proper form, diagnose with MSG. Otherwise, + return the address of of the first character after NUMBER, store + into *SIGN an integer consistent with the number's sign (-1, 0, or 1) + and store into *NUMSTART the address of NUMBER's first nonzero digit + (if NUMBER contains only zero digits, store the address of the first + character after NUMBER). +*/ +static const char * +parse_textint (const char *number, const char *msg, + int *sign, const char **numstart) +{ + const char *after_sign, *after_number; + const char *p = next_token (number); + int negative = *p == '-'; + int nonzero = 0; + + if (*p == '\0') + OS (fatal, *expanding_var, _("%s: empty value"), msg); + + p += negative || *p == '+'; + after_sign = p; + + while (*p == '0') + p++; + *numstart = p; + + while (ISDIGIT (*p)) + if (*p++ == '0') + nonzero = 1; + after_number = p; + *sign = negative ? -nonzero : nonzero; + + /* Check for extra non-whitespace stuff after the value. */ + if (after_number == after_sign || *next_token (p) != '\0') + OSS (fatal, *expanding_var, "%s: '%s'", msg, number); + + return after_number; +} + + /* $(intcmp lhs,rhs[,lt-part[,eq-part[,gt-part]]]) @@ -1293,34 +1336,40 @@ func_sort (char *o, char **argv, const char *funcname UNUSED) static char * func_intcmp (char *o, char **argv, const char *funcname UNUSED) { + int lsign, rsign; + const char *lnum, *rnum; char *lhs_str = expand_argument (argv[0], NULL); char *rhs_str = expand_argument (argv[1], NULL); - long long lhs, rhs; + const char *llim = parse_textint (lhs_str, _("non-numeric first argument to 'intcmp' function"), &lsign, &lnum); + const char *rlim = parse_textint (rhs_str, _("non-numeric second argument to 'intcmp' function"), &rsign, &rnum); + ptrdiff_t llen = llim - lnum; + ptrdiff_t rlen = rlim - rnum; + int cmp = lsign - rsign; - lhs = parse_numeric (lhs_str, - _("invalid first argument to 'intcmp' function")); - rhs = parse_numeric (rhs_str, - _("invalid second argument to 'intcmp' function")); - free (lhs_str); - free (rhs_str); + if (cmp == 0) + { + cmp = (llen > rlen) - (llen < rlen); + if (cmp == 0) + cmp = memcmp (lnum, rnum, llen); + } argv += 2; - if (*argv == NULL) + /* Handle the special case where there are only two arguments. */ + if (!*argv && cmp == 0) { - if (lhs == rhs) - { - char buf[INTSTR_LENGTH+1]; - sprintf (buf, "%lld", lhs); - o = variable_buffer_output(o, buf, strlen (buf)); - } - return o; + if (lsign < 0) + o = variable_buffer_output (o, "-", 1); + o = variable_buffer_output(o, lnum - !lsign, llen + !lsign); } - if (lhs >= rhs) + free (lhs_str); + free (rhs_str); + + if (*argv && cmp >= 0) { ++argv; - if (lhs > rhs && *argv && *(argv + 1)) + if (cmp > 0 && *argv && *(argv + 1)) ++argv; } diff --git a/tests/scripts/functions/intcmp b/tests/scripts/functions/intcmp index 6f3c1500..7466ff97 100644 --- a/tests/scripts/functions/intcmp +++ b/tests/scripts/functions/intcmp @@ -12,6 +12,7 @@ z = 0 p = 1000000000 min = -9223372036854775808 max = 9223372036854775807 +huge = 8857889956778499040639527525992734031025567913257255490371761260681427 .RECIPEPREFIX = > all: > @echo 0_1 $(intcmp $n,$n) @@ -31,28 +32,36 @@ all: > @echo 4_1 $(intcmp $(max),$(min),lt,eq,gt) > @echo 4_2 $(intcmp $(min),$(min),lt,eq,gt) > @echo 4_3 $(intcmp $(max),$(max),lt,eq,gt) -', '', "0_1 -10\n0_2\n0_3\n1_1\n1_2 lt\n1_3\n2_1 lt\n2_2 ge\n2_3 ge\n3_0\n3_1 lt\n3_2 gt\n3_3 eq\n4_0 lt\n4_1 gt\n4_2 eq\n4_3 eq\n"); +> @echo 5_0 $(intcmp -$(huge),$(huge),lt,eq,gt) +> @echo 5_1 $(intcmp $(huge),-$(huge),lt,eq,gt) +> @echo 5_2 $(intcmp -$(huge),-$(huge),lt,eq,gt) +> @echo 5_3 $(intcmp +$(huge),$(huge),lt,eq,gt) +', '', "0_1 -10\n0_2\n0_3\n1_1\n1_2 lt\n1_3\n2_1 lt\n2_2 ge\n2_3 ge\n3_0\n3_1 lt\n3_2 gt\n3_3 eq\n4_0 lt\n4_1 gt\n4_2 eq\n4_3 eq\n5_0 lt\n5_1 gt\n5_2 eq\n5_3 eq\n"); # Test error conditions run_make_test(' intcmp-e1: ; @echo $(intcmp 12a,1,foo) intcmp-e2: ; @echo $(intcmp 0,,foo) -intcmp-e3: ; @echo $(intcmp -1,9999999999999999999,foo) -intcmp-e4: ; @echo $(intcmp -1) -intcmp-e5: ; @echo $(intcmp ,55)', +intcmp-e3: ; @echo $(intcmp -1,) +intcmp-e4: ; @echo $(intcmp ,55)', 'intcmp-e1', - "#MAKEFILE#:2: *** invalid first argument to 'intcmp' function: '12a'. Stop.", + "#MAKEFILE#:2: *** non-numeric first argument to 'intcmp' function: '12a'. Stop.", 512); run_make_test(undef, 'intcmp-e2', - "#MAKEFILE#:3: *** invalid second argument to 'intcmp' function: empty value. Stop.", + "#MAKEFILE#:3: *** non-numeric second argument to 'intcmp' function: empty value. Stop.", 512); run_make_test(undef, 'intcmp-e3', - "#MAKEFILE#:4: *** invalid second argument to 'intcmp' function: '9999999999999999999' out of range. Stop.", + "#MAKEFILE#:4: *** non-numeric second argument to 'intcmp' function: empty value. Stop.", + 512); + +run_make_test(undef, + 'intcmp-e4', + "#MAKEFILE#:5: *** non-numeric first argument to 'intcmp' function: empty value. Stop.", 512);