ccaa2ac97b
Changes since 4.6: 4.7 (9 April 2004) * Language: . new commands @float, @caption, @shortcaption, @listoffloats for initial implementation of floating material (figures, tables, etc). Ironically, they do not yet actually float anywhere. . new commands @docbook, @ifdocbook, @ifnotdocbook for conditional Docbook. . new commands @ordf{} and @ordm{} for Spanish feminine/masculine ordinals. . new commands @deftypecv[x] for class variables in typed OO languages. . new command @registeredsymbol for the r-in-a-circle symbol. . new command @headitem to make a heading row in @multitable. . new command @LaTeX{} for the LaTeX logo. . new command @comma{} to avoid comma-parsing problems. . @url is now a synonym for @uref; new command @indicateurl has the old meaning of just displaying a url as text. . @quotation now accepts an optional argument for labelling the text as a `Note', `Tip', etc. . @defun (et al.) heading lines can now be continued with a lone @. . @acronym accepts an optional argument for the meaning of the acronym. * makeinfo: . New environment variable TEXINFO_OUTPUT_FORMAT determines the output format at runtime, if no options are specified. . New option --plaintext, equivalent to --no-headers with Info output. . All outputs: - sections are numbered by default. . Info output: - punctuation is inserted after @pxref and @ref, if needed to make cross-references valid. - line numbers included in index menus, so Info readers can go to the exact line of an entry, not just a node. Also in plaintext output. - ^@^H[index^@^H] cookie included in index menus, so Info readers can handle the ] etc. commands better. . HTML output: - new algorithm for cross-references to other manuals, for maximum portability and stability. - include node name in <title> with split output. - @multicolumn fractions become percentages. - entities used for bullets, quotes, dashes, and others. - index entries are links to the exact locations. - <h4> and <h5> used for @sub and @subsubsections again. - accented dotless i supported. . XML output: many new tags and structure to preserve more source features. . Docbook output: - upgraded DTD to Docbook XML 4.2, no longer using Docbook SGML. - improved translation in general, for instance: - line annotations and marked quotations. * texi2dvi: . if available, use etex (pdfetex if --pdf) by default. . if the input file includes thumbpdf.sty (for LaTeX), then run thumbpdf. . more output if --debug. * texinfo.tex: . @defun names are now printed in typewriter (instead of bold), and within the arguments, @var text is printed in slanted typewriter. . @tex code is executed inside a TeX group, so that any changes must be prefixed with \global (or the equivalent) to be effective. (This change was actually made years ago, but never made it into the NEWS.) * info: . new option --where (aka --location, -w) to report where an Info file would be found, instead of reading it. . by default, output ANSI terminal escape sequences as-is; new option --no-raw-escapes overrides this. . use the newly-generated index line numbers. * Distribution: . new script gendocs.sh (not installed), for use by GNU maintainers in getting their manuals on the GNU web site. Documented in maintain.texi (http://www.gnu.org/prep/maintain_toc.html). . Most code uses ANSI C prototypes, to some extent. . New translation: nb. . automake 1.8.3, autoconf 2.59, gettext 0.14.1.
890 lines
22 KiB
C
890 lines
22 KiB
C
/* $NetBSD: vasnprintf.c,v 1.1.1.1 2004/07/12 23:27:17 wiz Exp $ */
|
|
|
|
/* 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);
|
|
if (buf_malloced != NULL)
|
|
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;
|
|
}
|
|
|
|
if (buf_malloced != NULL)
|
|
free (buf_malloced);
|
|
CLEANUP ();
|
|
*lengthp = length;
|
|
return result;
|
|
|
|
out_of_memory:
|
|
if (!(result == resultbuf || result == NULL))
|
|
free (result);
|
|
if (buf_malloced != NULL)
|
|
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
|