1 /* Convert a floating-point number to string.
2    Copyright (C) 2016-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 /* Generic implementation for strfrom functions.  The implementation is generic
20    for several floating-point types (e.g.: float, double), so that each
21    function, such as strfromf and strfroml, share the same code, thus avoiding
22    code duplication.  */
23 
24 #include <ctype.h>
25 #include "../libio/libioP.h"
26 #include "../libio/strfile.h"
27 #include <printf.h>
28 #include <string.h>
29 #include <locale/localeinfo.h>
30 
31 #define UCHAR_T char
32 #define L_(Str) Str
33 #define ISDIGIT(Ch) isdigit (Ch)
34 #include "stdio-common/printf-parse.h"
35 
36 int
STRFROM(char * dest,size_t size,const char * format,FLOAT f)37 STRFROM (char *dest, size_t size, const char *format, FLOAT f)
38 {
39   _IO_strnfile sfile;
40 #ifdef _IO_MTSAFE_IO
41   sfile.f._sbf._f._lock = NULL;
42 #endif
43 
44   int done;
45 
46   /* Single-precision values need to be stored in a double type, because
47      __printf_fp_l and __printf_fphex do not accept the float type.  */
48   union {
49     double flt;
50     FLOAT value;
51   } fpnum;
52   const void *fpptr;
53   fpptr = &fpnum;
54 
55   /* Variables to control the output format.  */
56   int precision = -1; /* printf_fp and printf_fphex treat this internally.  */
57   int specifier;
58   struct printf_info info;
59 
60   /* Single-precision values need to be converted into double-precision,
61      because __printf_fp and __printf_fphex only accept double and long double
62      as the floating-point argument.  */
63   if (__builtin_types_compatible_p (FLOAT, float))
64     fpnum.flt = f;
65   else
66     fpnum.value = f;
67 
68   /* Check if the first character in the format string is indeed the '%'
69      character.  Otherwise, abort.  */
70   if (*format == '%')
71     format++;
72   else
73     abort ();
74 
75   /* The optional precision specification always starts with a '.'.  If such
76      character is present, read the precision.  */
77   if (*format == '.')
78     {
79       format++;
80 
81       /* Parse the precision.  */
82       if (ISDIGIT (*format))
83 	precision = read_int (&format);
84       /* If only the period is specified, the precision is taken as zero, as
85 	 described in ISO/IEC 9899:2011, section 7.21.6.1, 4th paragraph, 3rd
86 	 item.  */
87       else
88 	precision = 0;
89     }
90 
91   /* Now there is only the conversion specifier to be read.  */
92   switch (*format)
93     {
94     case 'a':
95     case 'A':
96     case 'e':
97     case 'E':
98     case 'f':
99     case 'F':
100     case 'g':
101     case 'G':
102       specifier = *format;
103       break;
104     default:
105       abort ();
106     }
107 
108   /* The following code to prepare the virtual file has been adapted from the
109      function __vsnprintf_internal from libio.  */
110 
111   if (size == 0)
112     {
113     /* When size is zero, nothing is written and dest may be a null pointer.
114        This is specified for snprintf in ISO/IEC 9899:2011, Section 7.21.6.5,
115        in the second paragraph.  Thus, if size is zero, prepare to use the
116        overflow buffer right from the start.  */
117       dest = sfile.overflow_buf;
118       size = sizeof (sfile.overflow_buf);
119     }
120 
121   /* Prepare the virtual string file.  */
122   _IO_no_init (&sfile.f._sbf._f, _IO_USER_LOCK, -1, NULL, NULL);
123   _IO_JUMPS (&sfile.f._sbf) = &_IO_strn_jumps;
124   _IO_str_init_static_internal (&sfile.f, dest, size - 1, dest);
125 
126   /* Prepare the format specification for printf_fp.  */
127   memset (&info, '\0', sizeof (info));
128 
129   /* The functions strfromd and strfromf pass a floating-point number with
130      double precision to printf_fp, whereas strfroml passes a floating-point
131      number with long double precision.  The following line informs printf_fp
132      which type of floating-point number is being passed.  */
133   info.is_long_double = __builtin_types_compatible_p (FLOAT, long double);
134 
135   /* Similarly, the function strfromf128 passes a floating-point number in
136      _Float128 format to printf_fp.  */
137 #if __HAVE_DISTINCT_FLOAT128
138   info.is_binary128 = __builtin_types_compatible_p (FLOAT, _Float128);
139 #endif
140 
141   /* Set info according to the format string.  */
142   info.prec = precision;
143   info.spec = specifier;
144 
145   if (info.spec != 'a' && info.spec != 'A')
146     done = __printf_fp_l (&sfile.f._sbf._f, _NL_CURRENT_LOCALE, &info, &fpptr);
147   else
148     done = __printf_fphex (&sfile.f._sbf._f, &info, &fpptr);
149 
150   /* Terminate the string.  */
151   if (sfile.f._sbf._f._IO_buf_base != sfile.overflow_buf)
152     *sfile.f._sbf._f._IO_write_ptr = '\0';
153 
154   return done;
155 }
156