blob: a97404c5ffac7dd1228dded1b753a83534da6754 [file] [log] [blame]
/* vsprintf with automatic memory allocation.
Copyright (C) 1999, 2002-2003 Free Software Foundation, Inc.
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published
by the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA. */
#ifndef _WIN32
/* Tell glibc's <stdio.h> to provide a prototype for snprintf().
This must come before <config.h> because <config.h> may include
<features.h>, and once <features.h> has been included, it's too late. */
#ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
#endif
#endif
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "gst-printf.h"
/* Specification. */
#include "vasnprintf.h"
#include <stdio.h> /* snprintf(), sprintf() */
#include <stdlib.h> /* abort(), malloc(), realloc(), free() */
#include <string.h> /* memcpy(), strlen() */
#include <errno.h> /* errno */
#include <limits.h> /* CHAR_BIT */
#include <float.h> /* DBL_MAX_EXP, LDBL_MAX_EXP */
#include "printf-parse.h"
#include "printf-extension.h"
#ifdef HAVE_WCHAR_T
# ifdef HAVE_WCSLEN
# define local_wcslen wcslen
# else
/* Solaris 2.5.1 has wcslen() in a separate library libw.so. To avoid
a dependency towards this library, here is a local substitute.
Define this substitute only once, even if this file is included
twice in the same compilation unit. */
# ifndef local_wcslen_defined
# define local_wcslen_defined 1
static size_t
local_wcslen (const wchar_t * s)
{
const wchar_t *ptr;
for (ptr = s; *ptr != (wchar_t) 0; ptr++);
return ptr - s;
}
# endif
# endif
#endif
/* For those losing systems which don't have 'alloca' we have to add
some additional code emulating it. */
#if defined (alloca) || defined (GLIB_HAVE_ALLOCA_H)
# define freea(p) /* nothing */
#else
# define alloca(n) malloc (n)
# define freea(p) free (p)
#endif
#ifndef HAVE_LONG_LONG_FORMAT
static inline int
print_long_long (char *buf,
int len,
int width,
int precision,
unsigned long flags, char conversion, unsigned long long number)
{
int negative = FALSE;
char buffer[128];
char *bufferend;
char *pointer;
int base;
static const char *upper = "0123456789ABCDEFX";
static const char *lower = "0123456789abcdefx";
const char *digits;
int i;
char *p;
int count;
#define EMIT(c) \
if (p - buf == len - 1) \
{ \
*p++ = '\0'; \
return len; \
} \
else \
*p++ = c;
p = buf;
switch (conversion) {
case 'o':
base = 8;
digits = lower;
negative = FALSE;
break;
case 'x':
base = 16;
digits = lower;
negative = FALSE;
break;
case 'X':
base = 16;
digits = upper;
negative = FALSE;
break;
case 'u':
base = 10;
digits = lower;
negative = FALSE;
break;
default:
base = 10;
digits = lower;
negative = (long long) number < 0;
if (negative)
number = -((long long) number);
break;
}
/* Build number */
pointer = bufferend = &buffer[sizeof (buffer) - 1];
*pointer-- = '\0';
for (i = 1; i < (int) sizeof (buffer); i++) {
*pointer-- = digits[number % base];
number /= base;
if (number == 0)
break;
}
/* Adjust width */
width -= (bufferend - pointer) - 1;
/* Adjust precision */
if (precision != -1) {
precision -= (bufferend - pointer) - 1;
if (precision < 0)
precision = 0;
flags |= FLAG_ZERO;
}
/* Adjust width further */
if (negative || (flags & FLAG_SHOWSIGN) || (flags & FLAG_SPACE))
width--;
if (flags & FLAG_ALT) {
switch (base) {
case 16:
width -= 2;
break;
case 8:
width--;
break;
default:
break;
}
}
/* Output prefixes spaces if needed */
if (!((flags & FLAG_LEFT) || ((flags & FLAG_ZERO) && (precision == -1)))) {
count = (precision == -1) ? 0 : precision;
while (width-- > count)
*p++ = ' ';
}
/* width has been adjusted for signs and alternatives */
if (negative) {
EMIT ('-');
} else if (flags & FLAG_SHOWSIGN) {
EMIT ('+');
} else if (flags & FLAG_SPACE) {
EMIT (' ');
}
if (flags & FLAG_ALT) {
switch (base) {
case 8:
EMIT ('0');
break;
case 16:
EMIT ('0');
EMIT (digits[16]);
break;
default:
break;
} /* switch base */
}
/* Output prefixed zero padding if needed */
if (flags & FLAG_ZERO) {
if (precision == -1)
precision = width;
while (precision-- > 0) {
EMIT ('0');
width--;
}
}
/* Output the number itself */
while (*(++pointer)) {
EMIT (*pointer);
}
/* Output trailing spaces if needed */
if (flags & FLAG_LEFT) {
while (width-- > 0)
EMIT (' ');
}
EMIT ('\0');
return p - buf - 1;
}
#endif
static void
printf_postprocess_args (char_directives * directives, arguments * arguments)
{
int i;
for (i = 0; i < directives->count; ++i) {
char_directive *dp;
argument *a;
dp = &directives->dir[i];
/* %% has no arguments, for example */
if (dp->arg_index < 0)
continue;
a = &arguments->arg[dp->arg_index];
if (a->type == TYPE_POINTER_EXT) {
char fmt[4];
fmt[0] = 'p';
fmt[1] = POINTER_EXT_SIGNIFIER_CHAR;
fmt[2] = dp->ptr_ext_char;
fmt[3] = '\0';
a->ext_string =
__gst_printf_pointer_extension_serialize (fmt, a->a.a_pointer);
}
}
}
char *
vasnprintf (char *resultbuf, size_t * lengthp, const char *format, va_list args)
{
char_directives d;
arguments a;
if (printf_parse (format, &d, &a) < 0) {
errno = EINVAL;
return NULL;
}
#define CLEANUP() \
free (d.dir); \
if (a.arg) { \
while (a.count--) { \
if (a.arg[a.count].ext_string) \
free (a.arg[a.count].ext_string); \
} \
free (a.arg); \
}
if (printf_fetchargs (args, &a) < 0) {
CLEANUP ();
errno = EINVAL;
return NULL;
}
/* collect TYPE_POINTER_EXT argument strings */
printf_postprocess_args (&d, &a);
{
char *buf =
(char *) alloca (7 + d.max_width_length + d.max_precision_length + 6);
const char *cp;
unsigned int i;
char_directive *dp;
/* Output string accumulator. */
char *result;
size_t allocated;
size_t length;
if (resultbuf != NULL) {
result = resultbuf;
allocated = *lengthp;
} else {
result = NULL;
allocated = 0;
}
length = 0;
/* Invariants:
result is either == resultbuf or == NULL or malloc-allocated.
If length > 0, then result != NULL. */
#define ENSURE_ALLOCATION(needed) \
if ((needed) > allocated) \
{ \
char *memory; \
\
allocated = (allocated > 0 ? 2 * allocated : 12); \
if ((needed) > allocated) \
allocated = (needed); \
if (result == resultbuf || result == NULL) \
memory = (char *) malloc (allocated); \
else \
memory = (char *) realloc (result, allocated); \
\
if (memory == NULL) \
{ \
if (!(result == resultbuf || result == NULL)) \
free (result); \
freea (buf); \
CLEANUP (); \
errno = ENOMEM; \
return NULL; \
} \
if (result == resultbuf && length > 0) \
memcpy (memory, result, length); \
result = memory; \
}
for (cp = format, i = 0, dp = &d.dir[0];; cp = dp->dir_end, i++, dp++) {
if (cp != dp->dir_start) {
size_t n = dp->dir_start - cp;
ENSURE_ALLOCATION (length + n);
memcpy (result + length, cp, n);
length += n;
}
if (i == d.count)
break;
/* Execute a single directive. */
if (dp->conversion == '%') {
if (!(dp->arg_index < 0))
abort ();
ENSURE_ALLOCATION (length + 1);
result[length] = '%';
length += 1;
} else {
if (!(dp->arg_index >= 0))
abort ();
if (dp->conversion == 'n') {
switch (a.arg[dp->arg_index].type) {
case TYPE_COUNT_SCHAR_POINTER:
*a.arg[dp->arg_index].a.a_count_schar_pointer = length;
break;
case TYPE_COUNT_SHORT_POINTER:
*a.arg[dp->arg_index].a.a_count_short_pointer = length;
break;
case TYPE_COUNT_INT_POINTER:
*a.arg[dp->arg_index].a.a_count_int_pointer = length;
break;
case TYPE_COUNT_LONGINT_POINTER:
*a.arg[dp->arg_index].a.a_count_longint_pointer = length;
break;
#ifdef HAVE_LONG_LONG
case TYPE_COUNT_LONGLONGINT_POINTER:
*a.arg[dp->arg_index].a.a_count_longlongint_pointer = length;
break;
#endif
default:
abort ();
}
} else {
arg_type type = a.arg[dp->arg_index].type;
char *p;
unsigned int prefix_count;
int prefixes[2];
#ifndef HAVE_SNPRINTF
unsigned int tmp_length;
char tmpbuf[700];
char *tmp;
/* Allocate a temporary buffer of sufficient size for calling
sprintf. */
{
unsigned int width;
unsigned int precision;
width = 0;
if (dp->width_start != dp->width_end) {
if (dp->width_arg_index >= 0) {
int arg;
if (!(a.arg[dp->width_arg_index].type == TYPE_INT))
abort ();
arg = a.arg[dp->width_arg_index].a.a_int;
width = (arg < 0 ? -arg : arg);
} else {
const char *digitp = dp->width_start;
do
width = width * 10 + (*digitp++ - '0');
while (digitp != dp->width_end);
}
}
precision = 6;
if (dp->precision_start != dp->precision_end) {
if (dp->precision_arg_index >= 0) {
int arg;
if (!(a.arg[dp->precision_arg_index].type == TYPE_INT))
abort ();
arg = a.arg[dp->precision_arg_index].a.a_int;
precision = (arg < 0 ? 0 : arg);
} else {
const char *digitp = dp->precision_start + 1;
precision = 0;
while (digitp != dp->precision_end)
precision = precision * 10 + (*digitp++ - '0');
}
}
switch (dp->conversion) {
case 'd':
case 'i':
case 'u':
# ifdef HAVE_LONG_LONG
if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT)
tmp_length = (unsigned int) (sizeof (unsigned long long) * CHAR_BIT * 0.30103 /* binary -> decimal */
* 2 /* estimate for FLAG_GROUP */
)
+ 1 /* turn floor into ceil */
+ 1; /* account for leading sign */
else
# endif
if (type == TYPE_LONGINT || type == TYPE_ULONGINT)
tmp_length = (unsigned int) (sizeof (unsigned long) * CHAR_BIT * 0.30103 /* binary -> decimal */
* 2 /* estimate for FLAG_GROUP */
)
+ 1 /* turn floor into ceil */
+ 1; /* account for leading sign */
else
tmp_length = (unsigned int) (sizeof (unsigned int) * CHAR_BIT * 0.30103 /* binary -> decimal */
* 2 /* estimate for FLAG_GROUP */
)
+ 1 /* turn floor into ceil */
+ 1; /* account for leading sign */
break;
case 'o':
# ifdef HAVE_LONG_LONG
if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT)
tmp_length = (unsigned int) (sizeof (unsigned long long) * CHAR_BIT * 0.333334 /* binary -> octal */
)
+ 1 /* turn floor into ceil */
+ 1; /* account for leading sign */
else
# endif
if (type == TYPE_LONGINT || type == TYPE_ULONGINT)
tmp_length = (unsigned int) (sizeof (unsigned long) * CHAR_BIT * 0.333334 /* binary -> octal */
)
+ 1 /* turn floor into ceil */
+ 1; /* account for leading sign */
else
tmp_length = (unsigned int) (sizeof (unsigned int) * CHAR_BIT * 0.333334 /* binary -> octal */
)
+ 1 /* turn floor into ceil */
+ 1; /* account for leading sign */
break;
case 'x':
case 'X':
# ifdef HAVE_LONG_LONG
if (type == TYPE_LONGLONGINT || type == TYPE_ULONGLONGINT)
tmp_length = (unsigned int) (sizeof (unsigned long long) * CHAR_BIT * 0.25 /* binary -> hexadecimal */
)
+ 1 /* turn floor into ceil */
+ 2; /* account for leading sign or alternate form */
else
# endif
# ifdef HAVE_INT64_AND_I64
if (type == TYPE_INT64 || type == TYPE_UINT64)
tmp_length = (unsigned int) (sizeof (unsigned __int64) * CHAR_BIT * 0.25 /* binary -> hexadecimal */
)
+ 1 /* turn floor into ceil */
+ 2; /* account for leading sign or alternate form */
else
# endif
if (type == TYPE_LONGINT || type == TYPE_ULONGINT)
tmp_length = (unsigned int) (sizeof (unsigned long) * CHAR_BIT * 0.25 /* binary -> hexadecimal */
)
+ 1 /* turn floor into ceil */
+ 2; /* account for leading sign or alternate form */
else
tmp_length = (unsigned int) (sizeof (unsigned int) * CHAR_BIT * 0.25 /* binary -> hexadecimal */
)
+ 1 /* turn floor into ceil */
+ 2; /* account for leading sign or alternate form */
break;
case 'f':
case 'F':
# ifdef HAVE_LONG_DOUBLE
if (type == TYPE_LONGDOUBLE)
tmp_length = (unsigned int) (LDBL_MAX_EXP * 0.30103 /* binary -> decimal */
* 2 /* estimate for FLAG_GROUP */
)
+ 1 /* turn floor into ceil */
+ precision + 10; /* sign, decimal point etc. */
else
# endif
tmp_length = (unsigned int) (DBL_MAX_EXP * 0.30103 /* binary -> decimal */
* 2 /* estimate for FLAG_GROUP */
)
+ 1 /* turn floor into ceil */
+ precision + 10; /* sign, decimal point etc. */
break;
case 'e':
case 'E':
case 'g':
case 'G':
case 'a':
case 'A':
tmp_length = precision + 12; /* sign, decimal point, exponent etc. */
break;
case 'c':
# ifdef HAVE_WINT_T
if (type == TYPE_WIDE_CHAR)
tmp_length = MB_CUR_MAX;
else
# endif
tmp_length = 1;
break;
case 's':
# ifdef HAVE_WCHAR_T
if (type == TYPE_WIDE_STRING)
tmp_length = (a.arg[dp->arg_index].a.a_wide_string == NULL ? 6 /* wcslen(L"(null)") */
: local_wcslen (a.arg[dp->arg_index].a.a_wide_string))
* MB_CUR_MAX;
else
# endif
tmp_length = a.arg[dp->arg_index].a.a_string == NULL ? 6 /* strlen("(null)") */
: strlen (a.arg[dp->arg_index].a.a_string);
break;
case 'p':
tmp_length = (unsigned int) (sizeof (void *) * CHAR_BIT * 0.25 /* binary -> hexadecimal */
)
+ 1 /* turn floor into ceil */
+ 2; /* account for leading 0x */
/* make sure we always have enough space for a plain %p, so + */
if (dp->flags & FLAG_PTR_EXT && a.arg[dp->arg_index].ext_string)
tmp_length += strlen (a.arg[dp->arg_index].ext_string);
break;
default:
abort ();
}
if (tmp_length < width)
tmp_length = width;
tmp_length++; /* account for trailing NUL */
}
if (tmp_length <= sizeof (tmpbuf))
tmp = tmpbuf;
else {
tmp = (char *) malloc (tmp_length);
if (tmp == NULL) {
/* Out of memory. */
if (!(result == resultbuf || result == NULL))
free (result);
freea (buf);
CLEANUP ();
errno = ENOMEM;
return NULL;
}
}
#endif
/* Construct the format string for calling snprintf or
sprintf. */
p = buf;
*p++ = '%';
if (dp->flags & FLAG_GROUP)
*p++ = '\'';
if (dp->flags & FLAG_LEFT)
*p++ = '-';
if (dp->flags & FLAG_SHOWSIGN)
*p++ = '+';
if (dp->flags & FLAG_SPACE)
*p++ = ' ';
if (dp->flags & FLAG_ALT)
*p++ = '#';
if (dp->flags & FLAG_ZERO)
*p++ = '0';
if (dp->width_start != dp->width_end) {
size_t n = dp->width_end - dp->width_start;
memcpy (p, dp->width_start, n);
p += n;
}
if (dp->precision_start != dp->precision_end) {
size_t n = dp->precision_end - dp->precision_start;
memcpy (p, dp->precision_start, n);
p += n;
}
switch (type) {
#ifdef HAVE_INT64_AND_I64
case TYPE_INT64:
case TYPE_UINT64:
*p++ = 'I';
*p++ = '6';
*p++ = '4';
break;
#endif
#ifdef HAVE_LONG_LONG
case TYPE_LONGLONGINT:
case TYPE_ULONGLONGINT:
#ifdef HAVE_INT64_AND_I64 /* The system (sn)printf uses %I64. Also assume
* that long long == __int64.
*/
*p++ = 'I';
*p++ = '6';
*p++ = '4';
break;
#else
*p++ = 'l';
/*FALLTHROUGH*/
#endif
#endif
case TYPE_LONGINT:
case TYPE_ULONGINT:
#ifdef HAVE_WINT_T
case TYPE_WIDE_CHAR:
#endif
#ifdef HAVE_WCHAR_T
case TYPE_WIDE_STRING:
#endif
*p++ = 'l';
break;
#ifdef HAVE_LONG_DOUBLE
case TYPE_LONGDOUBLE:
*p++ = 'L';
break;
#endif
default:
break;
}
*p = dp->conversion;
#ifdef HAVE_SNPRINTF
p[1] = '%';
p[2] = 'n';
p[3] = '\0';
#else
p[1] = '\0';
#endif
/* Construct the arguments for calling snprintf or sprintf. */
prefix_count = 0;
if (dp->width_arg_index >= 0) {
if (!(a.arg[dp->width_arg_index].type == TYPE_INT))
abort ();
prefixes[prefix_count++] = a.arg[dp->width_arg_index].a.a_int;
}
if (dp->precision_arg_index >= 0) {
if (!(a.arg[dp->precision_arg_index].type == TYPE_INT))
abort ();
prefixes[prefix_count++] = a.arg[dp->precision_arg_index].a.a_int;
}
#ifdef HAVE_SNPRINTF
/* Prepare checking whether snprintf returns the count
via %n. */
ENSURE_ALLOCATION (length + 1);
result[length] = '\0';
#endif
for (;;) {
size_t maxlen;
int count;
#ifdef HAVE_SNPRINTF
int retcount;
#endif
maxlen = allocated - length;
count = -1;
#ifdef HAVE_SNPRINTF
retcount = 0;
#define SNPRINTF_BUF(arg) \
switch (prefix_count) \
{ \
case 0: \
retcount = snprintf (result + length, maxlen, buf, \
arg, &count); \
break; \
case 1: \
retcount = snprintf (result + length, maxlen, buf, \
prefixes[0], arg, &count); \
break; \
case 2: \
retcount = snprintf (result + length, maxlen, buf, \
prefixes[0], prefixes[1], arg, \
&count); \
break; \
default: \
abort (); \
}
#else
#define SNPRINTF_BUF(arg) \
switch (prefix_count) \
{ \
case 0: \
count = sprintf (tmp, buf, arg); \
break; \
case 1: \
count = sprintf (tmp, buf, prefixes[0], arg); \
break; \
case 2: \
count = sprintf (tmp, buf, prefixes[0], prefixes[1],\
arg); \
break; \
default: \
abort (); \
}
#endif
switch (type) {
case TYPE_SCHAR:
{
int arg = a.arg[dp->arg_index].a.a_schar;
SNPRINTF_BUF (arg);
}
break;
case TYPE_UCHAR:
{
unsigned int arg = a.arg[dp->arg_index].a.a_uchar;
SNPRINTF_BUF (arg);
}
break;
case TYPE_SHORT:
{
int arg = a.arg[dp->arg_index].a.a_short;
SNPRINTF_BUF (arg);
}
break;
case TYPE_USHORT:
{
unsigned int arg = a.arg[dp->arg_index].a.a_ushort;
SNPRINTF_BUF (arg);
}
break;
case TYPE_INT:
{
int arg = a.arg[dp->arg_index].a.a_int;
SNPRINTF_BUF (arg);
}
break;
case TYPE_UINT:
{
unsigned int arg = a.arg[dp->arg_index].a.a_uint;
SNPRINTF_BUF (arg);
}
break;
case TYPE_LONGINT:
{
long int arg = a.arg[dp->arg_index].a.a_longint;
SNPRINTF_BUF (arg);
}
break;
case TYPE_ULONGINT:
{
unsigned long int arg = a.arg[dp->arg_index].a.a_ulongint;
SNPRINTF_BUF (arg);
}
break;
#ifdef HAVE_INT64_AND_I64
case TYPE_INT64:
{
__int64 arg = a.arg[dp->arg_index].a.a_int64;
SNPRINTF_BUF (arg);
}
break;
case TYPE_UINT64:
{
unsigned __int64 arg = a.arg[dp->arg_index].a.a_uint64;
SNPRINTF_BUF (arg);
}
break;
#endif
#ifdef HAVE_LONG_LONG
#ifndef HAVE_LONG_LONG_FORMAT
case TYPE_LONGLONGINT:
case TYPE_ULONGLONGINT:
{
unsigned long long int arg =
a.arg[dp->arg_index].a.a_ulonglongint;
int width;
int precision;
width = 0;
if (dp->width_start != dp->width_end) {
if (dp->width_arg_index >= 0) {
int arg;
if (!(a.arg[dp->width_arg_index].type == TYPE_INT))
abort ();
arg = a.arg[dp->width_arg_index].a.a_int;
width = (arg < 0 ? -arg : arg);
} else {
const char *digitp = dp->width_start;
do
width = width * 10 + (*digitp++ - '0');
while (digitp != dp->width_end);
}
}
precision = -1;
if (dp->precision_start != dp->precision_end) {
if (dp->precision_arg_index >= 0) {
int arg;
if (!(a.arg[dp->precision_arg_index].type == TYPE_INT))
abort ();
arg = a.arg[dp->precision_arg_index].a.a_int;
precision = (arg < 0 ? 0 : arg);
} else {
const char *digitp = dp->precision_start + 1;
precision = 0;
do
precision = precision * 10 + (*digitp++ - '0');
while (digitp != dp->precision_end);
}
}
#ifdef HAVE_SNPRINTF
count = print_long_long (result + length, maxlen,
width, precision, dp->flags, dp->conversion, arg);
#else
count = print_long_long (tmp, tmp_length,
width, precision, dp->flags, dp->conversion, arg);
#endif
}
break;
#else
case TYPE_LONGLONGINT:
{
long long int arg = a.arg[dp->arg_index].a.a_longlongint;
SNPRINTF_BUF (arg);
}
break;
case TYPE_ULONGLONGINT:
{
unsigned long long int arg =
a.arg[dp->arg_index].a.a_ulonglongint;
SNPRINTF_BUF (arg);
}
break;
#endif
#endif
case TYPE_DOUBLE:
{
double arg = a.arg[dp->arg_index].a.a_double;
SNPRINTF_BUF (arg);
}
break;
#ifdef HAVE_LONG_DOUBLE
case TYPE_LONGDOUBLE:
{
long double arg = a.arg[dp->arg_index].a.a_longdouble;
SNPRINTF_BUF (arg);
}
break;
#endif
case TYPE_CHAR:
{
int arg = a.arg[dp->arg_index].a.a_char;
SNPRINTF_BUF (arg);
}
break;
#ifdef HAVE_WINT_T
case TYPE_WIDE_CHAR:
{
wint_t arg = a.arg[dp->arg_index].a.a_wide_char;
SNPRINTF_BUF (arg);
}
break;
#endif
case TYPE_STRING:
{
const char *arg = a.arg[dp->arg_index].a.a_string == NULL
? "(null)" : a.arg[dp->arg_index].a.a_string;
SNPRINTF_BUF (arg);
}
break;
#ifdef HAVE_WCHAR_T
case TYPE_WIDE_STRING:
{
const wchar_t *arg =
a.arg[dp->arg_index].a.a_wide_string ==
NULL ? L"(null)" : a.arg[dp->arg_index].a.a_wide_string;
SNPRINTF_BUF (arg);
}
break;
#endif
case TYPE_POINTER:
{
void *arg = a.arg[dp->arg_index].a.a_pointer;
SNPRINTF_BUF (arg);
}
break;
case TYPE_POINTER_EXT:
{
void *arg = a.arg[dp->arg_index].a.a_pointer;
if (a.arg[dp->arg_index].ext_string != NULL) {
arg = a.arg[dp->arg_index].ext_string;
*p = 's';
}
SNPRINTF_BUF (arg);
}
break;
default:
abort ();
}
#ifdef HAVE_SNPRINTF
/* Portability: Not all implementations of snprintf()
are ISO C 99 compliant. Determine the number of
bytes that snprintf() has produced or would have
produced. */
if (count >= 0) {
/* Verify that snprintf() has NUL-terminated its
result. */
if (count < maxlen && result[length + count] != '\0')
abort ();
/* Portability hack. */
if (retcount > count)
count = retcount;
} else {
/* snprintf() doesn't understand the '%n'
directive. */
if (p[1] != '\0') {
/* Don't use the '%n' directive; instead, look
at the snprintf() return value. */
p[1] = '\0';
continue;
}
count = retcount;
}
#endif
/* Attempt to handle failure. */
if (count < 0) {
if (!(result == resultbuf || result == NULL))
free (result);
freea (buf);
CLEANUP ();
errno = EINVAL;
return NULL;
}
#ifndef HAVE_SNPRINTF
if (count >= tmp_length)
/* tmp_length was incorrectly calculated - fix the
code above! */
abort ();
#endif
/* Make room for the result. */
if (count >= maxlen) {
/* Need at least count bytes. But allocate
proportionally, to avoid looping eternally if
snprintf() reports a too small count. */
size_t n = length + count;
if (n < 2 * allocated)
n = 2 * allocated;
ENSURE_ALLOCATION (n);
#ifdef HAVE_SNPRINTF
continue;
#endif
}
#ifdef HAVE_SNPRINTF
/* The snprintf() result did fit. */
#else
/* Append the sprintf() result. */
memcpy (result + length, tmp, count);
if (tmp != tmpbuf)
free (tmp);
#endif
length += count;
break;
}
}
}
}
/* Add the final NUL. */
ENSURE_ALLOCATION (length + 1);
result[length] = '\0';
if (result != resultbuf && length + 1 < allocated) {
/* Shrink the allocated memory if possible. */
char *memory;
memory = (char *) realloc (result, length + 1);
if (memory != NULL)
result = memory;
}
freea (buf);
CLEANUP ();
*lengthp = length;
return result;
}
}