Annotation of OpenXM_contrib/gmp/printf/doprnt.c, Revision 1.1.1.1
1.1 ohara 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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>