[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     ! 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>