summaryrefslogtreecommitdiff
path: root/stdio-common/vfprintf.c
diff options
context:
space:
mode:
Diffstat (limited to 'stdio-common/vfprintf.c')
-rw-r--r--stdio-common/vfprintf.c260
1 files changed, 131 insertions, 129 deletions
diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c
index 6829d4dc8e..ae412e4b84 100644
--- a/stdio-common/vfprintf.c
+++ b/stdio-common/vfprintf.c
@@ -1,4 +1,4 @@
-/* Copyright (C) 1991-2016 Free Software Foundation, Inc.
+/* Copyright (C) 1991-2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
@@ -15,6 +15,7 @@
License along with the GNU C Library; if not, see
<http://www.gnu.org/licenses/>. */
+#include <array_length.h>
#include <ctype.h>
#include <limits.h>
#include <printf.h>
@@ -38,13 +39,12 @@
Beside this it is also shared between the normal and wide character
implementation as defined in ISO/IEC 9899:1990/Amendment 1:1995. */
-
#include <libioP.h>
-#define FILE _IO_FILE
-#undef va_list
-#define va_list _IO_va_list
-#undef BUFSIZ
-#define BUFSIZ _IO_BUFSIZ
+
+/* In some cases we need extra space for all the output which is not
+ counted in the width of the string. We assume 32 characters is
+ enough. */
+#define EXTSIZ 32
#define ARGCHECK(S, Format) \
do \
{ \
@@ -58,11 +58,11 @@
} \
if (Format == NULL) \
{ \
- MAYBE_SET_EINVAL; \
+ __set_errno (EINVAL); \
return -1; \
} \
} while (0)
-#define UNBUFFERED_P(S) ((S)->_IO_file_flags & _IO_UNBUFFERED)
+#define UNBUFFERED_P(S) ((S)->_flags & _IO_UNBUFFERED)
#define done_add(val) \
do { \
@@ -92,7 +92,7 @@ typedef const char *THOUSANDS_SEP_T;
do { \
if (width > 0) \
{ \
- _IO_ssize_t written = _IO_padn (s, (Padchar), width); \
+ ssize_t written = _IO_padn (s, (Padchar), width); \
if (__glibc_unlikely (written != width)) \
{ \
done = -1; \
@@ -122,7 +122,7 @@ typedef wchar_t THOUSANDS_SEP_T;
do { \
if (width > 0) \
{ \
- _IO_ssize_t written = _IO_wpadn (s, (Padchar), width); \
+ ssize_t written = _IO_wpadn (s, (Padchar), width); \
if (__glibc_unlikely (written != width)) \
{ \
done = -1; \
@@ -201,7 +201,7 @@ typedef wchar_t THOUSANDS_SEP_T;
static const CHAR_T null[] = L_("(null)");
/* Size of the work_buffer variable (in characters, not bytes. */
-enum { WORK_BUFFER_SIZE = 1000 };
+enum { WORK_BUFFER_SIZE = 1000 / sizeof (CHAR_T) };
/* This table maps a character into a number representing a class. In
each step there is a destination label for each class. */
@@ -591,9 +591,8 @@ static const uint8_t jump_table[] =
string = _itoa (number.longlong, workend, base, \
spec == L_('X')); \
if (group && grouping) \
- string = group_number (string, workend, grouping, \
- thousands_sep); \
- \
+ string = group_number (work_buffer, string, workend, \
+ grouping, thousands_sep); \
if (use_outdigits && base == 10) \
string = _i18n_number_rewrite (string, workend, workend); \
} \
@@ -649,9 +648,8 @@ static const uint8_t jump_table[] =
string = _itoa_word (number.word, workend, base, \
spec == L_('X')); \
if (group && grouping) \
- string = group_number (string, workend, grouping, \
- thousands_sep); \
- \
+ string = group_number (work_buffer, string, workend, \
+ grouping, thousands_sep); \
if (use_outdigits && base == 10) \
string = _i18n_number_rewrite (string, workend, workend); \
} \
@@ -766,7 +764,8 @@ static const uint8_t jump_table[] =
.pad = pad, \
.extra = 0, \
.i18n = use_outdigits, \
- .wide = sizeof (CHAR_T) != 1 }; \
+ .wide = sizeof (CHAR_T) != 1, \
+ .is_binary128 = 0}; \
\
if (is_long_double) \
the_arg.pa_long_double = va_arg (ap, long double); \
@@ -784,6 +783,8 @@ static const uint8_t jump_table[] =
fspec->data_arg_type = PA_DOUBLE; \
fspec->info.is_long_double = 0; \
} \
+ /* Not supported by *printf functions. */ \
+ fspec->info.is_binary128 = 0; \
\
function_done = __printf_fp (s, &fspec->info, &ptr); \
} \
@@ -823,7 +824,8 @@ static const uint8_t jump_table[] =
.group = group, \
.pad = pad, \
.extra = 0, \
- .wide = sizeof (CHAR_T) != 1 }; \
+ .wide = sizeof (CHAR_T) != 1, \
+ .is_binary128 = 0}; \
\
if (is_long_double) \
the_arg.pa_long_double = va_arg (ap, long double); \
@@ -838,6 +840,8 @@ static const uint8_t jump_table[] =
ptr = (const void *) &args_value[fspec->data_arg]; \
if (__ldbl_is_dbl) \
fspec->info.is_long_double = 0; \
+ /* Not supported by *printf functions. */ \
+ fspec->info.is_binary128 = 0; \
\
function_done = __printf_fphex (s, &fspec->info, &ptr); \
} \
@@ -987,11 +991,10 @@ static const uint8_t jump_table[] =
if (string == NULL) \
{ \
/* Write "(null)" if there's space. */ \
- if (prec == -1 \
- || prec >= (int) (sizeof (null) / sizeof (null[0])) - 1) \
+ if (prec == -1 || prec >= (int) array_length (null) - 1) \
{ \
string = (CHAR_T *) null; \
- len = (sizeof (null) / sizeof (null[0])) - 1; \
+ len = array_length (null) - 1; \
} \
else \
{ \
@@ -1082,7 +1085,7 @@ static const uint8_t jump_table[] =
LABEL (form_wcharacter): \
{ \
/* Wide character. */ \
- char buf[MB_CUR_MAX]; \
+ char buf[MB_LEN_MAX]; \
mbstate_t mbstate; \
size_t len; \
\
@@ -1212,10 +1215,10 @@ static const uint8_t jump_table[] =
/* Helper function to provide temporary buffering for unbuffered streams. */
static int buffered_vfprintf (FILE *stream, const CHAR_T *fmt, va_list)
- __THROW __attribute__ ((noinline)) internal_function;
+ __THROW __attribute__ ((noinline));
/* Handle positional format specifiers. */
-static int printf_positional (_IO_FILE *s,
+static int printf_positional (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,
@@ -1227,8 +1230,8 @@ static int printf_unknown (FILE *, const struct printf_info *,
const void *const *) __THROW;
/* Group digits of number string. */
-static CHAR_T *group_number (CHAR_T *, CHAR_T *, const char *, THOUSANDS_SEP_T)
- __THROW internal_function;
+static CHAR_T *group_number (CHAR_T *, CHAR_T *, CHAR_T *, const char *,
+ THOUSANDS_SEP_T);
/* The function itself. */
int
@@ -1456,20 +1459,19 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
left = 1;
}
- if (__glibc_unlikely (width >= INT_MAX / sizeof (CHAR_T) - 32))
+ if (__glibc_unlikely (width >= INT_MAX / sizeof (CHAR_T) - EXTSIZ))
{
__set_errno (EOVERFLOW);
done = -1;
goto all_done;
}
- if (width >= WORK_BUFFER_SIZE - 32)
+ if (width >= WORK_BUFFER_SIZE - EXTSIZ)
{
- /* We have to use a special buffer. The "32" is just a safe
- bet for all the output which is not counted in the width. */
- size_t needed = ((size_t) width + 32) * sizeof (CHAR_T);
+ /* We have to use a special buffer. */
+ size_t needed = ((size_t) width + EXTSIZ) * sizeof (CHAR_T);
if (__libc_use_alloca (needed))
- workend = (CHAR_T *) alloca (needed) + width + 32;
+ workend = (CHAR_T *) alloca (needed) + width + EXTSIZ;
else
{
workstart = (CHAR_T *) malloc (needed);
@@ -1478,7 +1480,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
done = -1;
goto all_done;
}
- workend = workstart + width + 32;
+ workend = workstart + width + EXTSIZ;
}
}
}
@@ -1489,20 +1491,19 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
width = read_int (&f);
if (__glibc_unlikely (width == -1
- || width >= INT_MAX / sizeof (CHAR_T) - 32))
+ || width >= INT_MAX / sizeof (CHAR_T) - EXTSIZ))
{
__set_errno (EOVERFLOW);
done = -1;
goto all_done;
}
- if (width >= WORK_BUFFER_SIZE - 32)
+ if (width >= WORK_BUFFER_SIZE - EXTSIZ)
{
- /* We have to use a special buffer. The "32" is just a safe
- bet for all the output which is not counted in the width. */
- size_t needed = ((size_t) width + 32) * sizeof (CHAR_T);
+ /* We have to use a special buffer. */
+ size_t needed = ((size_t) width + EXTSIZ) * sizeof (CHAR_T);
if (__libc_use_alloca (needed))
- workend = (CHAR_T *) alloca (needed) + width + 32;
+ workend = (CHAR_T *) alloca (needed) + width + EXTSIZ;
else
{
workstart = (CHAR_T *) malloc (needed);
@@ -1511,7 +1512,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
done = -1;
goto all_done;
}
- workend = workstart + width + 32;
+ workend = workstart + width + EXTSIZ;
}
}
if (*f == L_('$'))
@@ -1562,18 +1563,23 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
}
else
prec = 0;
- if (prec > width && prec > WORK_BUFFER_SIZE - 32)
+ if (prec > width && prec > WORK_BUFFER_SIZE - EXTSIZ)
{
- if (__glibc_unlikely (prec >= INT_MAX / sizeof (CHAR_T) - 32))
+ /* Deallocate any previously allocated buffer because it is
+ too small. */
+ if (__glibc_unlikely (workstart != NULL))
+ free (workstart);
+ workstart = NULL;
+ if (__glibc_unlikely (prec >= INT_MAX / sizeof (CHAR_T) - EXTSIZ))
{
__set_errno (EOVERFLOW);
done = -1;
goto all_done;
}
- size_t needed = ((size_t) prec + 32) * sizeof (CHAR_T);
+ size_t needed = ((size_t) prec + EXTSIZ) * sizeof (CHAR_T);
if (__libc_use_alloca (needed))
- workend = (CHAR_T *) alloca (needed) + prec + 32;
+ workend = (CHAR_T *) alloca (needed) + prec + EXTSIZ;
else
{
workstart = (CHAR_T *) malloc (needed);
@@ -1582,7 +1588,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
done = -1;
goto all_done;
}
- workend = workstart + prec + 32;
+ workend = workstart + prec + EXTSIZ;
}
}
JUMP (*f, step2_jumps);
@@ -1689,21 +1695,23 @@ do_positional:
}
static int
-printf_positional (_IO_FILE *s, const CHAR_T *format, int readonly_format,
+printf_positional (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 scratch_buffer specsbuf;
scratch_buffer_init (&specsbuf);
struct printf_spec *specs = specsbuf.data;
size_t specs_limit = specsbuf.length / sizeof (specs[0]);
+ /* Used as a backing store for args_value, args_size, args_type
+ below. */
+ struct scratch_buffer argsbuf;
+ scratch_buffer_init (&argsbuf);
+
/* Array with information about the needed arguments. This has to
be dynamically extensible. */
size_t nspecs = 0;
@@ -1712,10 +1720,6 @@ printf_positional (_IO_FILE *s, const CHAR_T *format, int readonly_format,
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
@@ -1765,38 +1769,29 @@ printf_positional (_IO_FILE *s, const CHAR_T *format, int readonly_format,
/* 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));
+ union printf_arg *args_value;
+ int *args_size;
+ int *args_type;
+ {
+ /* Calculate total size needed to represent a single argument
+ across all three argument-related arrays. */
+ size_t bytes_per_arg
+ = sizeof (*args_value) + sizeof (*args_size) + sizeof (*args_type);
+ if (!scratch_buffer_set_array_size (&argsbuf, nargs, bytes_per_arg))
+ {
+ done = -1;
+ goto all_done;
+ }
+ args_value = argsbuf.data;
+ /* 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
@@ -1959,23 +1954,23 @@ printf_positional (_IO_FILE *s, const CHAR_T *format, int readonly_format,
}
/* Maybe the buffer is too small. */
- if (MAX (prec, width) + 32 > WORK_BUFFER_SIZE)
+ if (MAX (prec, width) + EXTSIZ > WORK_BUFFER_SIZE)
{
- if (__libc_use_alloca ((MAX (prec, width) + 32)
+ if (__libc_use_alloca ((MAX (prec, width) + EXTSIZ)
* sizeof (CHAR_T)))
- workend = ((CHAR_T *) alloca ((MAX (prec, width) + 32)
+ workend = ((CHAR_T *) alloca ((MAX (prec, width) + EXTSIZ)
* sizeof (CHAR_T))
- + (MAX (prec, width) + 32));
+ + (MAX (prec, width) + EXTSIZ));
else
{
- workstart = (CHAR_T *) malloc ((MAX (prec, width) + 32)
+ workstart = (CHAR_T *) malloc ((MAX (prec, width) + EXTSIZ)
* sizeof (CHAR_T));
if (workstart == NULL)
{
done = -1;
goto all_done;
}
- workend = workstart + (MAX (prec, width) + 32);
+ workend = workstart + (MAX (prec, width) + EXTSIZ);
}
}
@@ -2062,10 +2057,9 @@ printf_positional (_IO_FILE *s, const CHAR_T *format, int readonly_format,
- specs[nspecs_done].end_of_fmt);
}
all_done:
- if (__glibc_unlikely (args_malloced != NULL))
- free (args_malloced);
if (__glibc_unlikely (workstart != NULL))
free (workstart);
+ scratch_buffer_free (&argsbuf);
scratch_buffer_free (&specsbuf);
return done;
}
@@ -2122,16 +2116,20 @@ printf_unknown (FILE *s, const struct printf_info *info,
return done;
}
-/* Group the digits according to the grouping rules of the current locale.
- The interpretation of GROUPING is as in `struct lconv' from <locale.h>. */
+/* Group the digits from W to REAR_PTR according to the grouping rules
+ of the current locale. The interpretation of GROUPING is as in
+ `struct lconv' from <locale.h>. The grouped number extends from
+ the returned pointer until REAR_PTR. FRONT_PTR to W is used as a
+ scratch area. */
static CHAR_T *
-internal_function
-group_number (CHAR_T *w, CHAR_T *rear_ptr, const char *grouping,
- THOUSANDS_SEP_T thousands_sep)
+group_number (CHAR_T *front_ptr, CHAR_T *w, CHAR_T *rear_ptr,
+ const char *grouping, THOUSANDS_SEP_T thousands_sep)
{
+ /* Length of the current group. */
int len;
- CHAR_T *src, *s;
#ifndef COMPILE_WPRINTF
+ /* Length of the separator (in wide mode, the separator is always a
+ single wide character). */
int tlen = strlen (thousands_sep);
#endif
@@ -2144,26 +2142,34 @@ group_number (CHAR_T *w, CHAR_T *rear_ptr, const char *grouping,
len = *grouping++;
/* Copy existing string so that nothing gets overwritten. */
- src = (CHAR_T *) alloca ((rear_ptr - w) * sizeof (CHAR_T));
- s = (CHAR_T *) __mempcpy (src, w,
- (rear_ptr - w) * sizeof (CHAR_T));
+ memmove (front_ptr, w, (rear_ptr - w) * sizeof (CHAR_T));
+ CHAR_T *s = front_ptr + (rear_ptr - w);
+
w = rear_ptr;
/* Process all characters in the string. */
- while (s > src)
+ while (s > front_ptr)
{
*--w = *--s;
- if (--len == 0 && s > src)
+ if (--len == 0 && s > front_ptr)
{
/* A new group begins. */
#ifdef COMPILE_WPRINTF
- *--w = thousands_sep;
+ if (w != s)
+ *--w = thousands_sep;
+ else
+ /* Not enough room for the separator. */
+ goto copy_rest;
#else
int cnt = tlen;
- do
- *--w = thousands_sep[--cnt];
- while (cnt > 0);
+ if (tlen < w - s)
+ do
+ *--w = thousands_sep[--cnt];
+ while (cnt > 0);
+ else
+ /* Not enough room for the separator. */
+ goto copy_rest;
#endif
if (*grouping == CHAR_MAX
@@ -2172,17 +2178,16 @@ group_number (CHAR_T *w, CHAR_T *rear_ptr, const char *grouping,
#endif
)
{
- /* No further grouping to be done.
- Copy the rest of the number. */
- do
- *--w = *--s;
- while (s > src);
+ copy_rest:
+ /* No further grouping to be done. Copy the rest of the
+ number. */
+ memmove (w, s, (front_ptr -s) * sizeof (CHAR_T));
break;
}
else if (*grouping != '\0')
- /* The previous grouping repeats ad infinitum. */
len = *grouping++;
else
+ /* The previous grouping repeats ad infinitum. */
len = grouping[-1];
}
}
@@ -2196,22 +2201,21 @@ struct helper_file
#ifdef COMPILE_WPRINTF
struct _IO_wide_data _wide_data;
#endif
- _IO_FILE *_put_stream;
+ FILE *_put_stream;
#ifdef _IO_MTSAFE_IO
_IO_lock_t lock;
#endif
};
static int
-_IO_helper_overflow (_IO_FILE *s, int c)
+_IO_helper_overflow (FILE *s, int c)
{
- _IO_FILE *target = ((struct helper_file*) s)->_put_stream;
+ FILE *target = ((struct helper_file*) s)->_put_stream;
#ifdef COMPILE_WPRINTF
int used = s->_wide_data->_IO_write_ptr - s->_wide_data->_IO_write_base;
if (used)
{
- _IO_size_t written = _IO_sputn (target, s->_wide_data->_IO_write_base,
- used);
+ size_t written = _IO_sputn (target, s->_wide_data->_IO_write_base, used);
if (written == 0 || written == WEOF)
return WEOF;
__wmemmove (s->_wide_data->_IO_write_base,
@@ -2223,7 +2227,7 @@ _IO_helper_overflow (_IO_FILE *s, int c)
int used = s->_IO_write_ptr - s->_IO_write_base;
if (used)
{
- _IO_size_t written = _IO_sputn (target, s->_IO_write_base, used);
+ size_t written = _IO_sputn (target, s->_IO_write_base, used);
if (written == 0 || written == EOF)
return EOF;
memmove (s->_IO_write_base, s->_IO_write_base + written,
@@ -2235,7 +2239,7 @@ _IO_helper_overflow (_IO_FILE *s, int c)
}
#ifdef COMPILE_WPRINTF
-static const struct _IO_jump_t _IO_helper_jumps =
+static const struct _IO_jump_t _IO_helper_jumps libio_vtable =
{
JUMP_INIT_DUMMY,
JUMP_INIT (finish, _IO_wdefault_finish),
@@ -2257,7 +2261,7 @@ static const struct _IO_jump_t _IO_helper_jumps =
JUMP_INIT (stat, _IO_default_stat)
};
#else
-static const struct _IO_jump_t _IO_helper_jumps =
+static const struct _IO_jump_t _IO_helper_jumps libio_vtable =
{
JUMP_INIT_DUMMY,
JUMP_INIT (finish, _IO_default_finish),
@@ -2281,13 +2285,11 @@ static const struct _IO_jump_t _IO_helper_jumps =
#endif
static int
-internal_function
-buffered_vfprintf (_IO_FILE *s, const CHAR_T *format,
- _IO_va_list args)
+buffered_vfprintf (FILE *s, const CHAR_T *format, va_list args)
{
- CHAR_T buf[_IO_BUFSIZ];
+ CHAR_T buf[BUFSIZ];
struct helper_file helper;
- _IO_FILE *hp = (_IO_FILE *) &helper._f;
+ FILE *hp = (FILE *) &helper._f;
int result, to_flush;
/* Orient the stream. */
@@ -2305,7 +2307,7 @@ buffered_vfprintf (_IO_FILE *s, const CHAR_T *format,
_IO_setp (hp, buf, buf + sizeof buf);
hp->_mode = -1;
#endif
- hp->_IO_file_flags = _IO_MAGIC|_IO_NO_READS|_IO_USER_LOCK;
+ hp->_flags = _IO_MAGIC|_IO_NO_READS|_IO_USER_LOCK;
#if _IO_JUMPS_OFFSET
hp->_vtable_offset = 0;
#endif