You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
884 lines
21 KiB
884 lines
21 KiB
/* 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. */ |
|
|
|
/* 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 |
|
|
|
#ifdef HAVE_CONFIG_H |
|
# include <config.h> |
|
#endif |
|
#ifndef IN_LIBINTL |
|
# include <alloca.h> |
|
#endif |
|
|
|
/* Specification. */ |
|
#if WIDE_CHAR_VERSION |
|
# include "vasnwprintf.h" |
|
#else |
|
# include "vasnprintf.h" |
|
#endif |
|
|
|
#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 */ |
|
#if WIDE_CHAR_VERSION |
|
# include "wprintf-parse.h" |
|
#else |
|
# include "printf-parse.h" |
|
#endif |
|
|
|
/* Checked size_t computations. */ |
|
#include "xsize.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 |
|
|
|
#if WIDE_CHAR_VERSION |
|
# define VASNPRINTF vasnwprintf |
|
# define CHAR_T wchar_t |
|
# define DIRECTIVE wchar_t_directive |
|
# define DIRECTIVES wchar_t_directives |
|
# define PRINTF_PARSE wprintf_parse |
|
# define USE_SNPRINTF 1 |
|
# if HAVE_DECL__SNWPRINTF |
|
/* On Windows, the function swprintf() has a different signature than |
|
on Unix; we use the _snwprintf() function instead. */ |
|
# define SNPRINTF _snwprintf |
|
# else |
|
/* Unix. */ |
|
# define SNPRINTF swprintf |
|
# endif |
|
#else |
|
# define VASNPRINTF vasnprintf |
|
# define CHAR_T char |
|
# define DIRECTIVE char_directive |
|
# define DIRECTIVES char_directives |
|
# define PRINTF_PARSE printf_parse |
|
# define USE_SNPRINTF (HAVE_DECL__SNPRINTF || HAVE_SNPRINTF) |
|
# if HAVE_DECL__SNPRINTF |
|
/* Windows. */ |
|
# define SNPRINTF _snprintf |
|
# else |
|
/* Unix. */ |
|
# define SNPRINTF snprintf |
|
# endif |
|
#endif |
|
|
|
CHAR_T * |
|
VASNPRINTF (CHAR_T *resultbuf, size_t *lengthp, const CHAR_T *format, va_list args) |
|
{ |
|
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; |
|
} |
|
|
|
{ |
|
size_t buf_neededlength; |
|
CHAR_T *buf; |
|
CHAR_T *buf_malloced; |
|
const CHAR_T *cp; |
|
size_t i; |
|
DIRECTIVE *dp; |
|
/* Output string accumulator. */ |
|
CHAR_T *result; |
|
size_t allocated; |
|
size_t length; |
|
|
|
/* Allocate a small buffer that will hold a directive passed to |
|
sprintf or snprintf. */ |
|
buf_neededlength = |
|
xsum4 (7, d.max_width_length, d.max_precision_length, 6); |
|
#if HAVE_ALLOCA |
|
if (buf_neededlength < 4000 / sizeof (CHAR_T)) |
|
{ |
|
buf = (CHAR_T *) alloca (buf_neededlength * sizeof (CHAR_T)); |
|
buf_malloced = NULL; |
|
} |
|
else |
|
#endif |
|
{ |
|
size_t buf_memsize = xtimes (buf_neededlength, sizeof (CHAR_T)); |
|
if (size_overflow_p (buf_memsize)) |
|
goto out_of_memory_1; |
|
buf = (CHAR_T *) malloc (buf_memsize); |
|
if (buf == NULL) |
|
goto out_of_memory_1; |
|
buf_malloced = buf; |
|
} |
|
|
|
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. */ |
|
|
|
/* Ensures that allocated >= needed. Aborts through a jump to |
|
out_of_memory if needed is SIZE_MAX or otherwise too big. */ |
|
#define ENSURE_ALLOCATION(needed) \ |
|
if ((needed) > allocated) \ |
|
{ \ |
|
size_t memory_size; \ |
|
CHAR_T *memory; \ |
|
\ |
|
allocated = (allocated > 0 ? xtimes (allocated, 2) : 12); \ |
|
if ((needed) > allocated) \ |
|
allocated = (needed); \ |
|
memory_size = xtimes (allocated, sizeof (CHAR_T)); \ |
|
if (size_overflow_p (memory_size)) \ |
|
goto out_of_memory; \ |
|
if (result == resultbuf || result == NULL) \ |
|
memory = (CHAR_T *) malloc (memory_size); \ |
|
else \ |
|
memory = (CHAR_T *) realloc (result, memory_size); \ |
|
if (memory == NULL) \ |
|
goto out_of_memory; \ |
|
if (result == resultbuf && length > 0) \ |
|
memcpy (memory, result, length * sizeof (CHAR_T)); \ |
|
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; |
|
size_t augmented_length = xsum (length, n); |
|
|
|
ENSURE_ALLOCATION (augmented_length); |
|
memcpy (result + length, cp, n * sizeof (CHAR_T)); |
|
length = augmented_length; |
|
} |
|
if (i == d.count) |
|
break; |
|
|
|
/* Execute a single directive. */ |
|
if (dp->conversion == '%') |
|
{ |
|
size_t augmented_length; |
|
|
|
if (!(dp->arg_index == ARG_NONE)) |
|
abort (); |
|
augmented_length = xsum (length, 1); |
|
ENSURE_ALLOCATION (augmented_length); |
|
result[length] = '%'; |
|
length = augmented_length; |
|
} |
|
else |
|
{ |
|
if (!(dp->arg_index != ARG_NONE)) |
|
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_T *p; |
|
unsigned int prefix_count; |
|
int prefixes[2]; |
|
#if !USE_SNPRINTF |
|
size_t tmp_length; |
|
CHAR_T tmpbuf[700]; |
|
CHAR_T *tmp; |
|
|
|
/* Allocate a temporary buffer of sufficient size for calling |
|
sprintf. */ |
|
{ |
|
size_t width; |
|
size_t precision; |
|
|
|
width = 0; |
|
if (dp->width_start != dp->width_end) |
|
{ |
|
if (dp->width_arg_index != ARG_NONE) |
|
{ |
|
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 ? (unsigned int) (-arg) : arg); |
|
} |
|
else |
|
{ |
|
const CHAR_T *digitp = dp->width_start; |
|
|
|
do |
|
width = xsum (xtimes (width, 10), *digitp++ - '0'); |
|
while (digitp != dp->width_end); |
|
} |
|
} |
|
|
|
precision = 6; |
|
if (dp->precision_start != dp->precision_end) |
|
{ |
|
if (dp->precision_arg_index != ARG_NONE) |
|
{ |
|
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_T *digitp = dp->precision_start + 1; |
|
|
|
precision = 0; |
|
do |
|
precision = xsum (xtimes (precision, 10), *digitp++ - '0'); |
|
while (digitp != dp->precision_end); |
|
} |
|
} |
|
|
|
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 |
|
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 */ |
|
+ 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 */ |
|
+ 10; /* sign, decimal point etc. */ |
|
tmp_length = xsum (tmp_length, precision); |
|
break; |
|
|
|
case 'e': case 'E': case 'g': case 'G': |
|
case 'a': case 'A': |
|
tmp_length = |
|
12; /* sign, decimal point, exponent etc. */ |
|
tmp_length = xsum (tmp_length, precision); |
|
break; |
|
|
|
case 'c': |
|
# if defined HAVE_WINT_T && !WIDE_CHAR_VERSION |
|
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 = |
|
local_wcslen (a.arg[dp->arg_index].a.a_wide_string); |
|
|
|
# if !WIDE_CHAR_VERSION |
|
tmp_length = xtimes (tmp_length, MB_CUR_MAX); |
|
# endif |
|
} |
|
else |
|
# endif |
|
tmp_length = 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 = xsum (tmp_length, 1); /* account for trailing NUL */ |
|
} |
|
|
|
if (tmp_length <= sizeof (tmpbuf) / sizeof (CHAR_T)) |
|
tmp = tmpbuf; |
|
else |
|
{ |
|
size_t tmp_memsize = xtimes (tmp_length, sizeof (CHAR_T)); |
|
|
|
if (size_overflow_p (tmp_memsize)) |
|
/* Overflow, would lead to out of memory. */ |
|
goto out_of_memory; |
|
tmp = (CHAR_T *) malloc (tmp_memsize); |
|
if (tmp == NULL) |
|
/* Out of memory. */ |
|
goto out_of_memory; |
|
} |
|
#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 * sizeof (CHAR_T)); |
|
p += n; |
|
} |
|
if (dp->precision_start != dp->precision_end) |
|
{ |
|
size_t n = dp->precision_end - dp->precision_start; |
|
memcpy (p, dp->precision_start, n * sizeof (CHAR_T)); |
|
p += n; |
|
} |
|
|
|
switch (type) |
|
{ |
|
#ifdef HAVE_LONG_LONG |
|
case TYPE_LONGLONGINT: |
|
case TYPE_ULONGLONGINT: |
|
*p++ = 'l'; |
|
/*FALLTHROUGH*/ |
|
#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 USE_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 != ARG_NONE) |
|
{ |
|
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 != ARG_NONE) |
|
{ |
|
if (!(a.arg[dp->precision_arg_index].type == TYPE_INT)) |
|
abort (); |
|
prefixes[prefix_count++] = a.arg[dp->precision_arg_index].a.a_int; |
|
} |
|
|
|
#if USE_SNPRINTF |
|
/* Prepare checking whether snprintf returns the count |
|
via %n. */ |
|
ENSURE_ALLOCATION (xsum (length, 1)); |
|
result[length] = '\0'; |
|
#endif |
|
|
|
for (;;) |
|
{ |
|
size_t maxlen; |
|
int count; |
|
int retcount; |
|
|
|
maxlen = allocated - length; |
|
count = -1; |
|
retcount = 0; |
|
|
|
#if USE_SNPRINTF |
|
# 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_LONG_LONG |
|
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 |
|
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; |
|
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; |
|
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 USE_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; |
|
} |
|
else |
|
{ |
|
/* Look at the snprintf() return value. */ |
|
if (retcount < 0) |
|
{ |
|
/* HP-UX 10.20 snprintf() is doubly deficient: |
|
It doesn't understand the '%n' directive, |
|
*and* it returns -1 (rather than the length |
|
that would have been required) when the |
|
buffer is too small. */ |
|
size_t bigger_need = |
|
xsum (xtimes (allocated, 2), 12); |
|
ENSURE_ALLOCATION (bigger_need); |
|
continue; |
|
} |
|
else |
|
count = retcount; |
|
} |
|
} |
|
#endif |
|
|
|
/* Attempt to handle failure. */ |
|
if (count < 0) |
|
{ |
|
if (!(result == resultbuf || result == NULL)) |
|
free (result); |
|
free (buf_malloced); |
|
CLEANUP (); |
|
errno = EINVAL; |
|
return NULL; |
|
} |
|
|
|
#if !USE_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 = |
|
xmax (xsum (length, count), xtimes (allocated, 2)); |
|
|
|
ENSURE_ALLOCATION (n); |
|
#if USE_SNPRINTF |
|
continue; |
|
#endif |
|
} |
|
|
|
#if USE_SNPRINTF |
|
/* The snprintf() result did fit. */ |
|
#else |
|
/* Append the sprintf() result. */ |
|
memcpy (result + length, tmp, count * sizeof (CHAR_T)); |
|
if (tmp != tmpbuf) |
|
free (tmp); |
|
#endif |
|
|
|
length += count; |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
|
|
/* Add the final NUL. */ |
|
ENSURE_ALLOCATION (xsum (length, 1)); |
|
result[length] = '\0'; |
|
|
|
if (result != resultbuf && length + 1 < allocated) |
|
{ |
|
/* Shrink the allocated memory if possible. */ |
|
CHAR_T *memory; |
|
|
|
memory = (CHAR_T *) realloc (result, (length + 1) * sizeof (CHAR_T)); |
|
if (memory != NULL) |
|
result = memory; |
|
} |
|
|
|
free (buf_malloced); |
|
CLEANUP (); |
|
*lengthp = length; |
|
return result; |
|
|
|
out_of_memory: |
|
if (!(result == resultbuf || result == NULL)) |
|
free (result); |
|
free (buf_malloced); |
|
out_of_memory_1: |
|
CLEANUP (); |
|
errno = ENOMEM; |
|
return NULL; |
|
} |
|
} |
|
|
|
#undef SNPRINTF |
|
#undef USE_SNPRINTF |
|
#undef PRINTF_PARSE |
|
#undef DIRECTIVES |
|
#undef DIRECTIVE |
|
#undef CHAR_T |
|
#undef VASNPRINTF
|
|
|