diff options
Diffstat (limited to 'stdlib/strfrom-skeleton.c')
-rw-r--r-- | stdlib/strfrom-skeleton.c | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/stdlib/strfrom-skeleton.c b/stdlib/strfrom-skeleton.c new file mode 100644 index 0000000000..8fd1beb7aa --- /dev/null +++ b/stdlib/strfrom-skeleton.c @@ -0,0 +1,149 @@ +/* Convert a floating-point number to string. + Copyright (C) 2016 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 + <http://www.gnu.org/licenses/>. */ + +/* 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 + function _IO_vsnprintf 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 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); + + /* 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; +} |