factor: fix integer validation and GMP fallback
authorPádraig Brady <P@draigBrady.com>
Mon, 8 Oct 2012 14:48:43 +0000 (15:48 +0100)
committerPádraig Brady <P@draigBrady.com>
Mon, 8 Oct 2012 23:07:35 +0000 (00:07 +0100)
In the recent factor rewrite, the GMP code
wasn't actually used; just an error was printed
on integer overflow.  While fixing that it was noticed
that correct input validation wasn't done in all cases
when falling back to the GMP code.

* src/factor.c (print_factors) Fallback to GMP on overflow.
(strto2uintmax): Scan the string for invalid characters,
so that case can be detected independently of overflow.
Return an error when an empty string is passed.
Also allow leading spaces and '+' in input numbers.
* tests/misc/factor.pl: Ensure the GMP code is exercised
when compiled in. Also add a test to verify leading
spaces and '+' are allowed.

src/factor.c
tests/misc/factor.pl

index d5f248a..73c59e9 100644 (file)
@@ -2222,14 +2222,30 @@ static strtol_error
 strto2uintmax (uintmax_t *hip, uintmax_t *lop, const char *s)
 {
   unsigned int lo_carry;
-  uintmax_t hi, lo;
+  uintmax_t hi = 0, lo = 0;
 
-  strtol_error err;
+  strtol_error err = LONGINT_INVALID;
 
-  hi = lo = 0;
+  /* Skip initial spaces and '+'.  */
   for (;;)
     {
-      unsigned int c = *s++;
+      char c = *s;
+      if (c == ' ')
+        s++;
+      else if (c == '+')
+        {
+          s++;
+          break;
+        }
+      else
+        break;
+    }
+
+  /* Initial scan for invalid digits.  */
+  const char *p = s;
+  for (;;)
+    {
+      unsigned int c = *p++;
       if (c == 0)
         break;
 
@@ -2238,9 +2254,17 @@ strto2uintmax (uintmax_t *hip, uintmax_t *lop, const char *s)
           err = LONGINT_INVALID;
           break;
         }
-      c -= '0';
 
       err = LONGINT_OK;           /* we've seen at least one valid digit */
+    }
+
+  for (;err == LONGINT_OK;)
+    {
+      unsigned int c = *s++;
+      if (c == 0)
+        break;
+
+      c -= '0';
 
       if (UNLIKELY (hi > ~(uintmax_t)0 / 10))
         {
@@ -2343,6 +2367,10 @@ print_factors (const char *input)
         }
       break;
 
+    case LONGINT_OVERFLOW:
+      /* Try GMP.  */
+      break;
+
     default:
       error (0, 0, _("%s is not a valid positive integer"), quote (input));
       return false;
index 38a5037..8f6edaa 100755 (executable)
@@ -28,6 +28,7 @@ my @Tests =
     (
      ['1', '9',          {OUT => '3 3'}],
      ['1a', '7',         {OUT => '7'}],
+     ['1b', '  +7',      {OUT => '7'}],
      ['2', '4294967291', {OUT => '4294967291'}],
      ['3', '4294967292', {OUT => '2 2 3 3 7 11 31 151 331'}],
      ['4', '4294967293', {OUT => '9241 464773'}],
@@ -74,6 +75,16 @@ my @Tests =
      ['bug-2012-e', '17754345703', {OUT => '94219 188437'}],
     );
 
+# If we have GMP support, append tests to exercise it.
+system "grep -w HAVE_GMP $ENV{CONFIG_HEADER} > /dev/null" == 0
+  and push (@Tests,
+            ['bug-gmp-2_sup_128', '340282366920938463463374607431768211456',
+             {OUT => '2 'x127 . '2'}],
+            ['bug-gmp-2_sup_256',
+             '115792089237316195423570985008687907853'
+             . '269984665640564039457584007913129639936',
+             {OUT => '2 'x255 . '2'}]);
+
 # Prepend the command line argument and append a newline to end
 # of each expected 'OUT' string.
 my $t;
@@ -81,7 +92,7 @@ my $t;
 Test:
 foreach $t (@Tests)
   {
-    my $arg1 = $t->[1];
+    (my $arg1 = $t->[1]) =~ s| *\+?||;
 
     # Don't fiddle with expected OUT string if there's a nonzero exit status.
     foreach my $e (@$t)