[BACK]Return to doprnt.c CVS log [TXT][DIR] Up to [local] / OpenXM_contrib / gmp / printf

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 = &param.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, &param, 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, &param,
                    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 = &param.prec;
                    504:             break;
                    505:
                    506:           case '*':
                    507:             {
                    508:               int n = va_arg (ap, int);
                    509:
                    510:               if (value == &param.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 == &param.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>