Home | History | Annotate | Download | only in Stdio
      1 /*  $NetBSD: vsnprintf_ss.c,v 1.2.2.1 2007/05/07 19:49:09 pavel Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 1990, 1993
      5  *  The Regents of the University of California.  All rights reserved.
      6  *
      7  * This code is derived from software contributed to Berkeley by
      8  * Chris Torek.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. Neither the name of the University nor the names of its contributors
     19  *    may be used to endorse or promote products derived from this software
     20  *    without specific prior written permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     32  * SUCH DAMAGE.
     33  */
     34 #include  <LibConfig.h>
     35 #include <sys/EfiCdefs.h>
     36 #if defined(LIBC_SCCS) && !defined(lint)
     37 #if 0
     38 static char sccsid[] = "@(#)vsnprintf.c 8.1 (Berkeley) 6/4/93";
     39 #else
     40 __RCSID("$NetBSD: vsnprintf_ss.c,v 1.2.2.1 2007/05/07 19:49:09 pavel Exp $");
     41 #endif
     42 #endif /* LIBC_SCCS and not lint */
     43 
     44 #include "namespace.h"
     45 
     46 #include <sys/types.h>
     47 #include <inttypes.h>
     48 #include <assert.h>
     49 #include <stdio.h>
     50 #include <errno.h>
     51 #include <stdarg.h>
     52 #include <string.h>
     53 #include "reentrant.h"
     54 #include "extern.h"
     55 #include "local.h"
     56 
     57 #ifdef __weak_alias
     58 __weak_alias(vsnprintf_ss,_vsnprintf_ss)
     59 #endif
     60 
     61 /*
     62  * vsnprintf_ss: scaled down version of printf(3).
     63  *
     64  * this version based on vfprintf() from libc which was derived from
     65  * software contributed to Berkeley by Chris Torek.
     66  *
     67  */
     68 
     69 /*
     70  * macros for converting digits to letters and vice versa
     71  */
     72 #define to_digit(c) ((c) - '0')
     73 #define is_digit(c) ((unsigned)to_digit(c) <= 9)
     74 #define to_char(n)  (char)((n) + '0')
     75 
     76 /*
     77  * flags used during conversion.
     78  */
     79 #define ALT   0x001   /* alternate form */
     80 #define HEXPREFIX 0x002   /* add 0x or 0X prefix */
     81 #define LADJUST   0x004   /* left adjustment */
     82 #define LONGDBL   0x008   /* long double; unimplemented */
     83 #define LONGINT   0x010   /* long integer */
     84 #define QUADINT   0x020   /* quad integer */
     85 #define SHORTINT  0x040   /* short integer */
     86 #define MAXINT    0x080   /* intmax_t */
     87 #define PTRINT    0x100   /* intptr_t */
     88 #define SIZEINT   0x200   /* size_t */
     89 #define ZEROPAD   0x400   /* zero (as opposed to blank) pad */
     90 #define FPT   0x800   /* Floating point number */
     91 
     92   /*
     93    * To extend shorts properly, we need both signed and unsigned
     94    * argument extraction methods.
     95    */
     96 #define SARG() \
     97   ((INT64)(flags&MAXINT ? va_arg(ap, intmax_t) : \
     98       flags&PTRINT ? va_arg(ap, intptr_t) : \
     99       flags&SIZEINT ? va_arg(ap, ssize_t) : /* XXX */ \
    100       flags&QUADINT ? va_arg(ap, quad_t) : \
    101       flags&LONGINT ? va_arg(ap, long) : \
    102       flags&SHORTINT ? (short)va_arg(ap, int) : \
    103       va_arg(ap, int)))
    104 
    105 #define UARG() \
    106   ((UINT64)(flags&MAXINT ? va_arg(ap, uintmax_t) : \
    107       flags&PTRINT ? va_arg(ap, uintptr_t) : \
    108       flags&SIZEINT ? va_arg(ap, size_t) : \
    109       flags&QUADINT ? va_arg(ap, u_quad_t) : \
    110       flags&LONGINT ? va_arg(ap, unsigned long) : \
    111       flags&SHORTINT ? (u_short)va_arg(ap, int) : \
    112       va_arg(ap, u_int)))
    113 
    114 #define PUTCHAR(C) do {         \
    115   if (sbuf < tailp)       \
    116     *sbuf++ = (C);        \
    117 } while (/*CONSTCOND*/0)
    118 
    119 int
    120 vsnprintf_ss(char *sbuf, size_t slen, const char *fmt0, va_list ap)
    121 {
    122   const char *fmt;  /* format string */
    123   int ch;     /* character from fmt */
    124   int n;      /* handy integer (short term usage) */
    125   char *cp;   /* handy char pointer (short term usage) */
    126   int flags;    /* flags as above */
    127   int ret;    /* return value accumulator */
    128   int width;    /* width from format (%8d), or 0 */
    129   int prec;   /* precision from format (%.3d), or -1 */
    130   char sign;    /* sign prefix (' ', '+', '-', or \0) */
    131 
    132   u_quad_t _uquad;  /* integer arguments %[diouxX] */
    133   enum { OCT, DEC, HEX } base;/* base for [diouxX] conversion */
    134   int dprec;    /* a copy of prec if [diouxX], 0 otherwise */
    135   int realsz;   /* field size expanded by dprec */
    136   int size;   /* size of converted field or string */
    137   const char *xdigs;  /* digits for [xX] conversion */
    138   char bf[128];     /* space for %c, %[diouxX] */
    139   char *tailp;    /* tail pointer for snprintf */
    140 
    141   static const char xdigs_lower[16] = "0123456789abcdef";
    142   static const char xdigs_upper[16] = "0123456789ABCDEF";
    143 
    144 
    145   _DIAGASSERT(n == 0 || sbuf != NULL);
    146   _DIAGASSERT(fmt != NULL);
    147 
    148   tailp = sbuf + slen;
    149 
    150   cp = NULL;  /* XXX: shutup gcc */
    151   size = 0; /* XXX: shutup gcc */
    152 
    153   fmt = fmt0;
    154   ret = 0;
    155 
    156   xdigs = NULL;   /* XXX: shut up gcc warning */
    157 
    158   /*
    159    * Scan the format for conversions (`%' character).
    160    */
    161   for (;;) {
    162     while (*fmt != '%' && *fmt) {
    163       ret++;
    164       PUTCHAR(*fmt++);
    165     }
    166     if (*fmt == 0)
    167       goto done;
    168 
    169     fmt++;    /* skip over '%' */
    170 
    171     flags = 0;
    172     dprec = 0;
    173     width = 0;
    174     prec = -1;
    175     sign = '\0';
    176 
    177 rflag:    ch = *fmt++;
    178 reswitch: switch (ch) {
    179     case ' ':
    180       /*
    181        * ``If the space and + flags both appear, the space
    182        * flag will be ignored.''
    183        *  -- ANSI X3J11
    184        */
    185       if (!sign)
    186         sign = ' ';
    187       goto rflag;
    188     case '#':
    189       flags |= ALT;
    190       goto rflag;
    191     case '*':
    192       /*
    193        * ``A negative field width argument is taken as a
    194        * - flag followed by a positive field width.''
    195        *  -- ANSI X3J11
    196        * They don't exclude field widths read from args.
    197        */
    198       if ((width = va_arg(ap, int)) >= 0)
    199         goto rflag;
    200       width = -width;
    201       /* FALLTHROUGH */
    202     case '-':
    203       flags |= LADJUST;
    204       goto rflag;
    205     case '+':
    206       sign = '+';
    207       goto rflag;
    208     case '.':
    209       if ((ch = *fmt++) == '*') {
    210         n = va_arg(ap, int);
    211         prec = n < 0 ? -1 : n;
    212         goto rflag;
    213       }
    214       n = 0;
    215       while (is_digit(ch)) {
    216         n = 10 * n + to_digit(ch);
    217         ch = *fmt++;
    218       }
    219       prec = n < 0 ? -1 : n;
    220       goto reswitch;
    221     case '0':
    222       /*
    223        * ``Note that 0 is taken as a flag, not as the
    224        * beginning of a field width.''
    225        *  -- ANSI X3J11
    226        */
    227       flags |= ZEROPAD;
    228       goto rflag;
    229     case '1': case '2': case '3': case '4':
    230     case '5': case '6': case '7': case '8': case '9':
    231       n = 0;
    232       do {
    233         n = 10 * n + to_digit(ch);
    234         ch = *fmt++;
    235       } while (is_digit(ch));
    236       width = n;
    237       goto reswitch;
    238     case 'h':
    239       flags |= SHORTINT;
    240       goto rflag;
    241     case 'j':
    242       flags |= MAXINT;
    243       goto rflag;
    244     case 'l':
    245       if (*fmt == 'l') {
    246         fmt++;
    247         flags |= QUADINT;
    248       } else {
    249         flags |= LONGINT;
    250       }
    251       goto rflag;
    252     case 'q':
    253       flags |= QUADINT;
    254       goto rflag;
    255     case 't':
    256       flags |= PTRINT;
    257       goto rflag;
    258     case 'z':
    259       flags |= SIZEINT;
    260       goto rflag;
    261     case 'c':
    262       *(cp = bf) = va_arg(ap, int);
    263       size = 1;
    264       sign = '\0';
    265       break;
    266     case 'D':
    267       flags |= LONGINT;
    268       /*FALLTHROUGH*/
    269     case 'd':
    270     case 'i':
    271       _uquad = SARG();
    272       if ((quad_t)_uquad < 0) {
    273         _uquad = -_uquad;
    274         sign = '-';
    275       }
    276       base = DEC;
    277       goto number;
    278     case 'n':
    279       if (flags & MAXINT)
    280         *va_arg(ap, intmax_t *) = ret;
    281       else if (flags & PTRINT)
    282         *va_arg(ap, intptr_t *) = ret;
    283       else if (flags & SIZEINT)
    284         *va_arg(ap, ssize_t *) = ret;
    285       else if (flags & QUADINT)
    286         *va_arg(ap, quad_t *) = ret;
    287       else if (flags & LONGINT)
    288         *va_arg(ap, long *) = (long)ret;
    289       else if (flags & SHORTINT)
    290         *va_arg(ap, short *) = (short)ret;
    291       else
    292         *va_arg(ap, int *) = ret;
    293       continue; /* no output */
    294     case 'O':
    295       flags |= LONGINT;
    296       /*FALLTHROUGH*/
    297     case 'o':
    298       _uquad = UARG();
    299       base = OCT;
    300       goto nosign;
    301     case 'p':
    302       /*
    303        * ``The argument shall be a pointer to void.  The
    304        * value of the pointer is converted to a sequence
    305        * of printable characters, in an implementation-
    306        * defined manner.''
    307        *  -- ANSI X3J11
    308        */
    309       /* NOSTRICT */
    310       _uquad = (u_long)va_arg(ap, void *);
    311       base = HEX;
    312       xdigs = xdigs_lower;
    313       flags |= HEXPREFIX;
    314       ch = 'x';
    315       goto nosign;
    316     case 's':
    317       if ((cp = va_arg(ap, char *)) == NULL)
    318         /*XXXUNCONST*/
    319         cp = __UNCONST("(null)");
    320       if (prec >= 0) {
    321         /*
    322          * can't use strlen; can only look for the
    323          * NUL in the first `prec' characters, and
    324          * strlen() will go further.
    325          */
    326         char *p = memchr(cp, 0, (size_t)prec);
    327 
    328         if (p != NULL) {
    329           size = p - cp;
    330           if (size > prec)
    331             size = prec;
    332         } else
    333           size = prec;
    334       } else
    335         size = strlen(cp);
    336       sign = '\0';
    337       break;
    338     case 'U':
    339       flags |= LONGINT;
    340       /*FALLTHROUGH*/
    341     case 'u':
    342       _uquad = UARG();
    343       base = DEC;
    344       goto nosign;
    345     case 'X':
    346       xdigs = xdigs_upper;
    347       goto hex;
    348     case 'x':
    349       xdigs = xdigs_lower;
    350 hex:      _uquad = UARG();
    351       base = HEX;
    352       /* leading 0x/X only if non-zero */
    353       if (flags & ALT && _uquad != 0)
    354         flags |= HEXPREFIX;
    355 
    356       /* unsigned conversions */
    357 nosign:     sign = '\0';
    358       /*
    359        * ``... diouXx conversions ... if a precision is
    360        * specified, the 0 flag will be ignored.''
    361        *  -- ANSI X3J11
    362        */
    363 number:     if ((dprec = prec) >= 0)
    364         flags &= ~ZEROPAD;
    365 
    366       /*
    367        * ``The result of converting a zero value with an
    368        * explicit precision of zero is no characters.''
    369        *  -- ANSI X3J11
    370        */
    371       cp = bf + sizeof(bf);
    372       if (_uquad != 0 || prec != 0) {
    373         /*
    374          * Unsigned mod is hard, and unsigned mod
    375          * by a constant is easier than that by
    376          * a variable; hence this switch.
    377          */
    378         switch (base) {
    379         case OCT:
    380           do {
    381             *--cp = to_char(_uquad & 7);
    382             _uquad >>= 3;
    383           } while (_uquad);
    384           /* handle octal leading 0 */
    385           if (flags & ALT && *cp != '0')
    386             *--cp = '0';
    387           break;
    388 
    389         case DEC:
    390           /* many numbers are 1 digit */
    391           while (_uquad >= 10) {
    392             *--cp = to_char(_uquad % 10);
    393             _uquad /= 10;
    394           }
    395           *--cp = to_char(_uquad);
    396           break;
    397 
    398         case HEX:
    399           do {
    400             *--cp = xdigs[(size_t)_uquad & 15];
    401             _uquad >>= 4;
    402           } while (_uquad);
    403           break;
    404 
    405         default:
    406           /*XXXUNCONST*/
    407           cp = __UNCONST("bug bad base");
    408           size = strlen(cp);
    409           goto skipsize;
    410         }
    411       }
    412       size = bf + sizeof(bf) - cp;
    413     skipsize:
    414       break;
    415     default:  /* "%?" prints ?, unless ? is NUL */
    416       if (ch == '\0')
    417         goto done;
    418       /* pretend it was %c with argument ch */
    419       cp = bf;
    420       *cp = ch;
    421       size = 1;
    422       sign = '\0';
    423       break;
    424     }
    425 
    426     /*
    427      * All reasonable formats wind up here.  At this point, `cp'
    428      * points to a string which (if not flags&LADJUST) should be
    429      * padded out to `width' places.  If flags&ZEROPAD, it should
    430      * first be prefixed by any sign or other prefix; otherwise,
    431      * it should be blank padded before the prefix is emitted.
    432      * After any left-hand padding and prefixing, emit zeroes
    433      * required by a decimal [diouxX] precision, then print the
    434      * string proper, then emit zeroes required by any leftover
    435      * floating precision; finally, if LADJUST, pad with blanks.
    436      *
    437      * Compute actual size, so we know how much to pad.
    438      * size excludes decimal prec; realsz includes it.
    439      */
    440     realsz = dprec > size ? dprec : size;
    441     if (sign)
    442       realsz++;
    443     else if (flags & HEXPREFIX)
    444       realsz+= 2;
    445 
    446     /* adjust ret */
    447     ret += width > realsz ? width : realsz;
    448 
    449     /* right-adjusting blank padding */
    450     if ((flags & (LADJUST|ZEROPAD)) == 0) {
    451       n = width - realsz;
    452       while (n-- > 0)
    453         PUTCHAR(' ');
    454     }
    455 
    456     /* prefix */
    457     if (sign) {
    458       PUTCHAR(sign);
    459     } else if (flags & HEXPREFIX) {
    460       PUTCHAR('0');
    461       PUTCHAR(ch);
    462     }
    463 
    464     /* right-adjusting zero padding */
    465     if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) {
    466       n = width - realsz;
    467       while (n-- > 0)
    468         PUTCHAR('0');
    469     }
    470 
    471     /* leading zeroes from decimal precision */
    472     n = dprec - size;
    473     while (n-- > 0)
    474       PUTCHAR('0');
    475 
    476     /* the string or number proper */
    477     while (size--)
    478       PUTCHAR(*cp++);
    479     /* left-adjusting padding (always blank) */
    480     if (flags & LADJUST) {
    481       n = width - realsz;
    482       while (n-- > 0)
    483         PUTCHAR(' ');
    484     }
    485   }
    486 
    487 done:
    488   if (sbuf == tailp)
    489     sbuf[-1] = '\0';
    490   else
    491     *sbuf = '\0';
    492   return (ret);
    493   /* NOTREACHED */
    494 }
    495