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
__weak_alias(vsnprintf_ss,_vsnprintf_ss)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