xref: /netbsd-src/external/lgpl3/gmp/dist/scanf/doscan.c (revision 72c7faa4dbb41dbb0238d6b4a109da0d4b236dd4)
1 /* __gmp_doscan -- formatted input internals.
2 
3    THE FUNCTIONS IN THIS FILE ARE FOR INTERNAL USE ONLY.  THEY'RE ALMOST
4    CERTAIN TO BE SUBJECT TO INCOMPATIBLE CHANGES OR DISAPPEAR COMPLETELY IN
5    FUTURE GNU MP RELEASES.
6 
7 Copyright 2001-2003 Free Software Foundation, Inc.
8 
9 This file is part of the GNU MP Library.
10 
11 The GNU MP Library is free software; you can redistribute it and/or modify
12 it under the terms of either:
13 
14   * the GNU Lesser General Public License as published by the Free
15     Software Foundation; either version 3 of the License, or (at your
16     option) any later version.
17 
18 or
19 
20   * the GNU General Public License as published by the Free Software
21     Foundation; either version 2 of the License, or (at your option) any
22     later version.
23 
24 or both in parallel, as here.
25 
26 The GNU MP Library is distributed in the hope that it will be useful, but
27 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
28 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
29 for more details.
30 
31 You should have received copies of the GNU General Public License and the
32 GNU Lesser General Public License along with the GNU MP Library.  If not,
33 see https://www.gnu.org/licenses/.  */
34 
35 #define _GNU_SOURCE    /* for DECIMAL_POINT in langinfo.h */
36 
37 #include "config.h"	/* needed for the HAVE_, could also move gmp incls */
38 
39 #include <stdarg.h>
40 #include <ctype.h>
41 #include <stddef.h>    /* for ptrdiff_t */
42 #include <stdio.h>
43 #include <stdlib.h>    /* for strtol */
44 #include <string.h>
45 
46 #if HAVE_LANGINFO_H
47 #include <langinfo.h>  /* for nl_langinfo */
48 #endif
49 
50 #if HAVE_LOCALE_H
51 #include <locale.h>    /* for localeconv */
52 #endif
53 
54 #if HAVE_INTTYPES_H
55 # include <inttypes.h> /* for intmax_t */
56 #else
57 # if HAVE_STDINT_H
58 #  include <stdint.h>
59 # endif
60 #endif
61 
62 #if HAVE_SYS_TYPES_H
63 #include <sys/types.h> /* for quad_t */
64 #endif
65 
66 #include "gmp-impl.h"
67 
68 
69 /* Change this to "#define TRACE(x) x" for some traces. */
70 #define TRACE(x)
71 
72 
73 /* General:
74 
75        It's necessary to parse up the format string to recognise the GMP
76        extra types F, Q and Z.  Other types and conversions are passed
77        across to the standard sscanf or fscanf via funs->scan, for ease of
78        implementation.  This is essential in the case of something like glibc
79        %p where the pointer format isn't actually documented.
80 
81        Because funs->scan doesn't get the whole input it can't put the right
82        values in for %n, so that's handled in __gmp_doscan.  Neither sscanf
83        nor fscanf directly indicate how many characters were read, so an
84        extra %n is appended to each run for that.  For fscanf this merely
85        supports our %n output, but for sscanf it lets funs->step move us
86        along the input string.
87 
88        Whitespace and literal matches in the format string, including %%,
89        are handled directly within __gmp_doscan.  This is reasonably
90        efficient, and avoids some suspicious behaviour observed in various
91        system libc's.  GLIBC 2.2.4 for instance returns 0 on
92 
93 	   sscanf(" ", " x")
94        or
95 	   sscanf(" ", " x%d",&n)
96 
97        whereas we think they should return EOF, since end-of-string is
98        reached when a match of "x" is required.
99 
100        For standard % conversions, funs->scan is called once for each
101        conversion.  If we had vfscanf and vsscanf and could rely on their
102        fixed text matching behaviour then we could call them with multiple
103        consecutive standard conversions.  But plain fscanf and sscanf work
104        fine, and parsing one field at a time shouldn't be too much of a
105        slowdown.
106 
107    gmpscan:
108 
109        gmpscan reads a gmp type.  It's only used from one place, but is a
110        separate subroutine to avoid a big chunk of complicated code in the
111        middle of __gmp_doscan.  Within gmpscan a couple of loopbacks make it
112        possible to share code for parsing integers, rationals and floats.
113 
114        In gmpscan normally one char of lookahead is maintained, but when width
115        is reached that stops, on the principle that an fgetc/ungetc of a char
116        past where we're told to stop would be undesirable.  "chars" is how many
117        characters have been read so far, including the current c.  When
118        chars==width and another character is desired then a jump is done to the
119        "convert" stage.  c is invalid and mustn't be unget'ed in this case;
120        chars is set to width+1 to indicate that.
121 
122        gmpscan normally returns the number of characters read.  -1 means an
123        invalid field, -2 means EOF reached before any matching characters
124        were read.
125 
126        For hex floats, the mantissa part is passed to mpf_set_str, then the
127        exponent is applied with mpf_mul_exp or mpf_div_2exp.  This is easier
128        than teaching mpf_set_str about an exponent factor (ie. 2) differing
129        from the mantissa radix point factor (ie. 16).  mpf_mul_exp and
130        mpf_div_2exp will preserve the application requested precision, so
131        nothing in that respect is lost by making this a two-step process.
132 
133    Matching and errors:
134 
135        C99 7.19.6.2 paras 9 and 10 say an input item is read as the longest
136        string which is a match for the appropriate type, or a prefix of a
137        match.  With that done, if it's only a prefix then the result is a
138        matching failure, ie. invalid input.
139 
140        This rule seems fairly clear, but doesn't seem to be universally
141        applied in system C libraries.  Even GLIBC doesn't seem to get it
142        right, insofar as it seems to accept some apparently invalid forms.
143        Eg. glibc 2.3.1 accepts "0x" for a "%i", where a reading of the
144        standard would suggest a non-empty sequence of digits should be
145        required after an "0x".
146 
147        A footnote to 7.19.6.2 para 17 notes how this input item reading can
148        mean inputs acceptable to strtol are not acceptable to fscanf.  We
149        think this confirms our reading of "0x" as invalid.
150 
151        Clearly gmp_sscanf could backtrack to a longest input which was a
152        valid match for a given item, but this is not done, since C99 says
153        sscanf is identical to fscanf, so we make gmp_sscanf identical to
154        gmp_fscanf.
155 
156    Types:
157 
158        C99 says "ll" is for long long, and "L" is for long double floats.
159        Unfortunately in GMP 4.1.1 we documented the two as equivalent.  This
160        doesn't affect us directly, since both are passed through to plain
161        scanf.  It seems wisest not to try to enforce the C99 rule.  This is
162        consistent with what we said before, though whether it actually
163        worked was always up to the C library.
164 
165    Alternatives:
166 
167        Consideration was given to using separate code for gmp_fscanf and
168        gmp_sscanf.  The sscanf case could zip across a string doing literal
169        matches or recognising digits in gmpscan, rather than making a
170        function call fun->get per character.  The fscanf could use getc
171        rather than fgetc too, which might help those systems where getc is a
172        macro or otherwise inlined.  But none of this scanning and converting
173        will be particularly fast, so the two are done together to keep it a
174        little simpler for now.
175 
176        Various multibyte string issues are not addressed, for a start C99
177        scanf says the format string is multibyte.  Since we pass %c, %s and
178        %[ to the system scanf, they might do multibyte reads already, but
179        it's another matter whether or not that can be used, since our digit
180        and whitespace parsing is only unibyte.  The plan is to quietly
181        ignore multibyte locales for now.  This is not as bad as it sounds,
182        since GMP is presumably used mostly on numbers, which can be
183        perfectly adequately treated in plain ASCII.
184 
185 */
186 
187 
188 struct gmp_doscan_params_t {
189   int	base;
190   int	ignore;
191   char	type;
192   int	width;
193 };
194 
195 
196 #define GET(c)			\
197   do {				\
198     ASSERT (chars <= width);	\
199     chars++;			\
200     if (chars > width)		\
201       goto convert;		\
202     (c) = (*funs->get) (data);	\
203   } while (0)
204 
205 /* store into "s", extending if necessary */
206 #define STORE(c)							\
207   do {									\
208     ASSERT (s_upto <= s_alloc);						\
209     if (s_upto >= s_alloc)						\
210       {									\
211 	size_t	s_alloc_new = s_alloc + S_ALLOC_STEP;			\
212 	s = __GMP_REALLOCATE_FUNC_TYPE (s, s_alloc, s_alloc_new, char); \
213 	s_alloc = s_alloc_new;						\
214       }									\
215     s[s_upto++] = c;							\
216   } while (0)
217 
218 #define S_ALLOC_STEP  512
219 
220 static int
gmpscan(const struct gmp_doscan_funs_t * funs,void * data,const struct gmp_doscan_params_t * p,void * dst)221 gmpscan (const struct gmp_doscan_funs_t *funs, void *data,
222 	 const struct gmp_doscan_params_t *p, void *dst)
223 {
224   int	  chars, c, base, first, width, seen_point, seen_digit, hexfloat;
225   size_t  s_upto, s_alloc, hexexp;
226   char	  *s;
227   int	  invalid = 0;
228 
229   TRACE (printf ("gmpscan\n"));
230 
231   ASSERT (p->type == 'F' || p->type == 'Q' || p->type == 'Z');
232 
233   c = (*funs->get) (data);
234   if (c == EOF)
235     return -2;
236 
237   chars = 1;
238   first = 1;
239   seen_point = 0;
240   width = (p->width == 0 ? INT_MAX-1 : p->width);
241   base = p->base;
242   s_alloc = S_ALLOC_STEP;
243   s = __GMP_ALLOCATE_FUNC_TYPE (s_alloc, char);
244   s_upto = 0;
245   hexfloat = 0;
246   hexexp = 0;
247 
248  another:
249   seen_digit = 0;
250   if (c == '-')
251     {
252       STORE (c);
253       goto get_for_sign;
254     }
255   else if (c == '+')
256     {
257       /* don't store '+', it's not accepted by mpz_set_str etc */
258     get_for_sign:
259       GET (c);
260     }
261 
262   if (base == 0)
263     {
264       base = 10;		  /* decimal if no base indicator */
265       if (c == '0')
266 	{
267 	  seen_digit = 1;	  /* 0 alone is a valid number */
268 	  if (p->type != 'F')
269 	    base = 8;		  /* leading 0 is octal, for non-floats */
270 	  STORE (c);
271 	  GET (c);
272 	  if (c == 'x' || c == 'X')
273 	    {
274 	      base = 16;
275 	      seen_digit = 0;	  /* must have digits after an 0x */
276 	      if (p->type == 'F') /* don't pass 'x' to mpf_set_str_point */
277 		hexfloat = 1;
278 	      else
279 		STORE (c);
280 	      GET (c);
281 	    }
282 	}
283     }
284 
285  digits:
286   for (;;)
287     {
288       if (base == 16)
289 	{
290 	  if (! isxdigit (c))
291 	    break;
292 	}
293       else
294 	{
295 	  if (! isdigit (c))
296 	    break;
297 	  if (base == 8 && (c == '8' || c == '9'))
298 	    break;
299 	}
300 
301       seen_digit = 1;
302       STORE (c);
303       GET (c);
304     }
305 
306   if (first)
307     {
308       /* decimal point */
309       if (p->type == 'F' && ! seen_point)
310 	{
311 	  /* For a multi-character decimal point, if the first character is
312 	     present then all of it must be, otherwise the input is
313 	     considered invalid.  */
314 	  const char  *point = GMP_DECIMAL_POINT;
315 	  int	      pc = (unsigned char) *point++;
316 	  if (c == pc)
317 	    {
318 	      for (;;)
319 		{
320 		  STORE (c);
321 		  GET (c);
322 		  pc = (unsigned char) *point++;
323 		  if (pc == '\0')
324 		    break;
325 		  if (c != pc)
326 		    goto set_invalid;
327 		}
328 	      seen_point = 1;
329 	      goto digits;
330 	    }
331 	}
332 
333       /* exponent */
334       if (p->type == 'F')
335 	{
336 	  if (hexfloat && (c == 'p' || c == 'P'))
337 	    {
338 	      hexexp = s_upto; /* exponent location */
339 	      base = 10;       /* exponent in decimal */
340 	      goto exponent;
341 	    }
342 	  else if (! hexfloat && (c == 'e' || c == 'E'))
343 	    {
344 	    exponent:
345 	      /* must have at least one digit in the mantissa, just an exponent
346 		 is not good enough */
347 	      if (! seen_digit)
348 		goto set_invalid;
349 
350 	    do_second:
351 	      first = 0;
352 	      STORE (c);
353 	      GET (c);
354 	      goto another;
355 	    }
356 	}
357 
358       /* denominator */
359       if (p->type == 'Q' && c == '/')
360 	{
361 	  /* must have at least one digit in the numerator */
362 	  if (! seen_digit)
363 	    goto set_invalid;
364 
365 	  /* now look for at least one digit in the denominator */
366 	  seen_digit = 0;
367 
368 	  /* allow the base to be redetermined for "%i" */
369 	  base = p->base;
370 	  goto do_second;
371 	}
372     }
373 
374  convert:
375   if (! seen_digit)
376     {
377     set_invalid:
378       invalid = 1;
379       goto done;
380     }
381 
382   if (! p->ignore)
383     {
384       STORE ('\0');
385       TRACE (printf ("	convert \"%s\"\n", s));
386 
387       /* We ought to have parsed out a valid string above, so just test
388 	 mpz_set_str etc with an ASSERT.  */
389       switch (p->type) {
390       case 'F':
391 	{
392 	  mpf_ptr  f = (mpf_ptr) dst;
393 	  if (hexexp != 0)
394 	    s[hexexp] = '\0';
395 	  ASSERT_NOCARRY (mpf_set_str (f, s, hexfloat ? 16 : 10));
396 	  if (hexexp != 0)
397 	    {
398 	      char *dummy;
399 	      long  exp;
400 	      exp = strtol (s + hexexp + 1, &dummy, 10);
401 	      if (exp >= 0)
402 		mpf_mul_2exp (f, f, (unsigned long) exp);
403 	      else
404 		mpf_div_2exp (f, f, NEG_CAST (unsigned long, exp));
405 	    }
406 	}
407 	break;
408       case 'Q':
409 	ASSERT_NOCARRY (mpq_set_str ((mpq_ptr) dst, s, p->base));
410 	break;
411       case 'Z':
412 	ASSERT_NOCARRY (mpz_set_str ((mpz_ptr) dst, s, p->base));
413 	break;
414       default:
415 	ASSERT (0);
416 	/*FALLTHRU*/
417 	break;
418       }
419     }
420 
421  done:
422   ASSERT (chars <= width+1);
423   if (chars != width+1)
424     {
425       (*funs->unget) (c, data);
426       TRACE (printf ("	ungetc %d, to give %d chars\n", c, chars-1));
427     }
428   chars--;
429 
430   (*__gmp_free_func) (s, s_alloc);
431 
432   if (invalid)
433     {
434       TRACE (printf ("	invalid\n"));
435       return -1;
436     }
437 
438   TRACE (printf ("  return %d chars (cf width %d)\n", chars, width));
439   return chars;
440 }
441 
442 
443 /* Read and discard whitespace, if any.  Return number of chars skipped.
444    Whitespace skipping never provokes the EOF return from __gmp_doscan, so
445    it's not necessary to watch for EOF from funs->get, */
446 static int
skip_white(const struct gmp_doscan_funs_t * funs,void * data)447 skip_white (const struct gmp_doscan_funs_t *funs, void *data)
448 {
449   int  c;
450   int  ret = 0;
451 
452   do
453     {
454       c = (funs->get) (data);
455       ret++;
456     }
457   while (isspace (c));
458 
459   (funs->unget) (c, data);
460   ret--;
461 
462   TRACE (printf ("  skip white %d\n", ret));
463   return ret;
464 }
465 
466 
467 int
__gmp_doscan(const struct gmp_doscan_funs_t * funs,void * data,const char * orig_fmt,va_list orig_ap)468 __gmp_doscan (const struct gmp_doscan_funs_t *funs, void *data,
469 	      const char *orig_fmt, va_list orig_ap)
470 {
471   struct gmp_doscan_params_t  param;
472   va_list     ap;
473   char	      *alloc_fmt;
474   const char  *fmt, *this_fmt, *end_fmt;
475   size_t      orig_fmt_len, alloc_fmt_size, len;
476   int	      new_fields, new_chars;
477   char	      fchar;
478   int	      fields = 0;
479   int	      chars = 0;
480 
481   TRACE (printf ("__gmp_doscan \"%s\"\n", orig_fmt);
482 	 if (funs->scan == (gmp_doscan_scan_t) sscanf)
483 	   printf ("  s=\"%s\"\n", * (const char **) data));
484 
485   /* Don't modify orig_ap, if va_list is actually an array and hence call by
486      reference.  It could be argued that it'd be more efficient to leave
487      callers to make a copy if they care, but doing so here is going to be a
488      very small part of the total work, and we may as well keep applications
489      out of trouble.  */
490   va_copy (ap, orig_ap);
491 
492   /* Parts of the format string are going to be copied so that a " %n" can
493      be appended.  alloc_fmt is some space for that.  orig_fmt_len+4 will be
494      needed if fmt consists of a single "%" specifier, but otherwise is an
495      overestimate.  We're not going to be very fast here, so use
496      __gmp_allocate_func rather than TMP_ALLOC.  */
497   orig_fmt_len = strlen (orig_fmt);
498   alloc_fmt_size = orig_fmt_len + 4;
499   alloc_fmt = __GMP_ALLOCATE_FUNC_TYPE (alloc_fmt_size, char);
500 
501   fmt = orig_fmt;
502   end_fmt = orig_fmt + orig_fmt_len;
503 
504   for (;;)
505     {
506     next:
507       fchar = *fmt++;
508 
509       if (fchar == '\0')
510 	break;
511 
512       if (isspace (fchar))
513 	{
514 	  chars += skip_white (funs, data);
515 	  continue;
516 	}
517 
518       if (fchar != '%')
519 	{
520 	  int  c;
521 	literal:
522 	  c = (funs->get) (data);
523 	  if (c != fchar)
524 	    {
525 	      (funs->unget) (c, data);
526 	      if (c == EOF)
527 		{
528 		eof_no_match:
529 		  if (fields == 0)
530 		    fields = EOF;
531 		}
532 	      goto done;
533 	    }
534 	  chars++;
535 	  continue;
536 	}
537 
538       param.type = '\0';
539       param.base = 0;	 /* for e,f,g,i */
540       param.ignore = 0;
541       param.width = 0;
542 
543       this_fmt = fmt-1;
544       TRACE (printf ("	this_fmt \"%s\"\n", this_fmt));
545 
546       for (;;)
547 	{
548 	  ASSERT (fmt <= end_fmt);
549 
550 	  fchar = *fmt++;
551 	  switch (fchar) {
552 
553 	  case '\0':  /* unterminated % sequence */
554 	    ASSERT (0);
555 	    goto done;
556 
557 	  case '%':   /* literal % */
558 	    goto literal;
559 
560 	  case '[':   /* character range */
561 	    fchar = *fmt++;
562 	    if (fchar == '^')
563 	      fchar = *fmt++;
564 	    /* ']' allowed as the first char (possibly after '^') */
565 	    if (fchar == ']')
566 	      fchar = *fmt++;
567 	    for (;;)
568 	      {
569 		ASSERT (fmt <= end_fmt);
570 		if (fchar == '\0')
571 		  {
572 		    /* unterminated % sequence */
573 		    ASSERT (0);
574 		    goto done;
575 		  }
576 		if (fchar == ']')
577 		  break;
578 		fchar = *fmt++;
579 	      }
580 	    /*FALLTHRU*/
581 	  case 'c':   /* characters */
582 	  case 's':   /* string of non-whitespace */
583 	  case 'p':   /* pointer */
584 	  libc_type:
585 	    len = fmt - this_fmt;
586 	    memcpy (alloc_fmt, this_fmt, len);
587 	    alloc_fmt[len++] = '%';
588 	    alloc_fmt[len++] = 'n';
589 	    alloc_fmt[len] = '\0';
590 
591 	    TRACE (printf ("  scan \"%s\"\n", alloc_fmt);
592 		   if (funs->scan == (gmp_doscan_scan_t) sscanf)
593 		     printf ("	s=\"%s\"\n", * (const char **) data));
594 
595 	    new_chars = -1;
596 	    if (param.ignore)
597 	      {
598 		new_fields = (*funs->scan) (data, alloc_fmt, &new_chars, NULL);
599 		ASSERT (new_fields == 0 || new_fields == EOF);
600 	      }
601 	    else
602 	      {
603 		void *arg = va_arg (ap, void *);
604 		new_fields = (*funs->scan) (data, alloc_fmt, arg, &new_chars);
605 		ASSERT (new_fields==0 || new_fields==1 || new_fields==EOF);
606 
607 		if (new_fields == 0)
608 		  goto done;  /* invalid input */
609 
610 		if (new_fields == 1)
611 		  ASSERT (new_chars != -1);
612 	      }
613 	    TRACE (printf ("  new_fields %d   new_chars %d\n",
614 			   new_fields, new_chars));
615 
616 	    if (new_fields == -1)
617 	      goto eof_no_match;  /* EOF before anything matched */
618 
619 	    /* Under param.ignore, when new_fields==0 we don't know if
620 	       it's a successful match or an invalid field.  new_chars
621 	       won't have been assigned if it was an invalid field.  */
622 	    if (new_chars == -1)
623 	      goto done;  /* invalid input */
624 
625 	    chars += new_chars;
626 	    (*funs->step) (data, new_chars);
627 
628 	  increment_fields:
629 	    if (! param.ignore)
630 	      fields++;
631 	    goto next;
632 
633 	  case 'd':   /* decimal */
634 	  case 'u':   /* decimal */
635 	    param.base = 10;
636 	    goto numeric;
637 
638 	  case 'e':   /* float */
639 	  case 'E':   /* float */
640 	  case 'f':   /* float */
641 	  case 'g':   /* float */
642 	  case 'G':   /* float */
643 	  case 'i':   /* integer with base marker */
644 	  numeric:
645 	    if (param.type != 'F' && param.type != 'Q' && param.type != 'Z')
646 	      goto libc_type;
647 
648 	    chars += skip_white (funs, data);
649 
650 	    new_chars = gmpscan (funs, data, &param,
651 				 param.ignore ? NULL : va_arg (ap, void*));
652 	    if (new_chars == -2)
653 	      goto eof_no_match;
654 	    if (new_chars == -1)
655 	      goto done;
656 
657 	    ASSERT (new_chars >= 0);
658 	    chars += new_chars;
659 	    goto increment_fields;
660 
661 	  case 'a':   /* glibc allocate string */
662 	  case '\'':  /* glibc digit groupings */
663 	    break;
664 
665 	  case 'F':   /* mpf_t */
666 	  case 'j':   /* intmax_t */
667 	  case 'L':   /* long long */
668 	  case 'q':   /* quad_t */
669 	  case 'Q':   /* mpq_t */
670 	  case 't':   /* ptrdiff_t */
671 	  case 'z':   /* size_t */
672 	  case 'Z':   /* mpz_t */
673 	  set_type:
674 	    param.type = fchar;
675 	    break;
676 
677 	  case 'h':   /* short or char */
678 	    if (param.type != 'h')
679 	      goto set_type;
680 	    param.type = 'H';	/* internal code for "hh" */
681 	    break;
682 
683 	    goto numeric;
684 
685 	  case 'l':   /* long, long long, double or long double */
686 	    if (param.type != 'l')
687 	      goto set_type;
688 	    param.type = 'L';	/* "ll" means "L" */
689 	    break;
690 
691 	  case 'n':
692 	    if (! param.ignore)
693 	      {
694 		void  *p;
695 		p = va_arg (ap, void *);
696 		TRACE (printf ("  store %%n to %p\n", p));
697 		switch (param.type) {
698 		case '\0': * (int	*) p = chars; break;
699 		case 'F':  mpf_set_si ((mpf_ptr) p, (long) chars); break;
700 		case 'H':  * (char	*) p = chars; break;
701 		case 'h':  * (short	*) p = chars; break;
702 #if HAVE_INTMAX_T
703 		case 'j':  * (intmax_t	*) p = chars; break;
704 #else
705 		case 'j':  ASSERT_FAIL (intmax_t not available); break;
706 #endif
707 		case 'l':  * (long	*) p = chars; break;
708 #if HAVE_QUAD_T && HAVE_LONG_LONG
709 		case 'q':
710 		  ASSERT_ALWAYS (sizeof (quad_t) == sizeof (long long));
711 		  /*FALLTHRU*/
712 #else
713 		case 'q':  ASSERT_FAIL (quad_t not available); break;
714 #endif
715 #if HAVE_LONG_LONG
716 		case 'L':  * (long long *) p = chars; break;
717 #else
718 		case 'L':  ASSERT_FAIL (long long not available); break;
719 #endif
720 		case 'Q':  mpq_set_si ((mpq_ptr) p, (long) chars, 1L); break;
721 #if HAVE_PTRDIFF_T
722 		case 't':  * (ptrdiff_t *) p = chars; break;
723 #else
724 		case 't':  ASSERT_FAIL (ptrdiff_t not available); break;
725 #endif
726 		case 'z':  * (size_t	*) p = chars; break;
727 		case 'Z':  mpz_set_si ((mpz_ptr) p, (long) chars); break;
728 		default: ASSERT (0); break;
729 		}
730 	      }
731 	    goto next;
732 
733 	  case 'o':
734 	    param.base = 8;
735 	    goto numeric;
736 
737 	  case 'x':
738 	  case 'X':
739 	    param.base = 16;
740 	    goto numeric;
741 
742 	  case '0': case '1': case '2': case '3': case '4':
743 	  case '5': case '6': case '7': case '8': case '9':
744 	    param.width = 0;
745 	    do {
746 	      param.width = param.width * 10 + (fchar-'0');
747 	      fchar = *fmt++;
748 	    } while (isdigit (fchar));
749 	    fmt--; /* unget the non-digit */
750 	    break;
751 
752 	  case '*':
753 	    param.ignore = 1;
754 	    break;
755 
756 	  default:
757 	    /* something invalid in a % sequence */
758 	    ASSERT (0);
759 	    goto next;
760 	  }
761 	}
762     }
763 
764  done:
765   (*__gmp_free_func) (alloc_fmt, alloc_fmt_size);
766   return fields;
767 }
768