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
38static 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
119int
120vsnprintf_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
177rflag:    ch = *fmt++;
178reswitch: 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;
350hex:      _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 */
357nosign:     sign = '\0';
358      /*
359       * ``... diouXx conversions ... if a precision is
360       * specified, the 0 flag will be ignored.''
361       *  -- ANSI X3J11
362       */
363number:     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
487done:
488  if (sbuf == tailp)
489    sbuf[-1] = '\0';
490  else
491    *sbuf = '\0';
492  return (ret);
493  /* NOTREACHED */
494}
495