From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-oa1-x2f.google.com (mail-oa1-x2f.google.com [IPv6:2001:4860:4864:20::2f]) by sourceware.org (Postfix) with ESMTPS id 3F8A13844B36 for ; Wed, 14 Dec 2022 20:54:36 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 3F8A13844B36 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=linaro.org Received: by mail-oa1-x2f.google.com with SMTP id 586e51a60fabf-1433ef3b61fso18288452fac.10 for ; Wed, 14 Dec 2022 12:54:36 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; h=content-transfer-encoding:in-reply-to:organization:from:references :to:content-language:subject:user-agent:mime-version:date:message-id :from:to:cc:subject:date:message-id:reply-to; bh=MZz8ABiUaNRZtxqce36Q1Rdcm7LpTAj31Q2k0ZAxXgU=; b=DPyUQLMXPP3APSR9cnJ4N3Izj0Nik2u9/5c/uQ7aHAeRaNobWTA6WLnD0Gn10qmKvd x0dPV+CZL1zT3ePNOeklqt01mM6hjqIQRTx5Ota8a8iwU8Xls/vfPfbdFPpV+qZLRjzd GXUZJELgryv9cU5VCb2gRKVESKQTlJcVWNvQW2uCSnA6Zi3oHfGPg3Afo/bDxp9FsiwD DjL6g8NMsyOdV+oVUxLxEvV8/hMlUob2R2q8e+LqcYw1IP5+i6QxifZCIyYrUmTMiWlP mP6zx+87gHbgNs7HsCyQpnOXI7YOq9JhOaszvwxyJVbOM+3F0HK026DoKExofaPmB84X ThkQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:in-reply-to:organization:from:references :to:content-language:subject:user-agent:mime-version:date:message-id :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=MZz8ABiUaNRZtxqce36Q1Rdcm7LpTAj31Q2k0ZAxXgU=; b=d60XwvO2lWJLIXAnocbydIsztIqQ213PiC/PFMRJSMnYJ4VkBEfYKhJ9KUreNEe3Vy nT2mMIA0PJs+WuDFtqDtJOU0c1TWKYb8y78H3AvrIqvz6Ew1ee9/NJaRsHvpGU80/6Ql /ejt/bWsw3jWui6gVl2WfF98R8z7iCVtuFxVh6gi7gWgw54Xm4h48Nc+aHlCNeSafIM9 v8SN76WpI4899txxWLy/HJ1prFYjuUdC+dpPe5vBuXsvl+hk05FaHVvm6/K4xdVDfNNK QZgYaa//UoQSZiGycYai5qdT6PJUJw4ExNJMviPUupA/x4uv70BgLQqGzmCP0xcIudjF cAQA== X-Gm-Message-State: ANoB5plRKvAmokZjgKVP+iRFQb8ztsrbFb5jHBQgVo4+NW0MPHQWrU4J DUEcj+wtJXqr+2KD9Y2CB+Y/pbWv/sXNCb0sxTc= X-Google-Smtp-Source: AA0mqf5Hjmfz3u+4WBy6M205n+txCb0R1jUzqvZSAO4x0s4felKnD5+9qcESbSMjvVsH7IvJ1VL+PA== X-Received: by 2002:a05:6870:ea08:b0:148:1263:37e1 with SMTP id g8-20020a056870ea0800b00148126337e1mr11725667oap.17.1671051273690; Wed, 14 Dec 2022 12:54:33 -0800 (PST) Received: from ?IPV6:2804:1b3:a7c2:f05e:98ce:57c2:5097:ae83? ([2804:1b3:a7c2:f05e:98ce:57c2:5097:ae83]) by smtp.gmail.com with ESMTPSA id k16-20020a056870819000b0014866eb34cesm3183346oae.48.2022.12.14.12.54.31 (version=TLS1_3 cipher=TLS_AES_128_GCM_SHA256 bits=128/128); Wed, 14 Dec 2022 12:54:32 -0800 (PST) Message-ID: <32c237bd-1f1d-671c-71df-99005dea6746@linaro.org> Date: Wed, 14 Dec 2022 17:54:29 -0300 MIME-Version: 1.0 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:102.0) Gecko/20100101 Thunderbird/102.5.1 Subject: Re: [PATCH v5 05/11] stdio-common: Convert vfprintf and related functions to buffers Content-Language: en-US To: Florian Weimer , libc-alpha@sourceware.org References: <2706c12ba5db0ed2c845c991bb1577a7e5be2f07.1670858473.git.fweimer@redhat.com> From: Adhemerval Zanella Netto Organization: Linaro In-Reply-To: <2706c12ba5db0ed2c845c991bb1577a7e5be2f07.1670858473.git.fweimer@redhat.com> Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 7bit X-Spam-Status: No, score=-12.9 required=5.0 tests=BAYES_00,DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,GIT_PATCH_0,KAM_SHORT,NICE_REPLY_A,RCVD_IN_DNSWL_NONE,SPF_HELO_NONE,SPF_PASS,TXREP autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org List-Id: 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 > --- > 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 > +#include > +#include > > -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 > . */ > > +#include > #include > > #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 > #include > #include > +#include > +#include > +#include > > -#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 > > -#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)) > - > - > -/* 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) > - > /* 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) > > - > -/* 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 @@ > . */ > > #include > +#include > #include > #include > #include > #include > +#include > #include > #include > #include > @@ -30,6 +32,9 @@ > #include > #include > #include > +#include > +#include > +#include > > #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 > - > -#include > -#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)) > - > - > -/* 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 > - > - > -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 @@ > . */ > > #include > +#include > #include > #include > #include > @@ -29,9 +30,12 @@ > #include > #include <_itoa.h> > #include > +#include > #include > #include > #include > +#include > +#include > > /* 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 . 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); > } > > -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; > } > > /* 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); > } > - > -/* 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 . 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; > -} > - > -/* 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 > #include "../locale/localeinfo.h" > #include > - > - > -#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 > +#include > > #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 > #include > #include > +#include > > #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.