Blame scanf/doscan.c

Packit 5c3484
/* __gmp_doscan -- formatted input internals.
Packit 5c3484
Packit 5c3484
   THE FUNCTIONS IN THIS FILE ARE FOR INTERNAL USE ONLY.  THEY'RE ALMOST
Packit 5c3484
   CERTAIN TO BE SUBJECT TO INCOMPATIBLE CHANGES OR DISAPPEAR COMPLETELY IN
Packit 5c3484
   FUTURE GNU MP RELEASES.
Packit 5c3484
Packit 5c3484
Copyright 2001-2003 Free Software Foundation, Inc.
Packit 5c3484
Packit 5c3484
This file is part of the GNU MP Library.
Packit 5c3484
Packit 5c3484
The GNU MP Library is free software; you can redistribute it and/or modify
Packit 5c3484
it under the terms of either:
Packit 5c3484
Packit 5c3484
  * the GNU Lesser General Public License as published by the Free
Packit 5c3484
    Software Foundation; either version 3 of the License, or (at your
Packit 5c3484
    option) any later version.
Packit 5c3484
Packit 5c3484
or
Packit 5c3484
Packit 5c3484
  * the GNU General Public License as published by the Free Software
Packit 5c3484
    Foundation; either version 2 of the License, or (at your option) any
Packit 5c3484
    later version.
Packit 5c3484
Packit 5c3484
or both in parallel, as here.
Packit 5c3484
Packit 5c3484
The GNU MP Library is distributed in the hope that it will be useful, but
Packit 5c3484
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
Packit 5c3484
or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
Packit 5c3484
for more details.
Packit 5c3484
Packit 5c3484
You should have received copies of the GNU General Public License and the
Packit 5c3484
GNU Lesser General Public License along with the GNU MP Library.  If not,
Packit 5c3484
see https://www.gnu.org/licenses/.  */
Packit 5c3484
Packit 5c3484
#define _GNU_SOURCE    /* for DECIMAL_POINT in langinfo.h */
Packit 5c3484
Packit 5c3484
#include "config.h"	/* needed for the HAVE_, could also move gmp incls */
Packit 5c3484
Packit 5c3484
#include <stdarg.h>
Packit 5c3484
#include <ctype.h>
Packit 5c3484
#include <stddef.h>    /* for ptrdiff_t */
Packit 5c3484
#include <stdio.h>
Packit 5c3484
#include <stdlib.h>    /* for strtol */
Packit 5c3484
#include <string.h>
Packit 5c3484
Packit 5c3484
#if HAVE_LANGINFO_H
Packit 5c3484
#include <langinfo.h>  /* for nl_langinfo */
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#if HAVE_LOCALE_H
Packit 5c3484
#include <locale.h>    /* for localeconv */
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#if HAVE_INTTYPES_H
Packit 5c3484
# include <inttypes.h> /* for intmax_t */
Packit 5c3484
#else
Packit 5c3484
# if HAVE_STDINT_H
Packit 5c3484
#  include <stdint.h>
Packit 5c3484
# endif
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#if HAVE_SYS_TYPES_H
Packit 5c3484
#include <sys/types.h> /* for quad_t */
Packit 5c3484
#endif
Packit 5c3484
Packit 5c3484
#include "gmp.h"
Packit 5c3484
#include "gmp-impl.h"
Packit 5c3484
Packit 5c3484
Packit 5c3484
/* Change this to "#define TRACE(x) x" for some traces. */
Packit 5c3484
#define TRACE(x)
Packit 5c3484
Packit 5c3484
Packit 5c3484
/* General:
Packit 5c3484
Packit 5c3484
       It's necessary to parse up the format string to recognise the GMP
Packit 5c3484
       extra types F, Q and Z.  Other types and conversions are passed
Packit 5c3484
       across to the standard sscanf or fscanf via funs->scan, for ease of
Packit 5c3484
       implementation.  This is essential in the case of something like glibc
Packit 5c3484
       %p where the pointer format isn't actually documented.
Packit 5c3484
Packit 5c3484
       Because funs->scan doesn't get the whole input it can't put the right
Packit 5c3484
       values in for %n, so that's handled in __gmp_doscan.  Neither sscanf
Packit 5c3484
       nor fscanf directly indicate how many characters were read, so an
Packit 5c3484
       extra %n is appended to each run for that.  For fscanf this merely
Packit 5c3484
       supports our %n output, but for sscanf it lets funs->step move us
Packit 5c3484
       along the input string.
Packit 5c3484
Packit 5c3484
       Whitespace and literal matches in the format string, including %%,
Packit 5c3484
       are handled directly within __gmp_doscan.  This is reasonably
Packit 5c3484
       efficient, and avoids some suspicious behaviour observed in various
Packit 5c3484
       system libc's.  GLIBC 2.2.4 for instance returns 0 on
Packit 5c3484
Packit 5c3484
	   sscanf(" ", " x")
Packit 5c3484
       or
Packit 5c3484
	   sscanf(" ", " x%d",&n)
Packit 5c3484
Packit 5c3484
       whereas we think they should return EOF, since end-of-string is
Packit 5c3484
       reached when a match of "x" is required.
Packit 5c3484
Packit 5c3484
       For standard % conversions, funs->scan is called once for each
Packit 5c3484
       conversion.  If we had vfscanf and vsscanf and could rely on their
Packit 5c3484
       fixed text matching behaviour then we could call them with multiple
Packit 5c3484
       consecutive standard conversions.  But plain fscanf and sscanf work
Packit 5c3484
       fine, and parsing one field at a time shouldn't be too much of a
Packit 5c3484
       slowdown.
Packit 5c3484
Packit 5c3484
   gmpscan:
Packit 5c3484
Packit 5c3484
       gmpscan reads a gmp type.  It's only used from one place, but is a
Packit 5c3484
       separate subroutine to avoid a big chunk of complicated code in the
Packit 5c3484
       middle of __gmp_doscan.  Within gmpscan a couple of loopbacks make it
Packit 5c3484
       possible to share code for parsing integers, rationals and floats.
Packit 5c3484
Packit 5c3484
       In gmpscan normally one char of lookahead is maintained, but when width
Packit 5c3484
       is reached that stops, on the principle that an fgetc/ungetc of a char
Packit 5c3484
       past where we're told to stop would be undesirable.  "chars" is how many
Packit 5c3484
       characters have been read so far, including the current c.  When
Packit 5c3484
       chars==width and another character is desired then a jump is done to the
Packit 5c3484
       "convert" stage.  c is invalid and mustn't be unget'ed in this case;
Packit 5c3484
       chars is set to width+1 to indicate that.
Packit 5c3484
Packit 5c3484
       gmpscan normally returns the number of characters read.  -1 means an
Packit 5c3484
       invalid field, -2 means EOF reached before any matching characters
Packit 5c3484
       were read.
Packit 5c3484
Packit 5c3484
       For hex floats, the mantissa part is passed to mpf_set_str, then the
Packit 5c3484
       exponent is applied with mpf_mul_exp or mpf_div_2exp.  This is easier
Packit 5c3484
       than teaching mpf_set_str about an exponent factor (ie. 2) differing
Packit 5c3484
       from the mantissa radix point factor (ie. 16).  mpf_mul_exp and
Packit 5c3484
       mpf_div_2exp will preserve the application requested precision, so
Packit 5c3484
       nothing in that respect is lost by making this a two-step process.
Packit 5c3484
Packit 5c3484
   Matching and errors:
Packit 5c3484
Packit 5c3484
       C99 7.19.6.2 paras 9 and 10 say an input item is read as the longest
Packit 5c3484
       string which is a match for the appropriate type, or a prefix of a
Packit 5c3484
       match.  With that done, if it's only a prefix then the result is a
Packit 5c3484
       matching failure, ie. invalid input.
Packit 5c3484
Packit 5c3484
       This rule seems fairly clear, but doesn't seem to be universally
Packit 5c3484
       applied in system C libraries.  Even GLIBC doesn't seem to get it
Packit 5c3484
       right, insofar as it seems to accept some apparently invalid forms.
Packit 5c3484
       Eg. glibc 2.3.1 accepts "0x" for a "%i", where a reading of the
Packit 5c3484
       standard would suggest a non-empty sequence of digits should be
Packit 5c3484
       required after an "0x".
Packit 5c3484
Packit 5c3484
       A footnote to 7.19.6.2 para 17 notes how this input item reading can
Packit 5c3484
       mean inputs acceptable to strtol are not acceptable to fscanf.  We
Packit 5c3484
       think this confirms our reading of "0x" as invalid.
Packit 5c3484
Packit 5c3484
       Clearly gmp_sscanf could backtrack to a longest input which was a
Packit 5c3484
       valid match for a given item, but this is not done, since C99 says
Packit 5c3484
       sscanf is identical to fscanf, so we make gmp_sscanf identical to
Packit 5c3484
       gmp_fscanf.
Packit 5c3484
Packit 5c3484
   Types:
Packit 5c3484
Packit 5c3484
       C99 says "ll" is for long long, and "L" is for long double floats.
Packit 5c3484
       Unfortunately in GMP 4.1.1 we documented the two as equivalent.  This
Packit 5c3484
       doesn't affect us directly, since both are passed through to plain
Packit 5c3484
       scanf.  It seems wisest not to try to enforce the C99 rule.  This is
Packit 5c3484
       consistent with what we said before, though whether it actually
Packit 5c3484
       worked was always up to the C library.
Packit 5c3484
Packit 5c3484
   Alternatives:
Packit 5c3484
Packit 5c3484
       Consideration was given to using separate code for gmp_fscanf and
Packit 5c3484
       gmp_sscanf.  The sscanf case could zip across a string doing literal
Packit 5c3484
       matches or recognising digits in gmpscan, rather than making a
Packit 5c3484
       function call fun->get per character.  The fscanf could use getc
Packit 5c3484
       rather than fgetc too, which might help those systems where getc is a
Packit 5c3484
       macro or otherwise inlined.  But none of this scanning and converting
Packit 5c3484
       will be particularly fast, so the two are done together to keep it a
Packit 5c3484
       little simpler for now.
Packit 5c3484
Packit 5c3484
       Various multibyte string issues are not addressed, for a start C99
Packit 5c3484
       scanf says the format string is multibyte.  Since we pass %c, %s and
Packit 5c3484
       %[ to the system scanf, they might do multibyte reads already, but
Packit 5c3484
       it's another matter whether or not that can be used, since our digit
Packit 5c3484
       and whitespace parsing is only unibyte.  The plan is to quietly
Packit 5c3484
       ignore multibyte locales for now.  This is not as bad as it sounds,
Packit 5c3484
       since GMP is presumably used mostly on numbers, which can be
Packit 5c3484
       perfectly adequately treated in plain ASCII.
Packit 5c3484
Packit 5c3484
*/
Packit 5c3484
Packit 5c3484
Packit 5c3484
struct gmp_doscan_params_t {
Packit 5c3484
  int	base;
Packit 5c3484
  int	ignore;
Packit 5c3484
  char	type;
Packit 5c3484
  int	width;
Packit 5c3484
};
Packit 5c3484
Packit 5c3484
Packit 5c3484
#define GET(c)			\
Packit 5c3484
  do {				\
Packit 5c3484
    ASSERT (chars <= width);	\
Packit 5c3484
    chars++;			\
Packit 5c3484
    if (chars > width)		\
Packit 5c3484
      goto convert;		\
Packit 5c3484
    (c) = (*funs->get) (data);	\
Packit 5c3484
  } while (0)
Packit 5c3484
Packit 5c3484
/* store into "s", extending if necessary */
Packit 5c3484
#define STORE(c)							\
Packit 5c3484
  do {									\
Packit 5c3484
    ASSERT (s_upto <= s_alloc);						\
Packit 5c3484
    if (s_upto >= s_alloc)						\
Packit 5c3484
      {									\
Packit 5c3484
	size_t	s_alloc_new = s_alloc + S_ALLOC_STEP;			\
Packit 5c3484
	s = __GMP_REALLOCATE_FUNC_TYPE (s, s_alloc, s_alloc_new, char); \
Packit 5c3484
	s_alloc = s_alloc_new;						\
Packit 5c3484
      }									\
Packit 5c3484
    s[s_upto++] = c;							\
Packit 5c3484
  } while (0)
Packit 5c3484
Packit 5c3484
#define S_ALLOC_STEP  512
Packit 5c3484
Packit 5c3484
static int
Packit 5c3484
gmpscan (const struct gmp_doscan_funs_t *funs, void *data,
Packit 5c3484
	 const struct gmp_doscan_params_t *p, void *dst)
Packit 5c3484
{
Packit 5c3484
  int	  chars, c, base, first, width, seen_point, seen_digit, hexfloat;
Packit 5c3484
  size_t  s_upto, s_alloc, hexexp;
Packit 5c3484
  char	  *s;
Packit 5c3484
  int	  invalid = 0;
Packit 5c3484
Packit 5c3484
  TRACE (printf ("gmpscan\n"));
Packit 5c3484
Packit 5c3484
  ASSERT (p->type == 'F' || p->type == 'Q' || p->type == 'Z');
Packit 5c3484
Packit 5c3484
  c = (*funs->get) (data);
Packit 5c3484
  if (c == EOF)
Packit 5c3484
    return -2;
Packit 5c3484
Packit 5c3484
  chars = 1;
Packit 5c3484
  first = 1;
Packit 5c3484
  seen_point = 0;
Packit 5c3484
  width = (p->width == 0 ? INT_MAX-1 : p->width);
Packit 5c3484
  base = p->base;
Packit 5c3484
  s_alloc = S_ALLOC_STEP;
Packit 5c3484
  s = __GMP_ALLOCATE_FUNC_TYPE (s_alloc, char);
Packit 5c3484
  s_upto = 0;
Packit 5c3484
  hexfloat = 0;
Packit 5c3484
  hexexp = 0;
Packit 5c3484
Packit 5c3484
 another:
Packit 5c3484
  seen_digit = 0;
Packit 5c3484
  if (c == '-')
Packit 5c3484
    {
Packit 5c3484
      STORE (c);
Packit 5c3484
      goto get_for_sign;
Packit 5c3484
    }
Packit 5c3484
  else if (c == '+')
Packit 5c3484
    {
Packit 5c3484
      /* don't store '+', it's not accepted by mpz_set_str etc */
Packit 5c3484
    get_for_sign:
Packit 5c3484
      GET (c);
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  if (base == 0)
Packit 5c3484
    {
Packit 5c3484
      base = 10;		  /* decimal if no base indicator */
Packit 5c3484
      if (c == '0')
Packit 5c3484
	{
Packit 5c3484
	  seen_digit = 1;	  /* 0 alone is a valid number */
Packit 5c3484
	  if (p->type != 'F')
Packit 5c3484
	    base = 8;		  /* leading 0 is octal, for non-floats */
Packit 5c3484
	  STORE (c);
Packit 5c3484
	  GET (c);
Packit 5c3484
	  if (c == 'x' || c == 'X')
Packit 5c3484
	    {
Packit 5c3484
	      base = 16;
Packit 5c3484
	      seen_digit = 0;	  /* must have digits after an 0x */
Packit 5c3484
	      if (p->type == 'F') /* don't pass 'x' to mpf_set_str_point */
Packit 5c3484
		hexfloat = 1;
Packit 5c3484
	      else
Packit 5c3484
		STORE (c);
Packit 5c3484
	      GET (c);
Packit 5c3484
	    }
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
 digits:
Packit 5c3484
  for (;;)
Packit 5c3484
    {
Packit 5c3484
      if (base == 16)
Packit 5c3484
	{
Packit 5c3484
	  if (! isxdigit (c))
Packit 5c3484
	    break;
Packit 5c3484
	}
Packit 5c3484
      else
Packit 5c3484
	{
Packit 5c3484
	  if (! isdigit (c))
Packit 5c3484
	    break;
Packit 5c3484
	  if (base == 8 && (c == '8' || c == '9'))
Packit 5c3484
	    break;
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      seen_digit = 1;
Packit 5c3484
      STORE (c);
Packit 5c3484
      GET (c);
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  if (first)
Packit 5c3484
    {
Packit 5c3484
      /* decimal point */
Packit 5c3484
      if (p->type == 'F' && ! seen_point)
Packit 5c3484
	{
Packit 5c3484
	  /* For a multi-character decimal point, if the first character is
Packit 5c3484
	     present then all of it must be, otherwise the input is
Packit 5c3484
	     considered invalid.  */
Packit 5c3484
	  const char  *point = GMP_DECIMAL_POINT;
Packit 5c3484
	  int	      pc = (unsigned char) *point++;
Packit 5c3484
	  if (c == pc)
Packit 5c3484
	    {
Packit 5c3484
	      for (;;)
Packit 5c3484
		{
Packit 5c3484
		  STORE (c);
Packit 5c3484
		  GET (c);
Packit 5c3484
		  pc = (unsigned char) *point++;
Packit 5c3484
		  if (pc == '\0')
Packit 5c3484
		    break;
Packit 5c3484
		  if (c != pc)
Packit 5c3484
		    goto set_invalid;
Packit 5c3484
		}
Packit 5c3484
	      seen_point = 1;
Packit 5c3484
	      goto digits;
Packit 5c3484
	    }
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      /* exponent */
Packit 5c3484
      if (p->type == 'F')
Packit 5c3484
	{
Packit 5c3484
	  if (hexfloat && (c == 'p' || c == 'P'))
Packit 5c3484
	    {
Packit 5c3484
	      hexexp = s_upto; /* exponent location */
Packit 5c3484
	      base = 10;       /* exponent in decimal */
Packit 5c3484
	      goto exponent;
Packit 5c3484
	    }
Packit 5c3484
	  else if (! hexfloat && (c == 'e' || c == 'E'))
Packit 5c3484
	    {
Packit 5c3484
	    exponent:
Packit 5c3484
	      /* must have at least one digit in the mantissa, just an exponent
Packit 5c3484
		 is not good enough */
Packit 5c3484
	      if (! seen_digit)
Packit 5c3484
		goto set_invalid;
Packit 5c3484
Packit 5c3484
	    do_second:
Packit 5c3484
	      first = 0;
Packit 5c3484
	      STORE (c);
Packit 5c3484
	      GET (c);
Packit 5c3484
	      goto another;
Packit 5c3484
	    }
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      /* denominator */
Packit 5c3484
      if (p->type == 'Q' && c == '/')
Packit 5c3484
	{
Packit 5c3484
	  /* must have at least one digit in the numerator */
Packit 5c3484
	  if (! seen_digit)
Packit 5c3484
	    goto set_invalid;
Packit 5c3484
Packit 5c3484
	  /* now look for at least one digit in the denominator */
Packit 5c3484
	  seen_digit = 0;
Packit 5c3484
Packit 5c3484
	  /* allow the base to be redetermined for "%i" */
Packit 5c3484
	  base = p->base;
Packit 5c3484
	  goto do_second;
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
 convert:
Packit 5c3484
  if (! seen_digit)
Packit 5c3484
    {
Packit 5c3484
    set_invalid:
Packit 5c3484
      invalid = 1;
Packit 5c3484
      goto done;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  if (! p->ignore)
Packit 5c3484
    {
Packit 5c3484
      STORE ('\0');
Packit 5c3484
      TRACE (printf ("	convert \"%s\"\n", s));
Packit 5c3484
Packit 5c3484
      /* We ought to have parsed out a valid string above, so just test
Packit 5c3484
	 mpz_set_str etc with an ASSERT.  */
Packit 5c3484
      switch (p->type) {
Packit 5c3484
      case 'F':
Packit 5c3484
	{
Packit 5c3484
	  mpf_ptr  f = (mpf_ptr) dst;
Packit 5c3484
	  if (hexexp != 0)
Packit 5c3484
	    s[hexexp] = '\0';
Packit 5c3484
	  ASSERT_NOCARRY (mpf_set_str (f, s, hexfloat ? 16 : 10));
Packit 5c3484
	  if (hexexp != 0)
Packit 5c3484
	    {
Packit 5c3484
	      char *dummy;
Packit 5c3484
	      long  exp;
Packit 5c3484
	      exp = strtol (s + hexexp + 1, &dummy, 10);
Packit 5c3484
	      if (exp >= 0)
Packit 5c3484
		mpf_mul_2exp (f, f, (unsigned long) exp);
Packit 5c3484
	      else
Packit 5c3484
		mpf_div_2exp (f, f, - (unsigned long) exp);
Packit 5c3484
	    }
Packit 5c3484
	}
Packit 5c3484
	break;
Packit 5c3484
      case 'Q':
Packit 5c3484
	ASSERT_NOCARRY (mpq_set_str ((mpq_ptr) dst, s, p->base));
Packit 5c3484
	break;
Packit 5c3484
      case 'Z':
Packit 5c3484
	ASSERT_NOCARRY (mpz_set_str ((mpz_ptr) dst, s, p->base));
Packit 5c3484
	break;
Packit 5c3484
      default:
Packit 5c3484
	ASSERT (0);
Packit 5c3484
	/*FALLTHRU*/
Packit 5c3484
	break;
Packit 5c3484
      }
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
 done:
Packit 5c3484
  ASSERT (chars <= width+1);
Packit 5c3484
  if (chars != width+1)
Packit 5c3484
    {
Packit 5c3484
      (*funs->unget) (c, data);
Packit 5c3484
      TRACE (printf ("	ungetc %d, to give %d chars\n", c, chars-1));
Packit 5c3484
    }
Packit 5c3484
  chars--;
Packit 5c3484
Packit 5c3484
  (*__gmp_free_func) (s, s_alloc);
Packit 5c3484
Packit 5c3484
  if (invalid)
Packit 5c3484
    {
Packit 5c3484
      TRACE (printf ("	invalid\n"));
Packit 5c3484
      return -1;
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
  TRACE (printf ("  return %d chars (cf width %d)\n", chars, width));
Packit 5c3484
  return chars;
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
Packit 5c3484
/* Read and discard whitespace, if any.  Return number of chars skipped.
Packit 5c3484
   Whitespace skipping never provokes the EOF return from __gmp_doscan, so
Packit 5c3484
   it's not necessary to watch for EOF from funs->get, */
Packit 5c3484
static int
Packit 5c3484
skip_white (const struct gmp_doscan_funs_t *funs, void *data)
Packit 5c3484
{
Packit 5c3484
  int  c;
Packit 5c3484
  int  ret = 0;
Packit 5c3484
Packit 5c3484
  do
Packit 5c3484
    {
Packit 5c3484
      c = (funs->get) (data);
Packit 5c3484
      ret++;
Packit 5c3484
    }
Packit 5c3484
  while (isspace (c));
Packit 5c3484
Packit 5c3484
  (funs->unget) (c, data);
Packit 5c3484
  ret--;
Packit 5c3484
Packit 5c3484
  TRACE (printf ("  skip white %d\n", ret));
Packit 5c3484
  return ret;
Packit 5c3484
}
Packit 5c3484
Packit 5c3484
Packit 5c3484
int
Packit 5c3484
__gmp_doscan (const struct gmp_doscan_funs_t *funs, void *data,
Packit 5c3484
	      const char *orig_fmt, va_list orig_ap)
Packit 5c3484
{
Packit 5c3484
  struct gmp_doscan_params_t  param;
Packit 5c3484
  va_list     ap;
Packit 5c3484
  char	      *alloc_fmt;
Packit 5c3484
  const char  *fmt, *this_fmt, *end_fmt;
Packit 5c3484
  size_t      orig_fmt_len, alloc_fmt_size, len;
Packit 5c3484
  int	      new_fields, new_chars;
Packit 5c3484
  char	      fchar;
Packit 5c3484
  int	      fields = 0;
Packit 5c3484
  int	      chars = 0;
Packit 5c3484
Packit 5c3484
  TRACE (printf ("__gmp_doscan \"%s\"\n", orig_fmt);
Packit 5c3484
	 if (funs->scan == (gmp_doscan_scan_t) sscanf)
Packit 5c3484
	   printf ("  s=\"%s\"\n", * (const char **) data));
Packit 5c3484
Packit 5c3484
  /* Don't modify orig_ap, if va_list is actually an array and hence call by
Packit 5c3484
     reference.  It could be argued that it'd be more efficient to leave
Packit 5c3484
     callers to make a copy if they care, but doing so here is going to be a
Packit 5c3484
     very small part of the total work, and we may as well keep applications
Packit 5c3484
     out of trouble.  */
Packit 5c3484
  va_copy (ap, orig_ap);
Packit 5c3484
Packit 5c3484
  /* Parts of the format string are going to be copied so that a " %n" can
Packit 5c3484
     be appended.  alloc_fmt is some space for that.  orig_fmt_len+4 will be
Packit 5c3484
     needed if fmt consists of a single "%" specifier, but otherwise is an
Packit 5c3484
     overestimate.  We're not going to be very fast here, so use
Packit 5c3484
     __gmp_allocate_func rather than TMP_ALLOC.  */
Packit 5c3484
  orig_fmt_len = strlen (orig_fmt);
Packit 5c3484
  alloc_fmt_size = orig_fmt_len + 4;
Packit 5c3484
  alloc_fmt = __GMP_ALLOCATE_FUNC_TYPE (alloc_fmt_size, char);
Packit 5c3484
Packit 5c3484
  fmt = orig_fmt;
Packit 5c3484
  end_fmt = orig_fmt + orig_fmt_len;
Packit 5c3484
Packit 5c3484
  for (;;)
Packit 5c3484
    {
Packit 5c3484
    next:
Packit 5c3484
      fchar = *fmt++;
Packit 5c3484
Packit 5c3484
      if (fchar == '\0')
Packit 5c3484
	break;
Packit 5c3484
Packit 5c3484
      if (isspace (fchar))
Packit 5c3484
	{
Packit 5c3484
	  chars += skip_white (funs, data);
Packit 5c3484
	  continue;
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      if (fchar != '%')
Packit 5c3484
	{
Packit 5c3484
	  int  c;
Packit 5c3484
	literal:
Packit 5c3484
	  c = (funs->get) (data);
Packit 5c3484
	  if (c != fchar)
Packit 5c3484
	    {
Packit 5c3484
	      (funs->unget) (c, data);
Packit 5c3484
	      if (c == EOF)
Packit 5c3484
		{
Packit 5c3484
		eof_no_match:
Packit 5c3484
		  if (fields == 0)
Packit 5c3484
		    fields = EOF;
Packit 5c3484
		}
Packit 5c3484
	      goto done;
Packit 5c3484
	    }
Packit 5c3484
	  chars++;
Packit 5c3484
	  continue;
Packit 5c3484
	}
Packit 5c3484
Packit 5c3484
      param.type = '\0';
Packit 5c3484
      param.base = 0;	 /* for e,f,g,i */
Packit 5c3484
      param.ignore = 0;
Packit 5c3484
      param.width = 0;
Packit 5c3484
Packit 5c3484
      this_fmt = fmt-1;
Packit 5c3484
      TRACE (printf ("	this_fmt \"%s\"\n", this_fmt));
Packit 5c3484
Packit 5c3484
      for (;;)
Packit 5c3484
	{
Packit 5c3484
	  ASSERT (fmt <= end_fmt);
Packit 5c3484
Packit 5c3484
	  fchar = *fmt++;
Packit 5c3484
	  switch (fchar) {
Packit 5c3484
Packit 5c3484
	  case '\0':  /* unterminated % sequence */
Packit 5c3484
	    ASSERT (0);
Packit 5c3484
	    goto done;
Packit 5c3484
Packit 5c3484
	  case '%':   /* literal % */
Packit 5c3484
	    goto literal;
Packit 5c3484
Packit 5c3484
	  case '[':   /* character range */
Packit 5c3484
	    fchar = *fmt++;
Packit 5c3484
	    if (fchar == '^')
Packit 5c3484
	      fchar = *fmt++;
Packit 5c3484
	    /* ']' allowed as the first char (possibly after '^') */
Packit 5c3484
	    if (fchar == ']')
Packit 5c3484
	      fchar = *fmt++;
Packit 5c3484
	    for (;;)
Packit 5c3484
	      {
Packit 5c3484
		ASSERT (fmt <= end_fmt);
Packit 5c3484
		if (fchar == '\0')
Packit 5c3484
		  {
Packit 5c3484
		    /* unterminated % sequence */
Packit 5c3484
		    ASSERT (0);
Packit 5c3484
		    goto done;
Packit 5c3484
		  }
Packit 5c3484
		if (fchar == ']')
Packit 5c3484
		  break;
Packit 5c3484
		fchar = *fmt++;
Packit 5c3484
	      }
Packit 5c3484
	    /*FALLTHRU*/
Packit 5c3484
	  case 'c':   /* characters */
Packit 5c3484
	  case 's':   /* string of non-whitespace */
Packit 5c3484
	  case 'p':   /* pointer */
Packit 5c3484
	  libc_type:
Packit 5c3484
	    len = fmt - this_fmt;
Packit 5c3484
	    memcpy (alloc_fmt, this_fmt, len);
Packit 5c3484
	    alloc_fmt[len++] = '%';
Packit 5c3484
	    alloc_fmt[len++] = 'n';
Packit 5c3484
	    alloc_fmt[len] = '\0';
Packit 5c3484
Packit 5c3484
	    TRACE (printf ("  scan \"%s\"\n", alloc_fmt);
Packit 5c3484
		   if (funs->scan == (gmp_doscan_scan_t) sscanf)
Packit 5c3484
		     printf ("	s=\"%s\"\n", * (const char **) data));
Packit 5c3484
Packit 5c3484
	    new_chars = -1;
Packit 5c3484
	    if (param.ignore)
Packit 5c3484
	      {
Packit 5c3484
		new_fields = (*funs->scan) (data, alloc_fmt, &new_chars, NULL);
Packit 5c3484
		ASSERT (new_fields == 0 || new_fields == EOF);
Packit 5c3484
	      }
Packit 5c3484
	    else
Packit 5c3484
	      {
Packit 5c3484
		void *arg = va_arg (ap, void *);
Packit 5c3484
		new_fields = (*funs->scan) (data, alloc_fmt, arg, &new_chars);
Packit 5c3484
		ASSERT (new_fields==0 || new_fields==1 || new_fields==EOF);
Packit 5c3484
Packit 5c3484
		if (new_fields == 0)
Packit 5c3484
		  goto done;  /* invalid input */
Packit 5c3484
Packit 5c3484
		if (new_fields == 1)
Packit 5c3484
		  ASSERT (new_chars != -1);
Packit 5c3484
	      }
Packit 5c3484
	    TRACE (printf ("  new_fields %d   new_chars %d\n",
Packit 5c3484
			   new_fields, new_chars));
Packit 5c3484
Packit 5c3484
	    if (new_fields == -1)
Packit 5c3484
	      goto eof_no_match;  /* EOF before anything matched */
Packit 5c3484
Packit 5c3484
	    /* Under param.ignore, when new_fields==0 we don't know if
Packit 5c3484
	       it's a successful match or an invalid field.  new_chars
Packit 5c3484
	       won't have been assigned if it was an invalid field.  */
Packit 5c3484
	    if (new_chars == -1)
Packit 5c3484
	      goto done;  /* invalid input */
Packit 5c3484
Packit 5c3484
	    chars += new_chars;
Packit 5c3484
	    (*funs->step) (data, new_chars);
Packit 5c3484
Packit 5c3484
	  increment_fields:
Packit 5c3484
	    if (! param.ignore)
Packit 5c3484
	      fields++;
Packit 5c3484
	    goto next;
Packit 5c3484
Packit 5c3484
	  case 'd':   /* decimal */
Packit 5c3484
	  case 'u':   /* decimal */
Packit 5c3484
	    param.base = 10;
Packit 5c3484
	    goto numeric;
Packit 5c3484
Packit 5c3484
	  case 'e':   /* float */
Packit 5c3484
	  case 'E':   /* float */
Packit 5c3484
	  case 'f':   /* float */
Packit 5c3484
	  case 'g':   /* float */
Packit 5c3484
	  case 'G':   /* float */
Packit 5c3484
	  case 'i':   /* integer with base marker */
Packit 5c3484
	  numeric:
Packit 5c3484
	    if (param.type != 'F' && param.type != 'Q' && param.type != 'Z')
Packit 5c3484
	      goto libc_type;
Packit 5c3484
Packit 5c3484
	    chars += skip_white (funs, data);
Packit 5c3484
Packit 5c3484
	    new_chars = gmpscan (funs, data, &param,
Packit 5c3484
				 param.ignore ? NULL : va_arg (ap, void*));
Packit 5c3484
	    if (new_chars == -2)
Packit 5c3484
	      goto eof_no_match;
Packit 5c3484
	    if (new_chars == -1)
Packit 5c3484
	      goto done;
Packit 5c3484
Packit 5c3484
	    ASSERT (new_chars >= 0);
Packit 5c3484
	    chars += new_chars;
Packit 5c3484
	    goto increment_fields;
Packit 5c3484
Packit 5c3484
	  case 'a':   /* glibc allocate string */
Packit 5c3484
	  case '\'':  /* glibc digit groupings */
Packit 5c3484
	    break;
Packit 5c3484
Packit 5c3484
	  case 'F':   /* mpf_t */
Packit 5c3484
	  case 'j':   /* intmax_t */
Packit 5c3484
	  case 'L':   /* long long */
Packit 5c3484
	  case 'q':   /* quad_t */
Packit 5c3484
	  case 'Q':   /* mpq_t */
Packit 5c3484
	  case 't':   /* ptrdiff_t */
Packit 5c3484
	  case 'z':   /* size_t */
Packit 5c3484
	  case 'Z':   /* mpz_t */
Packit 5c3484
	  set_type:
Packit 5c3484
	    param.type = fchar;
Packit 5c3484
	    break;
Packit 5c3484
Packit 5c3484
	  case 'h':   /* short or char */
Packit 5c3484
	    if (param.type != 'h')
Packit 5c3484
	      goto set_type;
Packit 5c3484
	    param.type = 'H';	/* internal code for "hh" */
Packit 5c3484
	    break;
Packit 5c3484
Packit 5c3484
	    goto numeric;
Packit 5c3484
Packit 5c3484
	  case 'l':   /* long, long long, double or long double */
Packit 5c3484
	    if (param.type != 'l')
Packit 5c3484
	      goto set_type;
Packit 5c3484
	    param.type = 'L';	/* "ll" means "L" */
Packit 5c3484
	    break;
Packit 5c3484
Packit 5c3484
	  case 'n':
Packit 5c3484
	    if (! param.ignore)
Packit 5c3484
	      {
Packit 5c3484
		void  *p;
Packit 5c3484
		p = va_arg (ap, void *);
Packit 5c3484
		TRACE (printf ("  store %%n to %p\n", p));
Packit 5c3484
		switch (param.type) {
Packit 5c3484
		case '\0': * (int	*) p = chars; break;
Packit 5c3484
		case 'F':  mpf_set_si ((mpf_ptr) p, (long) chars); break;
Packit 5c3484
		case 'H':  * (char	*) p = chars; break;
Packit 5c3484
		case 'h':  * (short	*) p = chars; break;
Packit 5c3484
#if HAVE_INTMAX_T
Packit 5c3484
		case 'j':  * (intmax_t	*) p = chars; break;
Packit 5c3484
#else
Packit 5c3484
		case 'j':  ASSERT_FAIL (intmax_t not available); break;
Packit 5c3484
#endif
Packit 5c3484
		case 'l':  * (long	*) p = chars; break;
Packit 5c3484
#if HAVE_QUAD_T && HAVE_LONG_LONG
Packit 5c3484
		case 'q':
Packit 5c3484
		  ASSERT_ALWAYS (sizeof (quad_t) == sizeof (long long));
Packit 5c3484
		  /*FALLTHRU*/
Packit 5c3484
#else
Packit 5c3484
		case 'q':  ASSERT_FAIL (quad_t not available); break;
Packit 5c3484
#endif
Packit 5c3484
#if HAVE_LONG_LONG
Packit 5c3484
		case 'L':  * (long long *) p = chars; break;
Packit 5c3484
#else
Packit 5c3484
		case 'L':  ASSERT_FAIL (long long not available); break;
Packit 5c3484
#endif
Packit 5c3484
		case 'Q':  mpq_set_si ((mpq_ptr) p, (long) chars, 1L); break;
Packit 5c3484
#if HAVE_PTRDIFF_T
Packit 5c3484
		case 't':  * (ptrdiff_t *) p = chars; break;
Packit 5c3484
#else
Packit 5c3484
		case 't':  ASSERT_FAIL (ptrdiff_t not available); break;
Packit 5c3484
#endif
Packit 5c3484
		case 'z':  * (size_t	*) p = chars; break;
Packit 5c3484
		case 'Z':  mpz_set_si ((mpz_ptr) p, (long) chars); break;
Packit 5c3484
		default: ASSERT (0); break;
Packit 5c3484
		}
Packit 5c3484
	      }
Packit 5c3484
	    goto next;
Packit 5c3484
Packit 5c3484
	  case 'o':
Packit 5c3484
	    param.base = 8;
Packit 5c3484
	    goto numeric;
Packit 5c3484
Packit 5c3484
	  case 'x':
Packit 5c3484
	  case 'X':
Packit 5c3484
	    param.base = 16;
Packit 5c3484
	    goto numeric;
Packit 5c3484
Packit 5c3484
	  case '0': case '1': case '2': case '3': case '4':
Packit 5c3484
	  case '5': case '6': case '7': case '8': case '9':
Packit 5c3484
	    param.width = 0;
Packit 5c3484
	    do {
Packit 5c3484
	      param.width = param.width * 10 + (fchar-'0');
Packit 5c3484
	      fchar = *fmt++;
Packit 5c3484
	    } while (isdigit (fchar));
Packit 5c3484
	    fmt--; /* unget the non-digit */
Packit 5c3484
	    break;
Packit 5c3484
Packit 5c3484
	  case '*':
Packit 5c3484
	    param.ignore = 1;
Packit 5c3484
	    break;
Packit 5c3484
Packit 5c3484
	  default:
Packit 5c3484
	    /* something invalid in a % sequence */
Packit 5c3484
	    ASSERT (0);
Packit 5c3484
	    goto next;
Packit 5c3484
	  }
Packit 5c3484
	}
Packit 5c3484
    }
Packit 5c3484
Packit 5c3484
 done:
Packit 5c3484
  (*__gmp_free_func) (alloc_fmt, alloc_fmt_size);
Packit 5c3484
  return fields;
Packit 5c3484
}