/* ** (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; }