Annotation of OpenXM_contrib/gmp/printf/doprnt.c, Revision 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>