1234287Sdim/* __gmp_doscan -- formatted input internals. 2234287Sdim 3234287Sdim THE FUNCTIONS IN THIS FILE ARE FOR INTERNAL USE ONLY. THEY'RE ALMOST 4234287Sdim CERTAIN TO BE SUBJECT TO INCOMPATIBLE CHANGES OR DISAPPEAR COMPLETELY IN 5234287Sdim FUTURE GNU MP RELEASES. 6234287Sdim 7234287SdimCopyright 2001-2003 Free Software Foundation, Inc. 8234287Sdim 9234287SdimThis file is part of the GNU MP Library. 10234287Sdim 11234287SdimThe GNU MP Library is free software; you can redistribute it and/or modify 12234287Sdimit under the terms of either: 13234287Sdim 14234287Sdim * the GNU Lesser General Public License as published by the Free 15234287Sdim Software Foundation; either version 3 of the License, or (at your 16234287Sdim option) any later version. 17234287Sdim 18234287Sdimor 19234287Sdim 20234287Sdim * the GNU General Public License as published by the Free Software 21234287Sdim Foundation; either version 2 of the License, or (at your option) any 22234287Sdim later version. 23234287Sdim 24234287Sdimor both in parallel, as here. 25234287Sdim 26234287SdimThe GNU MP Library is distributed in the hope that it will be useful, but 27234287SdimWITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 28234287Sdimor FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 29234287Sdimfor more details. 30234287Sdim 31276479SdimYou should have received copies of the GNU General Public License and the 32276479SdimGNU Lesser General Public License along with the GNU MP Library. If not, 33276479Sdimsee https://www.gnu.org/licenses/. */ 34276479Sdim 35276479Sdim#define _GNU_SOURCE /* for DECIMAL_POINT in langinfo.h */ 36276479Sdim 37276479Sdim#include "config.h" /* needed for the HAVE_, could also move gmp incls */ 38276479Sdim 39288943Sdim#include <stdarg.h> 40296417Sdim#include <ctype.h> 41288943Sdim#include <stddef.h> /* for ptrdiff_t */ 42296417Sdim#include <stdio.h> 43296417Sdim#include <stdlib.h> /* for strtol */ 44296417Sdim#include <string.h> 45296417Sdim 46296417Sdim#if HAVE_LANGINFO_H 47296417Sdim#include <langinfo.h> /* for nl_langinfo */ 48239462Sdim#endif 49234287Sdim 50280031Sdim#if HAVE_LOCALE_H 51234287Sdim#include <locale.h> /* for localeconv */ 52234287Sdim#endif 53288943Sdim 54234287Sdim#if HAVE_INTTYPES_H 55234287Sdim# include <inttypes.h> /* for intmax_t */ 56234287Sdim#else 57234287Sdim# if HAVE_STDINT_H 58234287Sdim# include <stdint.h> 59276479Sdim# endif 60288943Sdim#endif 61234287Sdim 62234287Sdim#if HAVE_SYS_TYPES_H 63234287Sdim#include <sys/types.h> /* for quad_t */ 64234287Sdim#endif 65234287Sdim 66276479Sdim#include "gmp-impl.h" 67288943Sdim 68276479Sdim 69276479Sdim/* Change this to "#define TRACE(x) x" for some traces. */ 70276479Sdim#define TRACE(x) 71276479Sdim 72276479Sdim 73288943Sdim/* General: 74234287Sdim 75234287Sdim It's necessary to parse up the format string to recognise the GMP 76234287Sdim extra types F, Q and Z. Other types and conversions are passed 77234287Sdim across to the standard sscanf or fscanf via funs->scan, for ease of 78234287Sdim implementation. This is essential in the case of something like glibc 79288943Sdim %p where the pointer format isn't actually documented. 80234287Sdim 81234287Sdim Because funs->scan doesn't get the whole input it can't put the right 82234287Sdim values in for %n, so that's handled in __gmp_doscan. Neither sscanf 83234287Sdim nor fscanf directly indicate how many characters were read, so an 84234287Sdim extra %n is appended to each run for that. For fscanf this merely 85288943Sdim supports our %n output, but for sscanf it lets funs->step move us 86234287Sdim along the input string. 87234287Sdim 88234287Sdim Whitespace and literal matches in the format string, including %%, 89234287Sdim are handled directly within __gmp_doscan. This is reasonably 90234287Sdim efficient, and avoids some suspicious behaviour observed in various 91296417Sdim system libc's. GLIBC 2.2.4 for instance returns 0 on 92239462Sdim 93234287Sdim sscanf(" ", " x") 94280031Sdim or 95234287Sdim sscanf(" ", " x%d",&n) 96234287Sdim 97234287Sdim whereas we think they should return EOF, since end-of-string is 98276479Sdim reached when a match of "x" is required. 99276479Sdim 100276479Sdim For standard % conversions, funs->scan is called once for each 101276479Sdim conversion. If we had vfscanf and vsscanf and could rely on their 102276479Sdim fixed text matching behaviour then we could call them with multiple 103276479Sdim consecutive standard conversions. But plain fscanf and sscanf work 104276479Sdim fine, and parsing one field at a time shouldn't be too much of a 105276479Sdim slowdown. 106288943Sdim 107234287Sdim gmpscan: 108234287Sdim 109234287Sdim gmpscan reads a gmp type. It's only used from one place, but is a 110234287Sdim separate subroutine to avoid a big chunk of complicated code in the 111234287Sdim middle of __gmp_doscan. Within gmpscan a couple of loopbacks make it 112276479Sdim possible to share code for parsing integers, rationals and floats. 113288943Sdim 114234287Sdim In gmpscan normally one char of lookahead is maintained, but when width 115234287Sdim is reached that stops, on the principle that an fgetc/ungetc of a char 116234287Sdim past where we're told to stop would be undesirable. "chars" is how many 117234287Sdim characters have been read so far, including the current c. When 118234287Sdim chars==width and another character is desired then a jump is done to the 119276479Sdim "convert" stage. c is invalid and mustn't be unget'ed in this case; 120288943Sdim chars is set to width+1 to indicate that. 121276479Sdim 122276479Sdim gmpscan normally returns the number of characters read. -1 means an 123276479Sdim invalid field, -2 means EOF reached before any matching characters 124276479Sdim were read. 125276479Sdim 126288943Sdim For hex floats, the mantissa part is passed to mpf_set_str, then the 127234287Sdim exponent is applied with mpf_mul_exp or mpf_div_2exp. This is easier 128234287Sdim than teaching mpf_set_str about an exponent factor (ie. 2) differing 129234287Sdim from the mantissa radix point factor (ie. 16). mpf_mul_exp and 130234287Sdim mpf_div_2exp will preserve the application requested precision, so 131234287Sdim nothing in that respect is lost by making this a two-step process. 132288943Sdim 133234287Sdim Matching and errors: 134234287Sdim 135234287Sdim C99 7.19.6.2 paras 9 and 10 say an input item is read as the longest 136234287Sdim string which is a match for the appropriate type, or a prefix of a 137234287Sdim match. With that done, if it's only a prefix then the result is a 138288943Sdim matching failure, ie. invalid input. 139234287Sdim 140234287Sdim This rule seems fairly clear, but doesn't seem to be universally 141234287Sdim applied in system C libraries. Even GLIBC doesn't seem to get it 142234287Sdim right, insofar as it seems to accept some apparently invalid forms. 143234287Sdim Eg. glibc 2.3.1 accepts "0x" for a "%i", where a reading of the 144296417Sdim standard would suggest a non-empty sequence of digits should be 145239462Sdim required after an "0x". 146234287Sdim 147280031Sdim A footnote to 7.19.6.2 para 17 notes how this input item reading can 148234287Sdim mean inputs acceptable to strtol are not acceptable to fscanf. We 149234287Sdim think this confirms our reading of "0x" as invalid. 150276479Sdim 151276479Sdim Clearly gmp_sscanf could backtrack to a longest input which was a 152288943Sdim valid match for a given item, but this is not done, since C99 says 153296417Sdim sscanf is identical to fscanf, so we make gmp_sscanf identical to 154288943Sdim gmp_fscanf. 155234287Sdim 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 188struct 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 220static int 221gmpscan (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, */ 446static int 447skip_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 467int 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, ¶m, 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