Files
sudo/compat/snprintf.c
Todd C. Miller 98ffa51545 Add guards in compat source files. Not really needed since we only
include them in the Makefile if they are needed but should not hurt
either.
2013-04-01 10:19:26 -04:00

711 lines
17 KiB
C

/*
* Copyright (c) 1999-2005, 2008, 2010-2011
* Todd C. Miller <Todd.Miller@courtesan.com>
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Chris Torek.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* From: @(#)vfprintf.c 8.1 (Berkeley) 6/4/93
*/
/*
* v?snprintf/v?asprintf based on 4.4BSD stdio.
* NOTE: does not support floating point.
*/
#include <config.h>
#if !defined(HAVE_VSNPRINTF) || !defined(HAVE_SNPRINTF) || !defined(HAVE_VASPRINTF) || !defined(HAVE_ASPRINTF)
#include <sys/types.h>
#include <stdio.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif /* STDC_HEADERS */
#ifdef HAVE_STDINT_H
# include <stdint.h>
#endif
#ifdef HAVE_STRING_H
# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
# include <memory.h>
# endif
# include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif /* HAVE_STRINGS_H */
#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS)
# include <malloc.h>
#endif /* HAVE_MALLOC_H && !STDC_HEADERS */
#include <limits.h>
#include <stdarg.h>
#include "missing.h"
static int xxxprintf(char **, size_t, int, const char *, va_list);
/*
* Some systems may not have these defined in <limits.h>
*/
#ifndef ULONG_MAX
# define ULONG_MAX ((unsigned long)-1)
#endif
#ifndef LONG_MAX
# define LONG_MAX (ULONG_MAX / 2)
#endif
#ifdef HAVE_LONG_LONG_INT
# ifndef ULLONG_MAX
# ifdef UQUAD_MAX
# define ULLONG_MAX UQUAD_MAX
# else
# define ULLONG_MAX ((unsigned long long)-1)
# endif
# endif
# ifndef LLONG_MAX
# ifdef QUAD_MAX
# define LLONG_MAX QUAD_MAX
# else
# define LLONG_MAX (ULLONG_MAX / 2)
# endif
# endif
#endif /* HAVE_LONG_LONG_INT */
/*
* Macros for converting digits to letters and vice versa
*/
#define to_digit(c) ((c) - '0')
#define is_digit(c) ((unsigned int)to_digit(c) <= 9)
#define to_char(n) ((n) + '0')
/*
* Flags used during conversion.
*/
#define ALT 0x001 /* alternate form */
#define HEXPREFIX 0x002 /* add 0x or 0X prefix */
#define LADJUST 0x004 /* left adjustment */
#define LONGDBL 0x008 /* long double; unimplemented */
#define LONGINT 0x010 /* long integer */
#define QUADINT 0x020 /* quad integer */
#define SHORTINT 0x040 /* short integer */
#define ZEROPAD 0x080 /* zero (as opposed to blank) pad */
#define BUF 68
/*
* Convert an unsigned long to ASCII for printf purposes, returning
* a pointer to the first character of the string representation.
* Octal numbers can be forced to have a leading zero; hex numbers
* use the given digits.
*/
static char *
__ultoa(unsigned long val, char *endp, int base, int octzero, char *xdigs)
{
char *cp = endp;
long sval;
/*
* Handle the three cases separately, in the hope of getting
* better/faster code.
*/
switch (base) {
case 10:
if (val < 10) { /* many numbers are 1 digit */
*--cp = to_char(val);
return cp;
}
/*
* On many machines, unsigned arithmetic is harder than
* signed arithmetic, so we do at most one unsigned mod and
* divide; this is sufficient to reduce the range of
* the incoming value to where signed arithmetic works.
*/
if (val > LONG_MAX) {
*--cp = to_char(val % 10);
sval = val / 10;
} else
sval = val;
do {
*--cp = to_char(sval % 10);
sval /= 10;
} while (sval != 0);
break;
case 8:
do {
*--cp = to_char(val & 7);
val >>= 3;
} while (val);
if (octzero && *cp != '0')
*--cp = '0';
break;
case 16:
do {
*--cp = xdigs[val & 15];
val >>= 4;
} while (val);
break;
default: /* oops */
abort();
}
return cp;
}
/* Identical to __ultoa, but for quads. */
#ifdef HAVE_LONG_LONG_INT
# if SIZEOF_LONG_INT == 8
# define __uqtoa(v, e, b, o, x) __ultoa((unsigned long)(v), (e), (b), (o), (x))
# else
static char *
__uqtoa(unsigned long long val, char *endp, int base, int octzero, char *xdigs)
{
char *cp = endp;
long long sval;
/* quick test for small values; __ultoa is typically much faster */
/* (perhaps instead we should run until small, then call __ultoa?) */
if (val <= (unsigned long long)ULONG_MAX)
return __ultoa((unsigned long)val, endp, base, octzero, xdigs);
switch (base) {
case 10:
if (val < 10) {
*--cp = to_char(val % 10);
return cp;
}
if (val > LLONG_MAX) {
*--cp = to_char(val % 10);
sval = val / 10;
} else
sval = val;
do {
*--cp = to_char(sval % 10);
sval /= 10;
} while (sval != 0);
break;
case 8:
do {
*--cp = to_char(val & 7);
val >>= 3;
} while (val);
if (octzero && *cp != '0')
*--cp = '0';
break;
case 16:
do {
*--cp = xdigs[val & 15];
val >>= 4;
} while (val);
break;
default: /* oops */
abort();
}
return cp;
}
# endif /* !SIZEOF_LONG_INT */
#endif /* HAVE_LONG_LONG_INT */
/*
* Actual printf innards.
*/
static int
xxxprintf(char **strp, size_t strsize, int alloc, const char *fmt0, va_list ap)
{
char *fmt; /* format string */
int ch; /* character from fmt */
int n; /* handy integer (short term usage) */
char *cp; /* handy char pointer (short term usage) */
int flags; /* flags as above */
int ret; /* return value accumulator */
int width; /* width from format (%8d), or 0 */
int prec; /* precision from format (%.3d), or -1 */
char sign; /* sign prefix (' ', '+', '-', or \0) */
unsigned long ulval = 0; /* integer arguments %[diouxX] */
#ifdef HAVE_LONG_LONG_INT
unsigned long long uqval = 0; /* %q (quad) integers */
#endif
int base; /* base for [diouxX] conversion */
int dprec; /* a copy of prec if [diouxX], 0 otherwise */
int fieldsz; /* field size expanded by sign, etc */
int realsz; /* field size expanded by dprec */
int size; /* size of converted field or string */
char *xdigs = ""; /* digits for [xX] conversion */
char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */
char ox[2]; /* space for 0x hex-prefix */
char *str; /* pointer to string to fill */
char *estr; /* pointer to last char in str */
/*
* Choose PADSIZE to trade efficiency vs. size. If larger printf
* fields occur frequently, increase PADSIZE and make the initialisers
* below longer.
*/
#define PADSIZE 16 /* pad chunk size */
static char blanks[PADSIZE] =
{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
static char zeroes[PADSIZE] =
{'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
/* Print chars to "str", (allocate as needed if alloc is set). */
#define PRINT(ptr, len) do { \
const char *p = ptr; \
const char *endp = ptr + len; \
while (p < endp && (str < estr || alloc)) { \
if (alloc && str >= estr) { \
char *t; \
strsize = (strsize << 1) + 1; \
if (!(t = (char *)realloc(*strp, strsize))) { \
free(str); \
*strp = NULL; \
ret = -1; \
goto done; \
} \
str = t + (str - *strp); \
estr = t + strsize - 1; \
*strp = t; \
} \
*str++ = *p++; \
} \
} while (0)
/* BEWARE, PAD uses `n'. */
#define PAD(plen, pstr) do { \
if ((n = (plen)) > 0) { \
while (n > PADSIZE) { \
PRINT(pstr, PADSIZE); \
n -= PADSIZE; \
} \
PRINT(pstr, n); \
} \
} while (0)
/*
* To extend shorts properly, we need both signed and unsigned
* argument extraction methods.
*/
#define SARG() \
(flags&LONGINT ? va_arg(ap, long) : \
flags&SHORTINT ? (long)(short)va_arg(ap, int) : \
(long)va_arg(ap, int))
#define UARG() \
(flags&LONGINT ? va_arg(ap, unsigned long) : \
flags&SHORTINT ? (unsigned long)(unsigned short)va_arg(ap, int) : \
(unsigned long)va_arg(ap, unsigned int))
fmt = (char *)fmt0;
ret = 0;
if (alloc) {
strsize = 128;
*strp = str = (char *)malloc(strsize);
if (str == NULL) {
ret = -1;
goto done;
}
estr = str + 127;
} else {
str = *strp;
if (strsize)
estr = str + strsize - 1;
else
estr = NULL;
}
/*
* Scan the format for conversions (`%' character).
*/
for (;;) {
for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++)
/* void */;
if ((n = fmt - cp) != 0) {
PRINT(cp, n);
ret += n;
}
if (ch == '\0')
goto done;
fmt++; /* skip over '%' */
flags = 0;
dprec = 0;
width = 0;
prec = -1;
sign = '\0';
rflag: ch = *fmt++;
reswitch: switch (ch) {
case ' ':
/*
* ``If the space and + flags both appear, the space
* flag will be ignored.''
* -- ANSI X3J11
*/
if (!sign)
sign = ' ';
goto rflag;
case '#':
flags |= ALT;
goto rflag;
case '*':
/*
* ``A negative field width argument is taken as a
* - flag followed by a positive field width.''
* -- ANSI X3J11
* They don't exclude field widths read from args.
*/
if ((width = va_arg(ap, int)) >= 0)
goto rflag;
width = -width;
/* FALLTHROUGH */
case '-':
flags |= LADJUST;
goto rflag;
case '+':
sign = '+';
goto rflag;
case '.':
if ((ch = *fmt++) == '*') {
n = va_arg(ap, int);
prec = n < 0 ? -1 : n;
goto rflag;
}
n = 0;
while (is_digit(ch)) {
n = 10 * n + to_digit(ch);
ch = *fmt++;
}
prec = n < 0 ? -1 : n;
goto reswitch;
case '0':
/*
* ``Note that 0 is taken as a flag, not as the
* beginning of a field width.''
* -- ANSI X3J11
*/
flags |= ZEROPAD;
goto rflag;
case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
n = 0;
do {
n = 10 * n + to_digit(ch);
ch = *fmt++;
} while (is_digit(ch));
width = n;
goto reswitch;
case 'h':
flags |= SHORTINT;
goto rflag;
case 'l':
flags |= LONGINT;
goto rflag;
#ifdef HAVE_LONG_LONG_INT
case 'q':
flags |= QUADINT;
goto rflag;
#endif /* HAVE_LONG_LONG_INT */
case 'c':
*(cp = buf) = va_arg(ap, int);
size = 1;
sign = '\0';
break;
case 'D':
flags |= LONGINT;
/*FALLTHROUGH*/
case 'd':
case 'i':
#ifdef HAVE_LONG_LONG_INT
if (flags & QUADINT) {
uqval = va_arg(ap, long long);
if ((long long)uqval < 0) {
uqval = -uqval;
sign = '-';
}
}
else
#endif /* HAVE_LONG_LONG_INT */
{
ulval = SARG();
if ((long)ulval < 0) {
ulval = -ulval;
sign = '-';
}
}
base = 10;
goto number;
case 'n':
#ifdef HAVE_LONG_LONG_INT
if (flags & QUADINT)
*va_arg(ap, long long *) = ret;
else
#endif /* HAVE_LONG_LONG_INT */
if (flags & LONGINT)
*va_arg(ap, long *) = ret;
else if (flags & SHORTINT)
*va_arg(ap, short *) = ret;
else
*va_arg(ap, int *) = ret;
continue; /* no output */
case 'O':
flags |= LONGINT;
/*FALLTHROUGH*/
case 'o':
#ifdef HAVE_LONG_LONG_INT
if (flags & QUADINT)
uqval = va_arg(ap, unsigned long long);
else
#endif /* HAVE_LONG_LONG_INT */
ulval = UARG();
base = 8;
goto nosign;
case 'p':
/*
* ``The argument shall be a pointer to void. The
* value of the pointer is converted to a sequence
* of printable characters, in an implementation-
* defined manner.''
* -- ANSI X3J11
*/
ulval = (unsigned long)va_arg(ap, void *);
base = 16;
xdigs = "0123456789abcdef";
flags = (flags & ~QUADINT) | HEXPREFIX;
ch = 'x';
goto nosign;
case 's':
if ((cp = va_arg(ap, char *)) == NULL)
cp = "(null)";
if (prec >= 0) {
/*
* can't use strlen; can only look for the
* NUL in the first `prec' characters, and
* strlen() will go further.
*/
char *p = memchr(cp, 0, prec);
if (p != NULL) {
size = p - cp;
if (size > prec)
size = prec;
} else
size = prec;
} else
size = strlen(cp);
sign = '\0';
break;
case 'U':
flags |= LONGINT;
/*FALLTHROUGH*/
case 'u':
#ifdef HAVE_LONG_LONG_INT
if (flags & QUADINT)
uqval = va_arg(ap, unsigned long long);
else
#endif /* HAVE_LONG_LONG_INT */
ulval = UARG();
base = 10;
goto nosign;
case 'X':
xdigs = "0123456789ABCDEF";
goto hex;
case 'x':
xdigs = "0123456789abcdef";
hex:
#ifdef HAVE_LONG_LONG_INT
if (flags & QUADINT)
uqval = va_arg(ap, unsigned long long);
else
#endif /* HAVE_LONG_LONG_INT */
ulval = UARG();
base = 16;
/* leading 0x/X only if non-zero */
if (flags & ALT &&
#ifdef HAVE_LONG_LONG_INT
(flags & QUADINT ? uqval != 0 : ulval != 0))
#else
ulval != 0)
#endif /* HAVE_LONG_LONG_INT */
flags |= HEXPREFIX;
/* unsigned conversions */
nosign: sign = '\0';
/*
* ``... diouXx conversions ... if a precision is
* specified, the 0 flag will be ignored.''
* -- ANSI X3J11
*/
number: if ((dprec = prec) >= 0)
flags &= ~ZEROPAD;
/*
* ``The result of converting a zero value with an
* explicit precision of zero is no characters.''
* -- ANSI X3J11
*/
cp = buf + BUF;
#ifdef HAVE_LONG_LONG_INT
if (flags & QUADINT) {
if (uqval != 0 || prec != 0)
cp = __uqtoa(uqval, cp, base,
flags & ALT, xdigs);
}
else
#endif /* HAVE_LONG_LONG_INT */
{
if (ulval != 0 || prec != 0)
cp = __ultoa(ulval, cp, base,
flags & ALT, xdigs);
}
size = buf + BUF - cp;
break;
default: /* "%?" prints ?, unless ? is NUL */
if (ch == '\0')
goto done;
/* pretend it was %c with argument ch */
cp = buf;
*cp = ch;
size = 1;
sign = '\0';
break;
}
/*
* All reasonable formats wind up here. At this point, `cp'
* points to a string which (if not flags&LADJUST) should be
* padded out to `width' places. If flags&ZEROPAD, it should
* first be prefixed by any sign or other prefix; otherwise,
* it should be blank padded before the prefix is emitted.
* After any left-hand padding and prefixing, emit zeroes
* required by a decimal [diouxX] precision, then print the
* string proper, then emit zeroes required by any leftover
* floating precision; finally, if LADJUST, pad with blanks.
*
* Compute actual size, so we know how much to pad.
* fieldsz excludes decimal prec; realsz includes it.
*/
fieldsz = size;
if (sign)
fieldsz++;
else if (flags & HEXPREFIX)
fieldsz += 2;
realsz = dprec > fieldsz ? dprec : fieldsz;
/* right-adjusting blank padding */
if ((flags & (LADJUST|ZEROPAD)) == 0)
PAD(width - realsz, blanks);
/* prefix */
if (sign) {
PRINT(&sign, 1);
} else if (flags & HEXPREFIX) {
ox[0] = '0';
ox[1] = ch;
PRINT(ox, 2);
}
/* right-adjusting zero padding */
if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
PAD(width - realsz, zeroes);
/* leading zeroes from decimal precision */
PAD(dprec - fieldsz, zeroes);
/* the string or number proper */
PRINT(cp, size);
/* left-adjusting padding (always blank) */
if (flags & LADJUST)
PAD(width - realsz, blanks);
/* finally, adjust ret */
ret += width > realsz ? width : realsz;
}
done:
if (strsize)
*str = '\0';
return ret;
/* NOTREACHED */
}
#ifndef HAVE_VSNPRINTF
int
vsnprintf(char *str, size_t n, const char *fmt, va_list ap)
{
return xxxprintf(&str, n, 0, fmt, ap);
}
#endif /* HAVE_VSNPRINTF */
#ifndef HAVE_SNPRINTF
int
snprintf(char *str, size_t n, char const *fmt, ...)
{
int ret;
va_list ap;
va_start(ap, fmt);
ret = xxxprintf(&str, n, 0, fmt, ap);
va_end(ap);
return ret;
}
#endif /* HAVE_SNPRINTF */
#ifndef HAVE_VASPRINTF
int
vasprintf(char **str, const char *fmt, va_list ap)
{
return xxxprintf(str, 0, 1, fmt, ap);
}
#endif /* HAVE_VASPRINTF */
#ifndef HAVE_ASPRINTF
int
asprintf(char **str, char const *fmt, ...)
{
int ret;
va_list ap;
va_start(ap, fmt);
ret = xxxprintf(str, 0, 1, fmt, ap);
va_end(ap);
return ret;
}
#endif /* HAVE_ASPRINTF */
#endif /* !HAVE_VSNPRINTF || !HAVE_SNPRINTF || !HAVE_VASPRINTF || !HAVE_ASPRINTF */