2016-06-29 04:30:42 +08:00
|
|
|
/* Convert a floating-point number to string.
|
2021-01-03 03:32:25 +08:00
|
|
|
Copyright (C) 2016-2021 Free Software Foundation, Inc.
|
2016-06-29 04:30:42 +08:00
|
|
|
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
|
Prefer https to http for gnu.org and fsf.org URLs
Also, change sources.redhat.com to sourceware.org.
This patch was automatically generated by running the following shell
script, which uses GNU sed, and which avoids modifying files imported
from upstream:
sed -ri '
s,(http|ftp)(://(.*\.)?(gnu|fsf|sourceware)\.org($|[^.]|\.[^a-z])),https\2,g
s,(http|ftp)(://(.*\.)?)sources\.redhat\.com($|[^.]|\.[^a-z]),https\2sourceware.org\4,g
' \
$(find $(git ls-files) -prune -type f \
! -name '*.po' \
! -name 'ChangeLog*' \
! -path COPYING ! -path COPYING.LIB \
! -path manual/fdl-1.3.texi ! -path manual/lgpl-2.1.texi \
! -path manual/texinfo.tex ! -path scripts/config.guess \
! -path scripts/config.sub ! -path scripts/install-sh \
! -path scripts/mkinstalldirs ! -path scripts/move-if-change \
! -path INSTALL ! -path locale/programs/charmap-kw.h \
! -path po/libc.pot ! -path sysdeps/gnu/errlist.c \
! '(' -name configure \
-execdir test -f configure.ac -o -f configure.in ';' ')' \
! '(' -name preconfigure \
-execdir test -f preconfigure.ac ';' ')' \
-print)
and then by running 'make dist-prepare' to regenerate files built
from the altered files, and then executing the following to cleanup:
chmod a+x sysdeps/unix/sysv/linux/riscv/configure
# Omit irrelevant whitespace and comment-only changes,
# perhaps from a slightly-different Autoconf version.
git checkout -f \
sysdeps/csky/configure \
sysdeps/hppa/configure \
sysdeps/riscv/configure \
sysdeps/unix/sysv/linux/csky/configure
# Omit changes that caused a pre-commit check to fail like this:
# remote: *** error: sysdeps/powerpc/powerpc64/ppc-mcount.S: trailing lines
git checkout -f \
sysdeps/powerpc/powerpc64/ppc-mcount.S \
sysdeps/unix/sysv/linux/s390/s390-64/syscall.S
# Omit change that caused a pre-commit check to fail like this:
# remote: *** error: sysdeps/sparc/sparc64/multiarch/memcpy-ultra3.S: last line does not end in newline
git checkout -f sysdeps/sparc/sparc64/multiarch/memcpy-ultra3.S
2019-09-07 13:40:42 +08:00
|
|
|
<https://www.gnu.org/licenses/>. */
|
2016-06-29 04:30:42 +08:00
|
|
|
|
|
|
|
/* Generic implementation for strfrom functions. The implementation is generic
|
|
|
|
for several floating-point types (e.g.: float, double), so that each
|
|
|
|
function, such as strfromf and strfroml, share the same code, thus avoiding
|
|
|
|
code duplication. */
|
|
|
|
|
|
|
|
#include <ctype.h>
|
|
|
|
#include "../libio/libioP.h"
|
|
|
|
#include "../libio/strfile.h"
|
|
|
|
#include <printf.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <locale/localeinfo.h>
|
|
|
|
|
|
|
|
#define UCHAR_T char
|
|
|
|
#define L_(Str) Str
|
|
|
|
#define ISDIGIT(Ch) isdigit (Ch)
|
|
|
|
#include "stdio-common/printf-parse.h"
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
/* Single-precision values need to be stored in a double type, because
|
|
|
|
__printf_fp_l and __printf_fphex do not accept the float type. */
|
|
|
|
union {
|
|
|
|
double flt;
|
|
|
|
FLOAT value;
|
|
|
|
} fpnum;
|
|
|
|
const void *fpptr;
|
|
|
|
fpptr = &fpnum;
|
|
|
|
|
|
|
|
/* Variables to control the output format. */
|
|
|
|
int precision = -1; /* printf_fp and printf_fphex treat this internally. */
|
|
|
|
int specifier;
|
|
|
|
struct printf_info info;
|
|
|
|
|
|
|
|
/* Single-precision values need to be converted into double-precision,
|
|
|
|
because __printf_fp and __printf_fphex only accept double and long double
|
|
|
|
as the floating-point argument. */
|
|
|
|
if (__builtin_types_compatible_p (FLOAT, float))
|
|
|
|
fpnum.flt = f;
|
|
|
|
else
|
|
|
|
fpnum.value = f;
|
|
|
|
|
|
|
|
/* Check if the first character in the format string is indeed the '%'
|
|
|
|
character. Otherwise, abort. */
|
|
|
|
if (*format == '%')
|
|
|
|
format++;
|
|
|
|
else
|
|
|
|
abort ();
|
|
|
|
|
|
|
|
/* The optional precision specification always starts with a '.'. If such
|
|
|
|
character is present, read the precision. */
|
|
|
|
if (*format == '.')
|
|
|
|
{
|
|
|
|
format++;
|
|
|
|
|
|
|
|
/* Parse the precision. */
|
|
|
|
if (ISDIGIT (*format))
|
|
|
|
precision = read_int (&format);
|
|
|
|
/* If only the period is specified, the precision is taken as zero, as
|
|
|
|
described in ISO/IEC 9899:2011, section 7.21.6.1, 4th paragraph, 3rd
|
|
|
|
item. */
|
|
|
|
else
|
|
|
|
precision = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now there is only the conversion specifier to be read. */
|
|
|
|
switch (*format)
|
|
|
|
{
|
|
|
|
case 'a':
|
|
|
|
case 'A':
|
|
|
|
case 'e':
|
|
|
|
case 'E':
|
|
|
|
case 'f':
|
|
|
|
case 'F':
|
|
|
|
case 'g':
|
|
|
|
case 'G':
|
|
|
|
specifier = *format;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
abort ();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The following code to prepare the virtual file has been adapted from the
|
Add __v*printf_internal with flags arguments
There are a lot more printf variants than there are scanf variants,
and the code for setting up and tearing down their custom FILE
variants around the call to __vf(w)printf is more complicated and
variable. Therefore, I have added _internal versions of all the
v*printf variants, rather than introducing helper routines so that
they can all directly call __vf(w)printf_internal, as was done with
scanf.
As with the scanf changes, in this patch the _internal functions still
look at the environmental mode bits and all callers pass 0 for the
flags parameter.
Several of the affected public functions had _IO_ name aliases that
were not exported (but, in one case, appeared in libio.h anyway);
I was originally planning to leave them as aliases to avoid having
to touch internal callers, but it turns out ldbl_*_alias only work
for exported symbols, so they've all been removed instead. It also
turns out there were hardly any internal callers. _IO_vsprintf and
_IO_vfprintf *are* exported, so those two stick around.
Summary for the changes to each of the affected symbols:
_IO_vfprintf, _IO_vsprintf:
All internal calls removed, thus the internal declarations, as well
as uses of libc_hidden_proto and libc_hidden_def, were also removed.
The external symbol is now exposed via uses of ldbl_strong_alias
to __vfprintf_internal and __vsprintf_internal, respectively.
_IO_vasprintf, _IO_vdprintf, _IO_vsnprintf,
_IO_vfwprintf, _IO_vswprintf,
_IO_obstack_vprintf, _IO_obstack_printf:
All internal calls removed, thus declaration in internal headers
were also removed. They were never exported, so there are no
aliases tying them to the internal functions. I.e.: entirely gone.
__vsnprintf:
Internal calls were always preceded by macros such as
#define __vsnprintf _IO_vsnprintf, and
#define __vsnprintf vsnprintf
The macros were removed and their uses replaced with calls to the
new internal function __vsnprintf_internal. Since there were no
internal calls, the internal declaration was also removed. The
external symbol is preserved with ldbl_weak_alias to ___vsnprintf.
__vfwprintf:
All internal calls converted into calls to __vfwprintf_internal,
thus the internal declaration was removed. The function is now a
wrapper that calls __vfwprintf_internal. The external symbol is
preserved.
__vswprintf:
Similarly, but no external symbol.
__vasprintf, __vdprintf, __vfprintf, __vsprintf:
New internal wrappers. Not exported.
vasprintf, vdprintf, vfprintf, vsprintf, vsnprintf,
vfwprintf, vswprintf,
obstack_vprintf, obstack_printf:
These functions used to be aliases to the respective _IO_* function,
they are now aliases to their respective __* functions.
Tested for powerpc and powerpc64le.
2018-03-08 03:32:01 +08:00
|
|
|
function __vsnprintf_internal from libio. */
|
2016-06-29 04:30:42 +08:00
|
|
|
|
|
|
|
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 format specification for printf_fp. */
|
|
|
|
memset (&info, '\0', sizeof (info));
|
|
|
|
|
|
|
|
/* The functions strfromd and strfromf pass a floating-point number with
|
|
|
|
double precision to printf_fp, whereas strfroml passes a floating-point
|
|
|
|
number with long double precision. The following line informs printf_fp
|
|
|
|
which type of floating-point number is being passed. */
|
|
|
|
info.is_long_double = __builtin_types_compatible_p (FLOAT, long double);
|
|
|
|
|
2016-11-03 22:37:08 +08:00
|
|
|
/* Similarly, the function strfromf128 passes a floating-point number in
|
|
|
|
_Float128 format to printf_fp. */
|
|
|
|
#if __HAVE_DISTINCT_FLOAT128
|
|
|
|
info.is_binary128 = __builtin_types_compatible_p (FLOAT, _Float128);
|
|
|
|
#endif
|
|
|
|
|
2016-06-29 04:30:42 +08:00
|
|
|
/* Set info according to the format string. */
|
|
|
|
info.prec = precision;
|
|
|
|
info.spec = specifier;
|
|
|
|
|
|
|
|
if (info.spec != 'a' && info.spec != 'A')
|
|
|
|
done = __printf_fp_l (&sfile.f._sbf._f, _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;
|
|
|
|
}
|