mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-26 18:20:44 +00:00
1041 lines
31 KiB
C
1041 lines
31 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"
|
|
#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;
|
|
}
|
|
}
|