From: Adhemerval Zanella Netto <adhemerval.zanella@linaro.org>
To: Florian Weimer <fweimer@redhat.com>, libc-alpha@sourceware.org
Subject: Re: [PATCH v5 05/11] stdio-common: Convert vfprintf and related functions to buffers
Date: Wed, 14 Dec 2022 17:54:29 -0300 [thread overview]
Message-ID: <32c237bd-1f1d-671c-71df-99005dea6746@linaro.org> (raw)
In-Reply-To: <2706c12ba5db0ed2c845c991bb1577a7e5be2f07.1670858473.git.fweimer@redhat.com>
On 12/12/22 12:23, Florian Weimer via Libc-alpha wrote:
> vfprintf is entangled with vfwprintf (of course), __printf_fp,
> __printf_fphex, __vstrfmon_l_internal, and the strfrom family of
> functions. The latter use the internal snprintf functionality,
> so vsnprintf is converted as well.
>
> The simples conversion is __printf_fphex, followed by
> __vstrfmon_l_internal and __printf_fp, and finally
> __vfprintf_internal and __vfwprintf_internal. __vsnprintf_internal
> and strfrom* are mostly consuming the new interfaces, so they
> are comparatively simple.
>
> __printf_fp is a public symbol, so the FILE *-based interface
> had to preserved.
>
> The __printf_fp rewrite does not change the actual binary-to-decimal
> conversion algorithm, and digits are still not emitted directly to
> the target buffer. However, the staging buffer now uses bytes
> instead of wide characters, and one buffer copy is eliminated.
>
> The changes are at least performance-neutral in my testing.
> Floating point printing and snprintf improved measurably, so that
> this Lua script
>
> for i=1,5000000 do
> print(i, i * math.pi)
> end
>
> runs about 5% faster for me. To preserve fprintf performance for
> a simple "%d" format, this commit has some logic changes under
> LABEL (unsigned_number) to avoid additional function calls. There
> are certainly some very easy performance improvements here: binary,
> octal and hexadecimal formatting can easily avoid the temporary work
> buffer (the number of digits can be computed ahead-of-time using one
> of the __builtin_clz* built-ins). Decimal formatting can use a
> specialized version of _itoa_word for base 10.
>
> The existing (inconsistent) width handling between strfmon and printf
> is preserved here. __print_fp_buffer_1 would have to use
> __translated_number_width to achieve ISO conformance for printf.
>
> Test expectations in libio/tst-vtables-common.c are adjusted because
> the internal staging buffer merges all virtual function calls into
> one.
>
> In general, stack buffer usage is greatly reduced, particularly for
> unbuffered input streams. __printf_fp can still use a large buffer
> in binary128 mode for %g, though.
Patch looks good, there is only a small nit on the libio vtable removal
that in turn showed an issue on elf/tst-relro-symbols.py.
LGTM, thanks.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
> ---
> include/printf.h | 29 +-
> include/printf_buffer.h | 36 +
> libio/tst-vtables-common.c | 9 +-
> libio/vsnprintf.c | 131 ++--
> stdio-common/printf_buffer_flush.c | 23 +
> stdio-common/printf_fp.c | 736 ++++++++----------
> stdio-common/printf_fphex.c | 260 +++----
> stdio-common/vfprintf-internal.c | 664 +++++-----------
> stdio-common/vfprintf-process-arg.c | 172 ++--
> stdlib/strfmon_l.c | 196 ++---
> stdlib/strfrom-skeleton.c | 38 +-
> sysdeps/ia64/fpu/printf_fphex.c | 8 +-
> .../ieee754/ldbl-128/printf_fphex_macros.h | 36 +-
> sysdeps/ieee754/ldbl-128ibm/printf_fphex.c | 36 +-
> sysdeps/ieee754/ldbl-96/printf_fphex.c | 22 +-
> sysdeps/x86_64/fpu/printf_fphex.c | 21 +-
> 16 files changed, 943 insertions(+), 1474 deletions(-)
>
> diff --git a/include/printf.h b/include/printf.h
> index 5127a45f9b..2c998059d4 100644
> --- a/include/printf.h
> +++ b/include/printf.h
> @@ -65,18 +65,31 @@ int __translated_number_width (locale_t loc,
> const char *first, const char *last)
> attribute_hidden;
>
> -extern int __printf_fphex (FILE *, const struct printf_info *,
> - const void *const *) attribute_hidden;
> +
> +struct __printf_buffer;
> +void __printf_buffer (struct __printf_buffer *buf, const char *format,
> + va_list ap, unsigned int mode_flags);
> +struct __wprintf_buffer;
> +void __wprintf_buffer (struct __wprintf_buffer *buf, const wchar_t *format,
> + va_list ap, unsigned int mode_flags);
> +
> extern int __printf_fp (FILE *, const struct printf_info *,
> const void *const *);
> libc_hidden_proto (__printf_fp)
> -extern int __printf_fp_l (FILE *, locale_t, const struct printf_info *,
> - const void *const *);
> -libc_hidden_proto (__printf_fp_l)
>
> -extern unsigned int __guess_grouping (unsigned int intdig_max,
> - const char *grouping)
> - attribute_hidden;
> +void __printf_fphex_l_buffer (struct __printf_buffer *, locale_t,
> + const struct printf_info *,
> + const void *const *) attribute_hidden;
> +void __printf_fp_l_buffer (struct __printf_buffer *, locale_t,
> + const struct printf_info *,
> + const void *const *) attribute_hidden;
> +struct __wprintf_buffer;
> +void __wprintf_fphex_l_buffer (struct __wprintf_buffer *, locale_t,
> + const struct printf_info *,
> + const void *const *) attribute_hidden;
> +void __wprintf_fp_l_buffer (struct __wprintf_buffer *, locale_t,
> + const struct printf_info *,
> + const void *const *) attribute_hidden;
>
> # endif /* !_ISOMAC */
> #endif
Ok.
> diff --git a/include/printf_buffer.h b/include/printf_buffer.h
> index ad08a72b71..e89f984aca 100644
> --- a/include/printf_buffer.h
> +++ b/include/printf_buffer.h
> @@ -44,7 +44,12 @@
> enum __printf_buffer_mode
> {
> __printf_buffer_mode_failed,
> + __printf_buffer_mode_snprintf,
> __printf_buffer_mode_to_file,
> + __printf_buffer_mode_strfmon,
> + __printf_buffer_mode_fp, /* For __printf_fp_l_buffer. */
> + __printf_buffer_mode_fp_to_wide, /* For __wprintf_fp_l_buffer. */
> + __printf_buffer_mode_fphex_to_wide, /* For __wprintf_fphex_l_buffer. */
> };
>
> /* Buffer for fast character writing with overflow handling.
> @@ -266,13 +271,44 @@ bool __wprintf_buffer_flush (struct __wprintf_buffer *buf) attribute_hidden;
> #define Xprintf_buffer_puts Xprintf (buffer_puts)
> #define Xprintf_buffer_write Xprintf (buffer_write)
>
> +/* Commonly used buffers. */
> +
> +struct __printf_buffer_snprintf
> +{
> + struct __printf_buffer base;
> + char discard[128]; /* Used in counting mode. */
> +};
> +
> +/* Sets up [BUFFER, BUFFER + LENGTH) as the write target. If LENGTH
> + is positive, also writes a NUL byte to *BUFFER. */
> +void __printf_buffer_snprintf_init (struct __printf_buffer_snprintf *,
> + char *buffer, size_t length)
> + attribute_hidden;
> +
> +/* Add the null terminator after everything has been written. The
> + return value is the one expected by printf (see __printf_buffer_done). */
> +int __printf_buffer_snprintf_done (struct __printf_buffer_snprintf *)
> + attribute_hidden;
> +
> /* Flush function implementations follow. They are called from
> __printf_buffer_flush. Generic code should not call these flush
> functions directly. Some modes have inline implementations. */
>
> +void __printf_buffer_flush_snprintf (struct __printf_buffer_snprintf *)
> + attribute_hidden;
> struct __printf_buffer_to_file;
> void __printf_buffer_flush_to_file (struct __printf_buffer_to_file *)
> attribute_hidden;
> +struct __printf_buffer_fp;
> +void __printf_buffer_flush_fp (struct __printf_buffer_fp *)
> + attribute_hidden;
> +struct __printf_buffer_fp_to_wide;
> +void __printf_buffer_flush_fp_to_wide (struct __printf_buffer_fp_to_wide *)
> + attribute_hidden;
> +struct __printf_buffer_fphex_to_wide;
> +void __printf_buffer_flush_fphex_to_wide (struct
> + __printf_buffer_fphex_to_wide *)
> + attribute_hidden;
>
> struct __wprintf_buffer_to_file;
> void __wprintf_buffer_flush_to_file (struct __wprintf_buffer_to_file *)
Ok.
> diff --git a/libio/tst-vtables-common.c b/libio/tst-vtables-common.c
> index d18df23e55..a310e516f2 100644
> --- a/libio/tst-vtables-common.c
> +++ b/libio/tst-vtables-common.c
> @@ -409,11 +409,14 @@ void _IO_init (FILE *fp, int flags);
> static void
> with_compatibility_fprintf (void *closure)
> {
> + /* A temporary staging buffer is used in the current fprintf
> + implementation, which is why there is just one call to
> + xsputn. */
> TEST_COMPARE (fprintf_ptr (shared->fp, "A%sCD", "B"), 4);
> - TEST_COMPARE (shared->calls, 3);
> - TEST_COMPARE (shared->calls_xsputn, 3);
> + TEST_COMPARE (shared->calls, 1);
> + TEST_COMPARE (shared->calls_xsputn, 1);
> TEST_COMPARE_BLOB (shared->buffer, shared->buffer_length,
> - "CD", 2);
> + "ABCD", 4);
> }
>
> static void
Ok.
> diff --git a/libio/vsnprintf.c b/libio/vsnprintf.c
> index 8dae66761d..7a9667f966 100644
> --- a/libio/vsnprintf.c
> +++ b/libio/vsnprintf.c
> @@ -25,97 +25,76 @@
> in files containing the exception. */
>
> #include "libioP.h"
> -#include "strfile.h"
>
> -static int _IO_strn_overflow (FILE *fp, int c) __THROW;
> +#include <array_length.h>
> +#include <printf.h>
> +#include <printf_buffer.h>
>
> -static int
> -_IO_strn_overflow (FILE *fp, int c)
> +void
> +__printf_buffer_flush_snprintf (struct __printf_buffer_snprintf *buf)
> {
> - /* When we come to here this means the user supplied buffer is
> - filled. But since we must return the number of characters which
> - would have been written in total we must provide a buffer for
> - further use. We can do this by writing on and on in the overflow
> - buffer in the _IO_strnfile structure. */
> - _IO_strnfile *snf = (_IO_strnfile *) fp;
> -
> - if (fp->_IO_buf_base != snf->overflow_buf)
> + /* Record the bytes written so far, before switching buffers. */
> + buf->base.written += buf->base.write_ptr - buf->base.write_base;
> +
> + if (buf->base.write_base != buf->discard)
> {
> - /* Terminate the string. We know that there is room for at
> - least one more character since we initialized the stream with
> - a size to make this possible. */
> - *fp->_IO_write_ptr = '\0';
> -
> - _IO_setb (fp, snf->overflow_buf,
> - snf->overflow_buf + sizeof (snf->overflow_buf), 0);
> -
> - fp->_IO_write_base = snf->overflow_buf;
> - fp->_IO_read_base = snf->overflow_buf;
> - fp->_IO_read_ptr = snf->overflow_buf;
> - fp->_IO_read_end = snf->overflow_buf + sizeof (snf->overflow_buf);
> - }
> + /* We just finished writing the caller-supplied buffer. Force
> + NUL termination if the string length is not zero. */
> + if (buf->base.write_base != buf->base.write_end)
> + buf->base.write_end[-1] = '\0';
>
> - fp->_IO_write_ptr = snf->overflow_buf;
> - fp->_IO_write_end = snf->overflow_buf;
>
> - /* Since we are not really interested in storing the characters
> - which do not fit in the buffer we simply ignore it. */
> - return c;
> -}
> + /* Switch to the discard buffer. */
> + buf->base.write_base = buf->discard;
> + buf->base.write_ptr = buf->discard;
> + buf->base.write_end = array_end (buf->discard);
> + }
>
> + buf->base.write_base = buf->discard;
> + buf->base.write_ptr = buf->discard;
> +}
>
> -const struct _IO_jump_t _IO_strn_jumps libio_vtable attribute_hidden =
We need to remove both _IO_strn_jumps definition from libio/strfile.h and
from tst-relro-libc.out.
> +void
> +__printf_buffer_snprintf_init (struct __printf_buffer_snprintf *buf,
> + char *buffer, size_t length)
> {
> - JUMP_INIT_DUMMY,
> - JUMP_INIT(finish, _IO_str_finish),
> - JUMP_INIT(overflow, _IO_strn_overflow),
> - JUMP_INIT(underflow, _IO_str_underflow),
> - JUMP_INIT(uflow, _IO_default_uflow),
> - JUMP_INIT(pbackfail, _IO_str_pbackfail),
> - JUMP_INIT(xsputn, _IO_default_xsputn),
> - JUMP_INIT(xsgetn, _IO_default_xsgetn),
> - JUMP_INIT(seekoff, _IO_str_seekoff),
> - JUMP_INIT(seekpos, _IO_default_seekpos),
> - JUMP_INIT(setbuf, _IO_default_setbuf),
> - JUMP_INIT(sync, _IO_default_sync),
> - JUMP_INIT(doallocate, _IO_default_doallocate),
> - JUMP_INIT(read, _IO_default_read),
> - JUMP_INIT(write, _IO_default_write),
> - JUMP_INIT(seek, _IO_default_seek),
> - JUMP_INIT(close, _IO_default_close),
> - JUMP_INIT(stat, _IO_default_stat),
> - JUMP_INIT(showmanyc, _IO_default_showmanyc),
> - JUMP_INIT(imbue, _IO_default_imbue)
> -};
> + __printf_buffer_init (&buf->base, buffer, length,
> + __printf_buffer_mode_snprintf);
> + if (length > 0)
> + /* Historic behavior for trivially overlapping buffers (checked by
> + the test suite). */
> + *buffer = '\0';
> +}
>
> +int
> +__printf_buffer_snprintf_done (struct __printf_buffer_snprintf *buf)
> +{
> + /* NB: Do not check for buf->base.fail here. Write the null
> + terminator even in case of errors. */
> +
> + if (buf->base.write_ptr < buf->base.write_end)
> + *buf->base.write_ptr = '\0';
> + else if (buf->base.write_ptr > buf->base.write_base)
> + /* If write_ptr == write_base, nothing has been written. No null
> + termination is needed because of the early truncation in
> + __printf_buffer_snprintf_init (the historic behavior).
> +
> + We might also be at the start of the discard buffer, but in
> + this case __printf_buffer_flush_snprintf has already written
> + the NUL terminator. */
> + buf->base.write_ptr[-1] = '\0';
> +
> + return __printf_buffer_done (&buf->base);
> +}
>
> int
> __vsnprintf_internal (char *string, size_t maxlen, const char *format,
> va_list args, unsigned int mode_flags)
> {
> - _IO_strnfile sf;
> - int ret;
> -#ifdef _IO_MTSAFE_IO
> - sf.f._sbf._f._lock = NULL;
> -#endif
> -
> - /* We need to handle the special case where MAXLEN is 0. Use the
> - overflow buffer right from the start. */
> - if (maxlen == 0)
> - {
> - string = sf.overflow_buf;
> - maxlen = sizeof (sf.overflow_buf);
> - }
> -
> - _IO_no_init (&sf.f._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
> - _IO_JUMPS (&sf.f._sbf) = &_IO_strn_jumps;
> - string[0] = '\0';
> - _IO_str_init_static_internal (&sf.f, string, maxlen - 1, string);
> - ret = __vfprintf_internal (&sf.f._sbf._f, format, args, mode_flags);
> -
> - if (sf.f._sbf._f._IO_buf_base != sf.overflow_buf)
> - *sf.f._sbf._f._IO_write_ptr = '\0';
> - return ret;
> + struct __printf_buffer_snprintf buf;
> + __printf_buffer_snprintf_init (&buf, string, maxlen);
> + __printf_buffer (&buf.base, format, args, mode_flags);
> + return __printf_buffer_snprintf_done (&buf);
> }
>
> int
Ok.
> diff --git a/stdio-common/printf_buffer_flush.c b/stdio-common/printf_buffer_flush.c
> index 9b25c0fde5..bfd1f9d733 100644
> --- a/stdio-common/printf_buffer_flush.c
> +++ b/stdio-common/printf_buffer_flush.c
> @@ -16,6 +16,7 @@
> License along with the GNU C Library; if not, see
> <https://www.gnu.org/licenses/>. */
>
> +#include <errno.h>
> #include <printf_buffer.h>
>
> #include "printf_buffer-char.h"
> @@ -24,7 +25,11 @@
> /* The __printf_buffer_flush_* functions are defined together with
> functions that are pulled in by strong references. */
> #ifndef SHARED
> +# pragma weak __printf_buffer_flush_snprintf
> # pragma weak __printf_buffer_flush_to_file
> +# pragma weak __printf_buffer_flush_fp
> +# pragma weak __printf_buffer_flush_fp_to_wide
> +# pragma weak __printf_buffer_flush_fphex_to_wide
> #endif /* !SHARED */
>
> static void
> @@ -34,9 +39,27 @@ __printf_buffer_do_flush (struct __printf_buffer *buf)
> {
> case __printf_buffer_mode_failed:
> return;
> + case __printf_buffer_mode_snprintf:
> + __printf_buffer_flush_snprintf ((struct __printf_buffer_snprintf *) buf);
> + return;
> case __printf_buffer_mode_to_file:
> __printf_buffer_flush_to_file ((struct __printf_buffer_to_file *) buf);
> return;
> + case __printf_buffer_mode_strfmon:
> + __set_errno (E2BIG);
> + __printf_buffer_mark_failed (buf);
> + return;
> + case __printf_buffer_mode_fp:
> + __printf_buffer_flush_fp ((struct __printf_buffer_fp *) buf);
> + return;
> + case __printf_buffer_mode_fp_to_wide:
> + __printf_buffer_flush_fp_to_wide
> + ((struct __printf_buffer_fp_to_wide *) buf);
> + return;
> + case __printf_buffer_mode_fphex_to_wide:
> + __printf_buffer_flush_fphex_to_wide
> + ((struct __printf_buffer_fphex_to_wide *) buf);
> + return;
> }
> __builtin_trap ();
> }
Ok.
> diff --git a/stdio-common/printf_fp.c b/stdio-common/printf_fp.c
> index 3a5560fc16..5c9c30aaee 100644
> --- a/stdio-common/printf_fp.c
> +++ b/stdio-common/printf_fp.c
> @@ -41,90 +41,12 @@
> #include <wchar.h>
> #include <stdbool.h>
> #include <rounding-mode.h>
> +#include <printf_buffer.h>
> +#include <printf_buffer_to_file.h>
> +#include <grouping_iterator.h>
>
> -#ifdef COMPILE_WPRINTF
> -# define CHAR_T wchar_t
> -#else
> -# define CHAR_T char
> -#endif
> -
> -#include "_i18n_number.h"
> -
> -#ifndef NDEBUG
> -# define NDEBUG /* Undefine this for debugging assertions. */
> -#endif
> #include <assert.h>
>
> -#define PUT(f, s, n) _IO_sputn (f, s, n)
> -#define PAD(f, c, n) (wide ? _IO_wpadn (f, c, n) : _IO_padn (f, c, n))
> -#undef putc
> -#define putc(c, f) (wide \
> - ? (int)_IO_putwc_unlocked (c, f) : _IO_putc_unlocked (c, f))
> -
> -\f
> -/* Macros for doing the actual output. */
> -
> -#define outchar(ch) \
> - do \
> - { \
> - const int outc = (ch); \
> - if (putc (outc, fp) == EOF) \
> - { \
> - if (buffer_malloced) \
> - { \
> - free (buffer); \
> - free (wbuffer); \
> - } \
> - return -1; \
> - } \
> - ++done; \
> - } while (0)
> -
> -#define PRINT(ptr, wptr, len) \
> - do \
> - { \
> - size_t outlen = (len); \
> - if (len > 20) \
> - { \
> - if (PUT (fp, wide ? (const char *) wptr : ptr, outlen) != outlen) \
> - { \
> - if (buffer_malloced) \
> - { \
> - free (buffer); \
> - free (wbuffer); \
> - } \
> - return -1; \
> - } \
> - ptr += outlen; \
> - done += outlen; \
> - } \
> - else \
> - { \
> - if (wide) \
> - while (outlen-- > 0) \
> - outchar (*wptr++); \
> - else \
> - while (outlen-- > 0) \
> - outchar (*ptr++); \
> - } \
> - } while (0)
> -
> -#define PADN(ch, len) \
> - do \
> - { \
> - if (PAD (fp, ch, len) != len) \
> - { \
> - if (buffer_malloced) \
> - { \
> - free (buffer); \
> - free (wbuffer); \
> - } \
> - return -1; \
> - } \
> - done += len; \
> - } \
> - while (0)
> -\f
> /* We use the GNU MP library to handle large numbers.
>
> An MP variable occupies a varying number of entries in its array. We keep
> @@ -145,10 +67,6 @@ extern mp_size_t __mpn_extract_long_double (mp_ptr res_ptr, mp_size_t size,
> long double value);
>
>
> -static wchar_t *group_number (wchar_t *buf, wchar_t *bufend,
> - unsigned int intdig_no, const char *grouping,
> - wchar_t thousands_sep, int ngroups);
> -
> struct hack_digit_param
> {
> /* Sign of the exponent. */
> @@ -165,7 +83,7 @@ struct hack_digit_param
> MPN_VAR(tmp);
> };
>
> -static wchar_t
> +static char
> hack_digit (struct hack_digit_param *p)
> {
> mp_limb_t hi;
> @@ -197,7 +115,7 @@ hack_digit (struct hack_digit_param *p)
> /* We're not prepared for an mpn variable with zero
> limbs. */
> p->fracsize = 1;
> - return L'0' + hi;
> + return '0' + hi;
> }
> }
>
> @@ -206,13 +124,22 @@ hack_digit (struct hack_digit_param *p)
> p->frac[p->fracsize++] = _cy;
> }
>
> - return L'0' + hi;
> + return '0' + hi;
> }
>
> -int
> -__printf_fp_l (FILE *fp, locale_t loc,
> - const struct printf_info *info,
> - const void *const *args)
> +/* Version that performs grouping (if INFO->group && THOUSANDS_SEP != 0),
> + but not i18n digit translation.
> +
> + The output buffer is always multibyte (not wide) at this stage.
> + Wide conversion and i18n digit translation happen later, with a
> + temporary buffer. To prepare for that, THOUSANDS_SEP_LENGTH is the
> + final length of the thousands separator. */
> +static void
> +__printf_fp_buffer_1 (struct __printf_buffer *buf, locale_t loc,
> + char thousands_sep, char decimal,
> + unsigned int thousands_sep_length,
> + const struct printf_info *info,
> + const void *const *args)
> {
> /* The floating-point value to output. */
> union
> @@ -225,18 +152,11 @@ __printf_fp_l (FILE *fp, locale_t loc,
> }
> fpnum;
>
> - /* Locale-dependent representation of decimal point. */
> - const char *decimal;
> - wchar_t decimalwc;
> -
> - /* Locale-dependent thousands separator and grouping specification. */
> - const char *thousands_sep = NULL;
> - wchar_t thousands_sepwc = 0;
> - const char *grouping;
> -
> /* "NaN" or "Inf" for the special cases. */
> const char *special = NULL;
> - const wchar_t *wspecial = NULL;
> +
> + /* Used to determine grouping rules. */
> + int lc_category = info->extra ? LC_MONETARY : LC_NUMERIC;
>
> /* When _Float128 is enabled in the library and ABI-distinct from long
> double, we need mp_limbs enough for any of them. */
Ok.
> @@ -256,90 +176,16 @@ __printf_fp_l (FILE *fp, locale_t loc,
> /* Sign of float number. */
> int is_neg = 0;
>
> - /* Counter for number of written characters. */
> - int done = 0;
> -
> /* General helper (carry limb). */
> mp_limb_t cy;
>
> - /* Nonzero if this is output on a wide character stream. */
> - int wide = info->wide;
> -
> /* Buffer in which we produce the output. */
> - wchar_t *wbuffer = NULL;
> - char *buffer = NULL;
> + char *wbuffer = NULL;
> /* Flag whether wbuffer and buffer are malloc'ed or not. */
> int buffer_malloced = 0;
>
> p.expsign = 0;
>
> - /* Figure out the decimal point character. */
> - if (info->extra == 0)
> - {
> - decimal = _nl_lookup (loc, LC_NUMERIC, DECIMAL_POINT);
> - decimalwc = _nl_lookup_word
> - (loc, LC_NUMERIC, _NL_NUMERIC_DECIMAL_POINT_WC);
> - }
> - else
> - {
> - decimal = _nl_lookup (loc, LC_MONETARY, MON_DECIMAL_POINT);
> - if (*decimal == '\0')
> - decimal = _nl_lookup (loc, LC_NUMERIC, DECIMAL_POINT);
> - decimalwc = _nl_lookup_word (loc, LC_MONETARY,
> - _NL_MONETARY_DECIMAL_POINT_WC);
> - if (decimalwc == L'\0')
> - decimalwc = _nl_lookup_word (loc, LC_NUMERIC,
> - _NL_NUMERIC_DECIMAL_POINT_WC);
> - }
> - /* The decimal point character must not be zero. */
> - assert (*decimal != '\0');
> - assert (decimalwc != L'\0');
> -
> - if (info->group)
> - {
> - if (info->extra == 0)
> - grouping = _nl_lookup (loc, LC_NUMERIC, GROUPING);
> - else
> - grouping = _nl_lookup (loc, LC_MONETARY, MON_GROUPING);
> -
> - if (*grouping <= 0 || *grouping == CHAR_MAX)
> - grouping = NULL;
> - else
> - {
> - /* Figure out the thousands separator character. */
> - if (wide)
> - {
> - if (info->extra == 0)
> - thousands_sepwc = _nl_lookup_word
> - (loc, LC_NUMERIC, _NL_NUMERIC_THOUSANDS_SEP_WC);
> - else
> - thousands_sepwc =
> - _nl_lookup_word (loc, LC_MONETARY,
> - _NL_MONETARY_THOUSANDS_SEP_WC);
> - }
> - else
> - {
> - if (info->extra == 0)
> - thousands_sep = _nl_lookup (loc, LC_NUMERIC, THOUSANDS_SEP);
> - else
> - thousands_sep = _nl_lookup
> - (loc, LC_MONETARY, MON_THOUSANDS_SEP);
> - }
> -
> - if ((wide && thousands_sepwc == L'\0')
> - || (! wide && *thousands_sep == '\0'))
> - grouping = NULL;
> - else if (thousands_sepwc == L'\0')
> - /* If we are printing multibyte characters and there is a
> - multibyte representation for the thousands separator,
> - we must ensure the wide character thousands separator
> - is available, even if it is fake. */
> - thousands_sepwc = 0xfffffffe;
> - }
> - }
> - else
> - grouping = NULL;
> -
> #define PRINTF_FP_FETCH(FLOAT, VAR, SUFFIX, MANT_DIG) \
> { \
> (VAR) = *(const FLOAT *) args[0]; \
> @@ -349,29 +195,17 @@ __printf_fp_l (FILE *fp, locale_t loc,
> { \
> is_neg = signbit (VAR); \
> if (isupper (info->spec)) \
> - { \
> - special = "NAN"; \
> - wspecial = L"NAN"; \
> - } \
> + special = "NAN"; \
> else \
> - { \
> - special = "nan"; \
> - wspecial = L"nan"; \
> - } \
> + special = "nan"; \
> } \
> else if (isinf (VAR)) \
> { \
> is_neg = signbit (VAR); \
> if (isupper (info->spec)) \
> - { \
> - special = "INF"; \
> - wspecial = L"INF"; \
> - } \
> + special = "INF"; \
> else \
> - { \
> - special = "inf"; \
> - wspecial = L"inf"; \
> - } \
> + special = "inf"; \
> } \
> else \
> { \
Ok.
> @@ -405,22 +239,22 @@ __printf_fp_l (FILE *fp, locale_t loc,
> --width;
> width -= 3;
>
> - if (!info->left && width > 0)
> - PADN (' ', width);
> + if (!info->left)
> + __printf_buffer_pad (buf, ' ', width);
>
> if (is_neg)
> - outchar ('-');
> + __printf_buffer_putc (buf, '-');
> else if (info->showsign)
> - outchar ('+');
> + __printf_buffer_putc (buf, '+');
> else if (info->space)
> - outchar (' ');
> + __printf_buffer_putc (buf, ' ');
>
> - PRINT (special, wspecial, 3);
> + __printf_buffer_puts (buf, special);
>
> - if (info->left && width > 0)
> - PADN (' ', width);
> + if (info->left)
> + __printf_buffer_pad (buf, ' ', width);
>
> - return done;
> + return;
> }
>
>
Ok.
> @@ -829,7 +663,7 @@ __printf_fp_l (FILE *fp, locale_t loc,
>
> {
> int width = info->width;
> - wchar_t *wstartp, *wcp;
> + char *wstartp, *wcp;
> size_t chars_needed;
> int expscale;
> int intdig_max, intdig_no = 0;
> @@ -837,7 +671,6 @@ __printf_fp_l (FILE *fp, locale_t loc,
> int fracdig_max;
> int dig_max;
> int significant;
> - int ngroups = 0;
> char spec = _tolower (info->spec);
>
> if (spec == 'e')
> @@ -898,38 +731,32 @@ __printf_fp_l (FILE *fp, locale_t loc,
> significant = 0; /* We count significant digits. */
> }
>
> - if (grouping)
> - {
> - /* Guess the number of groups we will make, and thus how
> - many spaces we need for separator characters. */
> - ngroups = __guess_grouping (intdig_max, grouping);
> - /* Allocate one more character in case rounding increases the
> - number of groups. */
> - chars_needed += ngroups + 1;
> - }
> -
> /* Allocate buffer for output. We need two more because while rounding
> it is possible that we need two more characters in front of all the
> other output. If the amount of memory we have to allocate is too
> large use `malloc' instead of `alloca'. */
> - if (__builtin_expect (chars_needed >= (size_t) -1 / sizeof (wchar_t) - 2
> - || chars_needed < fracdig_max, 0))
> + if (__glibc_unlikely (chars_needed >= (size_t) -1 - 2
> + || chars_needed < fracdig_max))
> {
> /* Some overflow occurred. */
> __set_errno (ERANGE);
> - return -1;
> + __printf_buffer_mark_failed (buf);
> + return;
> }
> - size_t wbuffer_to_alloc = (2 + chars_needed) * sizeof (wchar_t);
> + size_t wbuffer_to_alloc = 2 + chars_needed;
> buffer_malloced = ! __libc_use_alloca (wbuffer_to_alloc);
> if (__builtin_expect (buffer_malloced, 0))
> {
> - wbuffer = (wchar_t *) malloc (wbuffer_to_alloc);
> + wbuffer = malloc (wbuffer_to_alloc);
> if (wbuffer == NULL)
> - /* Signal an error to the caller. */
> - return -1;
> + {
> + /* Signal an error to the caller. */
> + __printf_buffer_mark_failed (buf);
> + return;
> + }
> }
> else
> - wbuffer = (wchar_t *) alloca (wbuffer_to_alloc);
> + wbuffer = alloca (wbuffer_to_alloc);
> wcp = wstartp = wbuffer + 2; /* Let room for rounding. */
>
> /* Do the real work: put digits in allocated buffer. */
Ok.
> @@ -945,15 +772,15 @@ __printf_fp_l (FILE *fp, locale_t loc,
> if (info->alt
> || fracdig_min > 0
> || (fracdig_max > 0 && (p.fracsize > 1 || p.frac[0] != 0)))
> - *wcp++ = decimalwc;
> + *wcp++ = decimal;
> }
> else
> {
> /* |fp| < 1.0 and the selected p.type is 'f', so put "0."
> in the buffer. */
> - *wcp++ = L'0';
> + *wcp++ = '0';
> --p.exponent;
> - *wcp++ = decimalwc;
> + *wcp++ = decimal;
> }
>
> /* Generate the needed number of fractional digits. */
> @@ -964,7 +791,7 @@ __printf_fp_l (FILE *fp, locale_t loc,
> {
> ++fracdig_no;
> *wcp = hack_digit (&p);
> - if (*wcp++ != L'0')
> + if (*wcp++ != '0')
> significant = 1;
> else if (significant == 0)
> {
> @@ -975,10 +802,10 @@ __printf_fp_l (FILE *fp, locale_t loc,
> }
>
> /* Do rounding. */
> - wchar_t last_digit = wcp[-1] != decimalwc ? wcp[-1] : wcp[-2];
> - wchar_t next_digit = hack_digit (&p);
> + char last_digit = wcp[-1] != decimal ? wcp[-1] : wcp[-2];
> + char next_digit = hack_digit (&p);
> bool more_bits;
> - if (next_digit != L'0' && next_digit != L'5')
> + if (next_digit != '0' && next_digit != '5')
> more_bits = true;
> else if (p.fracsize == 1 && p.frac[0] == 0)
> /* Rest of the number is zero. */
> @@ -995,29 +822,29 @@ __printf_fp_l (FILE *fp, locale_t loc,
> else
> more_bits = true;
> int rounding_mode = get_rounding_mode ();
> - if (round_away (is_neg, (last_digit - L'0') & 1, next_digit >= L'5',
> + if (round_away (is_neg, (last_digit - '0') & 1, next_digit >= '5',
> more_bits, rounding_mode))
> {
> - wchar_t *wtp = wcp;
> + char *wtp = wcp;
>
> if (fracdig_no > 0)
> {
> /* Process fractional digits. Terminate if not rounded or
> radix character is reached. */
> int removed = 0;
> - while (*--wtp != decimalwc && *wtp == L'9')
> + while (*--wtp != decimal && *wtp == '9')
> {
> - *wtp = L'0';
> + *wtp = '0';
> ++removed;
> }
> if (removed == fracdig_min && added_zeros > 0)
> --added_zeros;
> - if (*wtp != decimalwc)
> + if (*wtp != decimal)
> /* Round up. */
> (*wtp)++;
> else if (__builtin_expect (spec == 'g' && p.type == 'f' && info->alt
> && wtp == wstartp + 1
> - && wstartp[0] == L'0',
> + && wstartp[0] == '0',
> 0))
> /* This is a special case: the rounded number is 1.0,
> the format is 'g' or 'G', and the alternative format
> @@ -1025,14 +852,14 @@ __printf_fp_l (FILE *fp, locale_t loc,
> --added_zeros;
> }
>
> - if (fracdig_no == 0 || *wtp == decimalwc)
> + if (fracdig_no == 0 || *wtp == decimal)
> {
> /* Round the integer digits. */
> - if (*(wtp - 1) == decimalwc)
> + if (*(wtp - 1) == decimal)
> --wtp;
>
> - while (--wtp >= wstartp && *wtp == L'9')
> - *wtp = L'0';
> + while (--wtp >= wstartp && *wtp == '9')
> + *wtp = '0';
>
> if (wtp >= wstartp)
> /* Round up. */
> @@ -1056,13 +883,13 @@ __printf_fp_l (FILE *fp, locale_t loc,
> /* This is the case where for p.type %g the number fits
> really in the range for %f output but after rounding
> the number of digits is too big. */
> - *--wstartp = decimalwc;
> - *--wstartp = L'1';
> + *--wstartp = decimal;
> + *--wstartp = '1';
>
> if (info->alt || fracdig_no > 0)
> {
> /* Overwrite the old radix character. */
> - wstartp[intdig_no + 2] = L'0';
> + wstartp[intdig_no + 2] = '0';
> ++fracdig_no;
> }
>
> @@ -1077,7 +904,7 @@ __printf_fp_l (FILE *fp, locale_t loc,
> {
> /* We can simply add another another digit before the
> radix. */
> - *--wstartp = L'1';
> + *--wstartp = '1';
> ++intdig_no;
> }
>
Ok.
> @@ -1094,28 +921,16 @@ __printf_fp_l (FILE *fp, locale_t loc,
> }
>
> /* Now remove unnecessary '0' at the end of the string. */
> - while (fracdig_no > fracdig_min + added_zeros && *(wcp - 1) == L'0')
> + while (fracdig_no > fracdig_min + added_zeros && *(wcp - 1) == '0')
> {
> --wcp;
> --fracdig_no;
> }
> /* If we eliminate all fractional digits we perhaps also can remove
> the radix character. */
> - if (fracdig_no == 0 && !info->alt && *(wcp - 1) == decimalwc)
> + if (fracdig_no == 0 && !info->alt && *(wcp - 1) == decimal)
> --wcp;
>
> - if (grouping)
> - {
> - /* Rounding might have changed the number of groups. We allocated
> - enough memory but we need here the correct number of groups. */
> - if (intdig_no != intdig_max)
> - ngroups = __guess_grouping (intdig_no, grouping);
> -
> - /* Add in separator characters, overwriting the same buffer. */
> - wcp = group_number (wstartp, wcp, intdig_no, grouping, thousands_sepwc,
> - ngroups);
> - }
> -
> /* Write the p.exponent if it is needed. */
> if (p.type != 'f')
> {
> @@ -1125,12 +940,12 @@ __printf_fp_l (FILE *fp, locale_t loc,
> really smaller than -4, which requires the 'e'/'E' format.
> But after rounding the number has an p.exponent of -4. */
> assert (wcp >= wstartp + 1);
> - assert (wstartp[0] == L'1');
> - __wmemcpy (wstartp, L"0.0001", 6);
> - wstartp[1] = decimalwc;
> + assert (wstartp[0] == '1');
> + memcpy (wstartp, "0.0001", 6);
> + wstartp[1] = decimal;
> if (wcp >= wstartp + 2)
> {
> - __wmemset (wstartp + 6, L'0', wcp - (wstartp + 2));
> + memset (wstartp + 6, '0', wcp - (wstartp + 2));
> wcp += 4;
> }
> else
> @@ -1138,8 +953,8 @@ __printf_fp_l (FILE *fp, locale_t loc,
> }
> else
> {
> - *wcp++ = (wchar_t) p.type;
> - *wcp++ = p.expsign ? L'-' : L'+';
> + *wcp++ = p.type;
> + *wcp++ = p.expsign ? '-' : '+';
>
> /* Find the magnitude of the p.exponent. */
> expscale = 10;
> @@ -1148,220 +963,293 @@ __printf_fp_l (FILE *fp, locale_t loc,
>
> if (p.exponent < 10)
> /* Exponent always has at least two digits. */
> - *wcp++ = L'0';
> + *wcp++ = '0';
> else
> do
> {
> expscale /= 10;
> - *wcp++ = L'0' + (p.exponent / expscale);
> + *wcp++ = '0' + (p.exponent / expscale);
> p.exponent %= expscale;
> }
> while (expscale > 10);
> - *wcp++ = L'0' + p.exponent;
> + *wcp++ = '0' + p.exponent;
> }
> }
>
Ok.
> + struct grouping_iterator iter;
> + if (thousands_sep != '\0' && info->group)
> + __grouping_iterator_init (&iter, lc_category, loc, intdig_no);
> + else
> + iter.separators = 0;
> +
> /* Compute number of characters which must be filled with the padding
> character. */
> if (is_neg || info->showsign || info->space)
> --width;
> + /* To count bytes, we would have to use __translated_number_width
> + for info->i18n && !info->wide. See bug 28943. */
> width -= wcp - wstartp;
> + /* For counting bytes, we would have to multiply by
> + thousands_sep_length. */
> + width -= iter.separators;
>
> - if (!info->left && info->pad != '0' && width > 0)
> - PADN (info->pad, width);
> + if (!info->left && info->pad != '0')
> + __printf_buffer_pad (buf, info->pad, width);
>
> if (is_neg)
> - outchar ('-');
> + __printf_buffer_putc (buf, '-');
> else if (info->showsign)
> - outchar ('+');
> + __printf_buffer_putc (buf, '+');
> else if (info->space)
> - outchar (' ');
> + __printf_buffer_putc (buf, ' ');
>
> - if (!info->left && info->pad == '0' && width > 0)
> - PADN ('0', width);
> + if (!info->left && info->pad == '0')
> + __printf_buffer_pad (buf, '0', width);
>
> - {
> - char *buffer_end = NULL;
> - char *cp = NULL;
> - char *tmpptr;
> + if (iter.separators > 0)
> + {
> + char *cp = wstartp;
> + for (int i = 0; i < intdig_no; ++i)
> + {
> + if (__grouping_iterator_next (&iter))
> + __printf_buffer_putc (buf, thousands_sep);
> + __printf_buffer_putc (buf, *cp);
> + ++cp;
> + }
> + __printf_buffer_write (buf, cp, wcp - cp);
> + }
> + else
> + __printf_buffer_write (buf, wstartp, wcp - wstartp);
>
> - if (! wide)
> - {
> - /* Create the single byte string. */
> - size_t decimal_len;
> - size_t thousands_sep_len;
> - wchar_t *copywc;
> - size_t factor;
> - if (info->i18n)
> - factor = _nl_lookup_word (loc, LC_CTYPE, _NL_CTYPE_MB_CUR_MAX);
> - else
> - factor = 1;
> + if (info->left)
> + __printf_buffer_pad (buf, info->pad, width);
> + }
>
> - decimal_len = strlen (decimal);
> + if (buffer_malloced)
> + free (wbuffer);
> +}
>
> - if (thousands_sep == NULL)
> - thousands_sep_len = 0;
> - else
> - thousands_sep_len = strlen (thousands_sep);
> +/* ASCII to localization translation. Multibyte version. */
> +struct __printf_buffer_fp
> +{
> + struct __printf_buffer base;
>
> - size_t nbuffer = (2 + chars_needed * factor + decimal_len
> - + ngroups * thousands_sep_len);
> - if (__glibc_unlikely (buffer_malloced))
> - {
> - buffer = (char *) malloc (nbuffer);
> - if (buffer == NULL)
> - {
> - /* Signal an error to the caller. */
> - free (wbuffer);
> - return -1;
> - }
> - }
> - else
> - buffer = (char *) alloca (nbuffer);
> - buffer_end = buffer + nbuffer;
> -
> - /* Now copy the wide character string. Since the character
> - (except for the decimal point and thousands separator) must
> - be coming from the ASCII range we can esily convert the
> - string without mapping tables. */
> - for (cp = buffer, copywc = wstartp; copywc < wcp; ++copywc)
> - if (*copywc == decimalwc)
> - cp = (char *) __mempcpy (cp, decimal, decimal_len);
> - else if (*copywc == thousands_sepwc)
> - cp = (char *) __mempcpy (cp, thousands_sep, thousands_sep_len);
> - else
> - *cp++ = (char) *copywc;
> - }
> + /* Replacement for ',' and '.'. */
> + const char *thousands_sep;
> + const char *decimal;
> + unsigned char decimal_point_bytes;
> + unsigned char thousands_sep_length;
>
> - tmpptr = buffer;
> - if (__glibc_unlikely (info->i18n))
> - {
> -#ifdef COMPILE_WPRINTF
> - wstartp = _i18n_number_rewrite (wstartp, wcp,
> - wbuffer + wbuffer_to_alloc);
> - wcp = wbuffer + wbuffer_to_alloc;
> - assert ((uintptr_t) wbuffer <= (uintptr_t) wstartp);
> - assert ((uintptr_t) wstartp
> - < (uintptr_t) wbuffer + wbuffer_to_alloc);
> -#else
> - tmpptr = _i18n_number_rewrite (tmpptr, cp, buffer_end);
> - cp = buffer_end;
> - assert ((uintptr_t) buffer <= (uintptr_t) tmpptr);
> - assert ((uintptr_t) tmpptr < (uintptr_t) buffer_end);
> -#endif
> - }
> + /* Buffer to write to. */
> + struct __printf_buffer *next;
> +
> + /* Activates outdigit translation if not NULL. */
> + struct __locale_data *ctype;
>
> - PRINT (tmpptr, wstartp, wide ? wcp - wstartp : cp - tmpptr);
> + /* Buffer to which the untranslated ASCII digits are written. */
> + char untranslated[64];
> +};
>
> - /* Free the memory if necessary. */
> - if (__glibc_unlikely (buffer_malloced))
> +/* Multibyte buffer-to-buffer flush function with full translation. */
> +void
> +__printf_buffer_flush_fp (struct __printf_buffer_fp *buf)
> +{
> + /* No need to update buf->base.written; the actual count is
> + maintained in buf->next->written. */
> + for (char *p = buf->untranslated; p < buf->base.write_ptr; ++p)
> + {
> + char ch = *p;
> + const char *replacement = NULL;
> + unsigned int replacement_bytes;
> + if (ch == ',')
> + {
> + replacement = buf->thousands_sep;
> + replacement_bytes = buf->thousands_sep_length;
> + }
> + else if (ch == '.')
> + {
> + replacement = buf->decimal;
> + replacement_bytes = buf->decimal_point_bytes;
> + }
> + else if (buf->ctype != NULL && '0' <= ch && ch <= '9')
> {
> - free (buffer);
> - free (wbuffer);
> - /* Avoid a double free if the subsequent PADN encounters an
> - I/O error. */
> - buffer = NULL;
> - wbuffer = NULL;
> + int digit = ch - '0';
> + replacement
> + = buf->ctype->values[_NL_ITEM_INDEX (_NL_CTYPE_OUTDIGIT0_MB)
> + + digit].string;
> + struct lc_ctype_data *ctype = buf->ctype->private;
> + replacement_bytes = ctype->outdigit_bytes[digit];
> }
> + if (replacement == NULL)
> + __printf_buffer_putc (buf->next, ch);
> + else
> + __printf_buffer_write (buf->next, replacement, replacement_bytes);
> }
>
Ok.
> - if (info->left && width > 0)
> - PADN (info->pad, width);
> - }
> - return done;
> + if (!__printf_buffer_has_failed (buf->next))
> + buf->base.write_ptr = buf->untranslated;
> + else
> + __printf_buffer_mark_failed (&buf->base);
> }
> -libc_hidden_def (__printf_fp_l)
>
> -int
> -___printf_fp (FILE *fp, const struct printf_info *info,
> - const void *const *args)
> +void
> +__printf_fp_l_buffer (struct __printf_buffer *buf, locale_t loc,
> + const struct printf_info *info,
> + const void *const *args)
> {
> - return __printf_fp_l (fp, _NL_CURRENT_LOCALE, info, args);
> + struct __printf_buffer_fp tmp;
> +
> + if (info->extra)
> + {
> + tmp.thousands_sep = _nl_lookup (loc, LC_MONETARY, MON_THOUSANDS_SEP);
> + tmp.decimal = _nl_lookup (loc, LC_MONETARY, MON_DECIMAL_POINT);
> + if (tmp.decimal[0] == '\0')
> + tmp.decimal = _nl_lookup (loc, LC_NUMERIC, DECIMAL_POINT);
> + }
> + else
> + {
> + tmp.thousands_sep = _nl_lookup (loc, LC_NUMERIC, THOUSANDS_SEP);
> + tmp.decimal = _nl_lookup (loc, LC_NUMERIC, DECIMAL_POINT);
> + }
> +
> + tmp.thousands_sep_length = strlen (tmp.thousands_sep);
> + if (tmp.decimal[1] == '\0' && tmp.thousands_sep_length <= 1
> + && !info->i18n)
> + {
> + /* Emit the the characters directly. This is only possible if the
> + separators have length 1 (or 0 in case of thousands_sep). i18n
> + digit translation still needs the full conversion. */
> + __printf_fp_buffer_1 (buf, loc,
> + tmp.thousands_sep[0], tmp.decimal[0],
> + tmp.thousands_sep_length,
> + info, args);
> + return;
> + }
> +
> + tmp.decimal_point_bytes = strlen (tmp.decimal);
> +
> + if (info->i18n)
> + tmp.ctype = loc->__locales[LC_CTYPE];
> + else
> + tmp.ctype = NULL;
> + tmp.next = buf;
> +
> + __printf_buffer_init (&tmp.base, tmp.untranslated, sizeof (tmp.untranslated),
> + __printf_buffer_mode_fp);
> + __printf_fp_buffer_1 (&tmp.base, loc, ',', '.',
> + tmp.thousands_sep_length, info, args);
> + if (__printf_buffer_has_failed (&tmp.base))
> + {
> + __printf_buffer_mark_failed (tmp.next);
> + return;
> + }
> + __printf_buffer_flush_fp (&tmp);
> }
> -ldbl_hidden_def (___printf_fp, __printf_fp)
> -ldbl_strong_alias (___printf_fp, __printf_fp)
>
> -\f
> -/* Return the number of extra grouping characters that will be inserted
> - into a number with INTDIG_MAX integer digits. */
> +/* The wide version is implemented on top of the multibyte version using
> + translation. */
>
> -unsigned int
> -__guess_grouping (unsigned int intdig_max, const char *grouping)
> +struct __printf_buffer_fp_to_wide
> {
> - unsigned int groups;
> + struct __printf_buffer base;
> + wchar_t thousands_sep_wc;
> + wchar_t decimalwc;
> + struct __wprintf_buffer *next;
>
> - /* We treat all negative values like CHAR_MAX. */
> + /* Activates outdigit translation if not NULL. */
> + struct __locale_data *ctype;
>
> - if (*grouping == CHAR_MAX || *grouping <= 0)
> - /* No grouping should be done. */
> - return 0;
> + char untranslated[64];
> +};
>
> - groups = 0;
> - while (intdig_max > (unsigned int) *grouping)
> +void
> +__printf_buffer_flush_fp_to_wide (struct __printf_buffer_fp_to_wide *buf)
> +{
> + /* No need to update buf->base.written; the actual count is
> + maintained in buf->next->written. */
> + for (char *p = buf->untranslated; p < buf->base.write_ptr; ++p)
> {
> - ++groups;
> - intdig_max -= *grouping++;
> -
> - if (*grouping == CHAR_MAX
> -#if CHAR_MIN < 0
> - || *grouping < 0
> -#endif
> - )
> - /* No more grouping should be done. */
> - break;
> - else if (*grouping == 0)
> + /* wchar_t overlaps with char in the ASCII range. */
> + wchar_t ch = *p;
> + if (ch == L',')
> {
> - /* Same grouping repeats. */
> - groups += (intdig_max - 1) / grouping[-1];
> - break;
> + ch = buf->thousands_sep_wc;
> + if (ch == 0)
> + continue;
> }
> + else if (ch == L'.')
> + ch = buf->decimalwc;
> + else if (buf->ctype != NULL && L'0' <= ch && ch <= L'9')
> + ch = buf->ctype->values[_NL_ITEM_INDEX (_NL_CTYPE_OUTDIGIT0_WC)
> + + ch - L'0'].word;
> + __wprintf_buffer_putc (buf->next, ch);
> }
>
> - return groups;
> + if (!__wprintf_buffer_has_failed (buf->next))
> + buf->base.write_ptr = buf->untranslated;
> + else
> + __printf_buffer_mark_failed (&buf->base);
> }
>
> -/* Group the INTDIG_NO integer digits of the number in [BUF,BUFEND).
> - There is guaranteed enough space past BUFEND to extend it.
> - Return the new end of buffer. */
> -
> -static wchar_t *
> -group_number (wchar_t *buf, wchar_t *bufend, unsigned int intdig_no,
> - const char *grouping, wchar_t thousands_sep, int ngroups)
> +void
> +__wprintf_fp_l_buffer (struct __wprintf_buffer *buf, locale_t loc,
> + const struct printf_info *info,
> + const void *const *args)
> {
> - wchar_t *p;
> -
> - if (ngroups == 0)
> - return bufend;
> + struct __printf_buffer_fp_to_wide tmp;
> + if (info->extra)
> + {
> + tmp.decimalwc = _nl_lookup_word (loc, LC_MONETARY,
> + _NL_MONETARY_DECIMAL_POINT_WC);
> + tmp.thousands_sep_wc = _nl_lookup_word (loc, LC_MONETARY,
> + _NL_MONETARY_THOUSANDS_SEP_WC);
> + if (tmp.decimalwc == 0)
> + tmp.decimalwc = _nl_lookup_word (loc, LC_NUMERIC,
> + _NL_NUMERIC_DECIMAL_POINT_WC);
> + }
> + else
> + {
> + tmp.decimalwc = _nl_lookup_word (loc, LC_NUMERIC,
> + _NL_NUMERIC_DECIMAL_POINT_WC);
> + tmp.thousands_sep_wc = _nl_lookup_word (loc, LC_NUMERIC,
> + _NL_NUMERIC_THOUSANDS_SEP_WC);
> + }
>
> - /* Move the fractional part down. */
> - __wmemmove (buf + intdig_no + ngroups, buf + intdig_no,
> - bufend - (buf + intdig_no));
> + if (info->i18n)
> + tmp.ctype = loc->__locales[LC_CTYPE];
> + else
> + tmp.ctype = NULL;
> + tmp.next = buf;
>
> - p = buf + intdig_no + ngroups - 1;
> - do
> + __printf_buffer_init (&tmp.base, tmp.untranslated, sizeof (tmp.untranslated),
> + __printf_buffer_mode_fp_to_wide);
> + __printf_fp_buffer_1 (&tmp.base, loc, ',', '.', 1, info, args);
> + if (__printf_buffer_has_failed (&tmp.base))
> {
> - unsigned int len = *grouping++;
> - do
> - *p-- = buf[--intdig_no];
> - while (--len > 0);
> - *p-- = thousands_sep;
> + __wprintf_buffer_mark_failed (tmp.next);
> + return;
> + }
> + __printf_buffer_flush (&tmp.base);
> +}
>
> - if (*grouping == CHAR_MAX
> -#if CHAR_MIN < 0
> - || *grouping < 0
> -#endif
> - )
> - /* No more grouping should be done. */
> - break;
> - else if (*grouping == 0)
> - /* Same grouping repeats. */
> - --grouping;
> - } while (intdig_no > (unsigned int) *grouping);
> -
> - /* Copy the remaining ungrouped digits. */
> - do
> - *p-- = buf[--intdig_no];
> - while (p > buf);
> -
> - return bufend + ngroups;
> +int
> +___printf_fp (FILE *fp, const struct printf_info *info,
> + const void *const *args)
> +{
> + if (info->wide)
> + {
> + struct __wprintf_buffer_to_file buf;
> + __wprintf_buffer_to_file_init (&buf, fp);
> + __wprintf_fp_l_buffer (&buf.base, _NL_CURRENT_LOCALE, info, args);
> + return __wprintf_buffer_to_file_done (&buf);
> + }
> + else
> + {
> + struct __printf_buffer_to_file buf;
> + __printf_buffer_to_file_init (&buf, fp);
> + __printf_fp_l_buffer (&buf.base, _NL_CURRENT_LOCALE, info, args);
> + return __printf_buffer_to_file_done (&buf);
> + }
> }
> +ldbl_hidden_def (___printf_fp, __printf_fp)
> +ldbl_strong_alias (___printf_fp, __printf_fp)
Ok.
> diff --git a/stdio-common/printf_fphex.c b/stdio-common/printf_fphex.c
> index 5af380da62..957796f00b 100644
> --- a/stdio-common/printf_fphex.c
> +++ b/stdio-common/printf_fphex.c
> @@ -17,10 +17,12 @@
> <https://www.gnu.org/licenses/>. */
>
> #include <array_length.h>
> +#include <assert.h>
> #include <ctype.h>
> #include <ieee754.h>
> #include <math.h>
> #include <printf.h>
> +#include <libioP.h>
> #include <stdlib.h>
> #include <stdio.h>
> #include <string.h>
> @@ -30,6 +32,9 @@
> #include <locale/localeinfo.h>
> #include <stdbool.h>
> #include <rounding-mode.h>
> +#include <sys/param.h>
> +#include <printf_buffer.h>
> +#include <errno.h>
>
> #if __HAVE_DISTINCT_FLOAT128
> # include "ieee754_float128.h"
> @@ -39,58 +44,11 @@
> IEEE854_FLOAT128_BIAS)
> #endif
>
> -/* #define NDEBUG 1*/ /* Undefine this for debugging assertions. */
> -#include <assert.h>
> -
> -#include <libioP.h>
> -#define PUT(f, s, n) _IO_sputn (f, s, n)
> -#define PAD(f, c, n) (wide ? _IO_wpadn (f, c, n) : _IO_padn (f, c, n))
> -#undef putc
> -#define putc(c, f) (wide \
> - ? (int)_IO_putwc_unlocked (c, f) : _IO_putc_unlocked (c, f))
> -
> -\f
> -/* Macros for doing the actual output. */
> -
> -#define outchar(ch) \
> - do \
> - { \
> - const int outc = (ch); \
> - if (putc (outc, fp) == EOF) \
> - return -1; \
> - ++done; \
> - } while (0)
> -
> -#define PRINT(ptr, wptr, len) \
> - do \
> - { \
> - size_t outlen = (len); \
> - if (wide) \
> - while (outlen-- > 0) \
> - outchar (*wptr++); \
> - else \
> - while (outlen-- > 0) \
> - outchar (*ptr++); \
> - } while (0)
> -
> -#define PADN(ch, len) \
> - do \
> - { \
> - if (PAD (fp, ch, len) != len) \
> - return -1; \
> - done += len; \
> - } \
> - while (0)
> -
> -#ifndef MIN
> -# define MIN(a,b) ((a)<(b)?(a):(b))
> -#endif
> -\f
> -
> -int
> -__printf_fphex (FILE *fp,
> - const struct printf_info *info,
> - const void *const *args)
> +static void
> +__printf_fphex_buffer (struct __printf_buffer *buf,
> + const char *decimal,
> + const struct printf_info *info,
> + const void *const *args)
> {
> /* The floating-point value to output. */
> union
> @@ -106,34 +64,19 @@ __printf_fphex (FILE *fp,
> /* This function always uses LC_NUMERIC. */
> assert (info->extra == 0);
>
> - /* Locale-dependent representation of decimal point. Hexadecimal
> - formatting always using LC_NUMERIC (disregarding info->extra). */
> - const char *decimal = _NL_CURRENT (LC_NUMERIC, DECIMAL_POINT);
> - wchar_t decimalwc = _NL_CURRENT_WORD (LC_NUMERIC,
> - _NL_NUMERIC_DECIMAL_POINT_WC);
> -
> - /* The decimal point character must never be zero. */
> - assert (*decimal != '\0' && decimalwc != L'\0');
> -
> /* "NaN" or "Inf" for the special cases. */
> const char *special = NULL;
> - const wchar_t *wspecial = NULL;
>
> /* Buffer for the generated number string for the mantissa. The
> maximal size for the mantissa is 128 bits. */
> char numbuf[32];
> char *numstr;
> char *numend;
> - wchar_t wnumbuf[32];
> - wchar_t *wnumstr;
> - wchar_t *wnumend;
> int negative;
>
> /* The maximal exponent of two in decimal notation has 5 digits. */
> char expbuf[5];
> char *expstr;
> - wchar_t wexpbuf[5];
> - wchar_t *wexpstr;
> int expnegative;
> int exponent;
>
> @@ -149,12 +92,6 @@ __printf_fphex (FILE *fp,
> /* Width. */
> int width = info->width;
>
> - /* Number of characters written. */
> - int done = 0;
> -
> - /* Nonzero if this is output on a wide character stream. */
> - int wide = info->wide;
> -
> #define PRINTF_FPHEX_FETCH(FLOAT, VAR) \
> { \
> (VAR) = *(const FLOAT *) args[0]; \
> @@ -163,30 +100,18 @@ __printf_fphex (FILE *fp,
> if (isnan (VAR)) \
> { \
> if (isupper (info->spec)) \
> - { \
> - special = "NAN"; \
> - wspecial = L"NAN"; \
> - } \
> + special = "NAN"; \
> else \
> - { \
> - special = "nan"; \
> - wspecial = L"nan"; \
> - } \
> + special = "nan"; \
> } \
> else \
> { \
> if (isinf (VAR)) \
> { \
> if (isupper (info->spec)) \
> - { \
> - special = "INF"; \
> - wspecial = L"INF"; \
> - } \
> + special = "INF"; \
> else \
> - { \
> - special = "inf"; \
> - wspecial = L"inf"; \
> - } \
> + special = "inf"; \
> } \
> } \
> negative = signbit (VAR); \
> @@ -215,22 +140,22 @@ __printf_fphex (FILE *fp,
> --width;
> width -= 3;
>
> - if (!info->left && width > 0)
> - PADN (' ', width);
> + if (!info->left)
> + __printf_buffer_pad (buf, ' ', width);
>
> if (negative)
> - outchar ('-');
> + __printf_buffer_putc (buf, '-');
> else if (info->showsign)
> - outchar ('+');
> + __printf_buffer_putc (buf, '+');
> else if (info->space)
> - outchar (' ');
> + __printf_buffer_putc (buf, ' ');
>
> - PRINT (special, wspecial, 3);
> + __printf_buffer_puts (buf, special);
>
> - if (info->left && width > 0)
> - PADN (' ', width);
> + if (info->left)
> + __printf_buffer_pad (buf, ' ', width);
>
> - return done;
> + return;
> }
>
> #if __HAVE_DISTINCT_FLOAT128
> @@ -252,26 +177,15 @@ __printf_fphex (FILE *fp,
> zero_mantissa = num == 0;
>
> if (sizeof (unsigned long int) > 6)
> - {
> - wnumstr = _itowa_word (num, wnumbuf + (sizeof wnumbuf) / sizeof (wchar_t), 16,
> - info->spec == 'A');
> numstr = _itoa_word (num, numbuf + sizeof numbuf, 16,
> info->spec == 'A');
> - }
> else
> - {
> - wnumstr = _itowa (num, wnumbuf + sizeof wnumbuf / sizeof (wchar_t), 16,
> - info->spec == 'A');
> numstr = _itoa (num, numbuf + sizeof numbuf, 16,
> info->spec == 'A');
> - }
>
> /* Fill with zeroes. */
> - while (wnumstr > wnumbuf + (sizeof wnumbuf - 52) / sizeof (wchar_t))
> - {
> - *--wnumstr = L'0';
> - *--numstr = '0';
> - }
> + while (numstr > numbuf + (sizeof numbuf - 13))
> + *--numstr = '0';
>
> leading = fpnum.dbl.ieee.exponent == 0 ? '0' : '1';
>
> @@ -307,13 +221,9 @@ __printf_fphex (FILE *fp,
> /* Look for trailing zeroes. */
> if (! zero_mantissa)
> {
> - wnumend = array_end (wnumbuf);
> numend = array_end (numbuf);
> - while (wnumend[-1] == L'0')
> - {
> - --wnumend;
> + while (numend[-1] == '0')
> --numend;
> - }
>
> bool do_round_away = false;
>
> @@ -352,7 +262,6 @@ __printf_fphex (FILE *fp,
> like in ASCII. This is true for the rest of GNU, too. */
> if (ch == '9')
> {
> - wnumstr[cnt] = (wchar_t) info->spec;
> numstr[cnt] = info->spec; /* This is tricky,
> think about it! */
> break;
> @@ -360,14 +269,10 @@ __printf_fphex (FILE *fp,
> else if (tolower (ch) < 'f')
> {
> ++numstr[cnt];
> - ++wnumstr[cnt];
> break;
> }
> else
> - {
> - numstr[cnt] = '0';
> - wnumstr[cnt] = L'0';
> - }
> + numstr[cnt] = '0';
> }
> if (cnt < 0)
> {
> @@ -401,13 +306,10 @@ __printf_fphex (FILE *fp,
> if (precision == -1)
> precision = 0;
> numend = numstr;
> - wnumend = wnumstr;
> }
>
> /* Now we can compute the exponent string. */
> expstr = _itoa_word (exponent, expbuf + sizeof expbuf, 10, 0);
> - wexpstr = _itowa_word (exponent,
> - wexpbuf + sizeof wexpbuf / sizeof (wchar_t), 10, 0);
>
> /* Now we have all information to compute the size. */
> width -= ((negative || info->showsign || info->space)
> @@ -421,54 +323,110 @@ __printf_fphex (FILE *fp,
> A special case when the mantissa or the precision is zero and the `#'
> is not given. In this case we must not print the decimal point. */
> if (precision > 0 || info->alt)
> - width -= wide ? 1 : strlen (decimal);
> + --width;
>
> - if (!info->left && info->pad != '0' && width > 0)
> - PADN (' ', width);
> + if (!info->left && info->pad != '0')
> + __printf_buffer_pad (buf, ' ', width);
>
> if (negative)
> - outchar ('-');
> + __printf_buffer_putc (buf, '-');
> else if (info->showsign)
> - outchar ('+');
> + __printf_buffer_putc (buf, '+');
> else if (info->space)
> - outchar (' ');
> + __printf_buffer_putc (buf, ' ');
>
> - outchar ('0');
> + __printf_buffer_putc (buf, '0');
> if ('X' - 'A' == 'x' - 'a')
> - outchar (info->spec + ('x' - 'a'));
> + __printf_buffer_putc (buf, info->spec + ('x' - 'a'));
> else
> - outchar (info->spec == 'A' ? 'X' : 'x');
> + __printf_buffer_putc (buf, info->spec == 'A' ? 'X' : 'x');
>
> - if (!info->left && info->pad == '0' && width > 0)
> - PADN ('0', width);
> + if (!info->left && info->pad == '0')
> + __printf_buffer_pad (buf, '0', width);
>
> - outchar (leading);
> + __printf_buffer_putc (buf, leading);
>
> if (precision > 0 || info->alt)
> - {
> - const wchar_t *wtmp = &decimalwc;
> - PRINT (decimal, wtmp, wide ? 1 : strlen (decimal));
> - }
> + __printf_buffer_puts (buf, decimal);
>
> if (precision > 0)
> {
> ssize_t tofill = precision - (numend - numstr);
> - PRINT (numstr, wnumstr, MIN (numend - numstr, precision));
> - if (tofill > 0)
> - PADN ('0', tofill);
> + __printf_buffer_write (buf, numstr, MIN (numend - numstr, precision));
> + __printf_buffer_pad (buf, '0', tofill);
> }
>
> if ('P' - 'A' == 'p' - 'a')
> - outchar (info->spec + ('p' - 'a'));
> + __printf_buffer_putc (buf, info->spec + ('p' - 'a'));
> else
> - outchar (info->spec == 'A' ? 'P' : 'p');
> + __printf_buffer_putc (buf, info->spec == 'A' ? 'P' : 'p');
> +
> + __printf_buffer_putc (buf, expnegative ? '-' : '+');
> +
> + __printf_buffer_write (buf, expstr, (expbuf + sizeof expbuf) - expstr);
> +
> + if (info->left && info->pad != '0')
> + __printf_buffer_pad (buf, info->pad, width);
> +}
>
Ok.
+
> +void
> +__printf_fphex_l_buffer (struct __printf_buffer *buf, locale_t loc,
> + const struct printf_info *info,
> + const void *const *args)
> +{
> + __printf_fphex_buffer (buf, _nl_lookup (loc, LC_NUMERIC, DECIMAL_POINT),
> + info, args);
> +}
>
> - outchar (expnegative ? '-' : '+');
>
> - PRINT (expstr, wexpstr, (expbuf + sizeof expbuf) - expstr);
> +/* The wide buffer version is implemented by translating the output of
> + the multibyte verison. */
>
> - if (info->left && info->pad != '0' && width > 0)
> - PADN (info->pad, width);
> +struct __printf_buffer_fphex_to_wide
> +{
> + struct __printf_buffer base;
> + wchar_t decimalwc;
> + struct __wprintf_buffer *next;
> + char untranslated[64];
> +};
> +
> +/* Translate to wide characters, rewriting "." to the actual decimal
> + point. */
> +void
> +__printf_buffer_flush_fphex_to_wide (struct __printf_buffer_fphex_to_wide *buf)
> +{
> + /* No need to adjust buf->base.written, only buf->next->written matters. */
> + for (char *p = buf->untranslated; p < buf->base.write_ptr; ++p)
> + {
> + /* wchar_t overlaps with char in the ASCII range. */
> + wchar_t ch = *p;
> + if (ch == L'.')
> + ch = buf->decimalwc;
> + __wprintf_buffer_putc (buf->next, ch);
> + }
>
> - return done;
> + if (!__wprintf_buffer_has_failed (buf->next))
> + buf->base.write_ptr = buf->untranslated;
> + else
> + __printf_buffer_mark_failed (&buf->base);
> +}
> +
> +void
> +__wprintf_fphex_l_buffer (struct __wprintf_buffer *next, locale_t loc,
> + const struct printf_info *info,
> + const void *const *args)
> +{
> + struct __printf_buffer_fphex_to_wide buf;
> + __printf_buffer_init (&buf.base, buf.untranslated, sizeof (buf.untranslated),
> + __printf_buffer_mode_fphex_to_wide);
> + buf.decimalwc = _nl_lookup_word (loc, LC_NUMERIC,
> + _NL_NUMERIC_DECIMAL_POINT_WC);
> + buf.next = next;
> + __printf_fphex_buffer (&buf.base, ".", info, args);
> + if (__printf_buffer_has_failed (&buf.base))
> + {
> + __wprintf_buffer_mark_failed (buf.next);
> + return;
> + }
> + __printf_buffer_flush_fphex_to_wide (&buf);
> }
Ok.
> diff --git a/stdio-common/vfprintf-internal.c b/stdio-common/vfprintf-internal.c
> index fb94961f37..83a6aea510 100644
> --- a/stdio-common/vfprintf-internal.c
> +++ b/stdio-common/vfprintf-internal.c
> @@ -16,6 +16,7 @@
> <https://www.gnu.org/licenses/>. */
>
> #include <array_length.h>
> +#include <assert.h>
> #include <ctype.h>
> #include <limits.h>
> #include <printf.h>
> @@ -29,9 +30,12 @@
> #include <sys/param.h>
> #include <_itoa.h>
> #include <locale/localeinfo.h>
> +#include <grouping_iterator.h>
> #include <stdio.h>
> #include <scratch_buffer.h>
> #include <intprops.h>
> +#include <printf_buffer.h>
> +#include <printf_buffer_to_file.h>
>
> /* This code is shared between the standard stdio implementation found
> in GNU C library and the libio implementation originally found in
> @@ -47,21 +51,21 @@
> #endif
>
> #define ARGCHECK(S, Format) \
> - do \
> - { \
> - /* Check file argument for consistence. */ \
> - CHECK_FILE (S, -1); \
> - if (S->_flags & _IO_NO_WRITES) \
> - { \
> - S->_flags |= _IO_ERR_SEEN; \
> - __set_errno (EBADF); \
> - return -1; \
> - } \
> - if (Format == NULL) \
> - { \
> - __set_errno (EINVAL); \
> - return -1; \
> - } \
> + do \
> + { \
> + /* Check file argument for consistence. */ \
> + CHECK_FILE (S, -1); \
> + if (S->_flags & _IO_NO_WRITES) \
> + { \
> + S->_flags |= _IO_ERR_SEEN; \
> + __set_errno (EBADF); \
> + return -1; \
> + } \
> + if (Format == NULL) \
> + { \
> + __set_errno (EINVAL); \
> + return -1; \
> + } \
> } while (0)
> #define UNBUFFERED_P(S) ((S)->_flags & _IO_UNBUFFERED)
>
> @@ -116,37 +120,9 @@
> while (0)
> #endif
>
> -/* Add LENGTH to DONE. Return the new value of DONE, or -1 on
> - overflow (and set errno accordingly). */
> -static inline int
> -done_add_func (size_t length, int done)
> -{
> - if (done < 0)
> - return done;
> - int ret;
> - if (INT_ADD_WRAPV (done, length, &ret))
> - {
> - __set_errno (EOVERFLOW);
> - return -1;
> - }
> - return ret;
> -}
> -
> -#define done_add(val) \
> - do \
> - { \
> - /* Ensure that VAL has a type similar to int. */ \
> - _Static_assert (sizeof (val) == sizeof (int), "value int size"); \
> - _Static_assert ((__typeof__ (val)) -1 < 0, "value signed"); \
> - done = done_add_func ((val), done); \
> - if (done < 0) \
> - goto all_done; \
> - } \
> - while (0)
> -
> #ifndef COMPILE_WPRINTF
> +# include "printf_buffer-char.h"
> # define vfprintf __vfprintf_internal
> -# define CHAR_T char
> # define OTHER_CHAR_T wchar_t
> # define UCHAR_T unsigned char
> # define INT_T int
> @@ -155,14 +131,12 @@ typedef const char *THOUSANDS_SEP_T;
> # define ISDIGIT(Ch) ((unsigned int) ((Ch) - '0') < 10)
> # define STR_LEN(Str) strlen (Str)
>
> -# define PUT(F, S, N) _IO_sputn ((F), (S), (N))
> -# define PUTC(C, F) _IO_putc_unlocked (C, F)
> # define ORIENT if (_IO_vtable_offset (s) == 0 && _IO_fwide (s, -1) != -1)\
> return -1
> # define CONVERT_FROM_OTHER_STRING __wcsrtombs
> #else
> +# include "printf_buffer-wchar_t.h"
> # define vfprintf __vfwprintf_internal
> -# define CHAR_T wchar_t
> # define OTHER_CHAR_T char
> /* This is a hack!!! There should be a type uwchar_t. */
> # define UCHAR_T unsigned int /* uwchar_t */
> @@ -174,8 +148,6 @@ typedef wchar_t THOUSANDS_SEP_T;
>
> # include <_itowa.h>
>
> -# define PUT(F, S, N) _IO_sputn ((F), (S), (N))
> -# define PUTC(C, F) _IO_putwc_unlocked (C, F)
> # define ORIENT if (_IO_fwide (s, 1) != 1) return -1
> # define CONVERT_FROM_OTHER_STRING __mbsrtowcs
>
> @@ -186,76 +158,16 @@ typedef wchar_t THOUSANDS_SEP_T;
> # define EOF WEOF
> #endif
>
> -static inline int
> -pad_func (FILE *s, CHAR_T padchar, int width, int done)
> -{
> - if (width > 0)
> - {
> - ssize_t written;
> -#ifndef COMPILE_WPRINTF
> - written = _IO_padn (s, padchar, width);
> -#else
> - written = _IO_wpadn (s, padchar, width);
> -#endif
> - if (__glibc_unlikely (written != width))
> - return -1;
> - return done_add_func (width, done);
> - }
> - return done;
> -}
> -
> -#define PAD(Padchar) \
> - do \
> - { \
> - done = pad_func (s, (Padchar), width, done); \
> - if (done < 0) \
> - goto all_done; \
> - } \
> - while (0)
> -
> -#include "_i18n_number.h"
> -
> /* Include the shared code for parsing the format string. */
> #include "printf-parse.h"
>
>
> -#define outchar(Ch) \
> - do \
> - { \
> - const INT_T outc = (Ch); \
> - if (PUTC (outc, s) == EOF || done == INT_MAX) \
> - { \
> - done = -1; \
> - goto all_done; \
> - } \
> - ++done; \
> - } \
> - while (0)
> -
> -static inline int
> -outstring_func (FILE *s, const UCHAR_T *string, size_t length, int done)
> -{
> - assert ((size_t) done <= (size_t) INT_MAX);
> - if ((size_t) PUT (s, string, length) != (size_t) (length))
> - return -1;
> - return done_add_func (length, done);
> -}
> -
> -#define outstring(String, Len) \
> - do \
> - { \
> - const void *string_ = (String); \
> - done = outstring_func (s, string_, (Len), done); \
> - if (done < 0) \
> - goto all_done; \
> - } \
> - while (0)
> -
> /* Write the string SRC to S. If PREC is non-negative, write at most
> PREC bytes. If LEFT is true, perform left justification. */
> -static int
> -outstring_converted_wide_string (FILE *s, const OTHER_CHAR_T *src, int prec,
> - int width, bool left, int done)
> +static void
> +outstring_converted_wide_string (struct Xprintf_buffer *target,
> + const OTHER_CHAR_T *src, int prec,
> + int width, bool left)
> {
> /* Use a small buffer to combine processing of multiple characters.
> CONVERT_FROM_OTHER_STRING expects the buffer size in (wide)
> @@ -290,7 +202,10 @@ outstring_converted_wide_string (FILE *s, const OTHER_CHAR_T *src, int prec,
> size_t written = CONVERT_FROM_OTHER_STRING
> (buf, &src_copy, write_limit, &mbstate);
> if (written == (size_t) -1)
> - return -1;
> + {
> + Xprintf_buffer_mark_failed (target);
> + return;
> + }
> if (written == 0)
> break;
> total_written += written;
> @@ -299,12 +214,9 @@ outstring_converted_wide_string (FILE *s, const OTHER_CHAR_T *src, int prec,
> }
>
> /* Output initial padding. */
> - if (total_written < width)
> - {
> - done = pad_func (s, L_(' '), width - total_written, done);
> - if (done < 0)
> - return done;
> - }
> + Xprintf_buffer_pad (target, L_(' '), width - total_written);
> + if (Xprintf_buffer_has_failed (target))
> + return;
> }
>
> /* Convert the input string, piece by piece. */
> @@ -324,12 +236,13 @@ outstring_converted_wide_string (FILE *s, const OTHER_CHAR_T *src, int prec,
> size_t written = CONVERT_FROM_OTHER_STRING
> (buf, &src, write_limit, &mbstate);
> if (written == (size_t) -1)
> - return -1;
> + {
> + Xprintf_buffer_mark_failed (target);
> + return;
> + }
> if (written == 0)
> break;
> - done = outstring_func (s, (const UCHAR_T *) buf, written, done);
> - if (done < 0)
> - return done;
> + Xprintf_buffer_write (target, buf, written);
> total_written += written;
> if (prec >= 0)
> remaining -= written;
> @@ -337,21 +250,20 @@ outstring_converted_wide_string (FILE *s, const OTHER_CHAR_T *src, int prec,
> }
>
> /* Add final padding. */
> - if (width > 0 && left && total_written < width)
> - return pad_func (s, L_(' '), width - total_written, done);
> - return done;
> + if (width > 0 && left)
> + Xprintf_buffer_pad (target, L_(' '), width - total_written);
> }
>
> /* Calls __printf_fp or __printf_fphex based on the value of the
> format specifier INFO->spec. */
> -static inline int
> -__printf_fp_spec (FILE *fp, const struct printf_info *info,
> - const void *const *args)
> +static inline void
> +__printf_fp_spec (struct Xprintf_buffer *target,
> + const struct printf_info *info, const void *const *args)
> {
> if (info->spec == 'a' || info->spec == 'A')
> - return __printf_fphex (fp, info, args);
> + Xprintf (fphex_l_buffer) (target, _NL_CURRENT_LOCALE, info, args);
> else
> - return __printf_fp (fp, info, args);
> + Xprintf (fp_l_buffer) (target, _NL_CURRENT_LOCALE, info, args);
> }
>
> /* For handling long_double and longlong we use the same flag. If
> @@ -656,31 +568,29 @@ static const uint8_t jump_table[] =
> REF (form_binary), /* for 'B', 'b' */ \
> }
>
> -/* Helper function to provide temporary buffering for unbuffered streams. */
> -static int buffered_vfprintf (FILE *stream, const CHAR_T *fmt, va_list,
> - unsigned int)
> - __THROW __attribute__ ((noinline));
> -
> /* Handle positional format specifiers. */
> -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,
> - CHAR_T *work_buffer, int save_errno,
> - const char *grouping,
> - THOUSANDS_SEP_T thousands_sep,
> - unsigned int mode_flags);
> +static void printf_positional (struct Xprintf_buffer *buf,
> + const CHAR_T *format, int readonly_format,
> + va_list ap, va_list *ap_savep,
> + int nspecs_done, const UCHAR_T *lead_str_end,
> + CHAR_T *work_buffer, int save_errno,
> + const char *grouping,
> + THOUSANDS_SEP_T thousands_sep,
> + unsigned int mode_flags);
>
> /* Handle unknown format specifier. */
> -static int printf_unknown (FILE *, const struct printf_info *) __THROW;
> -
> -/* Group digits of number string. */
> -static CHAR_T *group_number (CHAR_T *, CHAR_T *, CHAR_T *, const char *,
> - THOUSANDS_SEP_T);
> -
> -/* The function itself. */
> -int
> -vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
> +static void printf_unknown (struct Xprintf_buffer *,
> + const struct printf_info *) __THROW;
> +
> +static void group_number (struct Xprintf_buffer *buf,
> + struct grouping_iterator *iter,
> + CHAR_T *from, CHAR_T *to,
> + THOUSANDS_SEP_T thousands_sep, bool i18n);
> +
> +/* The buffer-based function itself. */
> +void
> +Xprintf_buffer (struct Xprintf_buffer *buf, const CHAR_T *format,
> + va_list ap, unsigned int mode_flags)
> {
> /* The character used as thousands separator. */
> THOUSANDS_SEP_T thousands_sep = 0;
> @@ -688,9 +598,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
> /* The string describing the size of groups of digits. */
> const char *grouping;
>
> - /* Place to accumulate the result. */
> - int done;
> -
> /* Current character in format string. */
> const UCHAR_T *f;
>
> @@ -717,30 +624,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
> 0 if unknown. */
> int readonly_format = 0;
>
> - /* Orient the stream. */
> -#ifdef ORIENT
> - ORIENT;
> -#endif
> -
> - /* Sanity check of arguments. */
> - ARGCHECK (s, format);
> -
> -#ifdef ORIENT
> - /* Check for correct orientation. */
> - if (_IO_vtable_offset (s) == 0
> - && _IO_fwide (s, sizeof (CHAR_T) == 1 ? -1 : 1)
> - != (sizeof (CHAR_T) == 1 ? -1 : 1))
> - /* The stream is already oriented otherwise. */
> - return EOF;
> -#endif
> -
> - if (UNBUFFERED_P (s))
> - /* Use a helper function which will allocate a local temporary buffer
> - for the stream and then call us again. */
> - return buffered_vfprintf (s, format, ap, mode_flags);
> -
> /* Initialize local variables. */
> - done = 0;
> grouping = (const char *) -1;
> #ifdef __va_copy
> /* This macro will be available soon in gcc's <stdarg.h>. We need it
> @@ -759,17 +643,15 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
> f = lead_str_end = __find_specmb ((const UCHAR_T *) format);
> #endif
>
> - /* Lock stream. */
> - _IO_cleanup_region_start ((void (*) (void *)) &_IO_funlockfile, s);
> - _IO_flockfile (s);
> -
> /* Write the literal text before the first format. */
> - outstring ((const UCHAR_T *) format,
> - lead_str_end - (const UCHAR_T *) format);
> + Xprintf_buffer_write (buf, format,
> + lead_str_end - (const UCHAR_T *) format);
> + if (Xprintf_buffer_has_failed (buf))
> + return;
>
> /* If we only have to print a simple string, return now. */
> if (*f == L_('\0'))
> - goto all_done;
> + return;
>
> /* Use the slow path in case any printf handler is registered. */
> if (__glibc_unlikely (__printf_function_table != NULL
> @@ -885,7 +767,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
> if (pos == -1)
> {
> __set_errno (EOVERFLOW);
> - done = -1;
> + Xprintf_buffer_mark_failed (buf);
> goto all_done;
> }
>
> @@ -912,7 +794,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
> if (__glibc_unlikely (width == -1))
> {
> __set_errno (EOVERFLOW);
> - done = -1;
> + Xprintf_buffer_mark_failed (buf);
> goto all_done;
> }
>
> @@ -935,7 +817,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
> if (pos == -1)
> {
> __set_errno (EOVERFLOW);
> - done = -1;
> + Xprintf_buffer_mark_failed (buf);
> goto all_done;
> }
>
> @@ -958,7 +840,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
> if (prec == -1)
> {
> __set_errno (EOVERFLOW);
> - done = -1;
> + Xprintf_buffer_mark_failed (buf);
> goto all_done;
> }
> }
> @@ -1058,13 +940,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
> PARSE_FLOAT_VA_ARG_EXTENDED (info);
> const void *ptr = &the_arg;
>
> - int function_done = __printf_fp_spec (s, &info, &ptr);
> - if (function_done < 0)
> - {
> - done = -1;
> - goto all_done;
> - }
> - done_add (function_done);
> + __printf_fp_spec (buf, &info, &ptr);
> }
> break;
>
> @@ -1073,7 +949,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
> {
> /* The format string ended before the specifier is complete. */
> __set_errno (EINVAL);
> - done = -1;
> + Xprintf_buffer_mark_failed (buf);
> goto all_done;
> }
>
> @@ -1093,30 +969,28 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
> #endif
>
> /* Write the following constant string. */
> - outstring (end_of_spec, f - end_of_spec);
> + Xprintf_buffer_write (buf, (const CHAR_T *) end_of_spec,
> + f - end_of_spec);
> }
> - while (*f != L_('\0'));
> + while (*f != L_('\0') && !Xprintf_buffer_has_failed (buf));
>
> - /* Unlock stream and return. */
> - goto all_done;
> + all_done:
> + /* printf_positional performs cleanup under its all_done label, so
> + vfprintf-process-arg.c uses it for this function and
> + printf_positional below. */
> + return;
>
> /* Hand off processing for positional parameters. */
> do_positional:
> - done = printf_positional (s, format, readonly_format, ap, &ap_save,
> - done, nspecs_done, lead_str_end, work_buffer,
> - save_errno, grouping, thousands_sep, mode_flags);
> -
> - all_done:
> - /* Unlock the stream. */
> - _IO_funlockfile (s);
> - _IO_cleanup_region_end (0);
> -
> - return done;
> + printf_positional (buf, format, readonly_format, ap, &ap_save,
> + nspecs_done, lead_str_end, work_buffer,
> + save_errno, grouping, thousands_sep, mode_flags);
> }
> \f
> -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,
> +static void
> +printf_positional (struct Xprintf_buffer * buf, const CHAR_T *format,
> + int readonly_format,
> + va_list ap, va_list *ap_savep, int nspecs_done,
> const UCHAR_T *lead_str_end,
> CHAR_T *work_buffer, int save_errno,
> const char *grouping, THOUSANDS_SEP_T thousands_sep,
> @@ -1171,7 +1045,7 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format,
> {
> if (!scratch_buffer_grow_preserve (&specsbuf))
> {
> - done = -1;
> + Xprintf_buffer_mark_failed (buf);
> goto all_done;
> }
> specs = specsbuf.data;
> @@ -1199,7 +1073,7 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format,
> = sizeof (*args_value) + sizeof (*args_size) + sizeof (*args_type);
> if (!scratch_buffer_set_array_size (&argsbuf, nargs, bytes_per_arg))
> {
> - done = -1;
> + Xprintf_buffer_mark_failed (buf);
> goto all_done;
> }
> args_value = argsbuf.data;
> @@ -1312,7 +1186,8 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format,
> }
>
> /* Now walk through all format specifiers and process them. */
> - for (; (size_t) nspecs_done < nspecs; ++nspecs_done)
> + for (; (size_t) nspecs_done < nspecs && !Xprintf_buffer_has_failed (buf);
> + ++nspecs_done)
> {
> STEP4_TABLE;
>
> @@ -1376,26 +1251,19 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format,
> }
>
> /* Process format specifiers. */
> - while (1)
> + do
> {
> - 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);
> -
> + int function_done
> + = Xprintf (function_invoke) (buf,
> + __printf_function_table[(size_t) spec],
> + &args_value[specs[nspecs_done]
> + .data_arg],
> + specs[nspecs_done].ndata_args,
> + &specs[nspecs_done].info);
> if (function_done != -2)
> {
> /* If an error occurred we don't have information
> @@ -1403,11 +1271,9 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format,
> if (function_done < 0)
> {
> /* Function has set errno. */
> - done = -1;
> + Xprintf_buffer_mark_failed (buf);
> goto all_done;
> }
> -
> - done_add (function_done);
> break;
> }
> }
> @@ -1450,327 +1316,159 @@ printf_positional (FILE *s, const CHAR_T *format, int readonly_format,
> }
> SETUP_FLOAT128_INFO (specs[nspecs_done].info);
>
> - int function_done
> - = __printf_fp_spec (s, &specs[nspecs_done].info, &ptr);
> - if (function_done < 0)
> - {
> - /* Error in print handler; up to handler to set errno. */
> - done = -1;
> - goto all_done;
> - }
> - done_add (function_done);
> + __printf_fp_spec (buf, &specs[nspecs_done].info, &ptr);
> }
> break;
>
> LABEL (form_unknown):
> {
> - int function_done = printf_unknown (s, &specs[nspecs_done].info);
> -
> - /* 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);
> + printf_unknown (buf, &specs[nspecs_done].info);
> }
> break;
> }
> + while (Xprintf_buffer_has_failed (buf));
>
> /* Write the following constant string. */
> - outstring (specs[nspecs_done].end_of_fmt,
> - specs[nspecs_done].next_fmt
> - - specs[nspecs_done].end_of_fmt);
> + Xprintf_buffer_write (buf,
> + (const CHAR_T *) specs[nspecs_done].end_of_fmt,
> + (specs[nspecs_done].next_fmt
> + - specs[nspecs_done].end_of_fmt));
> }
> all_done:
> scratch_buffer_free (&argsbuf);
> scratch_buffer_free (&specsbuf);
> - return done;
> }
> \f
> /* Handle an unknown format specifier. This prints out a canonicalized
> representation of the format spec itself. */
> -static int
> -printf_unknown (FILE *s, const struct printf_info *info)
> +static void
> +printf_unknown (struct Xprintf_buffer *buf, const struct printf_info *info)
> {
> - int done = 0;
> CHAR_T work_buffer[MAX (sizeof (info->width), sizeof (info->prec)) * 3];
> CHAR_T *const workend
> = &work_buffer[sizeof (work_buffer) / sizeof (CHAR_T)];
> CHAR_T *w;
>
> - outchar (L_('%'));
> + Xprintf_buffer_putc (buf, L_('%'));
>
> if (info->alt)
> - outchar (L_('#'));
> + Xprintf_buffer_putc (buf, L_('#'));
> if (info->group)
> - outchar (L_('\''));
> + Xprintf_buffer_putc (buf, L_('\''));
> if (info->showsign)
> - outchar (L_('+'));
> + Xprintf_buffer_putc (buf, L_('+'));
> else if (info->space)
> - outchar (L_(' '));
> + Xprintf_buffer_putc (buf, L_(' '));
> if (info->left)
> - outchar (L_('-'));
> + Xprintf_buffer_putc (buf, L_('-'));
> if (info->pad == L_('0'))
> - outchar (L_('0'));
> + Xprintf_buffer_putc (buf, L_('0'));
> if (info->i18n)
> - outchar (L_('I'));
> + Xprintf_buffer_putc (buf, L_('I'));
>
> if (info->width != 0)
> {
> w = _itoa_word (info->width, workend, 10, 0);
> while (w < workend)
> - outchar (*w++);
> + Xprintf_buffer_putc (buf, *w++);
> }
>
> if (info->prec != -1)
> {
> - outchar (L_('.'));
> + Xprintf_buffer_putc (buf, L_('.'));
> w = _itoa_word (info->prec, workend, 10, 0);
> while (w < workend)
> - outchar (*w++);
> + Xprintf_buffer_putc (buf, *w++);
> }
>
> if (info->spec != L_('\0'))
> - outchar (info->spec);
> -
> - all_done:
> - return done;
> + Xprintf_buffer_putc (buf, info->spec);
> }
> -\f
> -/* 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 *
> -group_number (CHAR_T *front_ptr, CHAR_T *w, CHAR_T *rear_ptr,
> - const char *grouping, THOUSANDS_SEP_T thousands_sep)
> +
> +static void
> +group_number (struct Xprintf_buffer *buf,
> + struct grouping_iterator *iter,
> + CHAR_T *from, CHAR_T *to, THOUSANDS_SEP_T thousands_sep,
> + bool i18n)
> {
> - /* Length of the current group. */
> - int len;
> -#ifndef COMPILE_WPRINTF
> - /* Length of the separator (in wide mode, the separator is always a
> - single wide character). */
> - int tlen = strlen (thousands_sep);
> + if (!i18n)
> + for (CHAR_T *cp = from; cp != to; ++cp)
> + {
> + if (__grouping_iterator_next (iter))
> + {
> +#ifdef COMPILE_WPRINTF
> + __wprintf_buffer_putc (buf, thousands_sep);
> +#else
> + __printf_buffer_puts (buf, thousands_sep);
> #endif
> -
> - /* We treat all negative values like CHAR_MAX. */
> -
> - if (*grouping == CHAR_MAX || *grouping <= 0)
> - /* No grouping should be done. */
> - return w;
> -
> - len = *grouping++;
> -
> - /* Copy existing string so that nothing gets overwritten. */
> - 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 > front_ptr)
> + }
> + Xprintf_buffer_putc (buf, *cp);
> + }
> + else
> {
> - *--w = *--s;
> -
> - if (--len == 0 && s > front_ptr)
> + /* Apply digit translation and grouping. */
> + for (CHAR_T *cp = from; cp != to; ++cp)
> {
> - /* A new group begins. */
> + if (__grouping_iterator_next (iter))
> + {
> #ifdef COMPILE_WPRINTF
> - if (w != s)
> - *--w = thousands_sep;
> - else
> - /* Not enough room for the separator. */
> - goto copy_rest;
> + __wprintf_buffer_putc (buf, thousands_sep);
> #else
> - int cnt = tlen;
> - 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
> -#if CHAR_MIN < 0
> - || *grouping < 0
> + __printf_buffer_puts (buf, thousands_sep);
> #endif
> - )
> - {
> - copy_rest:
> - /* No further grouping to be done. Copy the rest of the
> - number. */
> - w -= s - front_ptr;
> - memmove (w, front_ptr, (s - front_ptr) * sizeof (CHAR_T));
> - break;
> }
> - else if (*grouping != '\0')
> - len = *grouping++;
> - else
> - /* The previous grouping repeats ad infinitum. */
> - len = grouping[-1];
> - }
> - }
> - return w;
> -}
> -\f
> -/* Helper "class" for `fprintf to unbuffered': creates a temporary buffer. */
> -struct helper_file
> - {
> - struct _IO_FILE_plus _f;
> + int digit = *cp - '0';
> #ifdef COMPILE_WPRINTF
> - struct _IO_wide_data _wide_data;
> -#endif
> - FILE *_put_stream;
> -#ifdef _IO_MTSAFE_IO
> - _IO_lock_t lock;
> -#endif
> - };
> -
> -static int
> -_IO_helper_overflow (FILE *s, int c)
> -{
> - 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)
> - {
> - 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,
> - s->_wide_data->_IO_write_base + written,
> - used - written);
> - s->_wide_data->_IO_write_ptr -= written;
> - }
> + __wprintf_buffer_putc
> + (buf, _NL_CURRENT_WORD (LC_CTYPE,
> + _NL_CTYPE_OUTDIGIT0_WC + digit));
> #else
> - int used = s->_IO_write_ptr - s->_IO_write_base;
> - if (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,
> - used - written);
> - s->_IO_write_ptr -= written;
> - }
> + __printf_buffer_puts
> + (buf, _NL_CURRENT (LC_CTYPE, _NL_CTYPE_OUTDIGIT0_MB + digit));
> #endif
> - return PUTC (c, s);
> + }
> + }
> }
>
> -#ifdef COMPILE_WPRINTF
> -static const struct _IO_jump_t _IO_helper_jumps libio_vtable =
> -{
> - JUMP_INIT_DUMMY,
> - JUMP_INIT (finish, _IO_wdefault_finish),
> - JUMP_INIT (overflow, _IO_helper_overflow),
> - JUMP_INIT (underflow, _IO_default_underflow),
> - JUMP_INIT (uflow, _IO_default_uflow),
> - JUMP_INIT (pbackfail, (_IO_pbackfail_t) _IO_wdefault_pbackfail),
> - JUMP_INIT (xsputn, _IO_wdefault_xsputn),
> - JUMP_INIT (xsgetn, _IO_wdefault_xsgetn),
> - JUMP_INIT (seekoff, _IO_default_seekoff),
> - JUMP_INIT (seekpos, _IO_default_seekpos),
> - JUMP_INIT (setbuf, _IO_default_setbuf),
> - JUMP_INIT (sync, _IO_default_sync),
> - JUMP_INIT (doallocate, _IO_wdefault_doallocate),
> - JUMP_INIT (read, _IO_default_read),
> - JUMP_INIT (write, _IO_default_write),
> - JUMP_INIT (seek, _IO_default_seek),
> - JUMP_INIT (close, _IO_default_close),
> - JUMP_INIT (stat, _IO_default_stat)
> -};
> -#else
> -static const struct _IO_jump_t _IO_helper_jumps libio_vtable =
> -{
> - JUMP_INIT_DUMMY,
> - JUMP_INIT (finish, _IO_default_finish),
> - JUMP_INIT (overflow, _IO_helper_overflow),
> - JUMP_INIT (underflow, _IO_default_underflow),
> - JUMP_INIT (uflow, _IO_default_uflow),
> - JUMP_INIT (pbackfail, _IO_default_pbackfail),
> - JUMP_INIT (xsputn, _IO_default_xsputn),
> - JUMP_INIT (xsgetn, _IO_default_xsgetn),
> - JUMP_INIT (seekoff, _IO_default_seekoff),
> - JUMP_INIT (seekpos, _IO_default_seekpos),
> - JUMP_INIT (setbuf, _IO_default_setbuf),
> - JUMP_INIT (sync, _IO_default_sync),
> - JUMP_INIT (doallocate, _IO_default_doallocate),
> - JUMP_INIT (read, _IO_default_read),
> - JUMP_INIT (write, _IO_default_write),
> - JUMP_INIT (seek, _IO_default_seek),
> - JUMP_INIT (close, _IO_default_close),
> - JUMP_INIT (stat, _IO_default_stat)
> -};
> -#endif
>
The _IO_helper_jumps should be removed from tst-relro-symbols.py required list,
and tst-relro-symbols.py not showing a regression raised some questions. It turned
out that it is not really testing the required symbols due a bug, with the fix below:
diff --git a/elf/tst-relro-symbols.py b/elf/tst-relro-symbols.py
index 368ea3349f..41e87b37ea 100644
--- a/elf/tst-relro-symbols.py
+++ b/elf/tst-relro-symbols.py
@@ -56,10 +56,10 @@ def get_parser():
"""Return an argument parser for this script."""
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('object', help='path to object file to check')
- parser.add_argument('--required', metavar='NAME', default=(),
- help='required symbol names', nargs='*')
- parser.add_argument('--optional', metavar='NAME', default=(),
- help='required symbol names', nargs='*')
+ parser.add_argument('--required', metavar='NAME', action='append',
+ help='required symbol names')
+ parser.add_argument('--optional', metavar='NAME', action='append',
+ help='required symbol names')
return parser
def main(argv):
$ make test t=elf/tst-relro-libc
[...]
FAIL: elf/tst-relro-libc
original exit status 1
[...]/libc.so: error: symbol '_IO_helper_jumps' not found
[...]/libc.so: error: symbol '_IO_strn_jumps' not found
(I will send a fix for this)
> -static int
> -buffered_vfprintf (FILE *s, const CHAR_T *format, va_list args,
> - unsigned int mode_flags)
> +/* The FILE-based function. */
> +int
> +vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
> {
> - CHAR_T buf[BUFSIZ];
> - struct helper_file helper;
> - FILE *hp = (FILE *) &helper._f;
> - int result, to_flush;
> -
> /* Orient the stream. */
> #ifdef ORIENT
> ORIENT;
> #endif
>
> - /* Initialize helper. */
> - helper._put_stream = s;
> -#ifdef COMPILE_WPRINTF
> - hp->_wide_data = &helper._wide_data;
> - _IO_wsetp (hp, buf, buf + sizeof buf / sizeof (CHAR_T));
> - hp->_mode = 1;
> -#else
> - _IO_setp (hp, buf, buf + sizeof buf);
> - hp->_mode = -1;
> -#endif
> - hp->_flags = _IO_MAGIC|_IO_NO_READS|_IO_USER_LOCK;
> -#if _IO_JUMPS_OFFSET
> - hp->_vtable_offset = 0;
> -#endif
> -#ifdef _IO_MTSAFE_IO
> - hp->_lock = NULL;
> + /* Sanity check of arguments. */
> + ARGCHECK (s, format);
> +
> +#ifdef ORIENT
> + /* Check for correct orientation. */
> + if (_IO_vtable_offset (s) == 0
> + && _IO_fwide (s, sizeof (CHAR_T) == 1 ? -1 : 1)
> + != (sizeof (CHAR_T) == 1 ? -1 : 1))
> + /* The stream is already oriented otherwise. */
> + return EOF;
> #endif
> - hp->_flags2 = s->_flags2;
> - _IO_JUMPS (&helper._f) = (struct _IO_jump_t *) &_IO_helper_jumps;
>
> - /* Now print to helper instead. */
> - result = vfprintf (hp, format, args, mode_flags);
> + int done;
>
> /* Lock stream. */
> - __libc_cleanup_region_start (1, (void (*) (void *)) &_IO_funlockfile, s);
> + _IO_cleanup_region_start ((void (*) (void *)) &_IO_funlockfile, s);
> _IO_flockfile (s);
>
> - /* Now flush anything from the helper to the S. */
> -#ifdef COMPILE_WPRINTF
> - if ((to_flush = (hp->_wide_data->_IO_write_ptr
> - - hp->_wide_data->_IO_write_base)) > 0)
> - {
> - if ((int) _IO_sputn (s, hp->_wide_data->_IO_write_base, to_flush)
> - != to_flush)
> - result = -1;
> - }
> -#else
> - if ((to_flush = hp->_IO_write_ptr - hp->_IO_write_base) > 0)
> - {
> - if ((int) _IO_sputn (s, hp->_IO_write_base, to_flush) != to_flush)
> - result = -1;
> - }
> -#endif
> + /* Set up the wrapping buffer. */
> + struct Xprintf (buffer_to_file) wrap;
> + Xprintf (buffer_to_file_init) (&wrap, s);
> +
> + /* Perform the printing operation on the buffer. */
> + Xprintf_buffer (&wrap.base, format, ap, mode_flags);
> + done = Xprintf (buffer_to_file_done) (&wrap);
>
> /* Unlock the stream. */
> _IO_funlockfile (s);
> - __libc_cleanup_region_end (0);
> + _IO_cleanup_region_end (0);
>
> - return result;
> + return done;
> }
Ok.
> diff --git a/stdio-common/vfprintf-process-arg.c b/stdio-common/vfprintf-process-arg.c
> index 4fe369e111..ca6402449d 100644
> --- a/stdio-common/vfprintf-process-arg.c
> +++ b/stdio-common/vfprintf-process-arg.c
> @@ -27,7 +27,7 @@
> now process the wanted format specifier. */
> LABEL (form_percent):
> /* Write a literal "%". */
> - outchar (L_('%'));
> + Xprintf_buffer_putc (buf, L_('%'));
> break;
>
> LABEL (form_integer):
> @@ -116,16 +116,8 @@ LABEL (unsigned_number): /* Unsigned number of base BASE. */
> *--string = L_('0');
> }
> else
> - {
> - /* Put the number in WORK. */
> - string = _itoa (number.longlong, workend, base,
> - spec == L_('X'));
> - if (group && grouping)
> - string = group_number (work_buffer, string, workend,
> - grouping, thousands_sep);
> - if (use_outdigits && base == 10)
> - string = _i18n_number_rewrite (string, workend, workend);
> - }
> + /* Put the number in WORK. */
> + string = _itoa (number.longlong, workend, base, spec == L_('X'));
> /* Simplify further test for num != 0. */
> number.word = number.longlong != 0;
> }
> @@ -159,27 +151,46 @@ LABEL (unsigned_number): /* Unsigned number of base BASE. */
> *--string = L_('0');
> }
> else
> - {
> - /* Put the number in WORK. */
> - string = _itoa_word (number.word, workend, base,
> - spec == L_('X'));
> - if (group && grouping)
> - string = group_number (work_buffer, string, workend,
> - grouping, thousands_sep);
> - if (use_outdigits && base == 10)
> - string = _i18n_number_rewrite (string, workend, workend);
> - }
> + /* Put the number in WORK. */
> + string = _itoa_word (number.word, workend, base,
> + spec == L_('X'));
> }
>
> - if (prec <= workend - string && number.word != 0 && alt && base == 8)
> - /* Add octal marker. */
> - *--string = L_('0');
> + /* Grouping is also used for outdigits translation. */
> + struct grouping_iterator iter;
> + bool number_slow_path = group || (use_outdigits && base == 10);
> + if (group)
> + __grouping_iterator_init (&iter, LC_NUMERIC, _NL_CURRENT_LOCALE,
> + workend - string);
> + else if (use_outdigits && base == 10)
> + __grouping_iterator_init_none (&iter, workend - string);
> +
> + int number_length;
> +#ifndef COMPILE_WPRINTF
> + if (use_outdigits && base == 10)
> + number_length = __translated_number_width (_NL_CURRENT_LOCALE,
> + string, workend);
> + else
> + number_length = workend - string;
> + if (group)
> + number_length += iter.separators * strlen (thousands_sep);
> +#else
> + number_length = workend - string;
> + /* All wide separators have length 1. */
> + if (group && thousands_sep != L'\0')
> + number_length += iter.separators;
> +#endif
> +
> + /* The marker comes right before the number, but is not subject
> + to grouping. */
> + bool octal_marker = (prec <= number_length && number.word != 0
> + && alt && base == 8);
>
> prec = MAX (0, prec - (workend - string));
>
> if (!left)
> {
> - width -= workend - string + prec;
> + width -= number_length + prec;
>
> if (number.word != 0 && alt && (base == 16 || base == 2))
> /* Account for 0X, 0x, 0B or 0b hex or binary marker. */
> @@ -190,27 +201,34 @@ LABEL (unsigned_number): /* Unsigned number of base BASE. */
>
> if (pad == L_(' '))
> {
> - PAD (L_(' '));
> + Xprintf_buffer_pad (buf, L_(' '), width);
> width = 0;
> }
>
> if (is_negative)
> - outchar (L_('-'));
> + Xprintf_buffer_putc (buf, L_('-'));
> else if (showsign)
> - outchar (L_('+'));
> + Xprintf_buffer_putc (buf, L_('+'));
> else if (space)
> - outchar (L_(' '));
> + Xprintf_buffer_putc (buf, L_(' '));
>
> if (number.word != 0 && alt && (base == 16 || base == 2))
> {
> - outchar (L_('0'));
> - outchar (spec);
> + Xprintf_buffer_putc (buf, L_('0'));
> + Xprintf_buffer_putc (buf, spec);
> }
>
> width += prec;
> - PAD (L_('0'));
> + Xprintf_buffer_pad (buf, L_('0'), width);
> +
> + if (octal_marker)
> + Xprintf_buffer_putc (buf, L_('0'));
>
> - outstring (string, workend - string);
> + if (number_slow_path)
> + group_number (buf, &iter, string, workend, thousands_sep,
> + use_outdigits && base == 10);
> + else
> + Xprintf_buffer_write (buf, string, workend - string);
>
> break;
> }
> @@ -218,40 +236,41 @@ LABEL (unsigned_number): /* Unsigned number of base BASE. */
> {
> if (is_negative)
> {
> - outchar (L_('-'));
> + Xprintf_buffer_putc (buf, L_('-'));
> --width;
> }
> else if (showsign)
> {
> - outchar (L_('+'));
> + Xprintf_buffer_putc (buf, L_('+'));
> --width;
> }
> else if (space)
> {
> - outchar (L_(' '));
> + Xprintf_buffer_putc (buf, L_(' '));
> --width;
> }
>
> if (number.word != 0 && alt && (base == 16 || base == 2))
> {
> - outchar (L_('0'));
> - outchar (spec);
> + Xprintf_buffer_putc (buf, L_('0'));
> + Xprintf_buffer_putc (buf, spec);
> width -= 2;
> }
>
> width -= workend - string + prec;
>
> - if (prec > 0)
> - {
> - int temp = width;
> - width = prec;
> - PAD (L_('0'));
> - width = temp;
> - }
> + Xprintf_buffer_pad (buf, L_('0'), prec);
> +
> + if (octal_marker)
> + Xprintf_buffer_putc (buf, L_('0'));
>
> - outstring (string, workend - string);
> + if (number_slow_path)
> + group_number (buf, &iter, string, workend, thousands_sep,
> + use_outdigits && base == 10);
> + else
> + Xprintf_buffer_write (buf, string, workend - string);
>
> - PAD (L_(' '));
> + Xprintf_buffer_pad (buf, L_(' '), width);
> break;
> }
>
> @@ -300,16 +319,17 @@ LABEL (form_number):
> }
> /* Answer the count of characters written. */
> void *ptrptr = process_arg_pointer ();
> + unsigned int written = Xprintf_buffer_done (buf);
> if (is_longlong)
> - *(long long int *) ptrptr = done;
> + *(long long int *) ptrptr = written;
> else if (is_long_num)
> - *(long int *) ptrptr = done;
> + *(long int *) ptrptr = written;
> else if (is_char)
> - *(char *) ptrptr = done;
> + *(char *) ptrptr = written;
> else if (!is_short)
> - *(int *) ptrptr = done;
> + *(int *) ptrptr = written;
> else
> - *(short int *) ptrptr = done;
> + *(short int *) ptrptr = written;
> break;
>
> LABEL (form_strerror):
> @@ -341,14 +361,16 @@ LABEL (form_character):
> goto LABEL (form_wcharacter);
> --width; /* Account for the character itself. */
> if (!left)
> - PAD (L_(' '));
> + Xprintf_buffer_pad (buf, L_(' '), width);
> #ifdef COMPILE_WPRINTF
> - outchar (__btowc ((unsigned char) process_arg_int ())); /* Promoted. */
> + __wprintf_buffer_putc (buf, __btowc ((unsigned char) /* Promoted. */
> + process_arg_int ()));
> #else
> - outchar ((unsigned char) process_arg_int ()); /* Promoted. */
> + __printf_buffer_putc (buf, (unsigned char) /* Promoted. */
> + process_arg_int ());
> #endif
> if (left)
> - PAD (L_(' '));
> + Xprintf_buffer_pad (buf, L_(' '), width);
> break;
>
> LABEL (form_string):
> @@ -382,10 +404,8 @@ LABEL (form_string):
> else if (!is_long && spec != L_('S'))
> {
> #ifdef COMPILE_WPRINTF
> - done = outstring_converted_wide_string
> - (s, (const char *) string, prec, width, left, done);
> - if (done < 0)
> - goto all_done;
> + outstring_converted_wide_string (buf, (const char *) string,
> + prec, width, left);
> /* The padding has already been written. */
> break;
> #else
> @@ -407,10 +427,8 @@ LABEL (form_string):
> else
> len = __wcslen (string);
> #else
> - done = outstring_converted_wide_string
> - (s, (const wchar_t *) string, prec, width, left, done);
> - if (done < 0)
> - goto all_done;
> + outstring_converted_wide_string (buf, (const wchar_t *) string,
> + prec, width, left);
> /* The padding has already been written. */
> break;
> #endif
> @@ -418,15 +436,15 @@ LABEL (form_string):
>
> if ((width -= len) < 0)
> {
> - outstring (string, len);
> + Xprintf_buffer_write (buf, string, len);
> break;
> }
>
> if (!left)
> - PAD (L_(' '));
> - outstring (string, len);
> + Xprintf_buffer_pad (buf, L_(' '), width);
> + Xprintf_buffer_write (buf, string, len);
> if (left)
> - PAD (L_(' '));
> + Xprintf_buffer_pad (buf, L_(' '), width);
> }
> break;
>
> @@ -436,10 +454,10 @@ LABEL (form_wcharacter):
> /* Wide character. */
> --width;
> if (!left)
> - PAD (L' ');
> - outchar (process_arg_wchar_t ());
> + Xprintf_buffer_pad (buf, L_(' '), width);
> + Xprintf_buffer_putc (buf, process_arg_wchar_t ());
> if (left)
> - PAD (L' ');
> + Xprintf_buffer_pad (buf, L_(' '), width);
> }
> break;
>
> @@ -447,24 +465,24 @@ LABEL (form_wcharacter):
> LABEL (form_wcharacter):
> {
> /* Wide character. */
> - char buf[MB_LEN_MAX];
> + char wcbuf[MB_LEN_MAX];
> mbstate_t mbstate;
> size_t len;
>
> memset (&mbstate, '\0', sizeof (mbstate_t));
> - len = __wcrtomb (buf, process_arg_wchar_t (), &mbstate);
> + len = __wcrtomb (wcbuf, process_arg_wchar_t (), &mbstate);
> if (len == (size_t) -1)
> {
> /* Something went wrong during the conversion. Bail out. */
> - done = -1;
> + __printf_buffer_mark_failed (buf);
> goto all_done;
> }
> width -= len;
> if (!left)
> - PAD (' ');
> - outstring (buf, len);
> + Xprintf_buffer_pad (buf, L_(' '), width);
> + Xprintf_buffer_write (buf, wcbuf, len);
> if (left)
> - PAD (' ');
> + Xprintf_buffer_pad (buf, L_(' '), width);
> }
> break;
> #endif /* !COMPILE_WPRINTF */
Ok.
> diff --git a/stdlib/strfmon_l.c b/stdlib/strfmon_l.c
> index d9b22088c7..6dc36e07cc 100644
> --- a/stdlib/strfmon_l.c
> +++ b/stdlib/strfmon_l.c
> @@ -29,33 +29,8 @@
> #include <string.h>
> #include "../locale/localeinfo.h"
> #include <bits/floatn.h>
> -
> -
> -#define out_char(Ch) \
> - do { \
> - if (dest >= s + maxsize - 1) \
> - { \
> - __set_errno (E2BIG); \
> - va_end (ap); \
> - return -1; \
> - } \
> - *dest++ = (Ch); \
> - } while (0)
> -
> -#define out_string(String) \
> - do { \
> - const char *_s = (String); \
> - while (*_s) \
> - out_char (*_s++); \
> - } while (0)
> -
> -#define out_nstring(String, N) \
> - do { \
> - int _n = (N); \
> - const char *_s = (String); \
> - while (_n-- > 0) \
> - out_char (*_s++); \
> - } while (0)
> +#include <stdio-common/grouping_iterator.h>
> +#include <printf_buffer.h>
>
> #define to_digit(Ch) ((Ch) - '0')
>
> @@ -75,21 +50,15 @@
> some information in the LC_MONETARY category which should be used,
> too. Some of the information contradicts the information which can
> be specified in format string. */
> -ssize_t
> -__vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
> - const char *format, va_list ap, unsigned int flags)
> +static void
> +__vstrfmon_l_buffer (struct __printf_buffer *buf, locale_t loc,
> + const char *fmt, va_list ap, unsigned int flags)
> {
> struct __locale_data *current = loc->__locales[LC_MONETARY];
> - _IO_strfile f;
> struct printf_info info;
> - char *dest; /* Pointer so copy the output. */
> - const char *fmt; /* Pointer that walks through format. */
> -
> - dest = s;
> - fmt = format;
>
> /* Loop through the format-string. */
> - while (*fmt != '\0')
> + while (*fmt != '\0' && !__printf_buffer_has_failed (buf))
> {
> /* The floating-point value to output. */
> union
> @@ -122,11 +91,9 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
> int other_cs_precedes;
> const char *sign_string;
> const char *other_sign_string;
> - int done;
> const char *currency_symbol;
> size_t currency_symbol_len;
> long int width;
> - char *startp;
> const void *ptr;
> char space_char;
>
> @@ -134,14 +101,14 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
> specification. */
> if (*fmt != '%')
> {
> - out_char (*fmt++);
> + __printf_buffer_putc (buf, *fmt++);
> continue;
> }
>
> /* "%%" means a single '%' character. */
> if (fmt[1] == '%')
> {
> - out_char (*++fmt);
> + __printf_buffer_putc (buf, *++fmt);
> ++fmt;
> continue;
> }
> @@ -171,7 +138,8 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
> {
> /* Premature EOS. */
> __set_errno (EINVAL);
> - return -1;
> + __printf_buffer_mark_failed (buf);
> + return;
> }
> continue;
> case '^': /* Don't group digits. */
> @@ -181,7 +149,8 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
> if (n_sign_posn != -2)
> {
> __set_errno (EINVAL);
> - return -1;
> + __printf_buffer_mark_failed (buf);
> + return;
> }
> p_sign_posn = *_NL_CURRENT (LC_MONETARY, P_SIGN_POSN);
> n_sign_posn = *_NL_CURRENT (LC_MONETARY, N_SIGN_POSN);
> @@ -190,7 +159,8 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
> if (n_sign_posn != -2)
> {
> __set_errno (EINVAL);
> - return -1;
> + __printf_buffer_mark_failed (buf);
> + return;
> }
> p_sign_posn = 0;
> n_sign_posn = 0;
> @@ -220,19 +190,12 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
> || (width == LONG_MAX && val > LONG_MAX % 10))
> {
> __set_errno (E2BIG);
> - return -1;
> + __printf_buffer_mark_failed (buf);
> + return;
> }
>
> width = width * 10 + val;
> }
> -
> - /* If we don't have enough room for the demanded width we
> - can stop now and return an error. */
> - if (width >= maxsize - (dest - s))
> - {
> - __set_errno (E2BIG);
> - return -1;
> - }
> }
>
> /* Recognize left precision. */
> @@ -241,7 +204,8 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
> if (!isdigit (*++fmt))
> {
> __set_errno (EINVAL);
> - return -1;
> + __printf_buffer_mark_failed (buf);
> + return;
> }
> left_prec = to_digit (*fmt);
>
> @@ -258,7 +222,8 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
> if (!isdigit (*++fmt))
> {
> __set_errno (EINVAL);
> - return -1;
> + __printf_buffer_mark_failed (buf);
> + return;
> }
> right_prec = to_digit (*fmt);
>
> @@ -306,7 +271,8 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
> break;
> default: /* Any unrecognized format is an error. */
> __set_errno (EINVAL);
> - return -1;
> + __printf_buffer_mark_failed (buf);
> + return;
> }
>
> /* If not specified by the format string now find the values for
> @@ -327,8 +293,11 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
> /* If we have to print the digits grouped determine how many
> extra characters this means. */
> if (group && left_prec != -1)
> - left_prec += __guess_grouping (left_prec,
> - _NL_CURRENT (LC_MONETARY, MON_GROUPING));
> + {
> + struct grouping_iterator it;
> + __grouping_iterator_init (&it, LC_MONETARY, loc, left_prec);
> + left_prec += it.separators;
> + }
>
> /* Now it's time to get the value. */
> if (is_long_double == 1)
> @@ -482,57 +451,46 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
> #define left_paren '('
> #define right_paren ')'
>
> - startp = dest; /* Remember start so we can compute length. */
> + char *startp = buf->write_ptr;
>
> - while (left_pad-- > 0)
> - out_char (' ');
> + __printf_buffer_pad (buf, ' ', left_pad);
>
> if (sign_posn == 0 && is_negative)
> - out_char (left_paren);
> + __printf_buffer_putc (buf, left_paren);
>
> if (cs_precedes)
> {
> if (sign_posn != 0 && sign_posn != 2 && sign_posn != 4
> && sign_posn != 5)
> {
> - out_string (sign_string);
> + __printf_buffer_puts (buf, sign_string);
> if (sep_by_space == 2)
> - out_char (' ');
> + __printf_buffer_putc (buf, ' ');
> }
>
> if (print_curr_symbol)
> - out_string (currency_symbol);
> + __printf_buffer_puts (buf, currency_symbol);
>
> if (sign_posn == 4)
> {
> if (print_curr_symbol && sep_by_space == 2)
> - out_char (space_char);
> - out_string (sign_string);
> + __printf_buffer_putc (buf, space_char);
> + __printf_buffer_puts (buf, sign_string);
> if (sep_by_space == 1)
> /* POSIX.2 and SUS are not clear on this case, but C99
> says a space follows the adjacent-symbol-and-sign */
> - out_char (' ');
> + __printf_buffer_putc (buf, ' ');
> }
> else
> if (print_curr_symbol && sep_by_space == 1)
> - out_char (space_char);
> + __printf_buffer_putc (buf, space_char);
> }
> else
> if (sign_posn != 0 && sign_posn != 2 && sign_posn != 3
> && sign_posn != 4 && sign_posn != 5)
> - out_string (sign_string);
> + __printf_buffer_puts (buf, sign_string);
>
> /* Print the number. */
> -#ifdef _IO_MTSAFE_IO
> - f._sbf._f._lock = NULL;
> -#endif
> - _IO_init_internal (&f._sbf._f, 0);
> - _IO_JUMPS (&f._sbf) = &_IO_str_jumps;
> - _IO_str_init_static_internal (&f, dest, (s + maxsize) - dest, dest);
> - /* We clear the last available byte so we can find out whether
> - the numeric representation is too long. */
> - s[maxsize - 1] = '\0';
> -
> memset (&info, '\0', sizeof (info));
> info.prec = right_prec;
> info.width = left_prec + (right_prec ? (right_prec + 1) : 0);
> @@ -544,25 +502,17 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
> info.extra = 1; /* This means use values from LC_MONETARY. */
>
> ptr = &fpnum;
> - done = __printf_fp_l (&f._sbf._f, loc, &info, &ptr);
> - if (done < 0)
> - return -1;
> -
> - if (s[maxsize - 1] != '\0')
> - {
> - __set_errno (E2BIG);
> - return -1;
> - }
> -
> - dest += done;
> + __printf_fp_l_buffer (buf, loc, &info, &ptr);
> + if (__printf_buffer_has_failed (buf))
> + return;
>
> if (!cs_precedes)
> {
> if (sign_posn == 3)
> {
> if (sep_by_space == 1)
> - out_char (' ');
> - out_string (sign_string);
> + __printf_buffer_putc (buf, ' ');
> + __printf_buffer_puts (buf, sign_string);
> }
>
> if (print_curr_symbol)
> @@ -572,55 +522,61 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
> || (sign_posn == 2 && sep_by_space == 1)
> || (sign_posn == 1 && sep_by_space == 1)
> || (sign_posn == 0 && sep_by_space == 1))
> - out_char (space_char);
> - out_nstring (currency_symbol, currency_symbol_len);
> + __printf_buffer_putc (buf, space_char);
> + __printf_buffer_write (buf, currency_symbol,
> + __strnlen (currency_symbol,
> + currency_symbol_len));
> }
>
> if (sign_posn == 4)
> {
> if (sep_by_space == 2)
> - out_char (' ');
> - out_string (sign_string);
> + __printf_buffer_putc (buf, ' ');
> + __printf_buffer_puts (buf, sign_string);
> }
> }
>
> if (sign_posn == 2)
> {
> if (sep_by_space == 2)
> - out_char (' ');
> - out_string (sign_string);
> + __printf_buffer_putc (buf, ' ');
> + __printf_buffer_puts (buf, sign_string);
> }
>
> if (sign_posn == 0 && is_negative)
> - out_char (right_paren);
> + __printf_buffer_putc (buf, right_paren);
>
> /* Now test whether the output width is filled. */
> - if (dest - startp < width)
> + if (buf->write_ptr - startp < width)
> {
> - if (left)
> - /* We simply have to fill using spaces. */
> - do
> - out_char (' ');
> - while (dest - startp < width);
> - else
> + size_t pad_width = width - (buf->write_ptr - startp);
> + __printf_buffer_pad (buf, ' ', pad_width);
> + if (__printf_buffer_has_failed (buf))
> + /* Implies length check. */
> + return;
> + /* Left padding is already in the correct position.
> + Otherwise move the field contents in place. */
> + if (!left)
> {
> - long int dist = width - (dest - startp);
> - for (char *cp = dest - 1; cp >= startp; --cp)
> - cp[dist] = cp[0];
> -
> - dest += dist;
> -
> - do
> - startp[--dist] = ' ';
> - while (dist > 0);
> + memmove (startp + pad_width, startp, buf->write_ptr - startp);
> + memset (startp, ' ', pad_width);
> }
> }
> }
> +}
>
> - /* Terminate the string. */
> - *dest = '\0';
> -
> - return dest - s;
> +ssize_t
> +__vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
> + const char *format, va_list ap, unsigned int flags)
> +{
> + struct __printf_buffer buf;
> + __printf_buffer_init (&buf, s, maxsize, __printf_buffer_mode_strfmon);
> + __vstrfmon_l_buffer (&buf, loc, format, ap, flags);
> + __printf_buffer_putc (&buf, '\0'); /* Terminate the string. */
> + if (__printf_buffer_has_failed (&buf))
> + return -1;
> + else
> + return buf.write_ptr - buf.write_base - 1; /* Exclude NUL byte. */
> }
>
> ssize_t
Ok.
> diff --git a/stdlib/strfrom-skeleton.c b/stdlib/strfrom-skeleton.c
> index 36e9adcad5..810eb315d0 100644
> --- a/stdlib/strfrom-skeleton.c
> +++ b/stdlib/strfrom-skeleton.c
> @@ -28,6 +28,7 @@
> #include <string.h>
> #include <locale/localeinfo.h>
> #include <fix-float-double-convert-nan.h>
> +#include <printf_buffer.h>
>
> #define UCHAR_T char
> #define L_(Str) Str
> @@ -37,12 +38,7 @@
> int
> STRFROM (char *dest, size_t size, const char *format, FLOAT f)
> {
> - _IO_strnfile sfile;
> -#ifdef _IO_MTSAFE_IO
> - sfile.f._sbf._f._lock = NULL;
> -#endif
> -
> - int done;
> + struct __printf_buffer_snprintf buf;
>
> /* Single-precision values need to be stored in a double type, because
> __printf_fp_l and __printf_fphex do not accept the float type. */
> @@ -106,23 +102,8 @@ STRFROM (char *dest, size_t size, const char *format, FLOAT f)
> abort ();
> }
>
> - /* The following code to prepare the virtual file has been adapted from the
> - function __vsnprintf_internal from libio. */
> -
> - if (size == 0)
> - {
> - /* When size is zero, nothing is written and dest may be a null pointer.
> - This is specified for snprintf in ISO/IEC 9899:2011, Section 7.21.6.5,
> - in the second paragraph. Thus, if size is zero, prepare to use the
> - overflow buffer right from the start. */
> - dest = sfile.overflow_buf;
> - size = sizeof (sfile.overflow_buf);
> - }
> -
> - /* Prepare the virtual string file. */
> - _IO_no_init (&sfile.f._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
> - _IO_JUMPS (&sfile.f._sbf) = &_IO_strn_jumps;
> - _IO_str_init_static_internal (&sfile.f, dest, size - 1, dest);
> + /* Prepare the string buffer. */
> + __printf_buffer_snprintf_init (&buf, dest, size);
>
> /* Prepare the format specification for printf_fp. */
> memset (&info, '\0', sizeof (info));
> @@ -144,13 +125,8 @@ STRFROM (char *dest, size_t size, const char *format, FLOAT f)
> info.spec = specifier;
>
> if (info.spec != 'a' && info.spec != 'A')
> - done = __printf_fp_l (&sfile.f._sbf._f, _NL_CURRENT_LOCALE, &info, &fpptr);
> + __printf_fp_l_buffer (&buf.base, _NL_CURRENT_LOCALE, &info, &fpptr);
> else
> - done = __printf_fphex (&sfile.f._sbf._f, &info, &fpptr);
> -
> - /* Terminate the string. */
> - if (sfile.f._sbf._f._IO_buf_base != sfile.overflow_buf)
> - *sfile.f._sbf._f._IO_write_ptr = '\0';
> -
> - return done;
> + __printf_fphex_l_buffer (&buf.base, _NL_CURRENT_LOCALE, &info, &fpptr);
> + return __printf_buffer_snprintf_done (&buf);
> }
Ok.
> diff --git a/sysdeps/ia64/fpu/printf_fphex.c b/sysdeps/ia64/fpu/printf_fphex.c
> index e7c3109510..91347b7e3d 100644
> --- a/sysdeps/ia64/fpu/printf_fphex.c
> +++ b/sysdeps/ia64/fpu/printf_fphex.c
> @@ -35,16 +35,10 @@ do { \
> \
> numstr = _itoa_word (num, numbuf + sizeof numbuf, 16, \
> info->spec == 'A'); \
> - wnumstr = _itowa_word (num, \
> - wnumbuf + sizeof (wnumbuf) / sizeof (wchar_t), \
> - 16, info->spec == 'A'); \
> \
> /* Fill with zeroes. */ \
> while (numstr > numbuf + (sizeof numbuf - 64 / 4)) \
> - { \
> - *--numstr = '0'; \
> - *--wnumstr = L'0'; \
> - } \
> + *--numstr = '0'; \
> \
> /* We use a full nibble for the leading digit. */ \
> leading = *numstr++; \
Ok.
> diff --git a/sysdeps/ieee754/ldbl-128/printf_fphex_macros.h b/sysdeps/ieee754/ldbl-128/printf_fphex_macros.h
> index 28dccb1170..bc712a2527 100644
> --- a/sysdeps/ieee754/ldbl-128/printf_fphex_macros.h
> +++ b/sysdeps/ieee754/ldbl-128/printf_fphex_macros.h
> @@ -36,45 +36,23 @@ do { \
> zero_mantissa = (num0|num1) == 0; \
> \
> if (sizeof (unsigned long int) > 6) \
> - { \
> - numstr = _itoa_word (num1, numbuf + sizeof numbuf, 16, \
> - info->spec == 'A'); \
> - wnumstr = _itowa_word (num1, \
> - wnumbuf + sizeof (wnumbuf) / sizeof (wchar_t),\
> - 16, info->spec == 'A'); \
> - } \
> + numstr = _itoa_word (num1, numbuf + sizeof numbuf, 16, \
> + info->spec == 'A'); \
> else \
> - { \
> - numstr = _itoa (num1, numbuf + sizeof numbuf, 16, \
> - info->spec == 'A'); \
> - wnumstr = _itowa (num1, \
> - wnumbuf + sizeof (wnumbuf) / sizeof (wchar_t), \
> - 16, info->spec == 'A'); \
> - } \
> + numstr = _itoa (num1, numbuf + sizeof numbuf, 16, \
> + info->spec == 'A'); \
> \
> while (numstr > numbuf + (sizeof numbuf - 64 / 4)) \
> - { \
> - *--numstr = '0'; \
> - *--wnumstr = L'0'; \
> - } \
> + *--numstr = '0'; \
> \
> if (sizeof (unsigned long int) > 6) \
> - { \
> - numstr = _itoa_word (num0, numstr, 16, info->spec == 'A'); \
> - wnumstr = _itowa_word (num0, wnumstr, 16, info->spec == 'A'); \
> - } \
> + numstr = _itoa_word (num0, numstr, 16, info->spec == 'A'); \
> else \
> - { \
> - numstr = _itoa (num0, numstr, 16, info->spec == 'A'); \
> - wnumstr = _itowa (num0, wnumstr, 16, info->spec == 'A'); \
> - } \
> + numstr = _itoa (num0, numstr, 16, info->spec == 'A'); \
> \
> /* Fill with zeroes. */ \
> while (numstr > numbuf + (sizeof numbuf - 112 / 4)) \
> - { \
> *--numstr = '0'; \
> - *--wnumstr = L'0'; \
> - } \
> \
> leading = u.ieee.exponent == 0 ? '0' : '1'; \
> \
Ok.
> diff --git a/sysdeps/ieee754/ldbl-128ibm/printf_fphex.c b/sysdeps/ieee754/ldbl-128ibm/printf_fphex.c
> index 58733f85e5..a06cbe7cb6 100644
> --- a/sysdeps/ieee754/ldbl-128ibm/printf_fphex.c
> +++ b/sysdeps/ieee754/ldbl-128ibm/printf_fphex.c
> @@ -68,45 +68,23 @@ do { \
> zero_mantissa = (num0|num1) == 0; \
> \
> if (sizeof (unsigned long int) > 6) \
> - { \
> - numstr = _itoa_word (num1, numbuf + sizeof numbuf, 16, \
> - info->spec == 'A'); \
> - wnumstr = _itowa_word (num1, \
> - wnumbuf + sizeof (wnumbuf) / sizeof (wchar_t),\
> - 16, info->spec == 'A'); \
> - } \
> + numstr = _itoa_word (num1, numbuf + sizeof numbuf, 16, \
> + info->spec == 'A'); \
> else \
> - { \
> - numstr = _itoa (num1, numbuf + sizeof numbuf, 16, \
> + numstr = _itoa (num1, numbuf + sizeof numbuf, 16, \
> info->spec == 'A'); \
> - wnumstr = _itowa (num1, \
> - wnumbuf + sizeof (wnumbuf) / sizeof (wchar_t), \
> - 16, info->spec == 'A'); \
> - } \
> \
> while (numstr > numbuf + (sizeof numbuf - 64 / 4)) \
> - { \
> - *--numstr = '0'; \
> - *--wnumstr = L'0'; \
> - } \
> + *--numstr = '0'; \
> \
> if (sizeof (unsigned long int) > 6) \
> - { \
> - numstr = _itoa_word (num0, numstr, 16, info->spec == 'A'); \
> - wnumstr = _itowa_word (num0, wnumstr, 16, info->spec == 'A'); \
> - } \
> + numstr = _itoa_word (num0, numstr, 16, info->spec == 'A'); \
> else \
> - { \
> - numstr = _itoa (num0, numstr, 16, info->spec == 'A'); \
> - wnumstr = _itowa (num0, wnumstr, 16, info->spec == 'A'); \
> - } \
> + numstr = _itoa (num0, numstr, 16, info->spec == 'A'); \
> \
> /* Fill with zeroes. */ \
> while (numstr > numbuf + (sizeof numbuf - 112 / 4)) \
> - { \
> - *--numstr = '0'; \
> - *--wnumstr = L'0'; \
> - } \
> + *--numstr = '0'; \
> \
> leading = u.d[0].ieee.exponent == 0 ? '0' : '1'; \
> \
Ok.
> diff --git a/sysdeps/ieee754/ldbl-96/printf_fphex.c b/sysdeps/ieee754/ldbl-96/printf_fphex.c
> index 26bc60682e..1f09b5f08b 100644
> --- a/sysdeps/ieee754/ldbl-96/printf_fphex.c
> +++ b/sysdeps/ieee754/ldbl-96/printf_fphex.c
> @@ -36,31 +36,17 @@ do { \
> zero_mantissa = num == 0; \
> \
> if (sizeof (unsigned long int) > 6) \
> - { \
> - numstr = _itoa_word (num, numbuf + sizeof numbuf, 16, \
> - info->spec == 'A'); \
> - wnumstr = _itowa_word (num, \
> - wnumbuf + sizeof (wnumbuf) / sizeof (wchar_t),\
> - 16, info->spec == 'A'); \
> - } \
> + numstr = _itoa_word (num, numbuf + sizeof numbuf, 16, \
> + info->spec == 'A'); \
> else \
> - { \
> - numstr = _itoa (num, numbuf + sizeof numbuf, 16, info->spec == 'A');\
> - wnumstr = _itowa (num, \
> - wnumbuf + sizeof (wnumbuf) / sizeof (wchar_t), \
> - 16, info->spec == 'A'); \
> - } \
> + numstr = _itoa (num, numbuf + sizeof numbuf, 16, info->spec == 'A'); \
> \
> /* Fill with zeroes. */ \
> while (numstr > numbuf + (sizeof numbuf - 64 / 4)) \
> - { \
> - *--numstr = '0'; \
> - *--wnumstr = L'0'; \
> - } \
> + *--numstr = '0'; \
> \
> /* We use a full nibble for the leading digit. */ \
> leading = *numstr++; \
> - wnumstr++; \
> \
> /* We have 3 bits from the mantissa in the leading nibble. \
> Therefore we are here using `IEEE854_LONG_DOUBLE_BIAS + 3'. */ \
Ok.
> diff --git a/sysdeps/x86_64/fpu/printf_fphex.c b/sysdeps/x86_64/fpu/printf_fphex.c
> index d2de75d4ac..bb702ec7c5 100644
> --- a/sysdeps/x86_64/fpu/printf_fphex.c
> +++ b/sysdeps/x86_64/fpu/printf_fphex.c
> @@ -34,31 +34,16 @@ do { \
> zero_mantissa = num == 0; \
> \
> if (sizeof (unsigned long int) > 6) \
> - { \
> - numstr = _itoa_word (num, numbuf + sizeof numbuf, 16, \
> - info->spec == 'A'); \
> - wnumstr = _itowa_word (num, \
> - wnumbuf + sizeof (wnumbuf) / sizeof (wchar_t),\
> - 16, info->spec == 'A'); \
> - } \
> + numstr = _itoa_word (num, numbuf + sizeof numbuf, 16, info->spec == 'A');\
> else \
> - { \
> - numstr = _itoa (num, numbuf + sizeof numbuf, 16, info->spec == 'A');\
> - wnumstr = _itowa (num, \
> - wnumbuf + sizeof (wnumbuf) / sizeof (wchar_t), \
> - 16, info->spec == 'A'); \
> - } \
> + numstr = _itoa (num, numbuf + sizeof numbuf, 16, info->spec == 'A'); \
> \
> /* Fill with zeroes. */ \
> while (numstr > numbuf + (sizeof numbuf - 64 / 4)) \
> - { \
> - *--numstr = '0'; \
> - *--wnumstr = L'0'; \
> - } \
> + *--numstr = '0'; \
> \
> /* We use a full nibble for the leading digit. */ \
> leading = *numstr++; \
> - wnumstr++; \
> \
> /* We have 3 bits from the mantissa in the leading nibble. \
> Therefore we are here using `IEEE854_LONG_DOUBLE_BIAS + 3'. */ \
Ok.
next prev parent reply other threads:[~2022-12-14 20:54 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-12-12 15:22 [PATCH v5 00/11] vfprintf refactor Florian Weimer
2022-12-12 15:22 ` [PATCH v5 01/11] locale: Implement struct grouping_iterator Florian Weimer
2022-12-12 15:22 ` [PATCH v5 02/11] stdio-common: Introduce buffers for implementing printf Florian Weimer
2022-12-14 14:10 ` Adhemerval Zanella Netto
2022-12-16 17:51 ` Florian Weimer
2022-12-12 15:23 ` [PATCH v5 03/11] stdio-common: Add __printf_function_invoke Florian Weimer
2022-12-14 14:28 ` Adhemerval Zanella Netto
2022-12-12 15:23 ` [PATCH v5 04/11] stdio-common: Add __translated_number_width Florian Weimer
2022-12-14 14:38 ` Adhemerval Zanella Netto
2022-12-12 15:23 ` [PATCH v5 05/11] stdio-common: Convert vfprintf and related functions to buffers Florian Weimer
2022-12-14 20:54 ` Adhemerval Zanella Netto [this message]
2022-12-16 17:58 ` Florian Weimer
2022-12-12 15:23 ` [PATCH v5 06/11] stdio-common: Add lock optimization to vfprintf and vfwprintf Florian Weimer
2022-12-15 15:58 ` Adhemerval Zanella Netto
2022-12-12 15:23 ` [PATCH v5 07/11] libio: Convert __vsprintf_internal to buffers Florian Weimer
2022-12-15 18:16 ` Adhemerval Zanella Netto
2022-12-16 18:07 ` Florian Weimer
2022-12-12 15:23 ` [PATCH v5 08/11] libio: Convert __vasprintf_internal " Florian Weimer
2022-12-15 18:40 ` Adhemerval Zanella Netto
2022-12-16 18:22 ` Florian Weimer
2022-12-12 15:23 ` [PATCH v5 09/11] libio: Convert __vdprintf_internal " Florian Weimer
2022-12-15 19:11 ` Adhemerval Zanella Netto
2022-12-12 15:24 ` [PATCH v5 10/11] libio: Convert __obstack_vprintf_internal to buffers (bug 27124) Florian Weimer
2022-12-15 19:14 ` Adhemerval Zanella Netto
2022-12-16 18:25 ` Florian Weimer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=32c237bd-1f1d-671c-71df-99005dea6746@linaro.org \
--to=adhemerval.zanella@linaro.org \
--cc=fweimer@redhat.com \
--cc=libc-alpha@sourceware.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).