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