]> git.ipfire.org Git - thirdparty/make.git/commitdiff
Remove arbitrary limits on intcmp integers
authorPaul Eggert <eggert@cs.ucla.edu>
Sun, 19 Dec 2021 19:48:14 +0000 (14:48 -0500)
committerPaul Smith <psmith@gnu.org>
Sun, 19 Dec 2021 21:34:19 +0000 (16:34 -0500)
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.

src/function.c
tests/scripts/functions/intcmp

index af6ad135f1c432a8ea7ed706767da08f5259c1f1..c107a387aa1cddd60ce352da94f4865d94ffe555 100644 (file)
@@ -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;
     }
 
index 6f3c1500f01b64fde0a4f3824805552612f18add..7466ff97132836530a53364f44b945e80c3d3077 100644 (file)
@@ -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);