diff options
Diffstat (limited to 'stdio-common/vfprintf.c')
-rw-r--r-- | stdio-common/vfprintf.c | 260 |
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 |