/*
** (c) Copyright 2003-2005 Matt Messier and John Viega
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**
** Redistributions of source code must retain the above copyright notice,
** this list of conditions and the following disclaimer.
**
** 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.
**
** Neither the name of the primary nor the names of the contributors may
** be used to endorse or promote products derived from this software
** without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT
** OWNER 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.
*/
#include "isafestr.h"
#define SAFESTR_FMTLIMIT_ARGCOUNT 256
#define SAFESTR_FMTARGTYPE_CHAR 1
#define SAFESTR_FMTARGTYPE_UCHAR 2
#define SAFESTR_FMTARGTYPE_SHORT 3
#define SAFESTR_FMTARGTYPE_USHORT 4
#define SAFESTR_FMTARGTYPE_INT 5
#define SAFESTR_FMTARGTYPE_UINT 6
#define SAFESTR_FMTARGTYPE_LONG 7
#define SAFESTR_FMTARGTYPE_ULONG 8
#define SAFESTR_FMTARGTYPE_LONG_LONG 9
#define SAFESTR_FMTARGTYPE_ULONG_LONG 10
#define SAFESTR_FMTARGTYPE_INTMAX_T 11
#define SAFESTR_FMTARGTYPE_UINTMAX_T 12
#define SAFESTR_FMTARGTYPE_SSIZE_T 13
#define SAFESTR_FMTARGTYPE_SIZE_T 14
#define SAFESTR_FMTARGTYPE_PTRDIFF_T 15
#define SAFESTR_FMTARGTYPE_UPTRDIFF_T 16
#define SAFESTR_FMTARGTYPE_SAFESTR_T 17
#define SAFESTR_FMTARGTYPE_VOID_PTR 18
#define SAFESTR_FMTARGTYPE_DOUBLE 19
#define SAFESTR_FMTARGTYPE_LONG_DOUBLE 20
#define SAFESTR_FMTFLAG_LEFT_JUSTIFY 0x00000001
#define SAFESTR_FMTFLAG_POSITIVE_SIGN 0x00000002
#define SAFESTR_FMTFLAG_POSITIVE_BLANK 0x00000004
#define SAFESTR_FMTFLAG_ALTERNATE_FORM 0x00000008
#define SAFESTR_FMTFLAG_ZERO_PAD 0x00000010
#define SAFESTR_FMTFLAG_WIDTH 0x00000020
#define SAFESTR_FMTFLAG_PRECISION 0x00000040
#define SAFESTR_FMTFLAG_OCTAL 0x00000080
#define SAFESTR_FMTFLAG_LOWER_HEX 0x00000100
#define SAFESTR_FMTFLAG_UPPER_HEX 0x00000200
#define SAFESTR_MODIFIER_CHAR 1
#define SAFESTR_MODIFIER_SHORT 2
#define SAFESTR_MODIFIER_LONG 3
#define SAFESTR_MODIFIER_LONG_LONG 4
#define SAFESTR_MODIFIER_INTMAX_T 5
#define SAFESTR_MODIFIER_SIZE_T 6
#define SAFESTR_MODIFIER_PTRDIFF_T 7
#define SAFESTR_MODIFIER_LONG_DOUBLE 8
#define DO_PADDING(len) \
do { \
if ((u_int32_t)(len) < (u_int32_t)width && (flags & SAFESTR_FMTFLAG_WIDTH)) { \
if (padding_size < (u_int32_t)(width - (len))) { \
if (!padding_size) \
padding = (char *)safestr_malloc(width - (len), XXL_ASSET_TEMPORARY, \
__FILE__, __LINE__); \
else \
padding = (char *)safestr_realloc(padding, width - (len), \
__FILE__, __LINE__); \
memset(padding + padding_size, ' ', (width - (len)) - padding_size); \
padding_size = width - (len); \
} \
*nbytes += outfn(padding, width - (len), arg); \
} \
} while (0)
#define PARSE_ARGNUM(arg) \
do { \
(arg) = 0; \
if (*(c + 1) >= '0' && *(c + 1) <= '9') { \
tmpint = 0; \
for (c_arg = c + 1; *c_arg; c_arg++) { \
if (*c_arg >= '0' && *c_arg <= '9') { \
tmpint = (tmpint * 10) + (*c_arg - '0'); \
continue; \
} \
if (*c_arg == '$') { \
if (tmpint < 1 || tmpint > SAFESTR_FMTLIMIT_ARGCOUNT) \
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_ARG, NULL); \
(arg) = (int)tmpint; \
c = c_arg; \
break; \
} \
break; \
} \
} \
} while (0)
#define SET_ARGLIST_TYPE(arg, _type) \
do { \
if ((arg) != -1) { \
if (!(arg)) { \
if (current_arg == SAFESTR_FMTLIMIT_ARGCOUNT) \
XXL_THROW_ERROR(SAFESTR_ERROR_TOO_MANY_FORMAT_ARGS, NULL); \
(arg) = current_arg++; \
} \
if ((arg) < max_arg && arglist->list[(arg)].type && \
arglist->list[(arg)].type != (_type)) \
{ \
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_ARG, NULL); \
} \
arglist->list[(arg)].type = (_type); \
if ((arg) >= max_arg) max_arg = (arg) + 1; \
} \
} while (0)
#define CHECK_OR_SET_ARGLIST() \
do { \
if (idx && !arglist->scanned) \
scan_format_string(ifmt, arglist); \
else if (!idx) { \
if (arglist->current == SAFESTR_FMTLIMIT_ARGCOUNT) \
XXL_THROW_ERROR(SAFESTR_ERROR_TOO_MANY_FORMAT_ARGS, NULL); \
idx = arglist->current++; \
if (idx == arglist->max) { \
arglist->list[idx].type = type; \
arglist->max++; \
load_argument(idx, arglist); \
} \
} \
if (idx >= arglist->max || \
(arglist->list[idx].type && arglist->list[idx].type != type)) \
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_ARG, NULL); \
} while (0)
typedef struct fmtarg_t
{
u_int32_t type;
union {
intmax_t fa_int;
uintmax_t fa_uint;
safestr_t fa_string;
void * fa_voidptr;
double fa_double;
#ifdef HAVE_LONG_DOUBLE
long double fa_long_double;
#endif
} arg;
} fmtarg_t;
typedef struct arglist_t
{
int current;
int max;
int scanned;
va_list ap;
fmtarg_t list[SAFESTR_FMTLIMIT_ARGCOUNT];
} arglist_t;
typedef u_int32_t (*output_fn)(char *, u_int32_t, void *);
static void load_argument (int, arglist_t *);
static void scan_format_string (isafestr_t, arglist_t *);
static intmax_t get_arglist_int (isafestr_t, arglist_t *, int, u_int32_t);
static uintmax_t get_arglist_uint (isafestr_t, arglist_t *, int, u_int32_t);
static safestr_t get_arglist_string (isafestr_t, arglist_t *, int, u_int32_t);
static void * get_arglist_voidptr (isafestr_t, arglist_t *, int, u_int32_t);
static double get_arglist_double (isafestr_t, arglist_t *, int, u_int32_t);
#ifdef HAVE_LONG_DOUBLE
static long double get_arglist_long_double(isafestr_t, arglist_t *, int, u_int32_t);
#endif
static u_int32_t output_to_isafestr (char *, u_int32_t, void *);
static u_int32_t output_to_stdout (char *, u_int32_t, void *);
static u_int32_t output_to_stream (char *, u_int32_t, void *);
static char * format_signed_int (intmax_t, u_int32_t, int, int, int *);
static char * format_unsigned_int (uintmax_t, u_int32_t, int, int, int *);
static int parse_format_string (output_fn, void *, isafestr_t, va_list, u_int32_t *);
static void
load_argument(int idx, arglist_t *arglist)
{
switch (arglist->list[idx].type)
{
case SAFESTR_FMTARGTYPE_CHAR:
arglist->list[idx].arg.fa_int = (intmax_t)(char)va_arg(arglist->ap, int);
break;
case SAFESTR_FMTARGTYPE_UCHAR:
arglist->list[idx].arg.fa_uint = (uintmax_t)(unsigned char)va_arg(arglist->ap, unsigned int);
break;
case SAFESTR_FMTARGTYPE_SHORT:
arglist->list[idx].arg.fa_int = (intmax_t)(short)va_arg(arglist->ap, int);
break;
case SAFESTR_FMTARGTYPE_USHORT:
arglist->list[idx].arg.fa_uint = (uintmax_t)(unsigned short)va_arg(arglist->ap, int);
break;
case SAFESTR_FMTARGTYPE_INT:
arglist->list[idx].arg.fa_int = (intmax_t)va_arg(arglist->ap, int);
break;
case SAFESTR_FMTARGTYPE_UINT:
arglist->list[idx].arg.fa_uint = (uintmax_t)va_arg(arglist->ap, unsigned int);
break;
case SAFESTR_FMTARGTYPE_LONG:
arglist->list[idx].arg.fa_int = (intmax_t)va_arg(arglist->ap, long);
break;
case SAFESTR_FMTARGTYPE_ULONG:
arglist->list[idx].arg.fa_uint = (uintmax_t)va_arg(arglist->ap, unsigned long);
break;
case SAFESTR_FMTARGTYPE_LONG_LONG:
arglist->list[idx].arg.fa_int = (intmax_t)va_arg(arglist->ap, int64_t);
break;
case SAFESTR_FMTARGTYPE_ULONG_LONG:
arglist->list[idx].arg.fa_uint = (uintmax_t)va_arg(arglist->ap, u_int64_t);
break;
case SAFESTR_FMTARGTYPE_INTMAX_T:
arglist->list[idx].arg.fa_int = va_arg(arglist->ap, intmax_t);
break;
case SAFESTR_FMTARGTYPE_UINTMAX_T:
arglist->list[idx].arg.fa_uint = va_arg(arglist->ap, uintmax_t);
break;
case SAFESTR_FMTARGTYPE_SSIZE_T:
arglist->list[idx].arg.fa_int = (intmax_t)va_arg(arglist->ap, ssize_t);
break;
case SAFESTR_FMTARGTYPE_SIZE_T:
arglist->list[idx].arg.fa_uint = (uintmax_t)va_arg(arglist->ap, size_t);
break;
case SAFESTR_FMTARGTYPE_PTRDIFF_T:
arglist->list[idx].arg.fa_int = (intmax_t)va_arg(arglist->ap, ptrdiff_t);
break;
case SAFESTR_FMTARGTYPE_UPTRDIFF_T:
arglist->list[idx].arg.fa_uint = (uintmax_t)va_arg(arglist->ap, uptrdiff_t);
break;
case SAFESTR_FMTARGTYPE_SAFESTR_T:
arglist->list[idx].arg.fa_string = va_arg(arglist->ap, safestr_t);
break;
case SAFESTR_FMTARGTYPE_VOID_PTR:
arglist->list[idx].arg.fa_voidptr = va_arg(arglist->ap, void *);
break;
case SAFESTR_FMTARGTYPE_DOUBLE:
arglist->list[idx].arg.fa_double = va_arg(arglist->ap, double);
break;
#ifdef HAVE_LONG_DOUBLE
case SAFESTR_FMTARGTYPE_LONG_DOUBLE:
arglist->list[idx].arg.fa_long_double = va_arg(arglist->ap, long double);
break;
#endif
default:
/* Grr, we have no idea what type this argument is. Do we
* throw an exception? Do we assume an int? Yet another
* example of how broken printf is ...
*/
arglist->list[idx].arg.fa_int = (intmax_t)va_arg(arglist->ap, int);
break;
}
}
static void
scan_format_string(isafestr_t ifmt, arglist_t *arglist)
{
int current_arg, done, format_arg, max_arg, modifier, precision_arg,
x, width_arg;
char *c, *c_arg;
va_list ap;
intmax_t tmpint;
u_int32_t type;
current_arg = 0;
max_arg = arglist->max;
for (c = ifmt->str; c < ifmt->str + ifmt->hdr.length; c++)
{
while (*c != '%' && c < ifmt->str + ifmt->hdr.length)
c++;
if (c == ifmt->str + ifmt->hdr.length)
break;
if (*(c + 1) == '%')
continue;
precision_arg = width_arg = -1;
PARSE_ARGNUM(format_arg);
for (done = 0, c++; *c && !done; c++)
{
switch (*c)
{
case '-':
case '+':
case ' ':
case '#':
case '0':
break;
default:
done = 1;
c--;
break;
}
}
if (*c == '*')
{
PARSE_ARGNUM(width_arg);
c++;
}
else
{
while (*c >= '0' && *c <= '9')
c++;
}
if (*c == '.')
{
c++;
if (*c == '*')
{
PARSE_ARGNUM(precision_arg);
c++;
}
else
{
while (*c >= '0' && *c <= '9')
c++;
}
}
switch (*c)
{
case 'h':
if (*++c != 'h')
modifier = SAFESTR_MODIFIER_SHORT;
else
{
c++;
modifier = SAFESTR_MODIFIER_CHAR;
}
break;
case 'l':
if (*++c != 'l')
modifier = SAFESTR_MODIFIER_LONG;
else
{
c++;
modifier = SAFESTR_MODIFIER_LONG_LONG;
}
break;
case 'j':
c++;
modifier = SAFESTR_MODIFIER_INTMAX_T;
break;
case 'z':
c++;
modifier = SAFESTR_MODIFIER_SIZE_T;
break;
case 't':
c++;
modifier = SAFESTR_MODIFIER_PTRDIFF_T;
break;
case 'L':
c++;
modifier = SAFESTR_MODIFIER_LONG_DOUBLE;
break;
default:
modifier = 0;
break;
}
type = 0;
switch (*c)
{
case 'd': case 'i':
switch (modifier)
{
case SAFESTR_MODIFIER_CHAR:
type = SAFESTR_FMTARGTYPE_CHAR;
break;
case SAFESTR_MODIFIER_SHORT:
type = SAFESTR_FMTARGTYPE_SHORT;
break;
case SAFESTR_MODIFIER_LONG:
type = SAFESTR_FMTARGTYPE_LONG;
break;
case SAFESTR_MODIFIER_LONG_LONG:
type = SAFESTR_FMTARGTYPE_LONG_LONG;
break;
case SAFESTR_MODIFIER_INTMAX_T:
type = SAFESTR_FMTARGTYPE_INTMAX_T;
break;
case SAFESTR_MODIFIER_SIZE_T:
type = SAFESTR_FMTARGTYPE_SSIZE_T;
break;
case SAFESTR_MODIFIER_PTRDIFF_T:
type = SAFESTR_FMTARGTYPE_PTRDIFF_T;
break;
case SAFESTR_MODIFIER_LONG_DOUBLE:
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_STRING, NULL);
break;
default:
type = SAFESTR_FMTARGTYPE_INT;
break;
}
break;
case 'o': case 'u': case 'x': case 'X':
switch (modifier)
{
case SAFESTR_MODIFIER_CHAR:
type = SAFESTR_FMTARGTYPE_UCHAR;
break;
case SAFESTR_MODIFIER_SHORT:
type = SAFESTR_FMTARGTYPE_USHORT;
break;
case SAFESTR_MODIFIER_LONG:
type = SAFESTR_FMTARGTYPE_ULONG;
break;
case SAFESTR_MODIFIER_LONG_LONG:
type = SAFESTR_FMTARGTYPE_ULONG_LONG;
break;
case SAFESTR_MODIFIER_INTMAX_T:
type = SAFESTR_FMTARGTYPE_UINTMAX_T;
break;
case SAFESTR_MODIFIER_SIZE_T:
type = SAFESTR_FMTARGTYPE_SIZE_T;
break;
case SAFESTR_MODIFIER_PTRDIFF_T:
type = SAFESTR_FMTARGTYPE_UPTRDIFF_T;
break;
case SAFESTR_MODIFIER_LONG_DOUBLE:
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_STRING, NULL);
break;
default:
type = SAFESTR_FMTARGTYPE_UINT;
break;
}
break;
case 'e': case 'E':
case 'f': case 'F':
case 'g': case 'G':
if (!modifier)
type = SAFESTR_FMTARGTYPE_DOUBLE;
else if (modifier == SAFESTR_MODIFIER_LONG_DOUBLE)
#ifdef HAVE_LONG_DOUBLE
type = SAFESTR_FMTARGTYPE_LONG_DOUBLE;
#else
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_ARG, NULL);
#endif
else
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_STRING, NULL);
break;
case 'c':
if (modifier)
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_STRING, NULL);
type = SAFESTR_FMTARGTYPE_CHAR;
break;
case 's':
if (modifier)
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_STRING, NULL);
type = SAFESTR_FMTARGTYPE_SAFESTR_T;
break;
case 'p':
if (modifier)
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_STRING, NULL);
type = SAFESTR_FMTARGTYPE_VOID_PTR;
break;
case 'n':
XXL_THROW_ERROR(SAFESTR_ERROR_ILLEGAL_PERCENT_N, NULL);
break;
case '%':
default:
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_STRING, NULL);
break;
}
SET_ARGLIST_TYPE(width_arg, SAFESTR_FMTARGTYPE_INT);
SET_ARGLIST_TYPE(precision_arg, SAFESTR_FMTARGTYPE_INT);
SET_ARGLIST_TYPE(format_arg, type);
}
va_copy(ap, arglist->ap);
for (x = arglist->current; x < max_arg; x++)
load_argument(x, arglist);
va_copy(arglist->ap, ap);
arglist->max = max_arg;
arglist->scanned = 1;
}
static intmax_t
get_arglist_int(isafestr_t ifmt, arglist_t *arglist, int idx, u_int32_t type)
{
CHECK_OR_SET_ARGLIST();
return arglist->list[idx].arg.fa_int;
}
static uintmax_t
get_arglist_uint(isafestr_t ifmt, arglist_t *arglist, int idx, u_int32_t type)
{
CHECK_OR_SET_ARGLIST();
return arglist->list[idx].arg.fa_uint;
}
static safestr_t
get_arglist_string(isafestr_t ifmt, arglist_t *arglist, int idx, u_int32_t type)
{
CHECK_OR_SET_ARGLIST();
return arglist->list[idx].arg.fa_string;
}
static void *
get_arglist_voidptr(isafestr_t ifmt, arglist_t *arglist, int idx, u_int32_t type)
{
CHECK_OR_SET_ARGLIST();
return arglist->list[idx].arg.fa_voidptr;
}
static double
get_arglist_double(isafestr_t ifmt, arglist_t *arglist, int idx, u_int32_t type)
{
CHECK_OR_SET_ARGLIST();
return arglist->list[idx].arg.fa_double;
}
#ifdef HAVE_LONG_DOUBLE
static long double
get_arglist_long_double(isafestr_t ifmt, arglist_t *arglist, int idx, u_int32_t type)
{
CHECK_OR_SET_ARGLIST();
return arglist->list[idx].arg.fa_long_double;
}
#endif
static u_int32_t
output_to_isafestr(char *data, u_int32_t nbytes, void *arg)
{
void *old_str;
isafestr_t *istr = (isafestr_t *)arg;
if ((*istr)->hdr.length + nbytes <= (*istr)->hdr.size)
(*istr)->hdr.length += nbytes;
else
{
old_str = (*istr)->str;
(*istr) = safestr_resize((*istr), (*istr)->hdr.length + nbytes);
XXL_ASSET_UPDATE(old_str, (*istr)->str);
}
memcpy((*istr)->str + (*istr)->hdr.length - nbytes, data, nbytes);
return nbytes;
}
static u_int32_t
output_to_stdout(char *data, u_int32_t nbytes, void *arg)
{
if (nbytes && fwrite(data, nbytes, 1, stdout) != 1)
XXL_THROW_ERROR(errno, NULL);
return nbytes;
}
static u_int32_t
output_to_stream(char *data, u_int32_t nbytes, void *arg)
{
if (nbytes && fwrite(data, nbytes, 1, (FILE *)arg) != 1)
XXL_THROW_ERROR(errno, NULL);
return nbytes;
}
static char *
format_signed_int(intmax_t value, u_int32_t flags, int width, int precision, int *result_length)
{
int length;
char *result;
intmax_t tmp;
static char *digits = "0123456789";
length = (value ? 0 : 1);
for (tmp = value; tmp; tmp /= 10)
length++;
if (length < precision)
length += (precision - length);
if (value < 0 || (flags & (SAFESTR_FMTFLAG_POSITIVE_SIGN | SAFESTR_FMTFLAG_POSITIVE_BLANK)))
length++;
if ((flags & SAFESTR_FMTFLAG_WIDTH) && (flags & SAFESTR_FMTFLAG_ZERO_PAD) && length < width)
length = width;
*result_length = length;
result = (char *)safestr_malloc(length + 1, XXL_ASSET_TEMPORARY, __FILE__, __LINE__);
result[length] = '\0';
if (!value)
result[--length] = '0';
for (tmp = value; tmp; tmp /= 10)
result[--length] = digits[(int)SAFESTR_ABS(tmp % 10)];
while (length)
result[--length] = '0';
if (value < 0)
result[0] = '-';
else if (flags & SAFESTR_FMTFLAG_POSITIVE_SIGN)
result[0] = '+';
else if (flags & SAFESTR_FMTFLAG_POSITIVE_BLANK)
result[0] = ' ';
return result;
}
static char *
format_unsigned_int(uintmax_t value, u_int32_t flags, int width, int precision, int *result_length)
{
int base, length;
char *result, *set;
uintmax_t tmp;
static char *lower_set = "0123456789abcdef";
static char *upper_set = "0123456789ABCDEF";
if (flags & SAFESTR_FMTFLAG_OCTAL) base = 8;
else if (flags & SAFESTR_FMTFLAG_LOWER_HEX) base = 16;
else if (flags & SAFESTR_FMTFLAG_UPPER_HEX) base = 16;
else base = 10;
set = ((flags & SAFESTR_FMTFLAG_LOWER_HEX) ? lower_set : upper_set);
length = (value ? 0 : 1);
for (tmp = value; tmp; tmp /= base)
length++;
if ((flags & SAFESTR_FMTFLAG_ALTERNATE_FORM) && (flags & SAFESTR_FMTFLAG_OCTAL))
if (length >= precision && value > 0)
precision = length + 1;
if (length < precision)
length += (precision - length);
if ((flags & SAFESTR_FMTFLAG_ALTERNATE_FORM) && (flags & (SAFESTR_FMTFLAG_LOWER_HEX | SAFESTR_FMTFLAG_UPPER_HEX)))
length += 2;
if ((flags & SAFESTR_FMTFLAG_WIDTH) && (flags & SAFESTR_FMTFLAG_ZERO_PAD) && length < width)
length = width;
*result_length = length;
result = (char *)safestr_malloc(length + 1, XXL_ASSET_TEMPORARY, __FILE__, __LINE__);
result[length] = '\0';
if (!value)
result[--length] = '0';
for (tmp = value; tmp; tmp /= base)
result[--length] = set[(tmp % base)];
while (length)
result[--length] = '0';
if (flags & SAFESTR_FMTFLAG_ALTERNATE_FORM)
{
if (flags & SAFESTR_FMTFLAG_LOWER_HEX) result[1] = 'x';
else if (flags & SAFESTR_FMTFLAG_UPPER_HEX) result[1] = 'X';
}
return result;
}
static int
parse_format_string(output_fn outfn, void *arg, isafestr_t ifmt,
va_list ap, u_int32_t *nbytes)
{
int argnum, done, length, modifier, trusted, width, precision, tmparg;
char *c, *c_arg, cs_c, *cs_fp, *padding, *result, *start;
intmax_t cs_d, tmpint;
arglist_t arglist;
safestr_t cs_s;
u_int32_t flags, padding_size, str_len;
uintmax_t cs_u;
isafestr_t ics_s;
arglist.current = 0;
arglist.max = 0;
arglist.scanned = 0;
va_copy(arglist.ap, ap);
memset(arglist.list, 0, sizeof(arglist.list));
cs_d = 0;
cs_u = 0;
padding = NULL;
*nbytes = padding_size = 0;
arglist.current = arglist.max = arglist.scanned = 0;
va_copy(arglist.ap, ap);
trusted = (ifmt->hdr.flags & SAFESTR_TRUSTED) == SAFESTR_TRUSTED;
for (c = start = ifmt->str; c < ifmt->str + ifmt->hdr.length; start = ++c)
{
while (*c != '%' && c < ifmt->str + ifmt->hdr.length)
c++;
if (c == ifmt->str + ifmt->hdr.length)
break;
if (c > start)
*nbytes += outfn(start, c - start, arg);
start = c; /* save this for use with a, A, e, E, f, F, g, and G */
if (*(c + 1) == '%')
{
*nbytes += outfn(++c, 1, arg);
continue;
}
/* check for nn$ argument specifier */
PARSE_ARGNUM(argnum);
/* flags */
done = width = precision = 0;
flags = 0;
for (c++; *c && !done; c++)
{
switch (*c)
{
case '-':
flags |= SAFESTR_FMTFLAG_LEFT_JUSTIFY;
break;
case '+':
flags |= SAFESTR_FMTFLAG_POSITIVE_SIGN;
break;
case ' ':
flags |= SAFESTR_FMTFLAG_POSITIVE_BLANK;
break;
case '#':
flags |= SAFESTR_FMTFLAG_ALTERNATE_FORM;
break;
case '0':
flags |= SAFESTR_FMTFLAG_ZERO_PAD;
break;
default:
done = 1;
c--;
break;
}
}
/* field width */
if (*c == '*')
{
PARSE_ARGNUM(tmparg);
flags |= SAFESTR_FMTFLAG_WIDTH;
width = (int)get_arglist_int(ifmt, &arglist, tmparg, SAFESTR_FMTARGTYPE_INT);
if (width < 0)
flags |= SAFESTR_FMTFLAG_LEFT_JUSTIFY;
c++;
}
else if (*c >= '0' && *c <= '9')
{
flags |= SAFESTR_FMTFLAG_WIDTH;
for (tmpint = 0; *c >= '0' && *c <= '9'; c++)
tmpint = (tmpint * 10) + (*c - '0');
if (tmpint > INT_MAX)
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_STRING, NULL);
width = (int)tmpint;
}
/* precision */
if (*c == '.')
{
precision = 0;
flags |= SAFESTR_FMTFLAG_PRECISION;
c++;
if (*c == '*')
{
PARSE_ARGNUM(tmparg);
precision = (int)get_arglist_int(ifmt, &arglist, tmparg, SAFESTR_FMTARGTYPE_INT);
if (precision < 0)
flags &= ~SAFESTR_FMTFLAG_PRECISION;
c++;
}
else if (*c >= '0' && *c <= '9')
{
for (tmpint = 0; *c >= '0' && *c <= '9'; c++)
tmpint = (tmpint * 10) + (*c - '0');
if (tmpint > INT_MAX)
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_STRING, NULL);
precision = (int)tmpint;
}
}
if ((flags & SAFESTR_FMTFLAG_PRECISION) && (flags & SAFESTR_FMTFLAG_ZERO_PAD))
flags &= ~SAFESTR_FMTFLAG_ZERO_PAD;
if ((flags & SAFESTR_FMTFLAG_POSITIVE_SIGN) && (flags & SAFESTR_FMTFLAG_POSITIVE_BLANK))
flags &= ~SAFESTR_FMTFLAG_POSITIVE_BLANK;
if ((flags & SAFESTR_FMTFLAG_LEFT_JUSTIFY) && (flags & SAFESTR_FMTFLAG_ZERO_PAD))
flags &= ~SAFESTR_FMTFLAG_ZERO_PAD;
/* length modifier */
modifier = 0;
switch (*c)
{
case 'h':
if (*++c != 'h')
modifier = SAFESTR_MODIFIER_SHORT;
else
{
c++;
modifier = SAFESTR_MODIFIER_CHAR;
}
break;
case 'l':
if (*++c != 'l')
modifier = SAFESTR_MODIFIER_LONG;
else
{
c++;
modifier = SAFESTR_MODIFIER_LONG_LONG;
}
break;
case 'j':
c++;
modifier = SAFESTR_MODIFIER_INTMAX_T;
break;
case 'z':
c++;
modifier = SAFESTR_MODIFIER_SIZE_T;
break;
case 't':
c++;
modifier = SAFESTR_MODIFIER_PTRDIFF_T;
break;
case 'L':
c++;
modifier = SAFESTR_MODIFIER_LONG_DOUBLE;
break;
}
/* conversion specifier */
switch (*c)
{
case 'd': case 'i':
if (flags & SAFESTR_FMTFLAG_ALTERNATE_FORM)
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_STRING, NULL);
if ((flags & SAFESTR_FMTFLAG_LEFT_JUSTIFY) && (flags & SAFESTR_FMTFLAG_ZERO_PAD))
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_STRING, NULL);
if (!(flags & SAFESTR_FMTFLAG_PRECISION))
precision = 1;
switch (modifier)
{
case SAFESTR_MODIFIER_CHAR:
cs_d = get_arglist_int(ifmt, &arglist, argnum, SAFESTR_FMTARGTYPE_CHAR);
break;
case SAFESTR_MODIFIER_SHORT:
cs_d = get_arglist_int(ifmt, &arglist, argnum, SAFESTR_FMTARGTYPE_SHORT);
break;
case SAFESTR_MODIFIER_LONG:
cs_d = get_arglist_int(ifmt, &arglist, argnum, SAFESTR_FMTARGTYPE_LONG);
break;
case SAFESTR_MODIFIER_LONG_LONG:
cs_d = get_arglist_int(ifmt, &arglist, argnum, SAFESTR_FMTARGTYPE_ULONG_LONG);
break;
case SAFESTR_MODIFIER_INTMAX_T:
cs_d = get_arglist_int(ifmt, &arglist, argnum, SAFESTR_FMTARGTYPE_INTMAX_T);
break;
case SAFESTR_MODIFIER_SIZE_T:
cs_d = get_arglist_int(ifmt, &arglist, argnum, SAFESTR_FMTARGTYPE_SSIZE_T);
break;
case SAFESTR_MODIFIER_PTRDIFF_T:
cs_d = get_arglist_int(ifmt, &arglist, argnum, SAFESTR_FMTARGTYPE_PTRDIFF_T);
break;
case SAFESTR_MODIFIER_LONG_DOUBLE:
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_STRING, NULL);
break;
default:
cs_d = get_arglist_int(ifmt, &arglist, argnum, SAFESTR_FMTARGTYPE_INT);
break;
}
result = format_signed_int(cs_d, flags, width, precision, &length);
if (!(flags & SAFESTR_FMTFLAG_LEFT_JUSTIFY))
DO_PADDING(length);
*nbytes += outfn(result, length, arg);
if (flags & SAFESTR_FMTFLAG_LEFT_JUSTIFY)
DO_PADDING(length);
break;
case 'o': case 'u': case 'x': case 'X':
if (*c == 'u' && (flags & SAFESTR_FMTFLAG_ALTERNATE_FORM))
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_STRING, NULL);
if (flags & (SAFESTR_FMTFLAG_POSITIVE_SIGN | SAFESTR_FMTFLAG_POSITIVE_BLANK))
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_STRING, NULL);
if (!(flags & SAFESTR_FMTFLAG_PRECISION))
precision = 1;
switch (modifier)
{
case SAFESTR_MODIFIER_CHAR:
cs_u = get_arglist_uint(ifmt, &arglist, argnum, SAFESTR_FMTARGTYPE_UCHAR);
break;
case SAFESTR_MODIFIER_SHORT:
cs_u = get_arglist_uint(ifmt, &arglist, argnum, SAFESTR_FMTARGTYPE_USHORT);
break;
case SAFESTR_MODIFIER_LONG:
cs_u = get_arglist_uint(ifmt, &arglist, argnum, SAFESTR_FMTARGTYPE_ULONG);
break;
case SAFESTR_MODIFIER_LONG_LONG:
cs_u = get_arglist_uint(ifmt, &arglist, argnum, SAFESTR_FMTARGTYPE_ULONG_LONG);
break;
case SAFESTR_MODIFIER_INTMAX_T:
cs_u = get_arglist_uint(ifmt, &arglist, argnum, SAFESTR_FMTARGTYPE_UINTMAX_T);
break;
case SAFESTR_MODIFIER_SIZE_T:
cs_u = get_arglist_uint(ifmt, &arglist, argnum, SAFESTR_FMTARGTYPE_SIZE_T);
break;
case SAFESTR_MODIFIER_PTRDIFF_T:
cs_u = get_arglist_uint(ifmt, &arglist, argnum, SAFESTR_FMTARGTYPE_PTRDIFF_T);
break;
case SAFESTR_MODIFIER_LONG_DOUBLE:
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_STRING, NULL);
break;
default:
cs_u = get_arglist_uint(ifmt, &arglist, argnum, SAFESTR_FMTARGTYPE_UINT);
break;
}
if (*c == 'o') flags |= SAFESTR_FMTFLAG_OCTAL;
if (*c == 'x') flags |= SAFESTR_FMTFLAG_LOWER_HEX;
if (*c == 'X') flags |= SAFESTR_FMTFLAG_UPPER_HEX;
result = format_unsigned_int(cs_u, flags, width, precision, &length);
if (!(flags & SAFESTR_FMTFLAG_LEFT_JUSTIFY))
DO_PADDING(length);
*nbytes += outfn(result, length, arg);
if (flags & SAFESTR_FMTFLAG_LEFT_JUSTIFY)
DO_PADDING(length);
break;
case 'e': case 'E':
case 'f': case 'F':
case 'g': case 'G':
if (modifier && modifier != SAFESTR_MODIFIER_LONG_DOUBLE)
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_STRING, NULL);
result = (char *)safestr_malloc(c - start + 2, XXL_ASSET_TEMPORARY, __FILE__, __LINE__);
memcpy(result, start, c - start + 1);
result[c - start + 1] = '\0';
if (!(flags & SAFESTR_FMTFLAG_PRECISION))
precision = 6;
length = precision + width + 64; /* 64 = fudge */
cs_fp = (char *)safestr_malloc(length + 1, XXL_ASSET_TEMPORARY, __FILE__, __LINE__);
if (modifier == SAFESTR_MODIFIER_LONG_DOUBLE)
{
#ifdef HAVE_LONG_DOUBLE
length = sprintf(cs_fp, result,
get_arglist_long_double(ifmt, &arglist, argnum, SAFESTR_FMTARGTYPE_LONG_DOUBLE));
#else
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_ARG, NULL);
#endif
}
else
{
length = sprintf(cs_fp, result,
get_arglist_double(ifmt, &arglist, argnum, SAFESTR_FMTARGTYPE_DOUBLE));
}
*nbytes += outfn(cs_fp, length, arg);
break;
case 'c':
/* Since wide characters are not supported, throw an error for
* any kind of length modifier even though C99 allows 'l' for 'c'
*/
if (modifier)
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_STRING, NULL);
if (flags & ~(SAFESTR_FMTFLAG_LEFT_JUSTIFY | SAFESTR_FMTFLAG_WIDTH))
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_STRING, NULL);
cs_c = (char)get_arglist_int(ifmt, &arglist, argnum, SAFESTR_FMTARGTYPE_CHAR);
if (!(flags & SAFESTR_FMTFLAG_LEFT_JUSTIFY))
DO_PADDING(1);
*nbytes += outfn(&cs_c, 1, arg);
if (flags & SAFESTR_FMTFLAG_LEFT_JUSTIFY)
DO_PADDING(1);
break;
case 's':
/* Since wide characters are not supported, throw an error for
* any kind of length modifier even though C99 allows 'l' for 's'
*/
if (modifier)
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_STRING, NULL);
#if 0
if (!(cs_s = get_arglist_string(ifmt, &arglist, argnum, SAFESTR_FMTARGTYPE_SAFESTR_T)))
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_ARG, NULL);
#else
if (!(cs_s = get_arglist_string(ifmt, &arglist, argnum, SAFESTR_FMTARGTYPE_SAFESTR_T)))
cs_s = SAFESTR_TEMP_TRUSTED("(null)");
else
safestr_reference(cs_s);
#endif
ics_s = (isafestr_t)((char *)cs_s - sizeof(small_isafestr_t));
if (ics_s->hdr.cookie != safestr_cookie)
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_ARG, NULL);
str_len = ics_s->hdr.length;
if (flags & SAFESTR_FMTFLAG_PRECISION && (u_int32_t)precision < ics_s->hdr.length)
str_len = precision;
if (!(flags & SAFESTR_FMTFLAG_LEFT_JUSTIFY))
DO_PADDING(str_len);
*nbytes += outfn(ics_s->str, str_len, arg);
safestr_release(cs_s);
if (flags & SAFESTR_FMTFLAG_LEFT_JUSTIFY)
DO_PADDING(str_len);
break;
case 'p':
if (modifier)
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_STRING, NULL);
if (flags & ~(SAFESTR_FMTFLAG_LEFT_JUSTIFY | SAFESTR_FMTFLAG_WIDTH | SAFESTR_FMTFLAG_PRECISION))
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_STRING, NULL);
flags |= (SAFESTR_FMTFLAG_ALTERNATE_FORM | SAFESTR_FMTFLAG_LOWER_HEX);
cs_u = (uintmax_t)(uintptr_t)get_arglist_voidptr(ifmt, &arglist, argnum, SAFESTR_FMTARGTYPE_VOID_PTR);
if (!(flags & SAFESTR_FMTFLAG_PRECISION))
precision = sizeof(void *) * 2;
else
precision = SAFESTR_MAX((int)(sizeof(void *) * 2), precision);
result = format_unsigned_int(cs_u, flags, width, precision, &length);
if (!(flags & SAFESTR_FMTFLAG_LEFT_JUSTIFY))
DO_PADDING(length);
*nbytes += outfn(result, length, arg);
if (flags & SAFESTR_FMTFLAG_LEFT_JUSTIFY)
DO_PADDING(length);
break;
case 'n':
XXL_THROW_ERROR(SAFESTR_ERROR_ILLEGAL_PERCENT_N, NULL);
break;
case '%':
default:
XXL_THROW_ERROR(SAFESTR_ERROR_INVALID_FORMAT_STRING, NULL);
break;
}
}
if (c > start)
*nbytes += outfn(start, c - start, arg);
return trusted;
}
u_int32_t
safestr_asprintf(safestr_t *str, safestr_t fmt, ...)
{
int trusted;
va_list ap;
u_int32_t result;
isafestr_t ifmt, iorig, istr;
XXL_ASSET_BLOCK_BEGIN
{
ifmt = safestr_get(fmt, SAFESTR_GET_READONLY);
*str = safestr_alloc(ifmt->hdr.length, SAFESTR_ASSET_PROMOTE);
iorig = istr = safestr_get(*str, SAFESTR_GET_WRITABLE);
va_start(ap, fmt);
trusted = parse_format_string(output_to_isafestr, (void *)&istr,
ifmt, ap, &result);
va_end(ap);
istr->str[istr->hdr.length] = '\0';
if (trusted) istr->hdr.flags |= SAFESTR_TRUSTED;
else istr->hdr.flags &= ~SAFESTR_TRUSTED;
*str = safestr_complete(iorig, istr);
}
XXL_ASSET_BLOCK_END;
return result;
}
u_int32_t
safestr_fprintf(FILE *stream, safestr_t fmt, ...)
{
va_list ap;
u_int32_t result;
isafestr_t ifmt;
XXL_ASSET_BLOCK_BEGIN
{
ifmt = safestr_get(fmt, SAFESTR_GET_READONLY);
va_start(ap, fmt);
parse_format_string(output_to_stream, stream, ifmt, ap, &result);
va_end(ap);
}
XXL_ASSET_BLOCK_END;
return result;
}
u_int32_t
safestr_printf(safestr_t fmt, ...)
{
va_list ap;
u_int32_t result;
isafestr_t ifmt;
XXL_ASSET_BLOCK_BEGIN
{
ifmt = safestr_get(fmt, SAFESTR_GET_READONLY);
va_start(ap, fmt);
parse_format_string(output_to_stdout, NULL, ifmt, ap, &result);
va_end(ap);
}
XXL_ASSET_BLOCK_END;
return result;
}
u_int32_t
safestr_sprintf(safestr_t *str, safestr_t fmt, ...)
{
int trusted;
va_list ap;
safestr_t temp;
u_int32_t result;
isafestr_t ifmt, iorig, istr, itemp;
XXL_ASSET_BLOCK_BEGIN
{
istr = iorig = safestr_get(*str, SAFESTR_GET_WRITABLE);
ifmt = safestr_get(fmt, SAFESTR_GET_READONLY);
temp = safestr_alloc(ifmt->hdr.length, SAFESTR_ASSET_TEMPORARY);
itemp = safestr_get(temp, SAFESTR_GET_WRITABLE);
va_start(ap, fmt);
trusted = parse_format_string(output_to_isafestr, (void *)&itemp,
ifmt, ap, &result);
va_end(ap);
itemp->str[itemp->hdr.length] = '\0';
if (istr->hdr.size < itemp->hdr.length)
istr = safestr_resize(istr, itemp->hdr.length);
else
istr->hdr.length = itemp->hdr.length;
memcpy(istr->str, itemp->str, itemp->hdr.length + 1);
if (trusted) istr->hdr.flags |= SAFESTR_TRUSTED;
else istr->hdr.flags &= ~SAFESTR_TRUSTED;
*str = safestr_complete(iorig, istr);
}
XXL_ASSET_BLOCK_END;
return result;
}
u_int32_t
safestr_vasprintf(safestr_t *str, safestr_t fmt, va_list ap)
{
int trusted;
u_int32_t result;
isafestr_t ifmt, iorig, istr;
XXL_ASSET_BLOCK_BEGIN
{
ifmt = safestr_get(fmt, SAFESTR_GET_READONLY);
*str = safestr_alloc(ifmt->hdr.length, SAFESTR_ASSET_PROMOTE);
iorig = istr = safestr_get(*str, SAFESTR_GET_WRITABLE);
trusted = parse_format_string(output_to_isafestr, (void *)&istr,
ifmt, ap, &result);
istr->str[istr->hdr.length] = '\0';
if (trusted) istr->hdr.flags |= SAFESTR_TRUSTED;
else istr->hdr.flags &= ~SAFESTR_TRUSTED;
*str = safestr_complete(iorig, istr);
}
XXL_ASSET_BLOCK_END;
return result;
}
u_int32_t
safestr_vfprintf(FILE *stream, safestr_t fmt, va_list ap)
{
u_int32_t result;
isafestr_t ifmt;
XXL_ASSET_BLOCK_BEGIN
{
ifmt = safestr_get(fmt, SAFESTR_GET_READONLY);
parse_format_string(output_to_stream, stream, ifmt, ap, &result);
}
XXL_ASSET_BLOCK_END;
return result;
}
u_int32_t
safestr_vprintf(safestr_t fmt, va_list ap)
{
u_int32_t result;
isafestr_t ifmt;
XXL_ASSET_BLOCK_BEGIN
{
ifmt = safestr_get(fmt, SAFESTR_GET_READONLY);
parse_format_string(output_to_stdout, NULL, ifmt, ap, &result);
}
XXL_ASSET_BLOCK_END;
return result;
}
u_int32_t
safestr_vsprintf(safestr_t *str, safestr_t fmt, va_list ap)
{
int trusted;
safestr_t temp;
u_int32_t result;
isafestr_t ifmt, iorig, istr, itemp;
XXL_ASSET_BLOCK_BEGIN
{
istr = iorig = safestr_get(*str, SAFESTR_GET_WRITABLE);
ifmt = safestr_get(fmt, SAFESTR_GET_READONLY);
temp = safestr_alloc(ifmt->hdr.length, SAFESTR_ASSET_TEMPORARY);
itemp = safestr_get(temp, SAFESTR_GET_WRITABLE);
trusted = parse_format_string(output_to_isafestr, (void *)&itemp,
ifmt, ap, &result);
itemp->str[itemp->hdr.length] = '\0';
if (istr->hdr.size < itemp->hdr.length)
istr = safestr_resize(istr, itemp->hdr.length);
else
istr->hdr.length = itemp->hdr.length;
memcpy(istr->str, itemp->str, itemp->hdr.length + 1);
if (trusted) istr->hdr.flags |= SAFESTR_TRUSTED;
else istr->hdr.flags &= ~SAFESTR_TRUSTED;
*str = safestr_complete(iorig, istr);
}
XXL_ASSET_BLOCK_END;
return result;
}
syntax highlighted by Code2HTML, v. 0.9.1