Austin Schuh | bb1338c | 2024-06-15 19:31:16 -0700 | [diff] [blame] | 1 | /* __gmp_doprnt -- printf style formatted output. |
| 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 glibc 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> /* for isdigit */ |
| 41 | #include <stddef.h> /* for ptrdiff_t */ |
| 42 | #include <string.h> |
| 43 | #include <stdio.h> /* for NULL */ |
| 44 | #include <stdlib.h> |
| 45 | |
| 46 | #if HAVE_INTTYPES_H |
| 47 | # include <inttypes.h> /* for intmax_t */ |
| 48 | #else |
| 49 | # if HAVE_STDINT_H |
| 50 | # include <stdint.h> |
| 51 | # endif |
| 52 | #endif |
| 53 | |
| 54 | #if HAVE_LANGINFO_H |
| 55 | #include <langinfo.h> /* for nl_langinfo */ |
| 56 | #endif |
| 57 | |
| 58 | #if HAVE_LOCALE_H |
| 59 | #include <locale.h> /* for localeconv */ |
| 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 diagnostics */ |
| 70 | #define TRACE(x) |
| 71 | |
| 72 | |
| 73 | /* Should be portable, but in any case this is only used under some ASSERTs. */ |
| 74 | #define va_equal(x, y) \ |
| 75 | (memcmp (&(x), &(y), sizeof(va_list)) == 0) |
| 76 | |
| 77 | |
| 78 | /* printf is convenient because it allows various types to be printed in one |
| 79 | fairly compact call, so having gmp_printf support the standard types as |
| 80 | well as the gmp ones is important. This ends up meaning all the standard |
| 81 | parsing must be duplicated, to get a new routine recognising the gmp |
| 82 | extras. |
| 83 | |
| 84 | With the currently favoured handling of mpz etc as Z, Q and F type |
| 85 | markers, it's not possible to use glibc register_printf_function since |
| 86 | that only accepts new conversion characters, not new types. If Z was a |
| 87 | conversion there'd be no way to specify hex, decimal or octal, or |
| 88 | similarly with F no way to specify fixed point or scientific format. |
| 89 | |
| 90 | It seems wisest to pass conversions %f, %e and %g of float, double and |
| 91 | long double over to the standard printf. It'd be hard to be sure of |
| 92 | getting the right handling for NaNs, rounding, etc. Integer conversions |
| 93 | %d etc and string conversions %s on the other hand could be easily enough |
| 94 | handled within gmp_doprnt, but if floats are going to libc then it's just |
| 95 | as easy to send all non-gmp types there. |
| 96 | |
| 97 | "Z" was a type marker for size_t in old glibc, but there seems no need to |
| 98 | provide access to that now "z" is standard. |
| 99 | |
| 100 | In GMP 4.1.1 we documented "ll" and "L" as being equivalent, but in C99 |
| 101 | in fact "ll" is just for long long and "L" just for long double. |
| 102 | Apparently GLIBC allows "L" for long long though. This doesn't affect |
| 103 | us as such, since both are passed through to the C library. To be |
| 104 | consistent with what we said before, the two are treated equivalently |
| 105 | here, and it's left to the C library to do what it thinks with them. |
| 106 | |
| 107 | Possibilities: |
| 108 | |
| 109 | "b" might be nice for binary output, and could even be supported for the |
| 110 | standard C types too if desired. |
| 111 | |
| 112 | POSIX style "%n$" parameter numbering would be possible, but would need |
| 113 | to be handled completely within gmp_doprnt, since the numbering will be |
| 114 | all different once the format string it cut into pieces. |
| 115 | |
| 116 | Some options for mpq formatting would be good. Perhaps a non-zero |
| 117 | precision field could give a width for the denominator and mean always |
| 118 | put a "/". A form "n+p/q" might interesting too, though perhaps that's |
| 119 | better left to applications. |
| 120 | |
| 121 | Right now there's no way for an application to know whether types like |
| 122 | intmax_t are supported here. If configure is doing its job and the same |
| 123 | compiler is used for gmp as for the application then there shouldn't be |
| 124 | any problem, but perhaps gmp.h should have some preprocessor symbols to |
| 125 | say what libgmp can do. */ |
| 126 | |
| 127 | |
| 128 | |
| 129 | /* If a gmp format is the very first thing or there are two gmp formats with |
| 130 | nothing in between then we'll reach here with this_fmt == last_fmt and we |
| 131 | can do nothing in that case. |
| 132 | |
| 133 | last_ap is always replaced after a FLUSH, so it doesn't matter if va_list |
| 134 | is a call-by-reference and the funs->format routine modifies it. */ |
| 135 | |
| 136 | #define FLUSH() \ |
| 137 | do { \ |
| 138 | if (this_fmt == last_fmt) \ |
| 139 | { \ |
| 140 | TRACE (printf ("nothing to flush\n")); \ |
| 141 | ASSERT (va_equal (this_ap, last_ap)); \ |
| 142 | } \ |
| 143 | else \ |
| 144 | { \ |
| 145 | ASSERT (*this_fmt == '%'); \ |
| 146 | *this_fmt = '\0'; \ |
| 147 | TRACE (printf ("flush \"%s\"\n", last_fmt)); \ |
| 148 | DOPRNT_FORMAT (last_fmt, last_ap); \ |
| 149 | } \ |
| 150 | } while (0) |
| 151 | |
| 152 | |
| 153 | /* Parse up the given format string and do the appropriate output using the |
| 154 | given "funs" routines. The data parameter is passed through to those |
| 155 | routines. */ |
| 156 | |
| 157 | int |
| 158 | __gmp_doprnt (const struct doprnt_funs_t *funs, void *data, |
| 159 | const char *orig_fmt, va_list orig_ap) |
| 160 | { |
| 161 | va_list ap, this_ap, last_ap; |
| 162 | size_t alloc_fmt_size, orig_fmt_size; |
| 163 | char *fmt, *alloc_fmt, *last_fmt, *this_fmt, *gmp_str; |
| 164 | int retval = 0; |
| 165 | int type, fchar, *value, seen_precision; |
| 166 | struct doprnt_params_t param; |
| 167 | |
| 168 | TRACE (printf ("gmp_doprnt \"%s\"\n", orig_fmt)); |
| 169 | |
| 170 | /* Don't modify orig_ap, if va_list is actually an array and hence call by |
| 171 | reference. It could be argued that it'd be more efficient to leave the |
| 172 | caller to make a copy if it cared, but doing so here is going to be a |
| 173 | very small part of the total work, and we may as well keep applications |
| 174 | out of trouble. */ |
| 175 | va_copy (ap, orig_ap); |
| 176 | |
| 177 | /* The format string is chopped up into pieces to be passed to |
| 178 | funs->format. Unfortunately that means it has to be copied so each |
| 179 | piece can be null-terminated. We're not going to be very fast here, so |
| 180 | use __gmp_allocate_func rather than TMP_ALLOC, to avoid overflowing the |
| 181 | stack if a long output string is given. */ |
| 182 | alloc_fmt_size = orig_fmt_size = strlen (orig_fmt) + 1; |
| 183 | #if _LONG_LONG_LIMB |
| 184 | /* for a long long limb we change %Mx to %llx, so could need an extra 1 |
| 185 | char for every 3 existing */ |
| 186 | alloc_fmt_size += alloc_fmt_size / 3; |
| 187 | #endif |
| 188 | alloc_fmt = __GMP_ALLOCATE_FUNC_TYPE (alloc_fmt_size, char); |
| 189 | fmt = alloc_fmt; |
| 190 | memcpy (fmt, orig_fmt, orig_fmt_size); |
| 191 | |
| 192 | /* last_fmt and last_ap are just after the last output, and hence where |
| 193 | the next output will begin, when that's done */ |
| 194 | last_fmt = fmt; |
| 195 | va_copy (last_ap, ap); |
| 196 | |
| 197 | for (;;) |
| 198 | { |
| 199 | TRACE (printf ("next: \"%s\"\n", fmt)); |
| 200 | |
| 201 | fmt = strchr (fmt, '%'); |
| 202 | if (fmt == NULL) |
| 203 | break; |
| 204 | |
| 205 | /* this_fmt and this_ap are the current '%' sequence being considered */ |
| 206 | this_fmt = fmt; |
| 207 | va_copy (this_ap, ap); |
| 208 | fmt++; /* skip the '%' */ |
| 209 | |
| 210 | TRACE (printf ("considering\n"); |
| 211 | printf (" last: \"%s\"\n", last_fmt); |
| 212 | printf (" this: \"%s\"\n", this_fmt)); |
| 213 | |
| 214 | type = '\0'; |
| 215 | value = ¶m.width; |
| 216 | |
| 217 | param.base = 10; |
| 218 | param.conv = 0; |
| 219 | param.expfmt = "e%c%02ld"; |
| 220 | param.exptimes4 = 0; |
| 221 | param.fill = ' '; |
| 222 | param.justify = DOPRNT_JUSTIFY_RIGHT; |
| 223 | param.prec = 6; |
| 224 | param.showbase = DOPRNT_SHOWBASE_NO; |
| 225 | param.showpoint = 0; |
| 226 | param.showtrailing = 1; |
| 227 | param.sign = '\0'; |
| 228 | param.width = 0; |
| 229 | seen_precision = 0; |
| 230 | |
| 231 | /* This loop parses a single % sequence. "break" from the switch |
| 232 | means continue with this %, "goto next" means the conversion |
| 233 | character has been seen and a new % should be sought. */ |
| 234 | for (;;) |
| 235 | { |
| 236 | fchar = *fmt++; |
| 237 | if (fchar == '\0') |
| 238 | break; |
| 239 | |
| 240 | switch (fchar) { |
| 241 | |
| 242 | case 'a': |
| 243 | /* %a behaves like %e, but defaults to all significant digits, |
| 244 | and there's no leading zeros on the exponent (which is in |
| 245 | fact bit-based) */ |
| 246 | param.base = 16; |
| 247 | param.expfmt = "p%c%ld"; |
| 248 | goto conv_a; |
| 249 | case 'A': |
| 250 | param.base = -16; |
| 251 | param.expfmt = "P%c%ld"; |
| 252 | conv_a: |
| 253 | param.conv = DOPRNT_CONV_SCIENTIFIC; |
| 254 | param.exptimes4 = 1; |
| 255 | if (! seen_precision) |
| 256 | param.prec = -1; /* default to all digits */ |
| 257 | param.showbase = DOPRNT_SHOWBASE_YES; |
| 258 | param.showtrailing = 1; |
| 259 | goto floating_a; |
| 260 | |
| 261 | case 'c': |
| 262 | /* Let's assume wchar_t will be promoted to "int" in the call, |
| 263 | the same as char will be. */ |
| 264 | (void) va_arg (ap, int); |
| 265 | goto next; |
| 266 | |
| 267 | case 'd': |
| 268 | case 'i': |
| 269 | case 'u': |
| 270 | integer: |
| 271 | TRACE (printf ("integer, base=%d\n", param.base)); |
| 272 | if (! seen_precision) |
| 273 | param.prec = -1; |
| 274 | switch (type) { |
| 275 | case 'j': |
| 276 | /* Let's assume uintmax_t is the same size as intmax_t. */ |
| 277 | #if HAVE_INTMAX_T |
| 278 | (void) va_arg (ap, intmax_t); |
| 279 | #else |
| 280 | ASSERT_FAIL (intmax_t not available); |
| 281 | #endif |
| 282 | break; |
| 283 | case 'l': |
| 284 | (void) va_arg (ap, long); |
| 285 | break; |
| 286 | case 'L': |
| 287 | #if HAVE_LONG_LONG |
| 288 | (void) va_arg (ap, long long); |
| 289 | #else |
| 290 | ASSERT_FAIL (long long not available); |
| 291 | #endif |
| 292 | break; |
| 293 | case 'N': |
| 294 | { |
| 295 | mp_ptr xp; |
| 296 | mp_size_t xsize, abs_xsize; |
| 297 | mpz_t z; |
| 298 | FLUSH (); |
| 299 | xp = va_arg (ap, mp_ptr); |
| 300 | PTR(z) = xp; |
| 301 | xsize = (int) va_arg (ap, mp_size_t); |
| 302 | abs_xsize = ABS (xsize); |
| 303 | MPN_NORMALIZE (xp, abs_xsize); |
| 304 | SIZ(z) = (xsize >= 0 ? abs_xsize : -abs_xsize); |
| 305 | ASSERT_CODE (ALLOC(z) = abs_xsize); |
| 306 | gmp_str = mpz_get_str (NULL, param.base, z); |
| 307 | goto gmp_integer; |
| 308 | } |
| 309 | /* break; */ |
| 310 | case 'q': |
| 311 | /* quad_t is probably the same as long long, but let's treat |
| 312 | it separately just to be sure. Also let's assume u_quad_t |
| 313 | will be the same size as quad_t. */ |
| 314 | #if HAVE_QUAD_T |
| 315 | (void) va_arg (ap, quad_t); |
| 316 | #else |
| 317 | ASSERT_FAIL (quad_t not available); |
| 318 | #endif |
| 319 | break; |
| 320 | case 'Q': |
| 321 | FLUSH (); |
| 322 | gmp_str = mpq_get_str (NULL, param.base, va_arg(ap, mpq_srcptr)); |
| 323 | goto gmp_integer; |
| 324 | case 't': |
| 325 | #if HAVE_PTRDIFF_T |
| 326 | (void) va_arg (ap, ptrdiff_t); |
| 327 | #else |
| 328 | ASSERT_FAIL (ptrdiff_t not available); |
| 329 | #endif |
| 330 | break; |
| 331 | case 'z': |
| 332 | (void) va_arg (ap, size_t); |
| 333 | break; |
| 334 | case 'Z': |
| 335 | { |
| 336 | int ret; |
| 337 | FLUSH (); |
| 338 | gmp_str = mpz_get_str (NULL, param.base, |
| 339 | va_arg (ap, mpz_srcptr)); |
| 340 | gmp_integer: |
| 341 | ret = __gmp_doprnt_integer (funs, data, ¶m, gmp_str); |
| 342 | __GMP_FREE_FUNC_TYPE (gmp_str, strlen(gmp_str)+1, char); |
| 343 | DOPRNT_ACCUMULATE (ret); |
| 344 | va_copy (last_ap, ap); |
| 345 | last_fmt = fmt; |
| 346 | } |
| 347 | break; |
| 348 | default: |
| 349 | /* default is an "int", and this includes h=short and hh=char |
| 350 | since they're promoted to int in a function call */ |
| 351 | (void) va_arg (ap, int); |
| 352 | break; |
| 353 | } |
| 354 | goto next; |
| 355 | |
| 356 | case 'E': |
| 357 | param.base = -10; |
| 358 | param.expfmt = "E%c%02ld"; |
| 359 | /*FALLTHRU*/ |
| 360 | case 'e': |
| 361 | param.conv = DOPRNT_CONV_SCIENTIFIC; |
| 362 | floating: |
| 363 | if (param.showbase == DOPRNT_SHOWBASE_NONZERO) |
| 364 | { |
| 365 | /* # in %e, %f and %g */ |
| 366 | param.showpoint = 1; |
| 367 | param.showtrailing = 1; |
| 368 | } |
| 369 | floating_a: |
| 370 | switch (type) { |
| 371 | case 'F': |
| 372 | FLUSH (); |
| 373 | DOPRNT_ACCUMULATE (__gmp_doprnt_mpf (funs, data, ¶m, |
| 374 | GMP_DECIMAL_POINT, |
| 375 | va_arg (ap, mpf_srcptr))); |
| 376 | va_copy (last_ap, ap); |
| 377 | last_fmt = fmt; |
| 378 | break; |
| 379 | case 'L': |
| 380 | #if HAVE_LONG_DOUBLE |
| 381 | (void) va_arg (ap, long double); |
| 382 | #else |
| 383 | ASSERT_FAIL (long double not available); |
| 384 | #endif |
| 385 | break; |
| 386 | default: |
| 387 | (void) va_arg (ap, double); |
| 388 | break; |
| 389 | } |
| 390 | goto next; |
| 391 | |
| 392 | case 'f': |
| 393 | param.conv = DOPRNT_CONV_FIXED; |
| 394 | goto floating; |
| 395 | |
| 396 | case 'F': /* mpf_t */ |
| 397 | case 'j': /* intmax_t */ |
| 398 | case 'L': /* long long */ |
| 399 | case 'N': /* mpn */ |
| 400 | case 'q': /* quad_t */ |
| 401 | case 'Q': /* mpq_t */ |
| 402 | case 't': /* ptrdiff_t */ |
| 403 | case 'z': /* size_t */ |
| 404 | case 'Z': /* mpz_t */ |
| 405 | set_type: |
| 406 | type = fchar; |
| 407 | break; |
| 408 | |
| 409 | case 'G': |
| 410 | param.base = -10; |
| 411 | param.expfmt = "E%c%02ld"; |
| 412 | /*FALLTHRU*/ |
| 413 | case 'g': |
| 414 | param.conv = DOPRNT_CONV_GENERAL; |
| 415 | param.showtrailing = 0; |
| 416 | goto floating; |
| 417 | |
| 418 | case 'h': |
| 419 | if (type != 'h') |
| 420 | goto set_type; |
| 421 | type = 'H'; /* internal code for "hh" */ |
| 422 | break; |
| 423 | |
| 424 | case 'l': |
| 425 | if (type != 'l') |
| 426 | goto set_type; |
| 427 | type = 'L'; /* "ll" means "L" */ |
| 428 | break; |
| 429 | |
| 430 | case 'm': |
| 431 | /* glibc strerror(errno), no argument */ |
| 432 | goto next; |
| 433 | |
| 434 | case 'M': /* mp_limb_t */ |
| 435 | /* mung format string to l or ll and let plain printf handle it */ |
| 436 | #if _LONG_LONG_LIMB |
| 437 | memmove (fmt+1, fmt, strlen (fmt)+1); |
| 438 | fmt[-1] = 'l'; |
| 439 | fmt[0] = 'l'; |
| 440 | fmt++; |
| 441 | type = 'L'; |
| 442 | #else |
| 443 | fmt[-1] = 'l'; |
| 444 | type = 'l'; |
| 445 | #endif |
| 446 | break; |
| 447 | |
| 448 | case 'n': |
| 449 | { |
| 450 | void *p; |
| 451 | FLUSH (); |
| 452 | p = va_arg (ap, void *); |
| 453 | switch (type) { |
| 454 | case '\0': * (int *) p = retval; break; |
| 455 | case 'F': mpf_set_si ((mpf_ptr) p, (long) retval); break; |
| 456 | case 'H': * (char *) p = retval; break; |
| 457 | case 'h': * (short *) p = retval; break; |
| 458 | #if HAVE_INTMAX_T |
| 459 | case 'j': * (intmax_t *) p = retval; break; |
| 460 | #else |
| 461 | case 'j': ASSERT_FAIL (intmax_t not available); break; |
| 462 | #endif |
| 463 | case 'l': * (long *) p = retval; break; |
| 464 | #if HAVE_QUAD_T && HAVE_LONG_LONG |
| 465 | case 'q': |
| 466 | ASSERT_ALWAYS (sizeof (quad_t) == sizeof (long long)); |
| 467 | /*FALLTHRU*/ |
| 468 | #else |
| 469 | case 'q': ASSERT_FAIL (quad_t not available); break; |
| 470 | #endif |
| 471 | #if HAVE_LONG_LONG |
| 472 | case 'L': * (long long *) p = retval; break; |
| 473 | #else |
| 474 | case 'L': ASSERT_FAIL (long long not available); break; |
| 475 | #endif |
| 476 | case 'N': |
| 477 | { |
| 478 | mp_size_t n; |
| 479 | n = va_arg (ap, mp_size_t); |
| 480 | n = ABS (n); |
| 481 | if (n != 0) |
| 482 | { |
| 483 | * (mp_ptr) p = retval; |
| 484 | MPN_ZERO ((mp_ptr) p + 1, n - 1); |
| 485 | } |
| 486 | } |
| 487 | break; |
| 488 | case 'Q': mpq_set_si ((mpq_ptr) p, (long) retval, 1L); break; |
| 489 | #if HAVE_PTRDIFF_T |
| 490 | case 't': * (ptrdiff_t *) p = retval; break; |
| 491 | #else |
| 492 | case 't': ASSERT_FAIL (ptrdiff_t not available); break; |
| 493 | #endif |
| 494 | case 'z': * (size_t *) p = retval; break; |
| 495 | case 'Z': mpz_set_si ((mpz_ptr) p, (long) retval); break; |
| 496 | } |
| 497 | } |
| 498 | va_copy (last_ap, ap); |
| 499 | last_fmt = fmt; |
| 500 | goto next; |
| 501 | |
| 502 | case 'o': |
| 503 | param.base = 8; |
| 504 | goto integer; |
| 505 | |
| 506 | case 'p': |
| 507 | case 's': |
| 508 | /* "void *" will be good enough for "char *" or "wchar_t *", no |
| 509 | need for separate code. */ |
| 510 | (void) va_arg (ap, const void *); |
| 511 | goto next; |
| 512 | |
| 513 | case 'x': |
| 514 | param.base = 16; |
| 515 | goto integer; |
| 516 | case 'X': |
| 517 | param.base = -16; |
| 518 | goto integer; |
| 519 | |
| 520 | case '%': |
| 521 | goto next; |
| 522 | |
| 523 | case '#': |
| 524 | param.showbase = DOPRNT_SHOWBASE_NONZERO; |
| 525 | break; |
| 526 | |
| 527 | case '\'': |
| 528 | /* glibc digit grouping, just pass it through, no support for it |
| 529 | on gmp types */ |
| 530 | break; |
| 531 | |
| 532 | case '+': |
| 533 | case ' ': |
| 534 | param.sign = fchar; |
| 535 | break; |
| 536 | |
| 537 | case '-': |
| 538 | param.justify = DOPRNT_JUSTIFY_LEFT; |
| 539 | break; |
| 540 | case '.': |
| 541 | seen_precision = 1; |
| 542 | param.prec = -1; /* "." alone means all necessary digits */ |
| 543 | value = ¶m.prec; |
| 544 | break; |
| 545 | |
| 546 | case '*': |
| 547 | { |
| 548 | int n = va_arg (ap, int); |
| 549 | |
| 550 | if (value == ¶m.width) |
| 551 | { |
| 552 | /* negative width means left justify */ |
| 553 | if (n < 0) |
| 554 | { |
| 555 | param.justify = DOPRNT_JUSTIFY_LEFT; |
| 556 | n = -n; |
| 557 | } |
| 558 | param.width = n; |
| 559 | } |
| 560 | else |
| 561 | { |
| 562 | /* don't allow negative precision */ |
| 563 | param.prec = MAX (0, n); |
| 564 | } |
| 565 | } |
| 566 | break; |
| 567 | |
| 568 | case '0': |
| 569 | if (value == ¶m.width) |
| 570 | { |
| 571 | /* in width field, set fill */ |
| 572 | param.fill = '0'; |
| 573 | |
| 574 | /* for right justify, put the fill after any minus sign */ |
| 575 | if (param.justify == DOPRNT_JUSTIFY_RIGHT) |
| 576 | param.justify = DOPRNT_JUSTIFY_INTERNAL; |
| 577 | } |
| 578 | else |
| 579 | { |
| 580 | /* in precision field, set value */ |
| 581 | *value = 0; |
| 582 | } |
| 583 | break; |
| 584 | |
| 585 | case '1': case '2': case '3': case '4': case '5': |
| 586 | case '6': case '7': case '8': case '9': |
| 587 | /* process all digits to form a value */ |
| 588 | { |
| 589 | int n = 0; |
| 590 | do { |
| 591 | n = n * 10 + (fchar-'0'); |
| 592 | fchar = *fmt++; |
| 593 | } while (isascii (fchar) && isdigit (fchar)); |
| 594 | fmt--; /* unget the non-digit */ |
| 595 | *value = n; |
| 596 | } |
| 597 | break; |
| 598 | |
| 599 | default: |
| 600 | /* something invalid */ |
| 601 | ASSERT (0); |
| 602 | goto next; |
| 603 | } |
| 604 | } |
| 605 | |
| 606 | next: |
| 607 | /* Stop parsing the current "%" format, look for a new one. */ |
| 608 | ; |
| 609 | } |
| 610 | |
| 611 | TRACE (printf ("remainder: \"%s\"\n", last_fmt)); |
| 612 | if (*last_fmt != '\0') |
| 613 | DOPRNT_FORMAT (last_fmt, last_ap); |
| 614 | |
| 615 | if (funs->final != NULL) |
| 616 | if ((*funs->final) (data) == -1) |
| 617 | goto error; |
| 618 | |
| 619 | done: |
| 620 | __GMP_FREE_FUNC_TYPE (alloc_fmt, alloc_fmt_size, char); |
| 621 | return retval; |
| 622 | |
| 623 | error: |
| 624 | retval = -1; |
| 625 | goto done; |
| 626 | } |