bug-gmp
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

libgmp v4.1.4: mpz_set_str() fails heavily


From: ThMO
Subject: libgmp v4.1.4: mpz_set_str() fails heavily
Date: Mon, 10 Oct 2005 23:54:04 +0200

Hello folks,

the conversion routine mpz_set_str() fails several times:
· an optional `+' sign is *not* an error
  e.g.: mpz_set_str( n, "+13", 10);
· trailing white-space *is* an error
  e.g.: mpz_set_str( n, "13 ", 10);
· white-space after a possible `-' sign *is* an error
  e.g.: mpz_set_str( n, "  -000 13", 10);
· if the given base is 0, which means the base shall be set upon scanning,
  and the base-signifier isn't followed by at least one valid digit, this
  *must* be an error
  e.g.: mpz_set_str( n, "0x", 0);  mpz_set_str( n, "0B", 0);
  cross check this with strtol()
  alternatively use my attached quick hack and try to give the base as
  follows:
    ./bug 0x abc
  -> this will correctly state an error, since 0x signifies base 16, but
     there are valid digits missing
· if base is 2 or 16 *and* the respective base-signifier is given at the
  front of the number, this *is* correct
  e.g.: mpz_set_str( n, "0xabc1262", 16);  mpz_set_str( n, "0b1101", 2);
· etc.pp.

I've attached a small C file, which contains the following:
· my_mpz_set_str():
  · this routine correctly handles not only the above examples
  · furthermore will this routine behave like strtol() and returns a pointer
    to the offensive character, which fails the conversion, in case the given
    input was faulty
  · there is an entry point at which the neccessary setup for mpn_set_str()
    could be coded, in order to replace the faulty library routine - just
    grep for FIXME
  · it's coded to *quickly* perform it's task
· main():
  this is a simple test-bed in order to cross check various input parameters
  Usage: ./bug base number

THX for listening.

CU Tom.
(Thomas M.Ott)
Germany
/* bug.c: compare buggy mpz_set_str() with my correct wrapper routine
 * ©2005-10-10 by (ThMO)
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <gmp.h>

#ifndef NULL
  #define NULL  ( (void *) 0 )
#endif

#ifndef TRUE
  #define FALSE 0
  #define TRUE  ( !FALSE)
#endif

#if     __GNUC__ >= 2
  #define  INLINE               inline
  #define  NORETURN             /* ((noreturn)) */
  #define  NORETURN2            __attribute__ ( (noreturn) )
  #define  PFL( fmt, par)       \
           __attribute__ ( (format( printf, (fmt), (par))) )
#else
  #define  INLINE               /* no inlining */
  #define  NORETURN             /* no pre-attributes */
  #define  NORETURN2            /* no post-attributes */
  #define  PFL( fmt, par)       /* no format checking */
#endif


static char
  *progname;

static int
  my_mpz_set_str( mpz_t, char *, char **, int);

static NORETURN void
  error( const char *, ...) NORETURN2 PFL( 1, 2);


static int
my_mpz_set_str( mpz_t n, char *ptr, char **endptr, int base)
{
  int            realbase, ch, errcode;
  unsigned char  is_neg;
  char           *str, *hold;

  is_neg= 0;
  str= ptr;
rescan:
  switch ( ch= *str++)
    {
      case ' ':  case '\t':  case '\n':
      case '\f':  case '\r':  case '\v':
        goto  rescan;
      case '-':
        ++is_neg;
      case '+':
        ch= *str++;
      default:
        break;
    }
  switch ( realbase= base)
    {
      case 0:
        realbase= 10;
        if ( ch == '0')
          switch ( ch= *str++)
            {
              case 'x':  case 'X':
                realbase= 16;
                ch= *str++;
                break;
              case 'b':  case 'B':
                realbase= 2;
                ch= *str++;
                break;
              default:
                ch= '0';
                --str;
                realbase= 8;
                break;
            }
        break;
      case 2:
        if ( ch == '0')
          switch ( ch= *str++)
            {
              case 'b':  case 'B':
                ch= *str++;
              default:
                break;
            }
        break;
      case 16:
        if ( ch == '0')
          switch ( ch= *str++)
            {
              case 'x':  case 'X':
                ch= *str++;
              default:
                break;
            }
      default:
        break;
    }
  for ( hold= str;  ch == '0';  ch= *str++)
    ;
  for ( errcode= 0;  ;  ch= *str++)
    {
      switch ( ch)
        {
          case 'a':  case 'b':  case 'c':  case 'd':  case 'e':
          case 'f':  case 'g':  case 'h':  case 'i':  case 'j':
          case 'k':  case 'l':  case 'm':  case 'n':  case 'o':
          case 'p':  case 'q':  case 'r':  case 's':  case 't':
          case 'u':  case 'v':  case 'w':  case 'x':  case 'y':
          case 'z':
            ch -= 'a'- 'A';
          case 'A':  case 'B':  case 'C':  case 'D':  case 'E':
          case 'F':  case 'G':  case 'H':  case 'I':  case 'J':
          case 'K':  case 'L':  case 'M':  case 'N':  case 'O':
          case 'P':  case 'Q':  case 'R':  case 'S':  case 'T':
          case 'U':  case 'V':  case 'W':  case 'X':  case 'Y':
          case 'Z':
            ch -= 'A'- ( '9'+ 1);
          case '0':  case '1':  case '2':  case '3':  case '4':
          case '5':  case '6':  case '7':  case '8':  case '9':
            if ( ( ch -= '0') >= 0 && ch < realbase)
              continue;         /* FIXME: prepare for mpn_set_str() directly */
            goto  flag_error;
          case '\0':
            if ( hold != str)
              {
                errcode= mpz_set_str( n, hold- 1, realbase);
                if ( errcode == 0 && is_neg)
                  mpz_neg( n, n);
                goto  all_done;
              }
          default:
      flag_error:
            if ( hold == str)
              str= ptr+ 1;
            break;
        }
      --errcode;
    all_done:
      if ( endptr != NULL)
        *endptr= str- 1;
      return( errcode);
    }
}

int
main( int argc, char **argv)
{
  mpz_t  n;
  int    res, base;
  char   *p, *endp;

  p= strrchr( *argv, '/');
  progname= p == NULL ? *argv : p+ 1;

  mpz_init( n);

  if ( argc != 3)
    NORETURN error( "missing parameters\nUsage: %s base number", progname);

  p= argv[ 1];
  base= strtol( p, &endp, 0);
  if ( p == endp || *endp != '\0' || base < 0 || base > 36)
    NORETURN error( "invalid base `%s'", p);

  p= argv[ 2];
  res= mpz_set_str( n, p, base);
  gmp_printf( "%s: str= \"%s\", n= %Zd, base= %d, res= %d\n",
              "lgmp", p, n, base, res);
  res= my_mpz_set_str( n, p, &endp, base);
  gmp_printf( "%s: str= \"%s\", n= %Zd, base= %d, res= %d\n",
              "mine", p, n, base, res);
  if ( res != 0)
    printf( "failed at: %*s\n", ( endp- p)+ 2, "^");

  mpz_clear( n);
  NORETURN exit( EXIT_SUCCESS);
}

static NORETURN void
error( const char *fmt, ...)
{
  va_list  par;

  fprintf( stderr, "%s: error: ", progname);
  va_start( par, fmt);
  vfprintf( stderr, fmt, par);
  va_end( par);
  fprintf( stderr, "\n");
  NORETURN exit( EXIT_FAILURE);
}

reply via email to

[Prev in Thread] Current Thread [Next in Thread]