vfprintf: Introduce printf_positional function

This splits a considerable chunk of code from the main vfprintf
function.  This will make it easier to remove the use of extend_alloca
from the positional argument handling code.
This commit is contained in:
Florian Weimer 2015-05-21 15:46:46 +01:00
parent f0f98189d8
commit f8194fa6f9
2 changed files with 399 additions and 370 deletions

View File

@ -1,3 +1,10 @@
2015-05-21 Florian Weimer <fweimer@redhat.com>
* stdio-common/vfprintf.c (vfprintf): Move local variables
args_malloced, specs, specs_malloced, and the code after
do_positional to the printf_positional function.
(printf_positional): New function.
2015-05-21 Florian Weimer <fweimer@redhat.com> 2015-05-21 Florian Weimer <fweimer@redhat.com>
* stdio-common/vfprintf.c (jump_table): Move out of the vfprintf * stdio-common/vfprintf.c (jump_table): Move out of the vfprintf

View File

@ -1209,6 +1209,14 @@ static const uint8_t jump_table[] =
static int buffered_vfprintf (FILE *stream, const CHAR_T *fmt, va_list) static int buffered_vfprintf (FILE *stream, const CHAR_T *fmt, va_list)
__THROW __attribute__ ((noinline)) internal_function; __THROW __attribute__ ((noinline)) internal_function;
/* Handle positional format specifiers. */
static int printf_positional (_IO_FILE *s,
const CHAR_T *format, int readonly_format,
va_list ap, va_list *ap_savep, int done,
int nspecs_done, const UCHAR_T *lead_str_end,
CHAR_T *work_buffer, int save_errno,
const char *grouping, THOUSANDS_SEP_T);
/* Handle unknown format specifier. */ /* Handle unknown format specifier. */
static int printf_unknown (FILE *, const struct printf_info *, static int printf_unknown (FILE *, const struct printf_info *,
const void *const *) __THROW; const void *const *) __THROW;
@ -1257,15 +1265,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
0 if unknown. */ 0 if unknown. */
int readonly_format = 0; int readonly_format = 0;
/* For the argument descriptions, which may be allocated on the heap. */
void *args_malloced = NULL;
/* For positional argument handling. */
struct printf_spec *specs;
/* Track if we malloced the SPECS array and thus must free it. */
bool specs_malloced = false;
/* Orient the stream. */ /* Orient the stream. */
#ifdef ORIENT #ifdef ORIENT
ORIENT; ORIENT;
@ -1670,232 +1669,265 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
/* Unlock stream and return. */ /* Unlock stream and return. */
goto all_done; goto all_done;
/* Here starts the more complex loop to handle positional parameters. */ /* Hand off processing for positional parameters. */
do_positional: do_positional:
{ if (__glibc_unlikely (workstart != NULL))
/* Array with information about the needed arguments. This has to {
be dynamically extensible. */
size_t nspecs = 0;
/* A more or less arbitrary start value. */
size_t nspecs_size = 32 * sizeof (struct printf_spec);
specs = alloca (nspecs_size);
/* The number of arguments the format string requests. This will
determine the size of the array needed to store the argument
attributes. */
size_t nargs = 0;
size_t bytes_per_arg;
union printf_arg *args_value;
int *args_size;
int *args_type;
/* Positional parameters refer to arguments directly. This could
also determine the maximum number of arguments. Track the
maximum number. */
size_t max_ref_arg = 0;
/* Just a counter. */
size_t cnt;
if (__glibc_unlikely (workstart != NULL))
free (workstart); free (workstart);
workstart = NULL; workstart = NULL;
}
done = printf_positional (s, format, readonly_format, ap, &ap_save,
done, nspecs_done, lead_str_end, work_buffer,
save_errno, grouping, thousands_sep);
if (grouping == (const char *) -1) all_done:
{ if (__glibc_unlikely (workstart != NULL))
free (workstart);
/* Unlock the stream. */
_IO_funlockfile (s);
_IO_cleanup_region_end (0);
return done;
}
static int
printf_positional (_IO_FILE *s, const CHAR_T *format, int readonly_format,
va_list ap, va_list *ap_savep, int done, int nspecs_done,
const UCHAR_T *lead_str_end,
CHAR_T *work_buffer, int save_errno,
const char *grouping, THOUSANDS_SEP_T thousands_sep)
{
/* For the argument descriptions, which may be allocated on the heap. */
void *args_malloced = NULL;
/* For positional argument handling. */
struct printf_spec *specs;
/* Track if we malloced the SPECS array and thus must free it. */
bool specs_malloced = false;
/* Array with information about the needed arguments. This has to
be dynamically extensible. */
size_t nspecs = 0;
/* A more or less arbitrary start value. */
size_t nspecs_size = 32 * sizeof (struct printf_spec);
specs = alloca (nspecs_size);
/* The number of arguments the format string requests. This will
determine the size of the array needed to store the argument
attributes. */
size_t nargs = 0;
size_t bytes_per_arg;
union printf_arg *args_value;
int *args_size;
int *args_type;
/* Positional parameters refer to arguments directly. This could
also determine the maximum number of arguments. Track the
maximum number. */
size_t max_ref_arg = 0;
/* Just a counter. */
size_t cnt;
CHAR_T *workstart = NULL;
if (grouping == (const char *) -1)
{
#ifdef COMPILE_WPRINTF #ifdef COMPILE_WPRINTF
thousands_sep = _NL_CURRENT_WORD (LC_NUMERIC, thousands_sep = _NL_CURRENT_WORD (LC_NUMERIC,
_NL_NUMERIC_THOUSANDS_SEP_WC); _NL_NUMERIC_THOUSANDS_SEP_WC);
#else #else
thousands_sep = _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP); thousands_sep = _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP);
#endif #endif
grouping = _NL_CURRENT (LC_NUMERIC, GROUPING); grouping = _NL_CURRENT (LC_NUMERIC, GROUPING);
if (*grouping == '\0' || *grouping == CHAR_MAX) if (*grouping == '\0' || *grouping == CHAR_MAX)
grouping = NULL; grouping = NULL;
} }
for (f = lead_str_end; *f != L_('\0'); f = specs[nspecs++].next_fmt) for (const UCHAR_T *f = lead_str_end; *f != L_('\0');
{ f = specs[nspecs++].next_fmt)
if (nspecs * sizeof (*specs) >= nspecs_size) {
{ if (nspecs * sizeof (*specs) >= nspecs_size)
/* Extend the array of format specifiers. */
if (nspecs_size * 2 < nspecs_size)
{
__set_errno (ENOMEM);
done = -1;
goto all_done;
}
struct printf_spec *old = specs;
if (__libc_use_alloca (2 * nspecs_size))
specs = extend_alloca (specs, nspecs_size, 2 * nspecs_size);
else
{
nspecs_size *= 2;
specs = malloc (nspecs_size);
if (specs == NULL)
{
__set_errno (ENOMEM);
specs = old;
done = -1;
goto all_done;
}
}
/* Copy the old array's elements to the new space. */
memmove (specs, old, nspecs * sizeof (*specs));
/* If we had previously malloc'd space for SPECS, then
release it after the copy is complete. */
if (specs_malloced)
free (old);
/* Now set SPECS_MALLOCED if needed. */
if (!__libc_use_alloca (nspecs_size))
specs_malloced = true;
}
/* Parse the format specifier. */
#ifdef COMPILE_WPRINTF
nargs += __parse_one_specwc (f, nargs, &specs[nspecs], &max_ref_arg);
#else
nargs += __parse_one_specmb (f, nargs, &specs[nspecs], &max_ref_arg);
#endif
}
/* Determine the number of arguments the format string consumes. */
nargs = MAX (nargs, max_ref_arg);
/* Calculate total size needed to represent a single argument across
all three argument-related arrays. */
bytes_per_arg = (sizeof (*args_value) + sizeof (*args_size)
+ sizeof (*args_type));
/* Check for potential integer overflow. */
if (__glibc_unlikely (nargs > INT_MAX / bytes_per_arg))
{
__set_errno (EOVERFLOW);
done = -1;
goto all_done;
}
/* Allocate memory for all three argument arrays. */
if (__libc_use_alloca (nargs * bytes_per_arg))
args_value = alloca (nargs * bytes_per_arg);
else
{
args_value = args_malloced = malloc (nargs * bytes_per_arg);
if (args_value == NULL)
{
done = -1;
goto all_done;
}
}
/* Set up the remaining two arrays to each point past the end of the
prior array, since space for all three has been allocated now. */
args_size = &args_value[nargs].pa_int;
args_type = &args_size[nargs];
memset (args_type, s->_flags2 & _IO_FLAGS2_FORTIFY ? '\xff' : '\0',
nargs * sizeof (*args_type));
/* XXX Could do sanity check here: If any element in ARGS_TYPE is
still zero after this loop, format is invalid. For now we
simply use 0 as the value. */
/* Fill in the types of all the arguments. */
for (cnt = 0; cnt < nspecs; ++cnt)
{
/* If the width is determined by an argument this is an int. */
if (specs[cnt].width_arg != -1)
args_type[specs[cnt].width_arg] = PA_INT;
/* If the precision is determined by an argument this is an int. */
if (specs[cnt].prec_arg != -1)
args_type[specs[cnt].prec_arg] = PA_INT;
switch (specs[cnt].ndata_args)
{
case 0: /* No arguments. */
break;
case 1: /* One argument; we already have the
type and size. */
args_type[specs[cnt].data_arg] = specs[cnt].data_arg_type;
args_size[specs[cnt].data_arg] = specs[cnt].size;
break;
default:
/* We have more than one argument for this format spec.
We must call the arginfo function again to determine
all the types. */
(void) (*__printf_arginfo_table[specs[cnt].info.spec])
(&specs[cnt].info,
specs[cnt].ndata_args, &args_type[specs[cnt].data_arg],
&args_size[specs[cnt].data_arg]);
break;
}
}
/* Now we know all the types and the order. Fill in the argument
values. */
for (cnt = 0; cnt < nargs; ++cnt)
switch (args_type[cnt])
{ {
#define T(tag, mem, type) \ /* Extend the array of format specifiers. */
case tag: \ if (nspecs_size * 2 < nspecs_size)
args_value[cnt].mem = va_arg (ap_save, type); \ {
__set_errno (ENOMEM);
done = -1;
goto all_done;
}
struct printf_spec *old = specs;
if (__libc_use_alloca (2 * nspecs_size))
specs = extend_alloca (specs, nspecs_size, 2 * nspecs_size);
else
{
nspecs_size *= 2;
specs = malloc (nspecs_size);
if (specs == NULL)
{
__set_errno (ENOMEM);
specs = old;
done = -1;
goto all_done;
}
}
/* Copy the old array's elements to the new space. */
memmove (specs, old, nspecs * sizeof (*specs));
/* If we had previously malloc'd space for SPECS, then
release it after the copy is complete. */
if (specs_malloced)
free (old);
/* Now set SPECS_MALLOCED if needed. */
if (!__libc_use_alloca (nspecs_size))
specs_malloced = true;
}
/* Parse the format specifier. */
#ifdef COMPILE_WPRINTF
nargs += __parse_one_specwc (f, nargs, &specs[nspecs], &max_ref_arg);
#else
nargs += __parse_one_specmb (f, nargs, &specs[nspecs], &max_ref_arg);
#endif
}
/* Determine the number of arguments the format string consumes. */
nargs = MAX (nargs, max_ref_arg);
/* Calculate total size needed to represent a single argument across
all three argument-related arrays. */
bytes_per_arg = (sizeof (*args_value) + sizeof (*args_size)
+ sizeof (*args_type));
/* Check for potential integer overflow. */
if (__glibc_unlikely (nargs > INT_MAX / bytes_per_arg))
{
__set_errno (EOVERFLOW);
done = -1;
goto all_done;
}
/* Allocate memory for all three argument arrays. */
if (__libc_use_alloca (nargs * bytes_per_arg))
args_value = alloca (nargs * bytes_per_arg);
else
{
args_value = args_malloced = malloc (nargs * bytes_per_arg);
if (args_value == NULL)
{
done = -1;
goto all_done;
}
}
/* Set up the remaining two arrays to each point past the end of the
prior array, since space for all three has been allocated now. */
args_size = &args_value[nargs].pa_int;
args_type = &args_size[nargs];
memset (args_type, s->_flags2 & _IO_FLAGS2_FORTIFY ? '\xff' : '\0',
nargs * sizeof (*args_type));
/* XXX Could do sanity check here: If any element in ARGS_TYPE is
still zero after this loop, format is invalid. For now we
simply use 0 as the value. */
/* Fill in the types of all the arguments. */
for (cnt = 0; cnt < nspecs; ++cnt)
{
/* If the width is determined by an argument this is an int. */
if (specs[cnt].width_arg != -1)
args_type[specs[cnt].width_arg] = PA_INT;
/* If the precision is determined by an argument this is an int. */
if (specs[cnt].prec_arg != -1)
args_type[specs[cnt].prec_arg] = PA_INT;
switch (specs[cnt].ndata_args)
{
case 0: /* No arguments. */
break;
case 1: /* One argument; we already have the
type and size. */
args_type[specs[cnt].data_arg] = specs[cnt].data_arg_type;
args_size[specs[cnt].data_arg] = specs[cnt].size;
break;
default:
/* We have more than one argument for this format spec.
We must call the arginfo function again to determine
all the types. */
(void) (*__printf_arginfo_table[specs[cnt].info.spec])
(&specs[cnt].info,
specs[cnt].ndata_args, &args_type[specs[cnt].data_arg],
&args_size[specs[cnt].data_arg]);
break;
}
}
/* Now we know all the types and the order. Fill in the argument
values. */
for (cnt = 0; cnt < nargs; ++cnt)
switch (args_type[cnt])
{
#define T(tag, mem, type) \
case tag: \
args_value[cnt].mem = va_arg (*ap_savep, type); \
break break
T (PA_WCHAR, pa_wchar, wint_t); T (PA_WCHAR, pa_wchar, wint_t);
case PA_CHAR: /* Promoted. */ case PA_CHAR: /* Promoted. */
case PA_INT|PA_FLAG_SHORT: /* Promoted. */ case PA_INT|PA_FLAG_SHORT: /* Promoted. */
#if LONG_MAX == INT_MAX #if LONG_MAX == INT_MAX
case PA_INT|PA_FLAG_LONG: case PA_INT|PA_FLAG_LONG:
#endif #endif
T (PA_INT, pa_int, int); T (PA_INT, pa_int, int);
#if LONG_MAX == LONG_LONG_MAX #if LONG_MAX == LONG_LONG_MAX
case PA_INT|PA_FLAG_LONG: case PA_INT|PA_FLAG_LONG:
#endif #endif
T (PA_INT|PA_FLAG_LONG_LONG, pa_long_long_int, long long int); T (PA_INT|PA_FLAG_LONG_LONG, pa_long_long_int, long long int);
#if LONG_MAX != INT_MAX && LONG_MAX != LONG_LONG_MAX #if LONG_MAX != INT_MAX && LONG_MAX != LONG_LONG_MAX
# error "he?" # error "he?"
#endif #endif
case PA_FLOAT: /* Promoted. */ case PA_FLOAT: /* Promoted. */
T (PA_DOUBLE, pa_double, double); T (PA_DOUBLE, pa_double, double);
case PA_DOUBLE|PA_FLAG_LONG_DOUBLE: case PA_DOUBLE|PA_FLAG_LONG_DOUBLE:
if (__ldbl_is_dbl) if (__ldbl_is_dbl)
{ {
args_value[cnt].pa_double = va_arg (ap_save, double); args_value[cnt].pa_double = va_arg (*ap_savep, double);
args_type[cnt] &= ~PA_FLAG_LONG_DOUBLE; args_type[cnt] &= ~PA_FLAG_LONG_DOUBLE;
} }
else else
args_value[cnt].pa_long_double = va_arg (ap_save, long double); args_value[cnt].pa_long_double = va_arg (*ap_savep, long double);
break; break;
case PA_STRING: /* All pointers are the same */ case PA_STRING: /* All pointers are the same */
case PA_WSTRING: /* All pointers are the same */ case PA_WSTRING: /* All pointers are the same */
T (PA_POINTER, pa_pointer, void *); T (PA_POINTER, pa_pointer, void *);
#undef T #undef T
default: default:
if ((args_type[cnt] & PA_FLAG_PTR) != 0) if ((args_type[cnt] & PA_FLAG_PTR) != 0)
args_value[cnt].pa_pointer = va_arg (ap_save, void *); args_value[cnt].pa_pointer = va_arg (*ap_savep, void *);
else if (__glibc_unlikely (__printf_va_arg_table != NULL) else if (__glibc_unlikely (__printf_va_arg_table != NULL)
&& __printf_va_arg_table[args_type[cnt] - PA_LAST] != NULL) && __printf_va_arg_table[args_type[cnt] - PA_LAST] != NULL)
{ {
args_value[cnt].pa_user = alloca (args_size[cnt]); args_value[cnt].pa_user = alloca (args_size[cnt]);
(*__printf_va_arg_table[args_type[cnt] - PA_LAST]) (*__printf_va_arg_table[args_type[cnt] - PA_LAST])
(args_value[cnt].pa_user, &ap_save); (args_value[cnt].pa_user, ap_savep);
} }
else else
args_value[cnt].pa_long_double = 0.0; args_value[cnt].pa_long_double = 0.0;
break; break;
case -1: case -1:
/* Error case. Not all parameters appear in N$ format /* Error case. Not all parameters appear in N$ format
strings. We have no way to determine their type. */ strings. We have no way to determine their type. */
assert (s->_flags2 & _IO_FLAGS2_FORTIFY); assert (s->_flags2 & _IO_FLAGS2_FORTIFY);
__libc_fatal ("*** invalid %N$ use detected ***\n"); __libc_fatal ("*** invalid %N$ use detected ***\n");
} }
/* Now walk through all format specifiers and process them. */ /* Now walk through all format specifiers and process them. */
for (; (size_t) nspecs_done < nspecs; ++nspecs_done) for (; (size_t) nspecs_done < nspecs; ++nspecs_done)
{ {
#undef REF #undef REF
#ifdef SHARED #ifdef SHARED
# undef JUMP_TABLE_BASE_LABEL # undef JUMP_TABLE_BASE_LABEL
@ -1906,184 +1938,174 @@ do_positional:
#endif #endif
#undef LABEL #undef LABEL
#define LABEL(Name) do2_##Name #define LABEL(Name) do2_##Name
STEP4_TABLE; STEP4_TABLE;
int is_negative; int is_negative;
union union
{
unsigned long long int longlong;
unsigned long int word;
} number;
int base;
union printf_arg the_arg;
CHAR_T *string; /* Pointer to argument string. */
/* Fill variables from values in struct. */
int alt = specs[nspecs_done].info.alt;
int space = specs[nspecs_done].info.space;
int left = specs[nspecs_done].info.left;
int showsign = specs[nspecs_done].info.showsign;
int group = specs[nspecs_done].info.group;
int is_long_double = specs[nspecs_done].info.is_long_double;
int is_short = specs[nspecs_done].info.is_short;
int is_char = specs[nspecs_done].info.is_char;
int is_long = specs[nspecs_done].info.is_long;
int width = specs[nspecs_done].info.width;
int prec = specs[nspecs_done].info.prec;
int use_outdigits = specs[nspecs_done].info.i18n;
char pad = specs[nspecs_done].info.pad;
CHAR_T spec = specs[nspecs_done].info.spec;
workstart = NULL;
CHAR_T *workend = work_buffer + WORK_BUFFER_SIZE;
/* Fill in last information. */
if (specs[nspecs_done].width_arg != -1)
{ {
unsigned long long int longlong; /* Extract the field width from an argument. */
unsigned long int word; specs[nspecs_done].info.width =
} number; args_value[specs[nspecs_done].width_arg].pa_int;
int base;
union printf_arg the_arg;
CHAR_T *string; /* Pointer to argument string. */
/* Fill variables from values in struct. */ if (specs[nspecs_done].info.width < 0)
int alt = specs[nspecs_done].info.alt; /* If the width value is negative left justification is
int space = specs[nspecs_done].info.space; selected and the value is taken as being positive. */
int left = specs[nspecs_done].info.left;
int showsign = specs[nspecs_done].info.showsign;
int group = specs[nspecs_done].info.group;
int is_long_double = specs[nspecs_done].info.is_long_double;
int is_short = specs[nspecs_done].info.is_short;
int is_char = specs[nspecs_done].info.is_char;
int is_long = specs[nspecs_done].info.is_long;
int width = specs[nspecs_done].info.width;
int prec = specs[nspecs_done].info.prec;
int use_outdigits = specs[nspecs_done].info.i18n;
char pad = specs[nspecs_done].info.pad;
CHAR_T spec = specs[nspecs_done].info.spec;
workstart = NULL;
workend = work_buffer + WORK_BUFFER_SIZE;
/* Fill in last information. */
if (specs[nspecs_done].width_arg != -1)
{
/* Extract the field width from an argument. */
specs[nspecs_done].info.width =
args_value[specs[nspecs_done].width_arg].pa_int;
if (specs[nspecs_done].info.width < 0)
/* If the width value is negative left justification is
selected and the value is taken as being positive. */
{
specs[nspecs_done].info.width *= -1;
left = specs[nspecs_done].info.left = 1;
}
width = specs[nspecs_done].info.width;
}
if (specs[nspecs_done].prec_arg != -1)
{
/* Extract the precision from an argument. */
specs[nspecs_done].info.prec =
args_value[specs[nspecs_done].prec_arg].pa_int;
if (specs[nspecs_done].info.prec < 0)
/* If the precision is negative the precision is
omitted. */
specs[nspecs_done].info.prec = -1;
prec = specs[nspecs_done].info.prec;
}
/* Maybe the buffer is too small. */
if (MAX (prec, width) + 32 > WORK_BUFFER_SIZE)
{
if (__libc_use_alloca ((MAX (prec, width) + 32)
* sizeof (CHAR_T)))
workend = ((CHAR_T *) alloca ((MAX (prec, width) + 32)
* sizeof (CHAR_T))
+ (MAX (prec, width) + 32));
else
{
workstart = (CHAR_T *) malloc ((MAX (prec, width) + 32)
* sizeof (CHAR_T));
if (workstart == NULL)
{
done = -1;
goto all_done;
}
workend = workstart + (MAX (prec, width) + 32);
}
}
/* Process format specifiers. */
while (1)
{
extern printf_function **__printf_function_table;
int function_done;
if (spec <= UCHAR_MAX
&& __printf_function_table != NULL
&& __printf_function_table[(size_t) spec] != NULL)
{
const void **ptr = alloca (specs[nspecs_done].ndata_args
* sizeof (const void *));
/* Fill in an array of pointers to the argument values. */
for (unsigned int i = 0; i < specs[nspecs_done].ndata_args;
++i)
ptr[i] = &args_value[specs[nspecs_done].data_arg + i];
/* Call the function. */
function_done = __printf_function_table[(size_t) spec]
(s, &specs[nspecs_done].info, ptr);
if (function_done != -2)
{
/* If an error occurred we don't have information
about # of chars. */
if (function_done < 0)
{
/* Function has set errno. */
done = -1;
goto all_done;
}
done_add (function_done);
break;
}
}
JUMP (spec, step4_jumps);
process_arg ((&specs[nspecs_done]));
process_string_arg ((&specs[nspecs_done]));
LABEL (form_unknown):
{ {
unsigned int i; specs[nspecs_done].info.width *= -1;
const void **ptr; left = specs[nspecs_done].info.left = 1;
}
width = specs[nspecs_done].info.width;
}
ptr = alloca (specs[nspecs_done].ndata_args if (specs[nspecs_done].prec_arg != -1)
* sizeof (const void *)); {
/* Extract the precision from an argument. */
specs[nspecs_done].info.prec =
args_value[specs[nspecs_done].prec_arg].pa_int;
/* Fill in an array of pointers to the argument values. */ if (specs[nspecs_done].info.prec < 0)
for (i = 0; i < specs[nspecs_done].ndata_args; ++i) /* If the precision is negative the precision is
ptr[i] = &args_value[specs[nspecs_done].data_arg + i]; omitted. */
specs[nspecs_done].info.prec = -1;
/* Call the function. */ prec = specs[nspecs_done].info.prec;
function_done = printf_unknown (s, &specs[nspecs_done].info, }
ptr);
/* If an error occurred we don't have information about # /* Maybe the buffer is too small. */
of chars. */ if (MAX (prec, width) + 32 > WORK_BUFFER_SIZE)
if (function_done < 0) {
if (__libc_use_alloca ((MAX (prec, width) + 32)
* sizeof (CHAR_T)))
workend = ((CHAR_T *) alloca ((MAX (prec, width) + 32)
* sizeof (CHAR_T))
+ (MAX (prec, width) + 32));
else
{
workstart = (CHAR_T *) malloc ((MAX (prec, width) + 32)
* sizeof (CHAR_T));
if (workstart == NULL)
{ {
/* Function has set errno. */
done = -1; done = -1;
goto all_done; goto all_done;
} }
workend = workstart + (MAX (prec, width) + 32);
done_add (function_done);
} }
break; }
/* Process format specifiers. */
while (1)
{
extern printf_function **__printf_function_table;
int function_done;
if (spec <= UCHAR_MAX
&& __printf_function_table != NULL
&& __printf_function_table[(size_t) spec] != NULL)
{
const void **ptr = alloca (specs[nspecs_done].ndata_args
* sizeof (const void *));
/* Fill in an array of pointers to the argument values. */
for (unsigned int i = 0; i < specs[nspecs_done].ndata_args;
++i)
ptr[i] = &args_value[specs[nspecs_done].data_arg + i];
/* Call the function. */
function_done = __printf_function_table[(size_t) spec]
(s, &specs[nspecs_done].info, ptr);
if (function_done != -2)
{
/* If an error occurred we don't have information
about # of chars. */
if (function_done < 0)
{
/* Function has set errno. */
done = -1;
goto all_done;
}
done_add (function_done);
break;
}
}
JUMP (spec, step4_jumps);
process_arg ((&specs[nspecs_done]));
process_string_arg ((&specs[nspecs_done]));
LABEL (form_unknown):
{
unsigned int i;
const void **ptr;
ptr = alloca (specs[nspecs_done].ndata_args
* sizeof (const void *));
/* Fill in an array of pointers to the argument values. */
for (i = 0; i < specs[nspecs_done].ndata_args; ++i)
ptr[i] = &args_value[specs[nspecs_done].data_arg + i];
/* Call the function. */
function_done = printf_unknown (s, &specs[nspecs_done].info,
ptr);
/* If an error occurred we don't have information about #
of chars. */
if (function_done < 0)
{
/* Function has set errno. */
done = -1;
goto all_done;
}
done_add (function_done);
} }
break;
}
if (__glibc_unlikely (workstart != NULL)) if (__glibc_unlikely (workstart != NULL))
free (workstart); free (workstart);
workstart = NULL; workstart = NULL;
/* Write the following constant string. */ /* Write the following constant string. */
outstring (specs[nspecs_done].end_of_fmt, outstring (specs[nspecs_done].end_of_fmt,
specs[nspecs_done].next_fmt specs[nspecs_done].next_fmt
- specs[nspecs_done].end_of_fmt); - specs[nspecs_done].end_of_fmt);
} }
} all_done:
all_done:
if (specs_malloced)
free (specs);
if (__glibc_unlikely (args_malloced != NULL))
free (args_malloced);
if (__glibc_unlikely (workstart != NULL)) if (__glibc_unlikely (workstart != NULL))
free (workstart); free (workstart);
/* Unlock the stream. */
_IO_funlockfile (s);
_IO_cleanup_region_end (0);
return done; return done;
} }