~ubuntu-branches/ubuntu/quantal/gclcvs/quantal

« back to all changes in this revision

Viewing changes to gmp3/scanf/doscan.c

  • Committer: Bazaar Package Importer
  • Author(s): Camm Maguire
  • Date: 2004-06-24 15:13:46 UTC
  • Revision ID: james.westby@ubuntu.com-20040624151346-xh0xaaktyyp7aorc
Tags: 2.7.0-26
C_GC_OFFSET is 2 on m68k-linux

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 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 the GNU Lesser General Public License as published by
 
13
the Free Software Foundation; either version 2.1 of the License, or (at your
 
14
option) any later version.
 
15
 
 
16
The GNU MP Library is distributed in the hope that it will be useful, but
 
17
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 
18
or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
 
19
License for more details.
 
20
 
 
21
You should have received a copy of the GNU Lesser General Public License
 
22
along with the GNU MP Library; see the file COPYING.LIB.  If not, write to
 
23
the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 
24
MA 02111-1307, USA. */
 
25
 
 
26
#include "config.h"
 
27
 
 
28
#if HAVE_STDARG
 
29
#include <stdarg.h>
 
30
#else
 
31
#include <varargs.h>
 
32
#endif
 
33
 
 
34
#include <ctype.h>
 
35
#include <stddef.h>    /* for ptrdiff_t */
 
36
#include <stdio.h>
 
37
#include <string.h>
 
38
 
 
39
#if HAVE_LOCALE_H
 
40
#include <locale.h>    /* for localeconv */
 
41
#endif
 
42
 
 
43
#if HAVE_STDINT_H
 
44
#include <stdint.h>    /* for intmax_t */
 
45
#endif
 
46
 
 
47
#if HAVE_SYS_TYPES_H
 
48
#include <sys/types.h> /* for quad_t */
 
49
#endif
 
50
 
 
51
#include "gmp.h"
 
52
#include "gmp-impl.h"
 
53
 
 
54
 
 
55
/* Change this to "#define TRACE(x) x" for some traces. */
 
56
#define TRACE(x)
 
57
 
 
58
 
 
59
/* It's necessary to parse up the string to recognise the GMP extra types F,
 
60
   Q and Z.  Other types and conversions are passed across to the standard
 
61
   sscanf or fscanf via funs->scan, for ease of implemenation.  This is
 
62
   essential in the case of something like glibc %p where the pointer format
 
63
   isn't actually documented.
 
64
 
 
65
   Because funs->scan doesn't get the whole input it can't put the right
 
66
   values in for %n, so that's handled in __gmp_doscan.  Neither sscanf nor
 
67
   fscanf directly indicate how many characters were read, so an extra %n is
 
68
   appended to each run for that.  For fscanf this merely supports our %n
 
69
   output, but for sscanf it lets funs->step move us along the input string.
 
70
 
 
71
   Whitespace and literal matches in the format string, including %%, are
 
72
   handled directly within __gmp_doscan.  This is reasonably efficient, and
 
73
   avoids some suspicious behaviour observed in various system libc's.
 
74
   GLIBC 2.2.4 for instance returns 0 on sscanf(" "," x") or on sscanf(" ",
 
75
   " x%d",&n), whereas we think they should return EOF, since end-of-string
 
76
   is reached when a match of "x" is required.
 
77
 
 
78
   For standard % conversions, funs->scan is called once for each
 
79
   conversion.  If we had vfscanf and vsscanf and could rely on their fixed
 
80
   text matching behaviour then we could call them with multiple consecutive
 
81
   standard conversions.  But plain fscanf and sscanf work fine, and parsing
 
82
   one field at a time shouldn't be too much of a slowdown.
 
83
 
 
84
   gmpscan reads a gmp type.  It's only used from one place, but is a
 
85
   separate subroutine to avoid a big chunk of complicated code in the
 
86
   middle of __gmp_doscan.  Within gmpscan a couple of loopbacks make it
 
87
   possible to share code for parsing integers, rationals and floats.
 
88
 
 
89
   In gmpscan normally one char of lookahead is maintained, but when width
 
90
   is reached that stops, on the principle that an fgetc/ungetc of a char
 
91
   past where we're told to stop would be undesirable.  "chars" is how many
 
92
   characters have been read so far, including the current c.  When
 
93
   chars==width and another character is desired then a jump is done to the
 
94
   "convert" stage.  c is invalid and mustn't be unget'ed in this case;
 
95
   chars is set to width+1 to indicate that.
 
96
 
 
97
   gmpscan normally returns the number of characters read.  -1 means an
 
98
   invalid field, like a "-" or "+" alone.  -2 means EOF reached before any
 
99
   matching characters were read.
 
100
 
 
101
   Consideration was given to using a separate code for gmp_fscanf and
 
102
   gmp_sscanf.  The sscanf case could zip across a string making literal
 
103
   matches or recognising digits in gmpscan, rather than making a function
 
104
   call fun->get per character.  The fscanf could use getc rather than fgetc
 
105
   too, which might help those systems where getc is a macro or otherwise
 
106
   inlined.  But none of this scanning and converting will be particularly
 
107
   fast, so the two are done together to keep it a bit simpler for now.
 
108
 
 
109
   Enhancements:
 
110
 
 
111
   A way to read the GLIBC printf %a format that we support in gmp_printf
 
112
   would be good.  That would probably be good for plain GLIBC scanf too, so
 
113
   perhaps we can simply follow its lead if it gets such a feature in the
 
114
   future.  */
 
115
 
 
116
 
 
117
struct gmp_doscan_params_t {
 
118
  int   base;
 
119
  int   ignore;
 
120
  char  type;
 
121
  int   width;
 
122
};
 
123
 
 
124
 
 
125
#define GET(c)                  \
 
126
  do {                          \
 
127
    ASSERT (chars <= width);    \
 
128
    chars++;                    \
 
129
    if (chars > width)          \
 
130
      goto convert;             \
 
131
    (c) = (*funs->get) (data);  \
 
132
  } while (0)
 
133
 
 
134
/* store into "s", extending if necessary */
 
135
#define STORE(c)                                                \
 
136
  do {                                                          \
 
137
    ASSERT (s_upto <= s_alloc);                                 \
 
138
    if (s_upto >= s_alloc)                                      \
 
139
      {                                                         \
 
140
        size_t  s_alloc_new = s_alloc + S_ALLOC_STEP;           \
 
141
        s = (*__gmp_reallocate_func) (s, s_alloc, s_alloc_new); \
 
142
        s_alloc = s_alloc_new;                                  \
 
143
      }                                                         \
 
144
    s[s_upto++] = c;                                            \
 
145
  } while (0)
 
146
 
 
147
#define S_ALLOC_STEP  512
 
148
 
 
149
static int
 
150
gmpscan (const struct gmp_doscan_funs_t *funs, void *data,
 
151
         const struct gmp_doscan_params_t *p, void *dst)
 
152
{
 
153
  int     chars, c, base, first, width, seen_point, seen_digit;
 
154
  size_t  s_upto, s_alloc;
 
155
  char    *s;
 
156
  int     invalid = 0;
 
157
 
 
158
  TRACE (printf ("gmpscan\n"));
 
159
 
 
160
  ASSERT (p->type == 'F' || p->type == 'Q' || p->type == 'Z');
 
161
 
 
162
  c = (*funs->get) (data);
 
163
  if (c == EOF)
 
164
    return -2;
 
165
 
 
166
  chars = 1;
 
167
  first = 1;
 
168
  seen_point = 0;
 
169
  seen_digit = 0;
 
170
  width = (p->width == 0 ? INT_MAX-1 : p->width);
 
171
  base = p->base;
 
172
  s_alloc = S_ALLOC_STEP;
 
173
  s = (*__gmp_allocate_func) (s_alloc);
 
174
  s_upto = 0;
 
175
 
 
176
 another:
 
177
  if (c == '-')
 
178
    {
 
179
      STORE (c);
 
180
      goto get_for_sign;
 
181
    }
 
182
  else if (c == '+')
 
183
    {
 
184
      /* don't store '+', it's not accepted by mpz_set_str etc */
 
185
    get_for_sign:
 
186
      GET (c);
 
187
    }
 
188
 
 
189
  if (base == 0)
 
190
    {
 
191
      base = 10;
 
192
      if (c == '0')
 
193
        {
 
194
          seen_digit = 1;
 
195
          base = 8;
 
196
          STORE (c);
 
197
          GET (c);
 
198
          if (c == 'x' || c == 'X')
 
199
            {
 
200
              base = 16;
 
201
            store_get_digits:
 
202
              STORE (c);
 
203
              GET (c);
 
204
            }
 
205
        }
 
206
    }
 
207
 
 
208
 digits:
 
209
  for (;;)
 
210
    {
 
211
      if (base == 16)
 
212
        {
 
213
          if (! (isascii (c) && isxdigit (c)))
 
214
            break;
 
215
        }
 
216
      else
 
217
        {
 
218
          if (! (isascii (c) && isdigit (c)))
 
219
            break;
 
220
          if (base == 8 && (c == '8' || c == '9'))
 
221
            break;
 
222
        }
 
223
 
 
224
      seen_digit = 1;
 
225
      STORE (c);
 
226
      GET (c);
 
227
    }
 
228
 
 
229
  if (first)
 
230
    {
 
231
      /* decimal point */
 
232
      if (p->type == 'F' && ! seen_point)
 
233
        {
 
234
#if HAVE_LOCALECONV
 
235
          /* For a multi-character decimal point, if the first character is
 
236
             present then all of it must be, otherwise the input is
 
237
             considered invalid.  */
 
238
          const char  *point;
 
239
          int         pc;
 
240
          point = localeconv()->decimal_point;
 
241
          pc = *point++;
 
242
          if (c == pc)
 
243
            {
 
244
              for (;;)
 
245
                {
 
246
                  STORE (c);
 
247
                  GET (c);
 
248
                  pc = *point++;
 
249
                  if (pc == '\0')
 
250
                    break;
 
251
                  if (c != pc)
 
252
                    goto invalid;
 
253
                }
 
254
              seen_point = 1;
 
255
              goto digits;
 
256
            }
 
257
#else
 
258
          if (c == '.')
 
259
            {
 
260
              seen_point = 1;
 
261
              goto store_get_digits;
 
262
            }
 
263
#endif
 
264
        }
 
265
 
 
266
      /* exponent */
 
267
      if (p->type == 'F' && (c == 'e' || c == 'E'))
 
268
        {
 
269
          /* must have at least one digit in the mantissa, just an exponent
 
270
             is not good enough */
 
271
          if (! seen_digit)
 
272
            goto invalid;
 
273
 
 
274
        exponent:
 
275
          first = 0;
 
276
          STORE (c);
 
277
          GET (c);
 
278
          goto another;
 
279
        }
 
280
 
 
281
      /* denominator */
 
282
      if (p->type == 'Q' && c == '/')
 
283
        {
 
284
          /* must have at least one digit in the numerator */
 
285
          if (! seen_digit)
 
286
            goto invalid;
 
287
 
 
288
          /* now look for at least one digit in the denominator */
 
289
          seen_digit = 0;
 
290
 
 
291
          /* allow the base to be redetermined for "%i" */
 
292
          base = p->base;
 
293
          goto exponent;
 
294
        }
 
295
    }
 
296
 
 
297
 convert:
 
298
  if (! seen_digit)
 
299
    {
 
300
    invalid:
 
301
      invalid = 1;
 
302
      goto done;
 
303
    }
 
304
 
 
305
  if (! p->ignore)
 
306
    {
 
307
      STORE ('\0');
 
308
      TRACE (printf ("  convert \"%s\"\n", s));
 
309
 
 
310
      /* We ought to have parsed out a valid string above, so just test
 
311
         mpz_set_str etc with an ASSERT.  */
 
312
      switch (p->type) {
 
313
      case 'F':
 
314
        ASSERT (p->base == 10);
 
315
        ASSERT_NOCARRY (mpf_set_str (dst, s, 10));
 
316
        break;
 
317
      case 'Q':
 
318
        ASSERT_NOCARRY (mpq_set_str (dst, s, p->base));
 
319
        break;
 
320
      case 'Z':
 
321
        ASSERT_NOCARRY (mpz_set_str (dst, s, p->base));
 
322
        break;
 
323
      default:
 
324
        ASSERT (0);
 
325
        /*FALLTHRU*/
 
326
        break;
 
327
      }
 
328
    }
 
329
 
 
330
 done:
 
331
  ASSERT (chars <= width+1);
 
332
  if (chars != width+1)
 
333
    {
 
334
      (*funs->unget) (c, data);
 
335
      TRACE (printf ("  ungetc %d, to give %d chars\n", c, chars-1));
 
336
    }
 
337
  chars--;
 
338
 
 
339
  (*__gmp_free_func) (s, s_alloc);
 
340
 
 
341
  if (invalid)
 
342
    {
 
343
      TRACE (printf ("  invalid\n"));
 
344
      return -1;
 
345
    }
 
346
 
 
347
  TRACE (printf ("  return %d chars (cf width %d)\n", chars, width));
 
348
  return chars;
 
349
}
 
350
 
 
351
 
 
352
/* Read and discard whitespace, if any.  Return number of chars skipped.
 
353
   Whitespace skipping never provokes the EOF return from __gmp_doscan, so
 
354
   it's not necessary to watch for EOF from funs->get, */
 
355
static int
 
356
skip_white (const struct gmp_doscan_funs_t *funs, void *data)
 
357
{
 
358
  int  c;
 
359
  int  ret = 0;
 
360
 
 
361
  do
 
362
    {
 
363
      c = (funs->get) (data);
 
364
      ret++;
 
365
    }
 
366
  while (isascii (c) && isspace (c));
 
367
 
 
368
  (funs->unget) (c, data);
 
369
  ret--;
 
370
 
 
371
  TRACE (printf ("  skip white %d\n", ret));
 
372
  return ret;
 
373
}
 
374
 
 
375
 
 
376
int
 
377
__gmp_doscan (const struct gmp_doscan_funs_t *funs, void *data,
 
378
              const char *orig_fmt, va_list orig_ap)
 
379
{
 
380
  struct gmp_doscan_params_t  param;
 
381
  va_list     ap;
 
382
  char        *alloc_fmt;
 
383
  const char  *fmt, *this_fmt, *end_fmt;
 
384
  size_t      orig_fmt_len, alloc_fmt_size, len;
 
385
  int         new_fields, new_chars;
 
386
  char        fchar;
 
387
  int         fields = 0;
 
388
  int         chars = 0;
 
389
 
 
390
  TRACE (printf ("__gmp_doscan \"%s\"\n", orig_fmt);
 
391
         if (funs->scan == (gmp_doscan_scan_t) sscanf)
 
392
           printf ("  s=\"%s\"\n", (const char *) data));
 
393
 
 
394
  /* Don't modify orig_ap, if va_list is actually an array and hence call by
 
395
     reference.  It could be argued that it'd be more efficient to leave
 
396
     callers to make a copy if they care, but doing so here is going to be a
 
397
     very small part of the total work, and we may as well keep applications
 
398
     out of trouble.  */
 
399
  va_copy (ap, orig_ap);
 
400
 
 
401
  /* Parts of the format string are going to be copied so that a " %n" can
 
402
     be appended.  alloc_fmt is some space for that.  orig_fmt_len+4 will be
 
403
     needed if fmt consists of a single "%" specifier, but otherwise is an
 
404
     overestimate.  We're not going to be very fast here, so use
 
405
     __gmp_allocate_func rather than TMP_ALLOC.  */
 
406
  orig_fmt_len = strlen (orig_fmt);
 
407
  alloc_fmt_size = orig_fmt_len + 4;
 
408
  alloc_fmt = (*__gmp_allocate_func) (alloc_fmt_size);
 
409
 
 
410
  fmt = orig_fmt;
 
411
  end_fmt = orig_fmt + orig_fmt_len;
 
412
 
 
413
  for (;;)
 
414
    {
 
415
    next:
 
416
      fchar = *fmt++;
 
417
 
 
418
      if (fchar == '\0')
 
419
        break;
 
420
 
 
421
      if (isascii (fchar) && isspace (fchar))
 
422
        {
 
423
          chars += skip_white (funs, data);
 
424
          continue;
 
425
        }
 
426
 
 
427
      if (fchar != '%')
 
428
        {
 
429
          int  c;
 
430
        literal:
 
431
          c = (funs->get) (data);
 
432
          if (c != fchar)
 
433
            {
 
434
              (funs->unget) (c, data);
 
435
              if (c == EOF)
 
436
                {
 
437
                eof_no_match:
 
438
                  if (fields == 0)
 
439
                    fields = EOF;
 
440
                }
 
441
              goto done;
 
442
            }
 
443
          chars++;
 
444
          continue;
 
445
        }
 
446
 
 
447
      param.type = '\0';
 
448
      param.base = 10;
 
449
      param.ignore = 0;
 
450
      param.width = 0;
 
451
 
 
452
      this_fmt = fmt-1;
 
453
      TRACE (printf ("  this_fmt \"%s\"\n", this_fmt));
 
454
 
 
455
      for (;;)
 
456
        {
 
457
          ASSERT (fmt <= end_fmt);
 
458
 
 
459
          fchar = *fmt++;
 
460
          switch (fchar) {
 
461
 
 
462
          case '\0':  /* unterminated % sequence */
 
463
            ASSERT (0);
 
464
            goto done;
 
465
 
 
466
          case '%':   /* literal % */
 
467
            goto literal;
 
468
 
 
469
          case '[':   /* character range */
 
470
            fchar = *fmt++;
 
471
            if (fchar == '^')
 
472
              fchar = *fmt++;
 
473
            /* ']' allowed as the first char (possibly after '^') */
 
474
            if (fchar == ']')
 
475
              fchar = *fmt++;
 
476
            for (;;)
 
477
              {
 
478
                ASSERT (fmt <= end_fmt);
 
479
                if (fchar == '\0')
 
480
                  {
 
481
                    /* unterminated % sequence */
 
482
                    ASSERT (0);
 
483
                    goto done;
 
484
                  }
 
485
                if (fchar == ']')
 
486
                  break;
 
487
                fchar = *fmt++;
 
488
              }
 
489
            /*FALLTHRU*/
 
490
          case 'c':   /* characters */
 
491
          case 's':   /* string of non-whitespace */
 
492
          case 'p':   /* pointer */
 
493
          libc_type:
 
494
            len = fmt - this_fmt;
 
495
            memcpy (alloc_fmt, this_fmt, len);
 
496
            alloc_fmt[len++] = '%';
 
497
            alloc_fmt[len++] = 'n';
 
498
            alloc_fmt[len] = '\0';
 
499
 
 
500
            TRACE (printf ("  scan \"%s\"\n", alloc_fmt);
 
501
                   if (funs->scan == (gmp_doscan_scan_t) sscanf)
 
502
                     printf ("  s=\"%s\"\n", (const char *) data));
 
503
 
 
504
            new_chars = -1;
 
505
            if (param.ignore)
 
506
              {
 
507
                new_fields = (*funs->scan) (data, alloc_fmt, &new_chars);
 
508
                ASSERT (new_fields == 0 || new_fields == EOF);
 
509
              }
 
510
            else
 
511
              {
 
512
                new_fields = (*funs->scan) (data, alloc_fmt,
 
513
                                            va_arg (ap, void *), &new_chars);
 
514
                ASSERT (new_fields==0 || new_fields==1 || new_fields==EOF);
 
515
 
 
516
                if (new_fields == 0)
 
517
                  goto done;  /* invalid input */
 
518
 
 
519
                if (new_fields == 1)
 
520
                  ASSERT (new_chars != -1);
 
521
              }
 
522
            TRACE (printf ("  new_fields %d   new_chars %d\n",
 
523
                           new_fields, new_chars));
 
524
 
 
525
            if (new_fields == -1)
 
526
              goto eof_no_match;  /* EOF before anything matched */
 
527
 
 
528
            /* Wnder param.ignore, when new_fields==0 we don't know if
 
529
               it's a successful match or an invalid field.  new_chars
 
530
               won't have been assigned if it was an invalid field.  */
 
531
            if (new_chars == -1)
 
532
              goto done;  /* invalid input */
 
533
 
 
534
            chars += new_chars;
 
535
            (*funs->step) (data, new_chars);
 
536
 
 
537
          increment_fields:
 
538
            if (! param.ignore)
 
539
              fields++;
 
540
            goto next;
 
541
 
 
542
          case 'd':   /* decimal */
 
543
          case 'e':   /* float */
 
544
          case 'E':   /* float */
 
545
          case 'f':   /* float */
 
546
          case 'g':   /* float */
 
547
          case 'G':   /* float */
 
548
          case 'u':   /* decimal */
 
549
          numeric:
 
550
            if (param.type != 'F' && param.type != 'Q' && param.type != 'Z')
 
551
              goto libc_type;
 
552
 
 
553
            chars += skip_white (funs, data);
 
554
 
 
555
            new_chars = gmpscan (funs, data, &param,
 
556
                                 param.ignore ? NULL : va_arg (ap, void*));
 
557
            if (new_chars == -2)
 
558
              goto eof_no_match;
 
559
            if (new_chars == -1)
 
560
              goto done;
 
561
 
 
562
            ASSERT (new_chars >= 0);
 
563
            chars += new_chars;
 
564
            goto increment_fields;
 
565
 
 
566
          case 'a':   /* glibc allocate string */
 
567
          case '\'':  /* glibc digit groupings */
 
568
            break;
 
569
 
 
570
          case 'F':   /* mpf_t */
 
571
          case 'j':   /* intmax_t */
 
572
          case 'L':   /* long long */
 
573
          case 'q':   /* quad_t */
 
574
          case 'Q':   /* mpq_t */
 
575
          case 't':   /* ptrdiff_t */
 
576
          case 'z':   /* size_t */
 
577
          case 'Z':   /* mpz_t */
 
578
          set_type:
 
579
            param.type = fchar;
 
580
            break;
 
581
 
 
582
          case 'h':   /* short or char */
 
583
            if (param.type != 'h')
 
584
              goto set_type;
 
585
            param.type = 'H';   /* internal code for "hh" */
 
586
            break;
 
587
 
 
588
          case 'i':
 
589
            param.base = 0;
 
590
            goto numeric;
 
591
 
 
592
          case 'l':   /* long, long long, double or long double */
 
593
            if (param.type != 'l')
 
594
              goto set_type;
 
595
            param.type = 'L';   /* "ll" means "L" */
 
596
            break;
 
597
 
 
598
          case 'n':
 
599
            if (! param.ignore)
 
600
              {
 
601
                void  *p;
 
602
                p = va_arg (ap, void *);
 
603
                TRACE (printf ("  store %%n to %p\n", p));
 
604
                switch (param.type) {
 
605
                case '\0': * (int       *) p = chars; break;
 
606
                case 'F':  mpf_set_si ((mpf_ptr) p, (long) chars); break;
 
607
                case 'H':  * (char      *) p = chars; break;
 
608
                case 'h':  * (short     *) p = chars; break;
 
609
#if HAVE_INTMAX_T
 
610
                case 'j':  * (intmax_t  *) p = chars; break;
 
611
#else
 
612
                case 'j':  ASSERT_FAIL (intmax_t not available); break;
 
613
#endif
 
614
                case 'l':  * (long      *) p = chars; break;
 
615
#if HAVE_QUAD_T && HAVE_LONG_LONG
 
616
                case 'q':
 
617
                  ASSERT_ALWAYS (sizeof (quad_t) == sizeof (long long));
 
618
                  /*FALLTHRU*/
 
619
#else
 
620
                case 'q':  ASSERT_FAIL (quad_t not available); break;
 
621
#endif
 
622
#if HAVE_LONG_LONG
 
623
                case 'L':  * (long long *) p = chars; break;
 
624
#else
 
625
                case 'L':  ASSERT_FAIL (long long not available); break;
 
626
#endif
 
627
                case 'Q':  mpq_set_si ((mpq_ptr) p, (long) chars, 1L); break;
 
628
#if HAVE_PTRDIFF_T
 
629
                case 't':  * (ptrdiff_t *) p = chars; break;
 
630
#else
 
631
                case 't':  ASSERT_FAIL (ptrdiff_t not available); break;
 
632
#endif
 
633
                case 'z':  * (size_t    *) p = chars; break;
 
634
                case 'Z':  mpz_set_si ((mpz_ptr) p, (long) chars); break;
 
635
                default: ASSERT (0); break;
 
636
                }
 
637
              }
 
638
            goto next;
 
639
 
 
640
          case 'o':
 
641
            param.base = 8;
 
642
            goto numeric;
 
643
 
 
644
          case 'x':
 
645
          case 'X':
 
646
            param.base = 16;
 
647
            goto numeric;
 
648
 
 
649
          case '0': case '1': case '2': case '3': case '4':
 
650
          case '5': case '6': case '7': case '8': case '9':
 
651
            param.width = 0;
 
652
            do {
 
653
              param.width = param.width * 10 + (fchar-'0');
 
654
              fchar = *fmt++;
 
655
            } while (isascii (fchar) && isdigit (fchar));
 
656
            fmt--; /* unget the non-digit */
 
657
            break;
 
658
 
 
659
          case '*':
 
660
            param.ignore = 1;
 
661
            break;
 
662
 
 
663
          default:
 
664
            /* something invalid in a % sequence */
 
665
            ASSERT (0);
 
666
            goto next;
 
667
          }
 
668
        }
 
669
    }
 
670
 
 
671
 done:
 
672
  (*__gmp_free_func) (alloc_fmt, alloc_fmt_size);
 
673
  return fields;
 
674
}