mirror of
git://sourceware.org/git/glibc.git
synced 2026-09-09 11:58:23 +08:00
Compare commits
11
Commits
master
...
fw/vfprintf-2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b30518eedb | ||
|
|
45234c5d6b | ||
|
|
2deb7d2cff | ||
|
|
214908c0de | ||
|
|
b5edce5385 | ||
|
|
a407f0532f | ||
|
|
2706c12ba5 | ||
|
|
8566149e25 | ||
|
|
8699991d4d | ||
|
|
9e6084af52 | ||
|
|
c0dee012ab |
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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"
|
||||
@@ -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 ();
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+109
-151
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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 ();
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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"
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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++; \
|
||||
|
||||
@@ -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'; \
|
||||
\
|
||||
|
||||
@@ -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'; \
|
||||
\
|
||||
|
||||
@@ -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'. */ \
|
||||
|
||||
@@ -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'. */ \
|
||||
|
||||
Reference in New Issue
Block a user