gstreamer/gst/printf/vasnprintf.c
Tim-Philipp Müller 8fc876f09f printf: add our own printf implementation for debug logging
We will add support for our own printf modifiers, so we can
get nice debug log output on all operating systems irrespective
of the specific libc version used.

https://bugzilla.gnome.org/show_bug.cgi?id=613081
2013-04-12 23:05:56 +01:00

980 lines
29 KiB
C

/* 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"
#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. */
#ifdef 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 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;
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
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) \
free (a.arg);
if (printf_fetchargs (args, &a) < 0) {
CLEANUP ();
errno = EINVAL;
return NULL;
}
{
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];
#if !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 */
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;
#if 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;
}
#if 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;
#if HAVE_SNPRINTF
int retcount;
#endif
maxlen = allocated - length;
count = -1;
#if 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);
}
}
#if 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;
default:
abort ();
}
#if 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;
}
#if !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);
#if HAVE_SNPRINTF
continue;
#endif
}
#if 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;
}
}