Compare commits

...
11 Commits
Author SHA1 Message Date
Florian Weimer b30518eedb libio: Convert __vswprintf_internal to buffers (bug 27857)
Always null-terminate the buffer and set E2BIG if the buffer is too
small.  This fixes bug 27857.
2022-12-12 15:40:20 +01:00
Florian Weimer 45234c5d6b libio: Convert __obstack_vprintf_internal to buffers (bug 27124)
This fixes bug 27124 because the problematic built-in vtable is gone.
2022-12-12 15:39:05 +01:00
Florian Weimer 2deb7d2cff libio: Convert __vdprintf_internal to buffers
The internal buffer size is set to 2048 bytes.  This is less than
the original BUFSIZ value used by buffered_vfprintf before
the conversion, but it hopefully covers all cases where write
boundaries matter.
2022-12-12 15:39:05 +01:00
Florian Weimer 214908c0de libio: Convert __vasprintf_internal to buffers
The buffer resizing algorithm is slightly different.  The initial
buffer is on the stack, and small buffers are directly allocated
on the heap using the exact required size.  The overhead of the
additional copy is compensated by the lowered setup cost for buffers
compared to libio streams.
2022-12-12 15:39:05 +01:00
Florian Weimer b5edce5385 libio: Convert __vsprintf_internal to buffers 2022-12-12 15:39:05 +01:00
Florian Weimer a407f0532f stdio-common: Add lock optimization to vfprintf and vfwprintf
After the rewrite and the implicit unbuffered streams handling, this
is very straightforward to add.
2022-12-12 15:39:05 +01:00
Florian Weimer 2706c12ba5 stdio-common: Convert vfprintf and related functions to buffers
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.
2022-12-12 15:39:03 +01:00
Florian Weimer 8566149e25 stdio-common: Add __translated_number_width
This function will be used to compute the width of a number
after i18n digit translation.
2022-12-12 15:38:32 +01:00
Florian Weimer 8699991d4d stdio-common: Add __printf_function_invoke
And __wprintf_function_invoke.  These functions will be used to
to call registered printf specifier callbacks on printf buffers
after vfprintf and vfwprintf have been converted to buffers.  The new
implementation avoids alloca/variable length arrays.
2022-12-12 15:38:32 +01:00
Florian Weimer 9e6084af52 stdio-common: Introduce buffers for implementing printf
These buffers will eventually be used instead of FILE * objects
to implement printf functions.  The multibyte buffer is struct
__printf_buffer, the wide buffer is struct __wprintf_buffer.

To enable writing type-generic code, the header files
printf_buffer-char.h and printf_buffer-wchar_t.h define the
Xprintf macro differently, enabling Xprintf (buffer) to stand
for __printf_buffer and __wprintf_buffer as appropriate.  For
common cases, macros like Xprintf_buffer are provided as a more
syntactically convenient shortcut.

Buffer-specific flush callbacks are implemented with a switch
statement instead of a function pointer, to avoid hardening issues
similar to those of libio vtables.  struct __printf_buffer_as_file
is needed to support custom printf specifiers because the public
interface for that requires passing a FILE *, which is why there
is a trapdoor back from these buffers to FILE * streams.

Since the immediate user of these interfaces knows when processing
has finished, there is no flush callback for the end of processing,
only a flush callback for the intermediate buffer flush.
2022-12-12 15:38:32 +01:00
Florian Weimer c0dee012ab locale: Implement struct grouping_iterator
The iterator allows grouping while scanning forward through
the digits.  This enables emitting digits as they are processed.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
2022-12-12 15:38:28 +01:00
57 changed files with 3411 additions and 1826 deletions
-1
View File
@@ -611,7 +611,6 @@ $(objpfx)tst-relro-libc.out: tst-relro-symbols.py $(..)/scripts/glibcelf.py \
--required=_IO_wfile_jumps_mmap \
--required=_IO_wmem_jumps \
--required=_IO_wstr_jumps \
--required=_IO_wstrn_jumps \
--optional=_IO_old_cookie_jumps \
--optional=_IO_old_file_jumps \
--optional=_IO_old_proc_jumps \
+43 -9
View File
@@ -41,21 +41,55 @@ union printf_arg
void *pa_user;
};
/* Invoke a registered printf callback. Called from vfprintf and vfwprintf. */
int __printf_function_invoke (void *, printf_function callback,
union printf_arg *args_value,
size_t ndata_args,
struct printf_info *info) attribute_hidden;
int __wprintf_function_invoke (void *, printf_function callback,
union printf_arg *args_value,
size_t ndata_args,
struct printf_info *info) attribute_hidden;
#include <bits/types/locale_t.h>
/* Now define the internal interfaces. */
extern int __printf_fphex (FILE *, const struct printf_info *,
const void *const *) attribute_hidden;
/* Returns the width (as for printf, in bytes) of the converted ASCII
number in the characters in the range [FIRST, LAST). The range
must only contain ASCII digits. The caller is responsible for
avoiding overflow.
This function is used during non-wide digit translation. Wide
digit translate produces one wide character per ASCII digit,
so the width is simply LAST - FIRST. */
int __translated_number_width (locale_t loc,
const char *first, const char *last)
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
+332
View File
@@ -0,0 +1,332 @@
/* Multibyte and wide buffers for implementing printf-related functions.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* The naming of the multibyte and wide variants is intentionally
consistent, so that it is possible to use the Xprintf macro in
stdio-common/printf_buffer-char.h and
stdio-common/printf_buffer-wchar_t.h to select between them in
type-generic code. */
#ifndef PRINTF_BUFFER_H
#define PRINTF_BUFFER_H
#include <stdbool.h>
#include <stdint.h>
#include <wchar.h>
/* <printf_buffer_as_file.h> introduces a way to use struct
__printf_buffer objects from FILE * streams. To avoid storing a
function pointer (or vtable pointer) in struct __printf_buffer
(which would defeat libio vtable hardening), a switch statement
over the different flush implementations is used to implement
__printf_buffer_flush.
__printf_buffer_mode_failed is special: it is the sticky failure
indicator. Unlike struct alloc_buffer, this is not folded into
write_ptr, so that snprintf and other string-writing functions can
discover the end of the string even in the error case, to be able
to add the null terminator. */
enum __printf_buffer_mode
{
__printf_buffer_mode_failed,
__printf_buffer_mode_sprintf,
__printf_buffer_mode_snprintf,
__printf_buffer_mode_sprintf_chk,
__printf_buffer_mode_to_file,
__printf_buffer_mode_asprintf,
__printf_buffer_mode_dprintf,
__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. */
__printf_buffer_mode_obstack,
};
/* Buffer for fast character writing with overflow handling.
Typically embedded in another struct with further data that is used
by the flush function. */
struct __printf_buffer
{
/* These pointer members follow FILE streams. write_ptr and
write_end must be initialized to cover the target buffer. See
__printf_buffer_init.
Data can be written directly to *write_ptr while write_ptr !=
write_end, and write_ptr can be advanced accordingly. Note that
is not possible to use the apparently-unused part of the buffer
as scratch space because sprintf (and snprintf, but that is a bit
iffy) must only write the minimum number of characters produced
by the format string and its arguments.
write_base must be initialized to be equal to write_ptr. The
framework uses this pointer to compute the total number of
written bytes, together with the written field. See
__printf_buffer_done.
write_base and write_end are only read by the generic functions
after initialization, only the flush implementation called from
__printf_buffer_flush might change these pointers. See the
comment on Xprintf (buffer_do_flush) in Xprintf_buffer_flush.c
for details regarding the flush operation. */
char *write_base;
char *write_ptr;
char *write_end;
/* Number of characters written so far (excluding the current
buffer). Potentially updated on flush. The actual number of
written bytes also includes the unflushed-but-written buffer
part, write_ptr - write_base. A 64-bit value is used to avoid
the need for overflow checks. */
uint64_t written;
/* Identifies the flush callback. */
enum __printf_buffer_mode mode;
};
/* Marks the buffer as failed, so that __printf_buffer_has_failed
returns true and future flush operations are no-ops. */
static inline void
__printf_buffer_mark_failed (struct __printf_buffer *buf)
{
buf->mode = __printf_buffer_mode_failed;
}
/* Returns true if the sticky error indicator of the buffer has been
set to failed. */
static inline bool __attribute_warn_unused_result__
__printf_buffer_has_failed (struct __printf_buffer *buf)
{
return buf->mode == __printf_buffer_mode_failed;
}
/* Initialization of a buffer, using the memory region from [BASE, BASE +LEN)
as the initial buffer contents. LEN can be zero. */
static inline void
__printf_buffer_init (struct __printf_buffer *buf, char *base, size_t len,
enum __printf_buffer_mode mode)
{
buf->write_base = base;
buf->write_ptr = base;
buf->write_end = base + len;
buf->written = 0;
buf->mode = mode;
}
/* Called by printf_buffer_putc for a full buffer. */
void __printf_buffer_putc_1 (struct __printf_buffer *buf, char ch)
attribute_hidden;
/* Writes CH to BUF. */
static inline void
__printf_buffer_putc (struct __printf_buffer *buf, char ch)
{
if (buf->write_ptr != buf->write_end)
*buf->write_ptr++ = ch;
else
__printf_buffer_putc_1 (buf, ch);
}
/* Writes COUNT repeats of CH to BUF. */
void __printf_buffer_pad_1 (struct __printf_buffer *buf,
char ch, size_t count) attribute_hidden;
/* __printf_buffer_pad with fast path for no padding. COUNT is a
long int, to accomodate signed uses in printf and elsewhere. */
static inline void
__printf_buffer_pad (struct __printf_buffer *buf, char ch, long int count)
{
if (count > 0)
__printf_buffer_pad_1 (buf, ch, count);
}
/* Write COUNT bytes starting at S to BUF. S must not overlap with
the internal buffer. */
void __printf_buffer_write (struct __printf_buffer *buf, const char *s,
size_t count) attribute_hidden;
/* Write S to BUF. S must not overlap with the internal buffer. */
void __printf_buffer_puts_1 (struct __printf_buffer *buf, const char *s)
attribute_hidden;
static inline void
__printf_buffer_puts (struct __printf_buffer *buf, const char *s)
{
if (__builtin_constant_p (__builtin_strlen (s)))
__printf_buffer_write (buf, s, __builtin_strlen (s));
else
__printf_buffer_puts_1 (buf, s);
}
/* Returns the number of bytes written through the buffer, or -1 if
there was an error (that is, __printf_buffer_has_failed (BUF) is true).
The number of written bytes includes pending bytes in the buffer
(between BUF->write_base and BUF->write_ptr).
If the number is larger than INT_MAX, returns -1 and sets errno to
EOVERFLOW. This function does not flush the buffer. If the caller
needs the side effect of flushing, it has to do this
separately. */
int __printf_buffer_done (struct __printf_buffer *buf) attribute_hidden;
/* Internally used to call the flush function. This can be called
explicitly for certain modes to flush the buffer prematuraly. In
such cases, it is often the case that the buffer mode is statically
known, and the flush implementation can be called directly. */
bool __printf_buffer_flush (struct __printf_buffer *buf) attribute_hidden;
/* Wide version of struct __printf_buffer follows. */
enum __wprintf_buffer_mode
{
__wprintf_buffer_mode_failed,
__wprintf_buffer_mode_swprintf,
__wprintf_buffer_mode_to_file,
};
struct __wprintf_buffer
{
wchar_t *write_base;
wchar_t *write_ptr;
wchar_t *write_end;
uint64_t written;
enum __wprintf_buffer_mode mode;
};
static inline void
__wprintf_buffer_mark_failed (struct __wprintf_buffer *buf)
{
buf->mode = __wprintf_buffer_mode_failed;
}
static inline bool __attribute_warn_unused_result__
__wprintf_buffer_has_failed (struct __wprintf_buffer *buf)
{
return buf->mode == __wprintf_buffer_mode_failed;
}
static inline void
__wprintf_buffer_init (struct __wprintf_buffer *buf,
wchar_t *base, size_t len,
enum __wprintf_buffer_mode mode)
{
buf->write_base = base;
buf->write_ptr = base;
buf->write_end = base + len;
buf->written = 0;
buf->mode = mode;
}
void __wprintf_buffer_putc_1 (struct __wprintf_buffer *buf, wchar_t ch)
attribute_hidden;
static inline void
__wprintf_buffer_putc (struct __wprintf_buffer *buf, wchar_t ch)
{
if (buf->write_ptr != buf->write_end)
*buf->write_ptr++ = ch;
else
__wprintf_buffer_putc_1 (buf, ch);
}
void __wprintf_buffer_pad_1 (struct __wprintf_buffer *buf,
wchar_t ch, size_t count) attribute_hidden;
static inline void
__wprintf_buffer_pad (struct __wprintf_buffer *buf, char ch, long int count)
{
if (count > 0)
__wprintf_buffer_pad_1 (buf, ch, count);
}
void __wprintf_buffer_write (struct __wprintf_buffer *buf, const wchar_t *s,
size_t count) attribute_hidden;
void __wprintf_buffer_puts (struct __wprintf_buffer *buf, const wchar_t *s)
attribute_hidden;
int __wprintf_buffer_done (struct __wprintf_buffer *buf) attribute_hidden;
bool __wprintf_buffer_flush (struct __wprintf_buffer *buf) attribute_hidden;
/* Type-generic convenience macros. They are useful if
printf_buffer-char.h or printf_buffer-wchar_t.h is included as
well. */
#define Xprintf_buffer Xprintf (buffer)
#define Xprintf_buffer_done Xprintf (buffer_done)
#define Xprintf_buffer_flush Xprintf (buffer_flush)
#define Xprintf_buffer_has_failed Xprintf (buffer_has_failed)
#define Xprintf_buffer_mark_failed Xprintf (buffer_mark_failed)
#define Xprintf_buffer_pad Xprintf (buffer_pad)
#define Xprintf_buffer_putc Xprintf (buffer_putc)
#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_asprintf;
void __printf_buffer_flush_asprintf (struct __printf_buffer_asprintf *)
attribute_hidden;
struct __printf_buffer_dprintf;
void __printf_buffer_flush_dprintf (struct __printf_buffer_dprintf *)
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 __printf_buffer_obstack;
void __printf_buffer_flush_obstack (struct __printf_buffer_obstack *)
attribute_hidden;
struct __wprintf_buffer_to_file;
void __wprintf_buffer_flush_to_file (struct __wprintf_buffer_to_file *)
attribute_hidden;
#endif /* PRINTF_BUFFER_H */
+42 -29
View File
@@ -24,41 +24,54 @@
This exception applies to code released by its copyright holders
in files containing the exception. */
#include <libioP.h>
#include <array_length.h>
#include <math_ldbl_opt.h>
#include <printf.h>
#include <stdio_ext.h>
#include <unistd.h>
#include <printf_buffer.h>
struct __printf_buffer_dprintf
{
struct __printf_buffer base;
int fd;
/* This should cover most of the packet-oriented file descriptors,
where boundaries between writes could be visible to readers. */
char buf[2048];
};
void
__printf_buffer_flush_dprintf (struct __printf_buffer_dprintf *buf)
{
char *p = buf->buf;
char *end = buf->base.write_ptr;
while (p < end)
{
ssize_t ret = TEMP_FAILURE_RETRY (write (buf->fd, p, end - p));
if (ret < 0)
{
__printf_buffer_mark_failed (&buf->base);
return;
}
p += ret;
}
buf->base.write_ptr = buf->buf;
}
int
__vdprintf_internal (int d, const char *format, va_list arg,
unsigned int mode_flags)
{
struct _IO_FILE_plus tmpfil;
struct _IO_wide_data wd;
int done;
#ifdef _IO_MTSAFE_IO
tmpfil.file._lock = NULL;
#endif
_IO_no_init (&tmpfil.file, _IO_USER_LOCK, 0, &wd, &_IO_wfile_jumps);
_IO_JUMPS (&tmpfil) = &_IO_file_jumps;
_IO_new_file_init_internal (&tmpfil);
if (_IO_file_attach (&tmpfil.file, d) == NULL)
{
_IO_un_link (&tmpfil);
return EOF;
}
tmpfil.file._flags |= _IO_DELETE_DONT_CLOSE;
_IO_mask_flags (&tmpfil.file, _IO_NO_READS,
_IO_NO_READS+_IO_NO_WRITES+_IO_IS_APPENDING);
done = __vfprintf_internal (&tmpfil.file, format, arg, mode_flags);
if (done != EOF && _IO_do_flush (&tmpfil.file) == EOF)
done = EOF;
_IO_FINISH (&tmpfil.file);
return done;
struct __printf_buffer_dprintf buf;
__printf_buffer_init (&buf.base, buf.buf, array_length (buf.buf),
__printf_buffer_mode_dprintf);
buf.fd = d;
__printf_buffer (&buf.base, format, arg, mode_flags);
if (__printf_buffer_has_failed (&buf.base))
return -1;
__printf_buffer_flush_dprintf (&buf);
return __printf_buffer_done (&buf.base);
}
int
+18 -52
View File
@@ -25,58 +25,18 @@
in files containing the exception. */
#include "libioP.h"
#include "strfile.h"
static int __THROW
_IO_str_chk_overflow (FILE *fp, int c)
{
/* If we get here, the user-supplied buffer would be overrun by
further output. */
__chk_fail ();
}
static const struct _IO_jump_t _IO_str_chk_jumps libio_vtable =
{
JUMP_INIT_DUMMY,
JUMP_INIT(finish, _IO_str_finish),
JUMP_INIT(overflow, _IO_str_chk_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)
};
/* This function is called by regular vsprintf with maxlen set to -1,
and by vsprintf_chk with maxlen set to the size of the output
string. In the former case, _IO_str_chk_overflow will never be
called; in the latter case it will crash the program if the buffer
overflows. */
#include <printf.h>
#include <stdint.h>
#include <printf_buffer.h>
int
__vsprintf_internal (char *string, size_t maxlen,
const char *format, va_list args,
unsigned int mode_flags)
{
_IO_strfile sf;
int ret;
struct __printf_buffer buf;
#ifdef _IO_MTSAFE_IO
sf._sbf._f._lock = NULL;
#endif
_IO_no_init (&sf._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
/* When called from fortified sprintf/vsprintf, erase the destination
buffer and try to detect overflows. When called from regular
sprintf/vsprintf, do not erase the destination buffer, because
@@ -84,19 +44,25 @@ __vsprintf_internal (char *string, size_t maxlen,
by ISO C), nor try to detect overflows. */
if ((mode_flags & PRINTF_CHK) != 0)
{
_IO_JUMPS (&sf._sbf) = &_IO_str_chk_jumps;
string[0] = '\0';
__printf_buffer_init (&buf, string, maxlen,
__printf_buffer_mode_sprintf_chk);
}
else
_IO_JUMPS (&sf._sbf) = &_IO_str_jumps;
_IO_str_init_static_internal (&sf, string,
(maxlen == -1) ? -1 : maxlen - 1,
string);
{
__printf_buffer_init (&buf, string, 0, __printf_buffer_mode_sprintf);
buf.write_end = (char *) ~(uintptr_t) 0; /* End of address space. */
}
ret = __vfprintf_internal (&sf._sbf._f, format, args, mode_flags);
__printf_buffer (&buf, format, args, mode_flags);
*sf._sbf._f._IO_write_ptr = '\0';
return ret;
/* Write the NUL terminator if there is room. Do not use the putc
operation to avoid overflowing the character write count. */
if ((mode_flags & PRINTF_CHK) != 0 && buf.write_ptr == buf.write_end)
__chk_fail ();
*buf.write_ptr = '\0';
return __printf_buffer_done (&buf);
}
int
+50 -120
View File
@@ -16,131 +16,59 @@
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <stdlib.h>
#include "libioP.h"
#include "strfile.h"
#include <assert.h>
#include <string.h>
#include <errno.h>
#include <math_ldbl_opt.h>
#include <obstack.h>
#include <printf.h>
#include <stdarg.h>
#include <stdio_ext.h>
#include <printf_buffer.h>
struct _IO_obstack_file
struct __printf_buffer_obstack
{
struct _IO_FILE_plus file;
struct __printf_buffer base;
struct obstack *obstack;
/* obstack_1grow is called for compatibility reasons. This needs
one extra character, and this is the backing store for it. */
char ch;
};
static int
_IO_obstack_overflow (FILE *fp, int c)
void
__printf_buffer_flush_obstack (struct __printf_buffer_obstack *buf)
{
struct obstack *obstack = ((struct _IO_obstack_file *) fp)->obstack;
int size;
/* About to switch buffers, so record the bytes written so far. */
buf->base.written += buf->base.write_ptr - buf->base.write_base;
/* Make room for another character. This might as well allocate a
new chunk a memory and moves the old contents over. */
assert (c != EOF);
obstack_1grow (obstack, c);
/* Setup the buffer pointers again. */
fp->_IO_write_base = obstack_base (obstack);
fp->_IO_write_ptr = obstack_next_free (obstack);
size = obstack_room (obstack);
fp->_IO_write_end = fp->_IO_write_ptr + size;
/* Now allocate the rest of the current chunk. */
obstack_blank_fast (obstack, size);
return c;
}
static size_t
_IO_obstack_xsputn (FILE *fp, const void *data, size_t n)
{
struct obstack *obstack = ((struct _IO_obstack_file *) fp)->obstack;
if (fp->_IO_write_ptr + n > fp->_IO_write_end)
if (buf->base.write_ptr == &buf->ch + 1)
{
int size;
/* We need some more memory. First shrink the buffer to the
space we really currently need. */
obstack_blank_fast (obstack, fp->_IO_write_ptr - fp->_IO_write_end);
/* Now grow for N bytes, and put the data there. */
obstack_grow (obstack, data, n);
/* Setup the buffer pointers again. */
fp->_IO_write_base = obstack_base (obstack);
fp->_IO_write_ptr = obstack_next_free (obstack);
size = obstack_room (obstack);
fp->_IO_write_end = fp->_IO_write_ptr + size;
/* Now allocate the rest of the current chunk. */
obstack_blank_fast (obstack, size);
/* Errors are reported via a callback mechanism (presumably for
process termination). */
obstack_1grow (buf->obstack, buf->ch);
buf->base.write_base = obstack_next_free (buf->obstack);
buf->base.write_ptr = buf->base.write_base;
size_t size = obstack_room (buf->obstack);
buf->base.write_end = buf->base.write_ptr + size;
/* Reserve the space on the obstack size. */
obstack_blank_fast (buf->obstack, size);
}
else
fp->_IO_write_ptr = __mempcpy (fp->_IO_write_ptr, data, n);
return n;
{
/* Obtain the extra character. */
buf->base.write_base = &buf->ch;
buf->base.write_ptr = &buf->ch;
buf->base.write_end = &buf->ch + 1;
}
}
/* the jump table. */
const struct _IO_jump_t _IO_obstack_jumps libio_vtable attribute_hidden =
{
JUMP_INIT_DUMMY,
JUMP_INIT(finish, NULL),
JUMP_INIT(overflow, _IO_obstack_overflow),
JUMP_INIT(underflow, NULL),
JUMP_INIT(uflow, NULL),
JUMP_INIT(pbackfail, NULL),
JUMP_INIT(xsputn, _IO_obstack_xsputn),
JUMP_INIT(xsgetn, NULL),
JUMP_INIT(seekoff, NULL),
JUMP_INIT(seekpos, NULL),
JUMP_INIT(setbuf, NULL),
JUMP_INIT(sync, NULL),
JUMP_INIT(doallocate, NULL),
JUMP_INIT(read, NULL),
JUMP_INIT(write, NULL),
JUMP_INIT(seek, NULL),
JUMP_INIT(close, NULL),
JUMP_INIT(stat, NULL),
JUMP_INIT(showmanyc, NULL),
JUMP_INIT(imbue, NULL)
};
int
__obstack_vprintf_internal (struct obstack *obstack, const char *format,
va_list args, unsigned int mode_flags)
{
struct obstack_FILE
{
struct _IO_obstack_file ofile;
} new_f;
int result;
int size;
int room;
#ifdef _IO_MTSAFE_IO
new_f.ofile.file.file._lock = NULL;
#endif
_IO_no_init (&new_f.ofile.file.file, _IO_USER_LOCK, -1, NULL, NULL);
_IO_JUMPS (&new_f.ofile.file) = &_IO_obstack_jumps;
room = obstack_room (obstack);
size = obstack_object_size (obstack) + room;
/* Legacy setup code for compatibility. */
size_t room = obstack_room (obstack);
size_t size = obstack_object_size (obstack) + room;
if (size == 0)
{
/* We have to handle the allocation a bit different since the
`_IO_str_init_static' function would handle a size of zero
different from what we expect. */
/* Get more memory. */
obstack_make_room (obstack, 64);
@@ -151,27 +79,29 @@ __obstack_vprintf_internal (struct obstack *obstack, const char *format,
assert (size != 0);
}
_IO_str_init_static_internal ((struct _IO_strfile_ *) &new_f.ofile,
obstack_base (obstack),
size, obstack_next_free (obstack));
struct __printf_buffer_obstack buf;
{
/* The obstack write location might be in the middle of an object. */
char *ptr = obstack_next_free (obstack);
char *end = obstack_base (obstack) + size;
__printf_buffer_init (&buf.base, ptr, end - ptr,
__printf_buffer_mode_obstack);
}
buf.obstack = obstack;
/* Now allocate the rest of the current chunk. */
assert (size == (new_f.ofile.file.file._IO_write_end
- new_f.ofile.file.file._IO_write_base));
assert (new_f.ofile.file.file._IO_write_ptr
== (new_f.ofile.file.file._IO_write_base
+ obstack_object_size (obstack)));
obstack_blank_fast (obstack, room);
new_f.ofile.obstack = obstack;
__printf_buffer (&buf.base, format, args, mode_flags);
result = __vfprintf_internal (&new_f.ofile.file.file, format, args,
mode_flags);
if (buf.base.write_ptr == &buf.ch + 1)
/* buf.ch is in use. Put it into the obstack. */
obstack_1grow (buf.obstack, buf.ch);
else if (buf.base.write_ptr != &buf.ch)
/* Shrink the buffer to the space we really currently need. */
obstack_blank_fast (buf.obstack, buf.base.write_ptr - buf.base.write_end);
/* Shrink the buffer to the space we really currently need. */
obstack_blank_fast (obstack, (new_f.ofile.file.file._IO_write_ptr
- new_f.ofile.file.file._IO_write_end));
return result;
return __printf_buffer_done (&buf.base);
}
int
+6 -3
View File
@@ -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
+28 -3
View File
@@ -1,4 +1,5 @@
#include <array_length.h>
#include <errno.h>
#include <stdio.h>
#include <support/check.h>
#include <sys/types.h>
@@ -37,6 +38,8 @@ do_test (void)
for (n = 0; n < array_length (tests); ++n)
{
wmemset (buf, 0xabcd, array_length (buf));
errno = 0;
ssize_t res = swprintf (buf, tests[n].n, L"%s", tests[n].str);
if (tests[n].exp < 0 && res >= 0)
@@ -52,9 +55,31 @@ do_test (void)
swprintf (buf, %zu, L\"%%s\", \"%s\") expected to return %zd, but got %zd\n",
tests[n].n, tests[n].str, tests[n].exp, res);
}
else
printf ("swprintf (buf, %zu, L\"%%s\", \"%s\") OK\n",
tests[n].n, tests[n].str);
else if (res < 0
&& tests[n].n > 0
&& wcsnlen (buf, array_length (buf)) == array_length (buf))
{
support_record_failure ();
printf ("\
error: swprintf (buf, %zu, L\"%%s\", \"%s\") missing null terminator\n",
tests[n].n, tests[n].str);
}
else if (res < 0
&& tests[n].n > 0
&& wcsnlen (buf, array_length (buf)) < array_length (buf)
&& buf[wcsnlen (buf, array_length (buf)) + 1] != 0xabcd)
{
support_record_failure ();
printf ("\
error: swprintf (buf, %zu, L\"%%s\", \"%s\") out of bounds write\n",
tests[n].n, tests[n].str);
}
if (res < 0 && tests[n].n < 0)
TEST_COMPARE (errno, E2BIG);
printf ("swprintf (buf, %zu, L\"%%s\", \"%s\") OK\n",
tests[n].n, tests[n].str);
}
TEST_COMPARE (swprintf (buf, array_length (buf), L"%.0s", "foo"), 0);
+96 -45
View File
@@ -24,64 +24,115 @@
This exception applies to code released by its copyright holders
in files containing the exception. */
#include <string.h>
#include <array_length.h>
#include <errno.h>
#include <limits.h>
#include <math_ldbl_opt.h>
#include <printf.h>
#include <stdio.h>
#include <stdlib.h>
#include <strfile.h>
#include <string.h>
#include <printf_buffer.h>
struct __printf_buffer_asprintf
{
/* base.write_base points either to a heap-allocated buffer, or to
the direct array below. */
struct __printf_buffer base;
/* Initial allocation. 200 should be large enough to copy almost
all asprintf usages with just a single (final, correctly sized)
heap allocation. */
char direct[200];
};
void
__printf_buffer_flush_asprintf (struct __printf_buffer_asprintf *buf)
{
size_t current_pos = buf->base.write_ptr - buf->base.write_base;
if (current_pos >= INT_MAX)
{
/* The result is not representable. No need to continue. */
__set_errno (EOVERFLOW);
__printf_buffer_mark_failed (&buf->base);
return;
}
size_t current_size = buf->base.write_end - buf->base.write_base;
/* Implement an exponentiatial sizing policy. Keep the size
congruent 8 (mod 16), to account for the footer in glibc
malloc. */
size_t new_size = ((current_size + current_size / 2) & -15) | 8;
char *new_buffer;
if (buf->base.write_base == buf->direct)
{
new_buffer = malloc (new_size);
if (new_buffer == NULL)
{
__printf_buffer_mark_failed (&buf->base);
return;
}
memcpy (new_buffer, buf->direct, current_pos);
}
else
{
new_buffer = realloc (buf->base.write_base, new_size);
if (new_buffer == NULL)
{
__printf_buffer_mark_failed (&buf->base);
return;
}
}
/* Set up the new write area. */
buf->base.write_base = new_buffer;
buf->base.write_ptr = new_buffer + current_pos;
buf->base.write_end = new_buffer + new_size;
}
int
__vasprintf_internal (char **result_ptr, const char *format, va_list args,
unsigned int mode_flags)
{
/* Initial size of the buffer to be used. Will be doubled each time an
overflow occurs. */
const size_t init_string_size = 100;
char *string;
_IO_strfile sf;
int ret;
size_t needed;
size_t allocated;
/* No need to clear the memory here (unlike for open_memstream) since
we know we will never seek on the stream. */
string = (char *) malloc (init_string_size);
if (string == NULL)
return -1;
#ifdef _IO_MTSAFE_IO
sf._sbf._f._lock = NULL;
#endif
_IO_no_init (&sf._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
_IO_JUMPS (&sf._sbf) = &_IO_str_jumps;
_IO_str_init_static_internal (&sf, string, init_string_size, string);
sf._sbf._f._flags &= ~_IO_USER_BUF;
sf._s._allocate_buffer_unused = (_IO_alloc_type) malloc;
sf._s._free_buffer_unused = (_IO_free_type) free;
ret = __vfprintf_internal (&sf._sbf._f, format, args, mode_flags);
if (ret < 0)
struct __printf_buffer_asprintf buf;
__printf_buffer_init (&buf.base, buf.direct, array_length (buf.direct),
__printf_buffer_mode_asprintf);
__printf_buffer (&buf.base, format, args, mode_flags);
int done = __printf_buffer_done (&buf.base);
if (done < 0)
{
free (sf._sbf._f._IO_buf_base);
return ret;
if (buf.base.write_base != buf.direct)
free (buf.base.write_base);
return done;
}
/* Transfer to the final buffer. */
char *result;
size_t size = buf.base.write_ptr - buf.base.write_base;
if (buf.base.write_base == buf.direct)
{
result = malloc (size + 1);
if (result == NULL)
return -1;
memcpy (result, buf.direct, size);
}
/* Only use realloc if the size we need is of the same (binary)
order of magnitude then the memory we allocated. */
needed = sf._sbf._f._IO_write_ptr - sf._sbf._f._IO_write_base + 1;
allocated = sf._sbf._f._IO_write_end - sf._sbf._f._IO_write_base;
if ((allocated >> 1) <= needed)
*result_ptr = (char *) realloc (sf._sbf._f._IO_buf_base, needed);
else
{
*result_ptr = (char *) malloc (needed);
if (*result_ptr != NULL)
result = realloc (buf.base.write_base, size + 1);
if (result == NULL)
{
memcpy (*result_ptr, sf._sbf._f._IO_buf_base, needed - 1);
free (sf._sbf._f._IO_buf_base);
free (buf.base.write_base);
return -1;
}
else
/* We have no choice, use the buffer we already have. */
*result_ptr = (char *) realloc (sf._sbf._f._IO_buf_base, needed);
}
if (*result_ptr == NULL)
*result_ptr = sf._sbf._f._IO_buf_base;
(*result_ptr)[needed - 1] = '\0';
return ret;
/* Add NUL termination. */
result[size] = '\0';
*result_ptr = result;
return done;
}
int
+52 -73
View File
@@ -25,97 +25,76 @@
in files containing the exception. */
#include "libioP.h"
#include "strfile.h"
static int _IO_strn_overflow (FILE *fp, int c) __THROW;
#include <array_length.h>
#include <printf.h>
#include <printf_buffer.h>
static int
_IO_strn_overflow (FILE *fp, int c)
void
__printf_buffer_flush_snprintf (struct __printf_buffer_snprintf *buf)
{
/* When we come to here this means the user supplied buffer is
filled. But since we must return the number of characters which
would have been written in total we must provide a buffer for
further use. We can do this by writing on and on in the overflow
buffer in the _IO_strnfile structure. */
_IO_strnfile *snf = (_IO_strnfile *) fp;
/* Record the bytes written so far, before switching buffers. */
buf->base.written += buf->base.write_ptr - buf->base.write_base;
if (fp->_IO_buf_base != snf->overflow_buf)
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';
/* 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';
_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);
/* 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);
}
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;
buf->base.write_base = buf->discard;
buf->base.write_ptr = buf->discard;
}
const struct _IO_jump_t _IO_strn_jumps libio_vtable attribute_hidden =
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
+18 -82
View File
@@ -24,101 +24,37 @@
This exception applies to code released by its copyright holders
in files containing the exception. */
#include "libioP.h"
#include "strfile.h"
static wint_t _IO_wstrn_overflow (FILE *fp, wint_t c) __THROW;
static wint_t
_IO_wstrn_overflow (FILE *fp, wint_t c)
{
/* 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_wstrnfile structure. */
_IO_wstrnfile *snf = (_IO_wstrnfile *) fp;
if (fp->_wide_data->_IO_buf_base != snf->overflow_buf)
{
_IO_wsetb (fp, snf->overflow_buf,
snf->overflow_buf + (sizeof (snf->overflow_buf)
/ sizeof (wchar_t)), 0);
fp->_wide_data->_IO_write_base = snf->overflow_buf;
fp->_wide_data->_IO_read_base = snf->overflow_buf;
fp->_wide_data->_IO_read_ptr = snf->overflow_buf;
fp->_wide_data->_IO_read_end = (snf->overflow_buf
+ (sizeof (snf->overflow_buf)
/ sizeof (wchar_t)));
}
fp->_wide_data->_IO_write_ptr = snf->overflow_buf;
fp->_wide_data->_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;
}
const struct _IO_jump_t _IO_wstrn_jumps libio_vtable attribute_hidden =
{
JUMP_INIT_DUMMY,
JUMP_INIT(finish, _IO_wstr_finish),
JUMP_INIT(overflow, (_IO_overflow_t) _IO_wstrn_overflow),
JUMP_INIT(underflow, (_IO_underflow_t) _IO_wstr_underflow),
JUMP_INIT(uflow, (_IO_underflow_t) _IO_wdefault_uflow),
JUMP_INIT(pbackfail, (_IO_pbackfail_t) _IO_wstr_pbackfail),
JUMP_INIT(xsputn, _IO_wdefault_xsputn),
JUMP_INIT(xsgetn, _IO_wdefault_xsgetn),
JUMP_INIT(seekoff, _IO_wstr_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),
JUMP_INIT(showmanyc, _IO_default_showmanyc),
JUMP_INIT(imbue, _IO_default_imbue)
};
#include <errno.h>
#include <math_ldbl_opt.h>
#include <printf.h>
#include <printf_buffer.h>
int
__vswprintf_internal (wchar_t *string, size_t maxlen, const wchar_t *format,
va_list args, unsigned int mode_flags)
{
_IO_wstrnfile sf;
int ret;
struct _IO_wide_data wd;
#ifdef _IO_MTSAFE_IO
sf.f._sbf._f._lock = NULL;
#endif
if (maxlen == 0)
/* Since we have to write at least the terminating L'\0' a buffer
length of zero always makes the function fail. */
return -1;
_IO_no_init (&sf.f._sbf._f, _IO_USER_LOCK, 0, &wd, &_IO_wstrn_jumps);
_IO_fwide (&sf.f._sbf._f, 1);
string[0] = L'\0';
_IO_wstr_init_static (&sf.f._sbf._f, string, maxlen - 1, string);
ret = __vfwprintf_internal ((FILE *) &sf.f._sbf, format, args, mode_flags);
struct __wprintf_buffer buf;
__wprintf_buffer_init (&buf, string, maxlen, __wprintf_buffer_mode_swprintf);
if (sf.f._sbf._f._wide_data->_IO_buf_base == sf.overflow_buf)
/* ISO C99 requires swprintf/vswprintf to return an error if the
output does not fit in the provided buffer. */
return -1;
__wprintf_buffer (&buf, format, args, mode_flags);
/* Terminate the string. */
*sf.f._sbf._f._wide_data->_IO_write_ptr = '\0';
if (buf.write_ptr == buf.write_end)
{
/* Buffer has been filled exactly, excluding the null wide
character. This is an error because the null wide character
is required. */
buf.write_end[-1] = L'\0';
return -1;
}
return ret;
buf.write_ptr[0] = L'\0';
return __wprintf_buffer_done (&buf);
}
int
+4 -3
View File
@@ -2412,9 +2412,10 @@ allocate at least @var{size} wide characters for the string @var{ws}.
The return value is the number of characters generated for the given
input, excluding the trailing null. If not all output fits into the
provided buffer a negative value is returned. You should try again with
a bigger output string. @emph{Note:} this is different from how
@code{snprintf} handles this situation.
provided buffer a negative value is returned, and @code{errno} is set to
@code{E2BIG}. (The setting of @code{errno} is a GNU extension.) You
should try again with a bigger output string. @emph{Note:} this is
different from how @code{snprintf} handles this situation.
Note that the corresponding narrow stream function takes fewer
parameters. @code{swprintf} in fact corresponds to the @code{snprintf}
+27 -1
View File
@@ -39,6 +39,7 @@ routines := \
gentempfd \
getline \
getw \
grouping_iterator \
iovfscanf \
isoc99_fscanf \
isoc99_scanf \
@@ -52,8 +53,17 @@ routines := \
perror \
printf \
printf-prs \
printf_buffer_as_file \
printf_buffer_done \
printf_buffer_flush \
printf_buffer_pad_1 \
printf_buffer_putc_1 \
printf_buffer_puts_1 \
printf_buffer_to_file \
printf_buffer_write \
printf_fp \
printf_fphex \
printf_function_invoke \
printf_size \
psiginfo \
psignal \
@@ -75,6 +85,7 @@ routines := \
tmpfile64 \
tmpnam \
tmpnam_r \
translated_number_width \
vfprintf \
vfprintf-internal \
vfscanf \
@@ -84,6 +95,15 @@ routines := \
vfwscanf \
vfwscanf-internal \
vprintf \
wprintf_buffer_as_file \
wprintf_buffer_done \
wprintf_buffer_flush \
wprintf_buffer_pad_1 \
wprintf_buffer_putc_1 \
wprintf_buffer_puts_1 \
wprintf_buffer_to_file \
wprintf_buffer_write \
wprintf_function_invoke \
# routines
aux := \
@@ -222,6 +242,10 @@ generated += \
siglist-aux.S \
# generated
tests-internal = \
tst-grouping_iterator \
# tests-internal
test-srcs = tst-unbputc tst-printf tst-printfsz-islongdouble
ifeq ($(run-built-tests),yes)
@@ -299,7 +323,8 @@ LOCALES := \
hi_IN.UTF-8 \
ja_JP.EUC-JP \
ps_AF.UTF-8 \
# LOCALES
tg_TJ.UTF-8 \
# LOCALES
include ../gen-locales.mk
$(objpfx)bug14.out: $(gen-locales)
@@ -307,6 +332,7 @@ $(objpfx)scanf13.out: $(gen-locales)
$(objpfx)test-vfprintf.out: $(gen-locales)
$(objpfx)tst-grouping.out: $(gen-locales)
$(objpfx)tst-grouping2.out: $(gen-locales)
$(objpfx)tst-grouping_iterator.out: $(gen-locales)
$(objpfx)tst-sprintf.out: $(gen-locales)
$(objpfx)tst-sscanf.out: $(gen-locales)
$(objpfx)tst-swprintf.out: $(gen-locales)
+40
View File
@@ -0,0 +1,40 @@
/* Final status reporting for struct __*printf_buffer. Generic version.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <errno.h>
#include <intprops.h>
#include <stdint.h>
int
Xprintf_buffer_done (struct Xprintf_buffer *buf)
{
if (Xprintf_buffer_has_failed (buf))
return -1;
/* Use uintptr_t here because for sprintf, the buffer range may
cover more than half of the address space. */
uintptr_t written_current = buf->write_ptr - buf->write_base;
int written_total;
if (INT_ADD_WRAPV (buf->written, written_current, &written_total))
{
__set_errno (EOVERFLOW);
return -1;
}
else
return written_total;
}
+72
View File
@@ -0,0 +1,72 @@
/* Flush wrapper for struct __*printf_buffer. Generic version.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <printf_buffer.h>
#include <assert.h>
#include <stdint.h>
/* Xprintf (buffer_do_flush) (BUF) performs the flush operation. The
actual implementation is specific to the multibyte and wide
variants.
If the flush fails, Xprintf_buffer_mark_failed (BUF) must be
called, and BUF->write_ptr and BUF->write_end can be left
unchanged.
The function must not do anything if failure has already occurred,
that is, if BUF->mode == Xprintf (buffer_mode_failed).
The framework implicitly invokes flush with BUF->write_ptr ==
BUF->write_end only. (This is particularly relevant to the
__sprintf_chk flush, which just calls __chk_fail.) But in some
cases, Xprintf_buffer_flush may be called explicitly (when
BUF->mode/the backing function is known). In that case, it is
possible that BUF->write_ptr < BUF->write_end is true.
If the flush succeeds, the pointers are changed so that
BUF->write_ptr < BUF->write_end. It is possible to switch to a
completely different buffer here. If the buffer is moved, it may
be necessary to updated BUF->write_base and BUF->written from the
flush function as well.
Note that when chaining buffers, in the flush function for the
outer buffer (to which data is written first), it is necessary to
check for BUF->next->failed (for the inner buffer) and set
BUF->base.failed to true (for the outer buffer). This should come
towards the end of the outer flush function. Usually, there is
also some unwrapping step afterwards; it has to check the outer
buffer (BUF->base.failed) and propagate any error to the inner
buffer (BUF->next->failed), so essentially in the other
direction. */
static void Xprintf (buffer_do_flush) (struct Xprintf_buffer *buf);
bool
Xprintf_buffer_flush (struct Xprintf_buffer *buf)
{
if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
return false;
Xprintf (buffer_do_flush) (buf);
if (Xprintf_buffer_has_failed (buf))
return false;
/* Ensure that the flush has made available some bytes. */
assert (buf->write_ptr != buf->write_end);
return true;
}
+44
View File
@@ -0,0 +1,44 @@
/* Write repeated characters to struct __*printf_buffer. Generic version.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <assert.h>
#include <string.h>
void
Xprintf (buffer_pad_1) (struct Xprintf_buffer *buf, CHAR_T ch, size_t count)
{
if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
return;
do
{
/* Proactively make room. __*printf_buffer_pad has already
checked for a zero-length write, so this function is only
called when there is actually data to write. */
if (buf->write_ptr == buf->write_end && !Xprintf_buffer_flush (buf))
return;
assert (buf->write_ptr != buf->write_end);
size_t to_fill = buf->write_end - buf->write_ptr;
if (to_fill > count)
to_fill = count;
MEMSET (buf->write_ptr, ch, to_fill);
buf->write_ptr += to_fill;
count -= to_fill;
}
while (count > 0);
}
+29
View File
@@ -0,0 +1,29 @@
/* Overflow write function for struct __*printf_buffer. Generic version.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <assert.h>
void
Xprintf (buffer_putc_1) (struct Xprintf_buffer *buf, CHAR_T ch)
{
if (__glibc_unlikely (Xprintf_buffer_has_failed (buf))
|| !Xprintf_buffer_flush (buf))
return;
assert (buf->write_ptr < buf->write_end);
*buf->write_ptr++ = ch;
}
+38
View File
@@ -0,0 +1,38 @@
/* String write function for struct __*printf_buffer. Generic version.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <assert.h>
#include <string.h>
void
Xprintf (buffer_puts_1) (struct Xprintf_buffer *buf, const CHAR_T *s)
{
if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
return;
while (*s != 0)
{
if (buf->write_ptr == buf->write_end && !Xprintf_buffer_flush (buf))
return;
assert (buf->write_ptr != buf->write_end);
size_t to_copy = STRNLEN (s, buf->write_end - buf->write_ptr);
MEMCPY (buf->write_ptr, s, to_copy);
buf->write_ptr += to_copy;
s += to_copy;
}
}
+44
View File
@@ -0,0 +1,44 @@
/* Blob write function for struct __*printf_buffer. Generic version.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <printf_buffer.h>
#include <assert.h>
#include <string.h>
void
Xprintf_buffer_write (struct Xprintf_buffer *buf,
const CHAR_T *s, size_t count)
{
if (__glibc_unlikely (Xprintf_buffer_has_failed (buf)))
return;
while (count > 0)
{
if (buf->write_ptr == buf->write_end && !Xprintf_buffer_flush (buf))
return;
assert (buf->write_ptr != buf->write_end);
size_t to_copy = buf->write_end - buf->write_ptr;
if (to_copy > count)
to_copy = count;
MEMCPY (buf->write_ptr, s, to_copy);
buf->write_ptr += to_copy;
s += to_copy;
count -= to_copy;
}
}
+58
View File
@@ -0,0 +1,58 @@
/* Invoke a printf specifier handler. Generic version.
Copyright (C) 1991-2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <array_length.h>
int
Xprintf (function_invoke) (void *buf,
printf_function callback,
union printf_arg *args_value,
size_t ndata_args,
struct printf_info *info)
{
/* Most custom specifiers expect just one argument. Use the heap
for larger argument arrays. */
const void *onstack_args[4];
const void **args;
if (ndata_args <= array_length (onstack_args))
args = onstack_args;
else
{
args = calloc (ndata_args, sizeof (*args));
if (args == NULL)
return -1;
}
for (unsigned int i = 0; i < ndata_args; ++i)
args[i] = &args_value[i];
struct Xprintf (buffer_as_file) s;
Xprintf (buffer_as_file_init) (&s, buf);
/* Call the function. */
int done = callback (Xprintf (buffer_as_file_get) (&s), info, args);
if (!Xprintf (buffer_as_file_terminate) (&s))
done = -1;
if (args != onstack_args)
free (args);
/* Potential error from the callback function. */
return done;
}
+126
View File
@@ -0,0 +1,126 @@
/* Iterator for inserting thousands separators into numbers.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <grouping_iterator.h>
#include <assert.h>
#include <limits.h>
#include <locale/localeinfo.h>
#include <stdint.h>
#include <string.h>
/* Initializes *IT with no grouping information for a string of length
DIGITS, and return false to indicate no grouping. */
bool
__grouping_iterator_init_none (struct grouping_iterator *it,
unsigned int digits)
{
memset (it, 0, sizeof (*it));
it->remaining_in_current_group = digits;
it->remaining = digits;
return false;
}
static bool
grouping_iterator_setup (struct grouping_iterator *it, unsigned int digits,
const char *grouping)
{
/* We treat all negative values like CHAR_MAX. */
if (*grouping == CHAR_MAX || *grouping <= 0)
/* No grouping should be done. */
return __grouping_iterator_init_none (it, digits);
unsigned int remaining_to_group = digits;
unsigned int non_repeating_groups = 0;
unsigned int groups = 0;
while (true)
{
non_repeating_groups += *grouping;
if (remaining_to_group <= (unsigned int) *grouping)
break;
++groups;
remaining_to_group -= *grouping++;
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;
non_repeating_groups -= *grouping; /* Over-counted. */
unsigned int repeats = (remaining_to_group - 1) / *grouping;
groups += repeats;
remaining_to_group -= repeats * *grouping;
break;
}
}
it->remaining_in_current_group = remaining_to_group;
it->remaining = digits;
it->groupings = grouping;
it->non_repeating_groups = non_repeating_groups;
it->separators = groups;
return it->separators > 0;
}
/* Returns the appropriate grouping item in LOC depending on CATEGORY
(which must be LC_MONETARY or LC_NUMERIC). */
static const char *
get_grouping (int category, locale_t loc)
{
return _nl_lookup (loc, category,
category == LC_MONETARY ? MON_GROUPING : GROUPING);
}
bool
__grouping_iterator_init (struct grouping_iterator *it,
int category, locale_t loc, unsigned int digits)
{
if (digits <= 1)
return __grouping_iterator_init_none (it, digits);
else
return grouping_iterator_setup (it, digits, get_grouping (category, loc));
}
bool
__grouping_iterator_next (struct grouping_iterator *it)
{
assert (it->remaining > 0);
--it->remaining;
if (it->remaining_in_current_group > 0)
{
--it->remaining_in_current_group;
return false;
}
/* If we are in the non-repeating part, switch group. */
if (it->remaining < it->non_repeating_groups)
--it->groupings;
it->remaining_in_current_group = *it->groupings - 1;
return true;
}
+65
View File
@@ -0,0 +1,65 @@
/* Iterator for grouping a number while scanning it forward.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#ifndef GROUPING_ITERATOR_H
#define GROUPING_ITERATOR_H
#include <locale.h>
#include <stdbool.h>
struct grouping_iterator
{
/* Number of characters in the current group. If this reaches zero,
a thousands separator needs to be emittted. */
unsigned int remaining_in_current_group;
/* Number of characters remaining in the number. This is used to
detect the start of the non-repeating groups. */
unsigned int remaining;
/* Points to the current grouping descriptor. */
const char *groupings;
/* Total number of characters in the non-repeating groups. */
unsigned int non_repeating_groups;
/* Number of separators that will be inserted if the whole number is
processed. (Does not change during iteration.) */
unsigned int separators;
};
struct __locale_data;
/* Initializes *IT with the data from LOCDATA (which must be for
LC_MONETARY or LC_NUMERIC). DIGITS is the length of the number.
Returns true if grouping is active, false if not. */
bool __grouping_iterator_init (struct grouping_iterator *it,
int category, locale_t loc,
unsigned int digits) attribute_hidden;
/* Initializes *IT with no grouping information for a string of length
DIGITS, and return false to indicate no grouping. */
bool __grouping_iterator_init_none (struct grouping_iterator *it,
unsigned int digits)
attribute_hidden;
/* Advances to the next character and returns true if a thousands
separator should be inserted before emitting it. */
bool __grouping_iterator_next (struct grouping_iterator *it);
#endif /* GROUPING_ITERATOR_H */
+24
View File
@@ -0,0 +1,24 @@
/* Macros for the multibyte (char) implementation of struct __printf_buffer.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#define Xprintf(n) __printf_##n
#define CHAR_T char
#define MEMCPY memcpy
#define MEMSET memset
#define STRNLEN __strnlen
+24
View File
@@ -0,0 +1,24 @@
/* Macros for wide (wchar_t) implementation of struct __wprintf_buffer.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#define Xprintf(n) __wprintf_##n
#define CHAR_T wchar_t
#define MEMCPY __wmemcpy
#define MEMSET __wmemset
#define STRNLEN __wcsnlen
+148
View File
@@ -0,0 +1,148 @@
/* FILE * interface to a struct __printf_buffer. Multibyte version.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <printf_buffer_as_file.h>
#include <assert.h>
#include <printf_buffer.h>
/* Commit the data directly written through the stdio stream. */
static void
__printf_buffer_as_file_commit (struct __printf_buffer_as_file *file)
{
/* Check that the write pointers in the file stream are consistent
with the next buffer. */
assert (file->stream._IO_write_ptr >= file->next->write_ptr);
assert (file->stream._IO_write_ptr <= file->next->write_end);
assert (file->stream._IO_write_base == file->next->write_base);
assert (file->stream._IO_write_end == file->next->write_end);
file->next->write_ptr = file->stream._IO_write_ptr;
}
/* Pointer the FILE * write buffer into the active printf_buffer
area. */
static void
__printf_buffer_as_file_switch_to_buffer (struct __printf_buffer_as_file *file)
{
file->stream._IO_write_base = file->next->write_base;
file->stream._IO_write_ptr = file->next->write_ptr;
file->stream._IO_write_end = file->next->write_end;
}
/* Only a small subset of the vtable functions is implemented here,
following _IO_obstack_jumps. */
static int
__printf_buffer_as_file_overflow (FILE *fp, int ch)
{
struct __printf_buffer_as_file *file = (struct __printf_buffer_as_file *) fp;
__printf_buffer_as_file_commit (file);
/* EOF means only a flush is requested. */
if (ch != EOF)
__printf_buffer_putc (file->next, ch);
/* Ensure that flushing actually produces room. */
if (!__printf_buffer_has_failed (file->next)
&& file->next->write_ptr == file->next->write_end)
__printf_buffer_flush (file->next);
__printf_buffer_as_file_switch_to_buffer (file);
if (!__printf_buffer_has_failed (file->next))
return (unsigned char) ch;
else
return EOF;
}
static size_t
__printf_buffer_as_file_xsputn (FILE *fp, const void *buf, size_t len)
{
struct __printf_buffer_as_file *file = (struct __printf_buffer_as_file *) fp;
__printf_buffer_as_file_commit (file);
/* Copy the data. */
__printf_buffer_write (file->next, buf, len);
__printf_buffer_as_file_switch_to_buffer (file);
if (!__printf_buffer_has_failed (file->next))
return len;
else
/* We may actually have written something. But the stream is
corrupted in this case anyway, so try not to divine the write
count here. */
return 0;
}
static const struct _IO_jump_t _IO_printf_buffer_as_file_jumps libio_vtable =
{
JUMP_INIT_DUMMY,
JUMP_INIT(finish, NULL),
JUMP_INIT(overflow, __printf_buffer_as_file_overflow),
JUMP_INIT(underflow, NULL),
JUMP_INIT(uflow, NULL),
JUMP_INIT(pbackfail, NULL),
JUMP_INIT(xsputn, __printf_buffer_as_file_xsputn),
JUMP_INIT(xsgetn, NULL),
JUMP_INIT(seekoff, NULL),
JUMP_INIT(seekpos, NULL),
JUMP_INIT(setbuf, NULL),
JUMP_INIT(sync, NULL),
JUMP_INIT(doallocate, NULL),
JUMP_INIT(read, NULL),
JUMP_INIT(write, NULL),
JUMP_INIT(seek, NULL),
JUMP_INIT(close, NULL),
JUMP_INIT(stat, NULL),
JUMP_INIT(showmanyc, NULL),
JUMP_INIT(imbue, NULL)
};
void
__printf_buffer_as_file_init (struct __printf_buffer_as_file *file,
struct __printf_buffer *next)
{
file->stream._lock = NULL;
_IO_no_init (&file->stream, _IO_USER_LOCK, -1, NULL, NULL);
file->vtable = &_IO_printf_buffer_as_file_jumps;
/* Set up the write buffer from the next buffer. */
file->next = next;
__printf_buffer_as_file_switch_to_buffer (file);
/* Mark the read area as inactive, by making all pointers equal. */
file->stream._IO_read_base = file->stream._IO_write_base;
file->stream._IO_read_ptr = file->stream._IO_write_base;
file->stream._IO_read_end = file->stream._IO_write_base;
}
bool
__printf_buffer_as_file_terminate (struct __printf_buffer_as_file *file)
{
if (file->stream._flags & _IO_ERR_SEEN)
return false;
else
{
__printf_buffer_as_file_commit (file);
return true;
}
}
+87
View File
@@ -0,0 +1,87 @@
/* FILE * interface to a struct __*printf_buffer.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* Registered printf format specifier callbacks produce data via a
FILE * stream. struct __printf_buffer_as_file enables vfprintf to
create a suitable stdio stream. Likewise struct
__wprintf_buffer_as_file for vfwprintf. */
#ifndef PRINTF_BUFFER_AS_FILE_H
#define PRINTF_BUFFER_AS_FILE_H
#include <libio/libioP.h>
struct __printf_buffer;
struct __printf_buffer_as_file
{
/* Interface to libio. */
FILE stream;
const struct _IO_jump_t *vtable;
/* Pointer to the underlying buffer. */
struct __printf_buffer *next;
};
/* Initialization *FP so that data written to its FILE * stream ends
up in NEXT. */
void __printf_buffer_as_file_init (struct __printf_buffer_as_file *fp,
struct __printf_buffer *next)
attribute_hidden;
/* Returns the FILE * that can be used to write data to the
buffer. */
static inline FILE *
__printf_buffer_as_file_get (struct __printf_buffer_as_file *file)
{
return &file->stream;
}
/* Transfers all pending data from the FILE * to the underlying
buffer. Returns true if there have been no errors. */
bool __printf_buffer_as_file_terminate (struct __printf_buffer_as_file *)
attribute_hidden;
/* Wide variant follows. */
struct __wprintf_buffer;
struct __wprintf_buffer_as_file
{
/* Interface to libio. */
FILE stream;
const struct _IO_jump_t *vtable;
struct _IO_wide_data wide_stream;
/* Pointer to the underlying buffer. */
struct __wprintf_buffer *next;
};
void __wprintf_buffer_as_file_init (struct __wprintf_buffer_as_file *fp,
struct __wprintf_buffer *next)
attribute_hidden;
static inline FILE *
__wprintf_buffer_as_file_get (struct __wprintf_buffer_as_file *file)
{
return &file->stream;
}
bool __wprintf_buffer_as_file_terminate (struct __wprintf_buffer_as_file *)
attribute_hidden;
#endif /* PRINTF_BUFFER_AS_FILE_H */
+21
View File
@@ -0,0 +1,21 @@
/* Final status reporting for struct __printf_buffer. Multibyte version.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <printf_buffer.h>
#include "printf_buffer-char.h"
#include "Xprintf_buffer_done.c"
+81
View File
@@ -0,0 +1,81 @@
/* Flush a struct __printf_buffer. Multibyte version.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <errno.h>
#include <printf_buffer.h>
#include "printf_buffer-char.h"
#include "Xprintf_buffer_flush.c"
/* 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_asprintf
# pragma weak __printf_buffer_flush_dprintf
# pragma weak __printf_buffer_flush_fp
# pragma weak __printf_buffer_flush_fp_to_wide
# pragma weak __printf_buffer_flush_fphex_to_wide
# pragma weak __printf_buffer_flush_obstack
#endif /* !SHARED */
static void
__printf_buffer_do_flush (struct __printf_buffer *buf)
{
switch (buf->mode)
{
case __printf_buffer_mode_failed:
case __printf_buffer_mode_sprintf:
return;
case __printf_buffer_mode_snprintf:
__printf_buffer_flush_snprintf ((struct __printf_buffer_snprintf *) buf);
return;
case __printf_buffer_mode_sprintf_chk:
__chk_fail ();
break;
case __printf_buffer_mode_to_file:
__printf_buffer_flush_to_file ((struct __printf_buffer_to_file *) buf);
return;
case __printf_buffer_mode_asprintf:
__printf_buffer_flush_asprintf ((struct __printf_buffer_asprintf *) buf);
return;
case __printf_buffer_mode_dprintf:
__printf_buffer_flush_dprintf ((struct __printf_buffer_dprintf *) 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;
case __printf_buffer_mode_obstack:
__printf_buffer_flush_obstack ((struct __printf_buffer_obstack *) buf);
return;
}
__builtin_trap ();
}
+21
View File
@@ -0,0 +1,21 @@
/* Write repeated characters to struct __printf_buffer. Multibyte version.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <printf_buffer.h>
#include "printf_buffer-char.h"
#include "Xprintf_buffer_pad_1.c"
+21
View File
@@ -0,0 +1,21 @@
/* Overflow character write function for struct __printf_buffer.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <printf_buffer.h>
#include "printf_buffer-char.h"
#include "Xprintf_buffer_putc_1.c"
+21
View File
@@ -0,0 +1,21 @@
/* String write function for struct __printf_buffer. Multibyte version.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <printf_buffer.h>
#include "printf_buffer-char.h"
#include "Xprintf_buffer_puts_1.c"
+122
View File
@@ -0,0 +1,122 @@
/* Multibyte printf buffers writing data to a FILE * stream.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <printf_buffer_to_file.h>
#include <assert.h>
#include <array_length.h>
#include <libio/libioP.h>
/* Switch to the file buffer if possible. If the file has write_ptr
== write_end, use the stage buffer instead. */
void
__printf_buffer_to_file_switch (struct __printf_buffer_to_file *buf)
{
if (buf->fp->_IO_write_ptr < buf->fp->_IO_write_end)
{
/* buf->fp has a buffer associated with it, so write directly to
it from now on. */
buf->base.write_ptr = buf->fp->_IO_write_ptr;
buf->base.write_end = buf->fp->_IO_write_end;
}
else
{
/* Use the staging area if no buffer is available in buf->fp. */
buf->base.write_ptr = buf->stage;
buf->base.write_end = array_end (buf->stage);
}
buf->base.write_base = buf->base.write_ptr;
}
void
__printf_buffer_flush_to_file (struct __printf_buffer_to_file *buf)
{
/* The bytes in the buffer are always consumed. */
buf->base.written += buf->base.write_ptr - buf->base.write_base;
if (buf->base.write_end == array_end (buf->stage))
{
/* If the stage buffer is used, make a copy into the file. The
stage buffer is always consumed fully, even if just partially
written, to ensure that the file stream has all the data. */
size_t count = buf->base.write_ptr - buf->stage;
if ((size_t) _IO_sputn (buf->fp, buf->stage, count) != count)
{
__printf_buffer_mark_failed (&buf->base);
return;
}
/* buf->fp may have a buffer now. */
__printf_buffer_to_file_switch (buf);
return;
}
else if (buf->base.write_end == buf->stage + 1)
{
/* Special one-character buffer case. This is used to avoid
flush-only overflow below. */
if (buf->base.write_ptr == buf->base.write_end)
{
if (__overflow (buf->fp, (unsigned char) *buf->stage) == EOF)
{
__printf_buffer_mark_failed (&buf->base);
return;
}
__printf_buffer_to_file_switch (buf);
}
/* Else there is nothing to write. */
return;
}
/* We have written directly into the buf->fp buffer. */
assert (buf->base.write_end == buf->fp->_IO_write_end);
/* Mark the bytes as written. */
buf->fp->_IO_write_ptr = buf->base.write_ptr;
if (buf->base.write_ptr == buf->base.write_end)
{
/* The buffer in buf->fp has been filled. This should just call
__overflow (buf->fp, EOF), but flush-only overflow is obscure
and not always correctly implemented. See bug 28949. Be
conservative and switch to a one-character buffer instead, to
obtain one more character for a regular __overflow call. */
buf->base.write_ptr = buf->stage;
buf->base.write_end = buf->stage + 1;
}
/* The bytes in the file stream were already marked as written above. */
buf->base.write_base = buf->base.write_ptr;
}
void
__printf_buffer_to_file_init (struct __printf_buffer_to_file *buf, FILE *fp)
{
__printf_buffer_init (&buf->base, buf->stage, array_length (buf->stage),
__printf_buffer_mode_to_file);
buf->fp = fp;
__printf_buffer_to_file_switch (buf);
}
int
__printf_buffer_to_file_done (struct __printf_buffer_to_file *buf)
{
if (__printf_buffer_has_failed (&buf->base))
return -1;
__printf_buffer_flush_to_file (buf);
return __printf_buffer_done (&buf->base);
}
+57
View File
@@ -0,0 +1,57 @@
/* Multibyte and wide printf buffers writing data to a FILE * stream.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#ifndef PRINTF_BUFFER_TO_FILE_H
#define PRINTF_BUFFER_TO_FILE_H
#include <bits/types/FILE.h>
#include <printf_buffer.h>
struct __printf_buffer_to_file
{
struct __printf_buffer base;
FILE *fp;
/* Staging buffer. All data goes through this buffer before
reaching the fp stream. (Any buffer in fp is not yet used.) */
char stage[128];
};
/* Initializes *BUF to write data to FP. */
void __printf_buffer_to_file_init (struct __printf_buffer_to_file *buf,
FILE *fp) attribute_hidden;
/* Transfers any pending data in BUF to BUF->FP. The return value
follows the printf convention (number bytes written; or -1 for error). */
int __printf_buffer_to_file_done (struct __printf_buffer_to_file *buf)
attribute_hidden;
/* Wide version below. */
struct __wprintf_buffer_to_file
{
struct __wprintf_buffer base;
FILE *fp;
wchar_t stage[128];
};
void __wprintf_buffer_to_file_init (struct __wprintf_buffer_to_file *buf,
FILE *fp) attribute_hidden;
int __wprintf_buffer_to_file_done (struct __wprintf_buffer_to_file *buf)
attribute_hidden;
#endif /* PRINTF_BUFFER_TO_FILE_H */
+21
View File
@@ -0,0 +1,21 @@
/* Blob write function for struct __printf_buffer. Multibyte version.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <printf_buffer.h>
#include "printf_buffer-char.h"
#include "Xprintf_buffer_write.c"
+328 -440
View File
File diff suppressed because it is too large Load Diff
+109 -151
View File
@@ -17,10 +17,12 @@
<https://www.gnu.org/licenses/>. */
#include <array_length.h>
#include <assert.h>
#include <ctype.h>
#include <ieee754.h>
#include <math.h>
#include <printf.h>
#include <libioP.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
@@ -30,6 +32,9 @@
#include <locale/localeinfo.h>
#include <stdbool.h>
#include <rounding-mode.h>
#include <sys/param.h>
#include <printf_buffer.h>
#include <errno.h>
#if __HAVE_DISTINCT_FLOAT128
# include "ieee754_float128.h"
@@ -39,58 +44,11 @@
IEEE854_FLOAT128_BIAS)
#endif
/* #define NDEBUG 1*/ /* Undefine this for debugging assertions. */
#include <assert.h>
#include <libioP.h>
#define PUT(f, s, n) _IO_sputn (f, s, n)
#define PAD(f, c, n) (wide ? _IO_wpadn (f, c, n) : _IO_padn (f, c, n))
#undef putc
#define putc(c, f) (wide \
? (int)_IO_putwc_unlocked (c, f) : _IO_putc_unlocked (c, f))
/* 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');
outchar (expnegative ? '-' : '+');
__printf_buffer_putc (buf, expnegative ? '-' : '+');
PRINT (expstr, wexpstr, (expbuf + sizeof expbuf) - expstr);
__printf_buffer_write (buf, expstr, (expbuf + sizeof expbuf) - expstr);
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);
}
return done;
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);
}
/* The wide buffer version is implemented by translating the output of
the multibyte verison. */
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);
}
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);
}
+22
View File
@@ -0,0 +1,22 @@
/* Invoke a printf specifier handler. Multibyte version.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <printf.h>
#include <printf_buffer_as_file.h>
#include "printf_buffer-char.h"
#include "Xprintf_function_invoke.c"
+42
View File
@@ -0,0 +1,42 @@
/* Compute the printf width of a sequence of ASCII digits.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <assert.h>
#include <limits.h>
#include <locale/localeinfo.h>
#include <printf.h>
int
__translated_number_width (locale_t loc, const char *first, const char *last)
{
struct lc_ctype_data *ctype = loc->__locales[LC_CTYPE]->private;
if (ctype->outdigit_bytes_all_equal > 0)
return (last - first) * ctype->outdigit_bytes_all_equal;
else
{
/* Digits have varying length, so the fast path cannot be used. */
int digits = 0;
for (const char *p = first; p < last; ++p)
{
assert ('0' <= *p && *p <= '9');
digits += ctype->outdigit_bytes[*p - '0'];
}
return digits;
}
}
+262
View File
@@ -0,0 +1,262 @@
/* Test for struct grouping_iterator.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* Rebuild the fail to access internal-only functions. */
#include <grouping_iterator.c>
#include <stdio.h>
#include <support/check.h>
#include <support/support.h>
#include <support/test-driver.h>
static void
check (int lineno, const char *groupings,
const char *input, const char *expected)
{
if (test_verbose)
{
printf ("info: %s:%d: \"%s\" via \"", __FILE__, lineno, input);
for (const char *p = groupings; *p != 0; ++p)
printf ("\\%o", *p & 0xff);
printf ("\" to \"%s\"\n", expected);
}
size_t initial_group = strchrnul (expected, '\'') - expected;
size_t separators = 0;
for (const char *p = expected; *p != '\0'; ++p)
separators += *p == '\'';
size_t digits = strlen (input);
char *out = xmalloc (2 * digits + 1);
struct grouping_iterator it;
TEST_COMPARE (grouping_iterator_setup (&it, digits, groupings),
strchr (expected, '\'') != NULL);
TEST_COMPARE (it.remaining, digits);
TEST_COMPARE (it.remaining_in_current_group, initial_group);
TEST_COMPARE (it.separators, separators);
char *p = out;
while (*input != '\0')
{
if (__grouping_iterator_next (&it))
*p++ = '\'';
TEST_COMPARE (it.separators, separators);
*p++ = *input++;
}
*p++ = '\0';
TEST_COMPARE (it.remaining, 0);
TEST_COMPARE (it.remaining_in_current_group, 0);
TEST_COMPARE_STRING (out, expected);
free (out);
}
static int
do_test (void)
{
check (__LINE__, "", "1", "1");
check (__LINE__, "", "12", "12");
check (__LINE__, "", "123", "123");
check (__LINE__, "", "1234", "1234");
check (__LINE__, "\3", "1", "1");
check (__LINE__, "\3", "12", "12");
check (__LINE__, "\3", "123", "123");
check (__LINE__, "\3", "1234", "1'234");
check (__LINE__, "\3", "12345", "12'345");
check (__LINE__, "\3", "123456", "123'456");
check (__LINE__, "\3", "1234567", "1'234'567");
check (__LINE__, "\3", "12345678", "12'345'678");
check (__LINE__, "\3", "123456789", "123'456'789");
check (__LINE__, "\3", "1234567890", "1'234'567'890");
check (__LINE__, "\2\3", "1", "1");
check (__LINE__, "\2\3", "12", "12");
check (__LINE__, "\2\3", "123", "1'23");
check (__LINE__, "\2\3", "1234", "12'34");
check (__LINE__, "\2\3", "12345", "123'45");
check (__LINE__, "\2\3", "123456", "1'234'56");
check (__LINE__, "\2\3", "1234567", "12'345'67");
check (__LINE__, "\2\3", "12345678", "123'456'78");
check (__LINE__, "\2\3", "123456789", "1'234'567'89");
check (__LINE__, "\2\3", "1234567890", "12'345'678'90");
check (__LINE__, "\3\2", "1", "1");
check (__LINE__, "\3\2", "12", "12");
check (__LINE__, "\3\2", "123", "123");
check (__LINE__, "\3\2", "1234", "1'234");
check (__LINE__, "\3\2", "12345", "12'345");
check (__LINE__, "\3\2", "123456", "1'23'456");
check (__LINE__, "\3\2", "1234567", "12'34'567");
check (__LINE__, "\3\2", "12345678", "1'23'45'678");
check (__LINE__, "\3\2", "123456789", "12'34'56'789");
check (__LINE__, "\3\2", "1234567890", "1'23'45'67'890");
check (__LINE__, "\3\2\1", "1", "1");
check (__LINE__, "\3\2\1", "12", "12");
check (__LINE__, "\3\2\1", "123", "123");
check (__LINE__, "\3\2\1", "1234", "1'234");
check (__LINE__, "\3\2\1", "12345", "12'345");
check (__LINE__, "\3\2\1", "123456", "1'23'456");
check (__LINE__, "\3\2\1", "1234567", "1'2'34'567");
check (__LINE__, "\3\2\1", "12345678", "1'2'3'45'678");
check (__LINE__, "\3\2\1", "123456789", "1'2'3'4'56'789");
check (__LINE__, "\3\2\1", "1234567890", "1'2'3'4'5'67'890");
check (__LINE__, "\2\3\1", "1", "1");
check (__LINE__, "\2\3\1", "12", "12");
check (__LINE__, "\2\3\1", "123", "1'23");
check (__LINE__, "\2\3\1", "1234", "12'34");
check (__LINE__, "\2\3\1", "12345", "123'45");
check (__LINE__, "\2\3\1", "123456", "1'234'56");
check (__LINE__, "\2\3\1", "1234567", "1'2'345'67");
check (__LINE__, "\2\3\1", "12345678", "1'2'3'456'78");
check (__LINE__, "\2\3\1", "123456789", "1'2'3'4'567'89");
check (__LINE__, "\2\3\1", "1234567890", "1'2'3'4'5'678'90");
/* No repeats. */
check (__LINE__, "\3\377", "1", "1");
check (__LINE__, "\3\377", "12", "12");
check (__LINE__, "\3\377", "123", "123");
check (__LINE__, "\3\377", "1234", "1'234");
check (__LINE__, "\3\377", "12345", "12'345");
check (__LINE__, "\3\377", "123456", "123'456");
check (__LINE__, "\3\377", "1234567", "1234'567");
check (__LINE__, "\3\377", "12345678", "12345'678");
check (__LINE__, "\2\3\377", "1", "1");
check (__LINE__, "\2\3\377", "12", "12");
check (__LINE__, "\2\3\377", "123", "1'23");
check (__LINE__, "\2\3\377", "1234", "12'34");
check (__LINE__, "\2\3\377", "12345", "123'45");
check (__LINE__, "\2\3\377", "123456", "1'234'56");
check (__LINE__, "\2\3\377", "1234567", "12'345'67");
check (__LINE__, "\2\3\377", "12345678", "123'456'78");
check (__LINE__, "\2\3\377", "123456789", "1234'567'89");
check (__LINE__, "\2\3\377", "1234567890", "12345'678'90");
check (__LINE__, "\3\2\377", "1", "1");
check (__LINE__, "\3\2\377", "12", "12");
check (__LINE__, "\3\2\377", "123", "123");
check (__LINE__, "\3\2\377", "1234", "1'234");
check (__LINE__, "\3\2\377", "12345", "12'345");
check (__LINE__, "\3\2\377", "123456", "1'23'456");
check (__LINE__, "\3\2\377", "1234567", "12'34'567");
check (__LINE__, "\3\2\377", "12345678", "123'45'678");
check (__LINE__, "\3\2\377", "123456789", "1234'56'789");
check (__LINE__, "\3\2\377", "1234567890", "12345'67'890");
/* Locale-based tests. */
locale_t loc;
struct lc_ctype_data *ctype;
struct grouping_iterator it;
loc = newlocale (LC_ALL_MASK, "de_DE.UTF-8", 0);
TEST_VERIFY_EXIT (loc != 0);
ctype = loc->__locales[LC_CTYPE]->private;
TEST_VERIFY (!ctype->outdigit_translation_needed);
for (int i = 0; i <= 9; ++i)
TEST_COMPARE (ctype->outdigit_bytes[i], 1);
TEST_COMPARE (ctype->outdigit_bytes_all_equal, 1);
TEST_COMPARE (__grouping_iterator_init (&it, LC_NUMERIC, loc, 8), true);
TEST_COMPARE (it.remaining_in_current_group, 2);
TEST_COMPARE (it.remaining, 8);
TEST_COMPARE (*it.groupings, 3);
TEST_COMPARE (it.non_repeating_groups, 3); /* Locale duplicates 3. */
TEST_COMPARE (it.separators, 2);
TEST_COMPARE (__grouping_iterator_init (&it, LC_MONETARY, loc, 8), true);
TEST_COMPARE (it.remaining_in_current_group, 2);
TEST_COMPARE (it.remaining, 8);
TEST_COMPARE (*it.groupings, 3);
TEST_COMPARE (it.non_repeating_groups, 3); /* Locale duplicates 3. */
TEST_COMPARE (it.separators, 2);
freelocale (loc);
loc = newlocale (LC_ALL_MASK, "tg_TJ.UTF-8", 0);
TEST_VERIFY_EXIT (loc != 0);
ctype = loc->__locales[LC_CTYPE]->private;
TEST_VERIFY (!ctype->outdigit_translation_needed);
for (int i = 0; i <= 9; ++i)
TEST_COMPARE (ctype->outdigit_bytes[i], 1);
TEST_COMPARE (ctype->outdigit_bytes_all_equal, 1);
TEST_COMPARE (__grouping_iterator_init (&it, LC_NUMERIC, loc, 8), true);
TEST_COMPARE (it.remaining_in_current_group, 2);
TEST_COMPARE (it.remaining, 8);
TEST_COMPARE (*it.groupings, 3);
TEST_COMPARE (it.non_repeating_groups, 3); /* Locale duplicates 3. */
TEST_COMPARE (it.separators, 2);
TEST_COMPARE (__grouping_iterator_init (&it, LC_MONETARY, loc, 8), true);
TEST_COMPARE (it.remaining_in_current_group, 2);
TEST_COMPARE (it.remaining, 8);
TEST_COMPARE (*it.groupings, 3);
TEST_COMPARE (it.non_repeating_groups, 3); /* Locale duplicates 3. */
TEST_COMPARE (it.separators, 2);
freelocale (loc);
loc = newlocale (LC_ALL_MASK, "hi_IN.UTF-8", 0);
TEST_VERIFY_EXIT (loc != 0);
ctype = loc->__locales[LC_CTYPE]->private;
TEST_VERIFY (ctype->outdigit_translation_needed);
for (int i = 0; i <= 9; ++i)
/* Locale uses Devanagari digits. */
TEST_COMPARE (ctype->outdigit_bytes[i], 3);
TEST_COMPARE (ctype->outdigit_bytes_all_equal, 3);
TEST_COMPARE (__grouping_iterator_init (&it, LC_NUMERIC, loc, 8), true);
TEST_COMPARE (it.remaining_in_current_group, 2);
TEST_COMPARE (it.remaining, 8);
TEST_COMPARE (*it.groupings, 3);
TEST_COMPARE (it.non_repeating_groups, 0);
TEST_COMPARE (it.separators, 2);
TEST_COMPARE (__grouping_iterator_init (&it, LC_MONETARY, loc, 8), true);
TEST_COMPARE (it.remaining_in_current_group, 1);
TEST_COMPARE (it.remaining, 8);
TEST_COMPARE (*it.groupings, 2);
TEST_COMPARE (it.non_repeating_groups, 3);
TEST_COMPARE (it.separators, 3);
freelocale (loc);
loc = newlocale (LC_ALL_MASK, "ps_AF.UTF-8", 0);
TEST_VERIFY_EXIT (loc != 0);
ctype = loc->__locales[LC_CTYPE]->private;
TEST_VERIFY (ctype->outdigit_translation_needed);
for (int i = 0; i <= 9; ++i)
/* Locale uses non-ASCII digits. */
TEST_COMPARE (ctype->outdigit_bytes[i], 2);
TEST_COMPARE (ctype->outdigit_bytes_all_equal, 2);
TEST_COMPARE (__grouping_iterator_init (&it, LC_NUMERIC, loc, 8), true);
TEST_COMPARE (it.remaining_in_current_group, 2);
TEST_COMPARE (it.remaining, 8);
TEST_COMPARE (*it.groupings, 3);
TEST_COMPARE (it.non_repeating_groups, 0);
TEST_COMPARE (it.separators, 2);
TEST_COMPARE (__grouping_iterator_init (&it, LC_MONETARY, loc, 8), true);
TEST_COMPARE (it.remaining_in_current_group, 2);
TEST_COMPARE (it.remaining, 8);
TEST_COMPARE (*it.groupings, 3);
TEST_COMPARE (it.non_repeating_groups, 0);
TEST_COMPARE (it.separators, 2);
freelocale (loc);
return 0;
}
#include <support/test-driver.c>
+191 -485
View File
@@ -16,6 +16,7 @@
<https://www.gnu.org/licenses/>. */
#include <array_length.h>
#include <assert.h>
#include <ctype.h>
#include <limits.h>
#include <printf.h>
@@ -29,9 +30,12 @@
#include <sys/param.h>
#include <_itoa.h>
#include <locale/localeinfo.h>
#include <grouping_iterator.h>
#include <stdio.h>
#include <scratch_buffer.h>
#include <intprops.h>
#include <printf_buffer.h>
#include <printf_buffer_to_file.h>
/* This code is shared between the standard stdio implementation found
in GNU C library and the libio implementation originally found in
@@ -47,21 +51,21 @@
#endif
#define ARGCHECK(S, Format) \
do \
{ \
/* Check file argument for consistence. */ \
CHECK_FILE (S, -1); \
if (S->_flags & _IO_NO_WRITES) \
{ \
S->_flags |= _IO_ERR_SEEN; \
__set_errno (EBADF); \
return -1; \
} \
if (Format == NULL) \
{ \
__set_errno (EINVAL); \
return -1; \
} \
do \
{ \
/* Check file argument for consistence. */ \
CHECK_FILE (S, -1); \
if (S->_flags & _IO_NO_WRITES) \
{ \
S->_flags |= _IO_ERR_SEEN; \
__set_errno (EBADF); \
return -1; \
} \
if (Format == NULL) \
{ \
__set_errno (EINVAL); \
return -1; \
} \
} while (0)
#define UNBUFFERED_P(S) ((S)->_flags & _IO_UNBUFFERED)
@@ -116,37 +120,9 @@
while (0)
#endif
/* Add LENGTH to DONE. Return the new value of DONE, or -1 on
overflow (and set errno accordingly). */
static inline int
done_add_func (size_t length, int done)
{
if (done < 0)
return done;
int ret;
if (INT_ADD_WRAPV (done, length, &ret))
{
__set_errno (EOVERFLOW);
return -1;
}
return ret;
}
#define done_add(val) \
do \
{ \
/* Ensure that VAL has a type similar to int. */ \
_Static_assert (sizeof (val) == sizeof (int), "value int size"); \
_Static_assert ((__typeof__ (val)) -1 < 0, "value signed"); \
done = done_add_func ((val), done); \
if (done < 0) \
goto all_done; \
} \
while (0)
#ifndef COMPILE_WPRINTF
# include "printf_buffer-char.h"
# define vfprintf __vfprintf_internal
# define CHAR_T char
# define OTHER_CHAR_T wchar_t
# define UCHAR_T unsigned char
# define INT_T int
@@ -155,14 +131,12 @@ typedef const char *THOUSANDS_SEP_T;
# define ISDIGIT(Ch) ((unsigned int) ((Ch) - '0') < 10)
# define STR_LEN(Str) strlen (Str)
# define PUT(F, S, N) _IO_sputn ((F), (S), (N))
# define PUTC(C, F) _IO_putc_unlocked (C, F)
# define ORIENT if (_IO_vtable_offset (s) == 0 && _IO_fwide (s, -1) != -1)\
return -1
# define CONVERT_FROM_OTHER_STRING __wcsrtombs
#else
# include "printf_buffer-wchar_t.h"
# define vfprintf __vfwprintf_internal
# define CHAR_T wchar_t
# define OTHER_CHAR_T char
/* This is a hack!!! There should be a type uwchar_t. */
# define UCHAR_T unsigned int /* uwchar_t */
@@ -174,8 +148,6 @@ typedef wchar_t THOUSANDS_SEP_T;
# include <_itowa.h>
# define PUT(F, S, N) _IO_sputn ((F), (S), (N))
# define PUTC(C, F) _IO_putwc_unlocked (C, F)
# define ORIENT if (_IO_fwide (s, 1) != 1) return -1
# define CONVERT_FROM_OTHER_STRING __mbsrtowcs
@@ -186,76 +158,16 @@ typedef wchar_t THOUSANDS_SEP_T;
# define EOF WEOF
#endif
static inline int
pad_func (FILE *s, CHAR_T padchar, int width, int done)
{
if (width > 0)
{
ssize_t written;
#ifndef COMPILE_WPRINTF
written = _IO_padn (s, padchar, width);
#else
written = _IO_wpadn (s, padchar, width);
#endif
if (__glibc_unlikely (written != width))
return -1;
return done_add_func (width, done);
}
return done;
}
#define PAD(Padchar) \
do \
{ \
done = pad_func (s, (Padchar), width, done); \
if (done < 0) \
goto all_done; \
} \
while (0)
#include "_i18n_number.h"
/* Include the shared code for parsing the format string. */
#include "printf-parse.h"
#define outchar(Ch) \
do \
{ \
const INT_T outc = (Ch); \
if (PUTC (outc, s) == EOF || done == INT_MAX) \
{ \
done = -1; \
goto all_done; \
} \
++done; \
} \
while (0)
static inline int
outstring_func (FILE *s, const UCHAR_T *string, size_t length, int done)
{
assert ((size_t) done <= (size_t) INT_MAX);
if ((size_t) PUT (s, string, length) != (size_t) (length))
return -1;
return done_add_func (length, done);
}
#define outstring(String, Len) \
do \
{ \
const void *string_ = (String); \
done = outstring_func (s, string_, (Len), done); \
if (done < 0) \
goto all_done; \
} \
while (0)
/* Write the string SRC to S. If PREC is non-negative, write at most
PREC bytes. If LEFT is true, perform left justification. */
static int
outstring_converted_wide_string (FILE *s, const OTHER_CHAR_T *src, int prec,
int width, bool left, int done)
static void
outstring_converted_wide_string (struct Xprintf_buffer *target,
const OTHER_CHAR_T *src, int prec,
int width, bool left)
{
/* Use a small buffer to combine processing of multiple characters.
CONVERT_FROM_OTHER_STRING expects the buffer size in (wide)
@@ -290,7 +202,10 @@ outstring_converted_wide_string (FILE *s, const OTHER_CHAR_T *src, int prec,
size_t written = CONVERT_FROM_OTHER_STRING
(buf, &src_copy, write_limit, &mbstate);
if (written == (size_t) -1)
return -1;
{
Xprintf_buffer_mark_failed (target);
return;
}
if (written == 0)
break;
total_written += written;
@@ -299,12 +214,9 @@ outstring_converted_wide_string (FILE *s, const OTHER_CHAR_T *src, int prec,
}
/* Output initial padding. */
if (total_written < width)
{
done = pad_func (s, L_(' '), width - total_written, done);
if (done < 0)
return done;
}
Xprintf_buffer_pad (target, L_(' '), width - total_written);
if (Xprintf_buffer_has_failed (target))
return;
}
/* Convert the input string, piece by piece. */
@@ -324,12 +236,13 @@ outstring_converted_wide_string (FILE *s, const OTHER_CHAR_T *src, int prec,
size_t written = CONVERT_FROM_OTHER_STRING
(buf, &src, write_limit, &mbstate);
if (written == (size_t) -1)
return -1;
{
Xprintf_buffer_mark_failed (target);
return;
}
if (written == 0)
break;
done = outstring_func (s, (const UCHAR_T *) buf, written, done);
if (done < 0)
return done;
Xprintf_buffer_write (target, buf, written);
total_written += written;
if (prec >= 0)
remaining -= written;
@@ -337,21 +250,20 @@ outstring_converted_wide_string (FILE *s, const OTHER_CHAR_T *src, int prec,
}
/* Add final padding. */
if (width > 0 && left && total_written < width)
return pad_func (s, L_(' '), width - total_written, done);
return done;
if (width > 0 && left)
Xprintf_buffer_pad (target, L_(' '), width - total_written);
}
/* Calls __printf_fp or __printf_fphex based on the value of the
format specifier INFO->spec. */
static inline int
__printf_fp_spec (FILE *fp, const struct printf_info *info,
const void *const *args)
static inline void
__printf_fp_spec (struct Xprintf_buffer *target,
const struct printf_info *info, const void *const *args)
{
if (info->spec == 'a' || info->spec == 'A')
return __printf_fphex (fp, info, args);
Xprintf (fphex_l_buffer) (target, _NL_CURRENT_LOCALE, info, args);
else
return __printf_fp (fp, info, args);
Xprintf (fp_l_buffer) (target, _NL_CURRENT_LOCALE, info, args);
}
/* For handling long_double and longlong we use the same flag. If
@@ -656,31 +568,29 @@ static const uint8_t jump_table[] =
REF (form_binary), /* for 'B', 'b' */ \
}
/* Helper function to provide temporary buffering for unbuffered streams. */
static int buffered_vfprintf (FILE *stream, const CHAR_T *fmt, va_list,
unsigned int)
__THROW __attribute__ ((noinline));
/* Handle positional format specifiers. */
static int printf_positional (FILE *s,
const CHAR_T *format, int readonly_format,
va_list ap, va_list *ap_savep, int done,
int nspecs_done, const UCHAR_T *lead_str_end,
CHAR_T *work_buffer, int save_errno,
const char *grouping,
THOUSANDS_SEP_T thousands_sep,
unsigned int mode_flags);
static void printf_positional (struct Xprintf_buffer *buf,
const CHAR_T *format, int readonly_format,
va_list ap, va_list *ap_savep,
int nspecs_done, const UCHAR_T *lead_str_end,
CHAR_T *work_buffer, int save_errno,
const char *grouping,
THOUSANDS_SEP_T thousands_sep,
unsigned int mode_flags);
/* Handle unknown format specifier. */
static int printf_unknown (FILE *, const struct printf_info *) __THROW;
static void printf_unknown (struct Xprintf_buffer *,
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);
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 function itself. */
int
vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
/* The buffer-based function itself. */
void
Xprintf_buffer (struct Xprintf_buffer *buf, const CHAR_T *format,
va_list ap, unsigned int mode_flags)
{
/* The character used as thousands separator. */
THOUSANDS_SEP_T thousands_sep = 0;
@@ -688,9 +598,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
/* The string describing the size of groups of digits. */
const char *grouping;
/* Place to accumulate the result. */
int done;
/* Current character in format string. */
const UCHAR_T *f;
@@ -717,30 +624,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
0 if unknown. */
int readonly_format = 0;
/* Orient the stream. */
#ifdef ORIENT
ORIENT;
#endif
/* Sanity check of arguments. */
ARGCHECK (s, format);
#ifdef ORIENT
/* Check for correct orientation. */
if (_IO_vtable_offset (s) == 0
&& _IO_fwide (s, sizeof (CHAR_T) == 1 ? -1 : 1)
!= (sizeof (CHAR_T) == 1 ? -1 : 1))
/* The stream is already oriented otherwise. */
return EOF;
#endif
if (UNBUFFERED_P (s))
/* Use a helper function which will allocate a local temporary buffer
for the stream and then call us again. */
return buffered_vfprintf (s, format, ap, mode_flags);
/* Initialize local variables. */
done = 0;
grouping = (const char *) -1;
#ifdef __va_copy
/* This macro will be available soon in gcc's <stdarg.h>. We need it
@@ -759,17 +643,15 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
f = lead_str_end = __find_specmb ((const UCHAR_T *) format);
#endif
/* Lock stream. */
_IO_cleanup_region_start ((void (*) (void *)) &_IO_funlockfile, s);
_IO_flockfile (s);
/* Write the literal text before the first format. */
outstring ((const UCHAR_T *) format,
lead_str_end - (const UCHAR_T *) format);
Xprintf_buffer_write (buf, format,
lead_str_end - (const UCHAR_T *) format);
if (Xprintf_buffer_has_failed (buf))
return;
/* If we only have to print a simple string, return now. */
if (*f == L_('\0'))
goto all_done;
return;
/* Use the slow path in case any printf handler is registered. */
if (__glibc_unlikely (__printf_function_table != NULL
@@ -885,7 +767,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
if (pos == -1)
{
__set_errno (EOVERFLOW);
done = -1;
Xprintf_buffer_mark_failed (buf);
goto all_done;
}
@@ -912,7 +794,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
if (__glibc_unlikely (width == -1))
{
__set_errno (EOVERFLOW);
done = -1;
Xprintf_buffer_mark_failed (buf);
goto all_done;
}
@@ -935,7 +817,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
if (pos == -1)
{
__set_errno (EOVERFLOW);
done = -1;
Xprintf_buffer_mark_failed (buf);
goto all_done;
}
@@ -958,7 +840,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
if (prec == -1)
{
__set_errno (EOVERFLOW);
done = -1;
Xprintf_buffer_mark_failed (buf);
goto all_done;
}
}
@@ -1058,13 +940,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
PARSE_FLOAT_VA_ARG_EXTENDED (info);
const void *ptr = &the_arg;
int function_done = __printf_fp_spec (s, &info, &ptr);
if (function_done < 0)
{
done = -1;
goto all_done;
}
done_add (function_done);
__printf_fp_spec (buf, &info, &ptr);
}
break;
@@ -1073,7 +949,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
{
/* The format string ended before the specifier is complete. */
__set_errno (EINVAL);
done = -1;
Xprintf_buffer_mark_failed (buf);
goto all_done;
}
@@ -1093,30 +969,28 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap, unsigned int mode_flags)
#endif
/* Write the following constant string. */
outstring (end_of_spec, f - end_of_spec);
Xprintf_buffer_write (buf, (const CHAR_T *) end_of_spec,
f - end_of_spec);
}
while (*f != L_('\0'));
while (*f != L_('\0') && !Xprintf_buffer_has_failed (buf));
/* Unlock stream and return. */
goto all_done;
all_done:
/* printf_positional performs cleanup under its all_done label, so
vfprintf-process-arg.c uses it for this function and
printf_positional below. */
return;
/* Hand off processing for positional parameters. */
do_positional:
done = printf_positional (s, format, readonly_format, ap, &ap_save,
done, nspecs_done, lead_str_end, work_buffer,
save_errno, grouping, thousands_sep, mode_flags);
all_done:
/* Unlock the stream. */
_IO_funlockfile (s);
_IO_cleanup_region_end (0);
return done;
printf_positional (buf, format, readonly_format, ap, &ap_save,
nspecs_done, lead_str_end, work_buffer,
save_errno, grouping, thousands_sep, mode_flags);
}
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,167 @@ 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 <locale.h>. The grouped number extends from
the returned pointer until REAR_PTR. FRONT_PTR to W is used as a
scratch area. */
static CHAR_T *
group_number (CHAR_T *front_ptr, CHAR_T *w, CHAR_T *rear_ptr,
const char *grouping, THOUSANDS_SEP_T thousands_sep)
static void
group_number (struct Xprintf_buffer *buf,
struct grouping_iterator *iter,
CHAR_T *from, CHAR_T *to, THOUSANDS_SEP_T thousands_sep,
bool i18n)
{
/* Length of the current group. */
int len;
#ifndef COMPILE_WPRINTF
/* Length of the separator (in wide mode, the separator is always a
single wide character). */
int tlen = strlen (thousands_sep);
#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)
{
*--w = *--s;
if (--len == 0 && s > front_ptr)
{
/* A new group begins. */
if (!i18n)
for (CHAR_T *cp = from; cp != to; ++cp)
{
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;
__printf_buffer_puts (buf, thousands_sep);
#endif
if (*grouping == CHAR_MAX
#if CHAR_MIN < 0
|| *grouping < 0
#endif
)
}
Xprintf_buffer_putc (buf, *cp);
}
else
{
/* Apply digit translation and grouping. */
for (CHAR_T *cp = from; cp != to; ++cp)
{
if (__grouping_iterator_next (iter))
{
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;
#ifdef COMPILE_WPRINTF
__wprintf_buffer_putc (buf, thousands_sep);
#else
__printf_buffer_puts (buf, thousands_sep);
#endif
}
else if (*grouping != '\0')
len = *grouping++;
else
/* The previous grouping repeats ad infinitum. */
len = grouping[-1];
int digit = *cp - '0';
#ifdef COMPILE_WPRINTF
__wprintf_buffer_putc
(buf, _NL_CURRENT_WORD (LC_CTYPE,
_NL_CTYPE_OUTDIGIT0_WC + digit));
#else
__printf_buffer_puts
(buf, _NL_CURRENT (LC_CTYPE, _NL_CTYPE_OUTDIGIT0_MB + digit));
#endif
}
}
return w;
}
/* Helper "class" for `fprintf to unbuffered': creates a temporary buffer. */
struct helper_file
{
struct _IO_FILE_plus _f;
#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;
}
#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;
}
#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
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;
#endif
hp->_flags2 = s->_flags2;
_IO_JUMPS (&helper._f) = (struct _IO_jump_t *) &_IO_helper_jumps;
/* Sanity check of arguments. */
ARGCHECK (s, format);
/* Now print to helper instead. */
result = vfprintf (hp, format, args, mode_flags);
#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 (!_IO_need_lock (s))
{
struct Xprintf (buffer_to_file) wrap;
Xprintf (buffer_to_file_init) (&wrap, s);
Xprintf_buffer (&wrap.base, format, ap, mode_flags);
return Xprintf (buffer_to_file_done) (&wrap);
}
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;
}
+95 -77
View File
@@ -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);
outstring (string, workend - string);
if (octal_marker)
Xprintf_buffer_putc (buf, L_('0'));
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);
outstring (string, workend - string);
if (octal_marker)
Xprintf_buffer_putc (buf, L_('0'));
PAD (L_(' '));
if (number_slow_path)
group_number (buf, &iter, string, workend, thousands_sep,
use_outdigits && base == 10);
else
Xprintf_buffer_write (buf, string, workend - string);
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 */
+153
View File
@@ -0,0 +1,153 @@
/* FILE * interface to a struct __wprintf_buffer. Wide version.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <printf_buffer_as_file.h>
#include <assert.h>
#include <printf_buffer.h>
/* Commit the data directly written through the stdio stream. */
static void
__wprintf_buffer_as_file_commit (struct __wprintf_buffer_as_file *file)
{
/* Check that the write pointers in the file stream are consistent
with the next buffer. */
assert (file->wide_stream._IO_write_ptr >= file->next->write_ptr);
assert (file->wide_stream._IO_write_ptr <= file->next->write_end);
assert (file->wide_stream._IO_write_base == file->next->write_base);
assert (file->wide_stream._IO_write_end == file->next->write_end);
file->next->write_ptr = file->wide_stream._IO_write_ptr;
}
/* Pointer the FILE * write buffer into the active struct __wprintf_buffer
area. */
static void
__wprintf_buffer_as_file_switch_to_buffer (struct __wprintf_buffer_as_file *file)
{
file->wide_stream._IO_write_base = file->next->write_base;
file->wide_stream._IO_write_ptr = file->next->write_ptr;
file->wide_stream._IO_write_end = file->next->write_end;
}
/* Only a small subset of the vtable functions is implemented here,
following _IO_obstack_jumps. */
static wint_t
__wprintf_buffer_as_file_overflow (FILE *fp, int ch)
{
struct __wprintf_buffer_as_file *file
= (struct __wprintf_buffer_as_file *) fp;
__wprintf_buffer_as_file_commit (file);
/* EOF means only a flush is requested. */
if (ch != WEOF)
__wprintf_buffer_putc (file->next, ch);
else
ch = 0;
/* Ensure that flushing actually produces room. */
if (!__wprintf_buffer_has_failed (file->next)
&& file->next->write_ptr == file->next->write_end)
__wprintf_buffer_flush (file->next);
__wprintf_buffer_as_file_switch_to_buffer (file);
if (!__wprintf_buffer_has_failed (file->next))
return (unsigned char) ch;
else
return WEOF;
}
static size_t
__wprintf_buffer_as_file_xsputn (FILE *fp, const void *buf, size_t len)
{
struct __wprintf_buffer_as_file *file
= (struct __wprintf_buffer_as_file *) fp;
__wprintf_buffer_as_file_commit (file);
/* Copy the data. */
__wprintf_buffer_write (file->next, buf, len);
__wprintf_buffer_as_file_switch_to_buffer (file);
if (!__wprintf_buffer_has_failed (file->next))
return len;
else
/* We may actually have written something. But the stream is
corrupted in this case anyway, so try not to divine the write
count here. */
return 0;
}
static const struct _IO_jump_t _IO_wprintf_buffer_as_file_jumps libio_vtable =
{
JUMP_INIT_DUMMY,
JUMP_INIT(finish, NULL),
JUMP_INIT(overflow, (_IO_overflow_t) __wprintf_buffer_as_file_overflow),
JUMP_INIT(underflow, NULL),
JUMP_INIT(uflow, NULL),
JUMP_INIT(pbackfail, NULL),
JUMP_INIT(xsputn, __wprintf_buffer_as_file_xsputn),
JUMP_INIT(xsgetn, NULL),
JUMP_INIT(seekoff, NULL),
JUMP_INIT(seekpos, NULL),
JUMP_INIT(setbuf, NULL),
JUMP_INIT(sync, NULL),
JUMP_INIT(doallocate, NULL),
JUMP_INIT(read, NULL),
JUMP_INIT(write, NULL),
JUMP_INIT(seek, NULL),
JUMP_INIT(close, NULL),
JUMP_INIT(stat, NULL),
JUMP_INIT(showmanyc, NULL),
JUMP_INIT(imbue, NULL)
};
void
__wprintf_buffer_as_file_init (struct __wprintf_buffer_as_file *file,
struct __wprintf_buffer *next)
{
file->stream._lock = NULL;
_IO_no_init (&file->stream, _IO_USER_LOCK, 0, &file->wide_stream,
&_IO_wprintf_buffer_as_file_jumps);
_IO_fwide (&file->stream, 1);
/* Set up the write buffer from the next buffer. */
file->next = next;
__wprintf_buffer_as_file_switch_to_buffer (file);
/* Mark the read area as inactive, by making all pointers equal. */
file->stream._IO_read_base = file->stream._IO_write_base;
file->stream._IO_read_ptr = file->stream._IO_write_base;
file->stream._IO_read_end = file->stream._IO_write_base;
}
bool
__wprintf_buffer_as_file_terminate (struct __wprintf_buffer_as_file *file)
{
if (file->stream._flags & _IO_ERR_SEEN)
return false;
else
{
__wprintf_buffer_as_file_commit (file);
return true;
}
}
+21
View File
@@ -0,0 +1,21 @@
/* Final status reporting for struct __wprintf_buffer. Wide version.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <printf_buffer.h>
#include "printf_buffer-wchar_t.h"
#include "Xprintf_buffer_done.c"
+42
View File
@@ -0,0 +1,42 @@
/* Flush a struct __wprintf_buffer. Wide version.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <errno.h>
#include <printf_buffer.h>
#include "printf_buffer-wchar_t.h"
#include "Xprintf_buffer_flush.c"
static void
__wprintf_buffer_do_flush (struct __wprintf_buffer *buf)
{
switch (buf->mode)
{
case __wprintf_buffer_mode_failed:
return;
case __wprintf_buffer_mode_to_file:
__wprintf_buffer_flush_to_file ((struct __wprintf_buffer_to_file *) buf);
return;
case __wprintf_buffer_mode_swprintf:
buf->write_end[-1] = L'\0';
__set_errno (E2BIG);
__wprintf_buffer_mark_failed (buf);
return;
}
__builtin_trap ();
}
+21
View File
@@ -0,0 +1,21 @@
/* Write repeated characters to struct __wprintf_buffer. Wide version.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <printf_buffer.h>
#include "printf_buffer-wchar_t.h"
#include "Xprintf_buffer_pad_1.c"
+21
View File
@@ -0,0 +1,21 @@
/* Overflow character write function for struct __wprintf_buffer. Wide version.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <printf_buffer.h>
#include "printf_buffer-wchar_t.h"
#include "Xprintf_buffer_putc_1.c"
+21
View File
@@ -0,0 +1,21 @@
/* String write function struct __wprintf_buffer. Wide version.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <printf_buffer.h>
#include "printf_buffer-wchar_t.h"
#include "Xprintf_buffer_puts_1.c"
+55
View File
@@ -0,0 +1,55 @@
/* Wide printf buffers writing data to a FILE *.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
/* This implementation is not optimized (unlikely the multibyte
implementation) and always writes to the temporary buffer first. */
#include <printf_buffer_to_file.h>
#include <array_length.h>
#include <libio/libioP.h>
void
__wprintf_buffer_flush_to_file (struct __wprintf_buffer_to_file *buf)
{
size_t count = buf->base.write_ptr - buf->stage;
if ((size_t) _IO_sputn (buf->fp, buf->stage, count) != count)
{
__wprintf_buffer_mark_failed (&buf->base);
return;
}
buf->base.written += count;
buf->base.write_ptr = buf->stage;
}
void
__wprintf_buffer_to_file_init (struct __wprintf_buffer_to_file *buf, FILE *fp)
{
__wprintf_buffer_init (&buf->base, buf->stage, array_length (buf->stage),
__wprintf_buffer_mode_to_file);
buf->fp = fp;
}
int
__wprintf_buffer_to_file_done (struct __wprintf_buffer_to_file *buf)
{
if (__wprintf_buffer_has_failed (&buf->base))
return -1;
__wprintf_buffer_flush_to_file (buf);
return __wprintf_buffer_done (&buf->base);
}
+21
View File
@@ -0,0 +1,21 @@
/* Blob write function for struct __wprintf_buffer. Wide version.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <printf_buffer.h>
#include "printf_buffer-wchar_t.h"
#include "Xprintf_buffer_write.c"
+22
View File
@@ -0,0 +1,22 @@
/* Invoke a printf specifier handler. Wide version.
Copyright (C) 2022 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <printf.h>
#include <printf_buffer_as_file.h>
#include "printf_buffer-wchar_t.h"
#include "Xprintf_function_invoke.c"
+76 -120
View File
@@ -29,33 +29,8 @@
#include <string.h>
#include "../locale/localeinfo.h"
#include <bits/floatn.h>
#define out_char(Ch) \
do { \
if (dest >= s + maxsize - 1) \
{ \
__set_errno (E2BIG); \
va_end (ap); \
return -1; \
} \
*dest++ = (Ch); \
} while (0)
#define out_string(String) \
do { \
const char *_s = (String); \
while (*_s) \
out_char (*_s++); \
} while (0)
#define out_nstring(String, N) \
do { \
int _n = (N); \
const char *_s = (String); \
while (_n-- > 0) \
out_char (*_s++); \
} while (0)
#include <stdio-common/grouping_iterator.h>
#include <printf_buffer.h>
#define to_digit(Ch) ((Ch) - '0')
@@ -75,21 +50,15 @@
some information in the LC_MONETARY category which should be used,
too. Some of the information contradicts the information which can
be specified in format string. */
ssize_t
__vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
const char *format, va_list ap, unsigned int flags)
static void
__vstrfmon_l_buffer (struct __printf_buffer *buf, locale_t loc,
const char *fmt, va_list ap, unsigned int flags)
{
struct __locale_data *current = loc->__locales[LC_MONETARY];
_IO_strfile f;
struct printf_info info;
char *dest; /* Pointer so copy the output. */
const char *fmt; /* Pointer that walks through format. */
dest = s;
fmt = format;
/* Loop through the format-string. */
while (*fmt != '\0')
while (*fmt != '\0' && !__printf_buffer_has_failed (buf))
{
/* The floating-point value to output. */
union
@@ -122,11 +91,9 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
int other_cs_precedes;
const char *sign_string;
const char *other_sign_string;
int done;
const char *currency_symbol;
size_t currency_symbol_len;
long int width;
char *startp;
const void *ptr;
char space_char;
@@ -134,14 +101,14 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
specification. */
if (*fmt != '%')
{
out_char (*fmt++);
__printf_buffer_putc (buf, *fmt++);
continue;
}
/* "%%" means a single '%' character. */
if (fmt[1] == '%')
{
out_char (*++fmt);
__printf_buffer_putc (buf, *++fmt);
++fmt;
continue;
}
@@ -171,7 +138,8 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
{
/* Premature EOS. */
__set_errno (EINVAL);
return -1;
__printf_buffer_mark_failed (buf);
return;
}
continue;
case '^': /* Don't group digits. */
@@ -181,7 +149,8 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
if (n_sign_posn != -2)
{
__set_errno (EINVAL);
return -1;
__printf_buffer_mark_failed (buf);
return;
}
p_sign_posn = *_NL_CURRENT (LC_MONETARY, P_SIGN_POSN);
n_sign_posn = *_NL_CURRENT (LC_MONETARY, N_SIGN_POSN);
@@ -190,7 +159,8 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
if (n_sign_posn != -2)
{
__set_errno (EINVAL);
return -1;
__printf_buffer_mark_failed (buf);
return;
}
p_sign_posn = 0;
n_sign_posn = 0;
@@ -220,19 +190,12 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
|| (width == LONG_MAX && val > LONG_MAX % 10))
{
__set_errno (E2BIG);
return -1;
__printf_buffer_mark_failed (buf);
return;
}
width = width * 10 + val;
}
/* If we don't have enough room for the demanded width we
can stop now and return an error. */
if (width >= maxsize - (dest - s))
{
__set_errno (E2BIG);
return -1;
}
}
/* Recognize left precision. */
@@ -241,7 +204,8 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
if (!isdigit (*++fmt))
{
__set_errno (EINVAL);
return -1;
__printf_buffer_mark_failed (buf);
return;
}
left_prec = to_digit (*fmt);
@@ -258,7 +222,8 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
if (!isdigit (*++fmt))
{
__set_errno (EINVAL);
return -1;
__printf_buffer_mark_failed (buf);
return;
}
right_prec = to_digit (*fmt);
@@ -306,7 +271,8 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
break;
default: /* Any unrecognized format is an error. */
__set_errno (EINVAL);
return -1;
__printf_buffer_mark_failed (buf);
return;
}
/* If not specified by the format string now find the values for
@@ -327,8 +293,11 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
/* If we have to print the digits grouped determine how many
extra characters this means. */
if (group && left_prec != -1)
left_prec += __guess_grouping (left_prec,
_NL_CURRENT (LC_MONETARY, MON_GROUPING));
{
struct grouping_iterator it;
__grouping_iterator_init (&it, LC_MONETARY, loc, left_prec);
left_prec += it.separators;
}
/* Now it's time to get the value. */
if (is_long_double == 1)
@@ -482,57 +451,46 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
#define left_paren '('
#define right_paren ')'
startp = dest; /* Remember start so we can compute length. */
char *startp = buf->write_ptr;
while (left_pad-- > 0)
out_char (' ');
__printf_buffer_pad (buf, ' ', left_pad);
if (sign_posn == 0 && is_negative)
out_char (left_paren);
__printf_buffer_putc (buf, left_paren);
if (cs_precedes)
{
if (sign_posn != 0 && sign_posn != 2 && sign_posn != 4
&& sign_posn != 5)
{
out_string (sign_string);
__printf_buffer_puts (buf, sign_string);
if (sep_by_space == 2)
out_char (' ');
__printf_buffer_putc (buf, ' ');
}
if (print_curr_symbol)
out_string (currency_symbol);
__printf_buffer_puts (buf, currency_symbol);
if (sign_posn == 4)
{
if (print_curr_symbol && sep_by_space == 2)
out_char (space_char);
out_string (sign_string);
__printf_buffer_putc (buf, space_char);
__printf_buffer_puts (buf, sign_string);
if (sep_by_space == 1)
/* POSIX.2 and SUS are not clear on this case, but C99
says a space follows the adjacent-symbol-and-sign */
out_char (' ');
__printf_buffer_putc (buf, ' ');
}
else
if (print_curr_symbol && sep_by_space == 1)
out_char (space_char);
__printf_buffer_putc (buf, space_char);
}
else
if (sign_posn != 0 && sign_posn != 2 && sign_posn != 3
&& sign_posn != 4 && sign_posn != 5)
out_string (sign_string);
__printf_buffer_puts (buf, sign_string);
/* Print the number. */
#ifdef _IO_MTSAFE_IO
f._sbf._f._lock = NULL;
#endif
_IO_init_internal (&f._sbf._f, 0);
_IO_JUMPS (&f._sbf) = &_IO_str_jumps;
_IO_str_init_static_internal (&f, dest, (s + maxsize) - dest, dest);
/* We clear the last available byte so we can find out whether
the numeric representation is too long. */
s[maxsize - 1] = '\0';
memset (&info, '\0', sizeof (info));
info.prec = right_prec;
info.width = left_prec + (right_prec ? (right_prec + 1) : 0);
@@ -544,25 +502,17 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
info.extra = 1; /* This means use values from LC_MONETARY. */
ptr = &fpnum;
done = __printf_fp_l (&f._sbf._f, loc, &info, &ptr);
if (done < 0)
return -1;
if (s[maxsize - 1] != '\0')
{
__set_errno (E2BIG);
return -1;
}
dest += done;
__printf_fp_l_buffer (buf, loc, &info, &ptr);
if (__printf_buffer_has_failed (buf))
return;
if (!cs_precedes)
{
if (sign_posn == 3)
{
if (sep_by_space == 1)
out_char (' ');
out_string (sign_string);
__printf_buffer_putc (buf, ' ');
__printf_buffer_puts (buf, sign_string);
}
if (print_curr_symbol)
@@ -572,55 +522,61 @@ __vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
|| (sign_posn == 2 && sep_by_space == 1)
|| (sign_posn == 1 && sep_by_space == 1)
|| (sign_posn == 0 && sep_by_space == 1))
out_char (space_char);
out_nstring (currency_symbol, currency_symbol_len);
__printf_buffer_putc (buf, space_char);
__printf_buffer_write (buf, currency_symbol,
__strnlen (currency_symbol,
currency_symbol_len));
}
if (sign_posn == 4)
{
if (sep_by_space == 2)
out_char (' ');
out_string (sign_string);
__printf_buffer_putc (buf, ' ');
__printf_buffer_puts (buf, sign_string);
}
}
if (sign_posn == 2)
{
if (sep_by_space == 2)
out_char (' ');
out_string (sign_string);
__printf_buffer_putc (buf, ' ');
__printf_buffer_puts (buf, sign_string);
}
if (sign_posn == 0 && is_negative)
out_char (right_paren);
__printf_buffer_putc (buf, right_paren);
/* Now test whether the output width is filled. */
if (dest - startp < width)
if (buf->write_ptr - startp < width)
{
if (left)
/* We simply have to fill using spaces. */
do
out_char (' ');
while (dest - startp < width);
else
size_t pad_width = width - (buf->write_ptr - startp);
__printf_buffer_pad (buf, ' ', pad_width);
if (__printf_buffer_has_failed (buf))
/* Implies length check. */
return;
/* Left padding is already in the correct position.
Otherwise move the field contents in place. */
if (!left)
{
long int dist = width - (dest - startp);
for (char *cp = dest - 1; cp >= startp; --cp)
cp[dist] = cp[0];
dest += dist;
do
startp[--dist] = ' ';
while (dist > 0);
memmove (startp + pad_width, startp, buf->write_ptr - startp);
memset (startp, ' ', pad_width);
}
}
}
}
/* Terminate the string. */
*dest = '\0';
return dest - s;
ssize_t
__vstrfmon_l_internal (char *s, size_t maxsize, locale_t loc,
const char *format, va_list ap, unsigned int flags)
{
struct __printf_buffer buf;
__printf_buffer_init (&buf, s, maxsize, __printf_buffer_mode_strfmon);
__vstrfmon_l_buffer (&buf, loc, format, ap, flags);
__printf_buffer_putc (&buf, '\0'); /* Terminate the string. */
if (__printf_buffer_has_failed (&buf))
return -1;
else
return buf.write_ptr - buf.write_base - 1; /* Exclude NUL byte. */
}
ssize_t
+7 -31
View File
@@ -28,6 +28,7 @@
#include <string.h>
#include <locale/localeinfo.h>
#include <fix-float-double-convert-nan.h>
#include <printf_buffer.h>
#define UCHAR_T char
#define L_(Str) Str
@@ -37,12 +38,7 @@
int
STRFROM (char *dest, size_t size, const char *format, FLOAT f)
{
_IO_strnfile sfile;
#ifdef _IO_MTSAFE_IO
sfile.f._sbf._f._lock = NULL;
#endif
int done;
struct __printf_buffer_snprintf buf;
/* Single-precision values need to be stored in a double type, because
__printf_fp_l and __printf_fphex do not accept the float type. */
@@ -106,23 +102,8 @@ STRFROM (char *dest, size_t size, const char *format, FLOAT f)
abort ();
}
/* The following code to prepare the virtual file has been adapted from the
function __vsnprintf_internal from libio. */
if (size == 0)
{
/* When size is zero, nothing is written and dest may be a null pointer.
This is specified for snprintf in ISO/IEC 9899:2011, Section 7.21.6.5,
in the second paragraph. Thus, if size is zero, prepare to use the
overflow buffer right from the start. */
dest = sfile.overflow_buf;
size = sizeof (sfile.overflow_buf);
}
/* Prepare the virtual string file. */
_IO_no_init (&sfile.f._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
_IO_JUMPS (&sfile.f._sbf) = &_IO_strn_jumps;
_IO_str_init_static_internal (&sfile.f, dest, size - 1, dest);
/* Prepare the string buffer. */
__printf_buffer_snprintf_init (&buf, dest, size);
/* Prepare the format specification for printf_fp. */
memset (&info, '\0', sizeof (info));
@@ -144,13 +125,8 @@ STRFROM (char *dest, size_t size, const char *format, FLOAT f)
info.spec = specifier;
if (info.spec != 'a' && info.spec != 'A')
done = __printf_fp_l (&sfile.f._sbf._f, _NL_CURRENT_LOCALE, &info, &fpptr);
__printf_fp_l_buffer (&buf.base, _NL_CURRENT_LOCALE, &info, &fpptr);
else
done = __printf_fphex (&sfile.f._sbf._f, &info, &fpptr);
/* Terminate the string. */
if (sfile.f._sbf._f._IO_buf_base != sfile.overflow_buf)
*sfile.f._sbf._f._IO_write_ptr = '\0';
return done;
__printf_fphex_l_buffer (&buf.base, _NL_CURRENT_LOCALE, &info, &fpptr);
return __printf_buffer_snprintf_done (&buf);
}
+1 -7
View File
@@ -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++; \
+7 -29
View File
@@ -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'; \
\
+7 -29
View File
@@ -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'; \
\
+4 -18
View File
@@ -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'. */ \
+3 -18
View File
@@ -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'. */ \