1 /*
2  * Copyright (c) 1990 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Chris Torek.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 
34 #define _DEFAULT_SOURCE
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <limits.h>
39 #include <stdint.h>
40 #include <math.h>
41 #include <wchar.h>
42 #include <sys/lock.h>
43 #include <stdarg.h>
44 #include "local.h"
45 #include "../stdlib/local.h"
46 #include "fvwrite.h"
47 #include "vfieeefp.h"
48 #include "nano-vfprintf_local.h"
49 
50 char *__cvt (_PRINTF_FLOAT_TYPE value, int ndigits,
51 	     int flags, char *sign, int *decpt, int ch, int *length,
52 	     char *buf);
53 
54 int __exponent (char *p0, int exp, int fmtch);
55 
56 #ifdef FLOATING_POINT
57 
58 /* Using reentrant DATA, convert finite VALUE into a string of digits
59    with no decimal point, using NDIGITS precision and FLAGS as guides
60    to whether trailing zeros must be included.  Set *SIGN to nonzero
61    if VALUE was negative.  Set *DECPT to the exponent plus one.  Set
62    *LENGTH to the length of the returned string.  CH must be one of
63    [aAeEfFgG]; if it is [aA], then the return string lives in BUF,
64    otherwise the return value shares the mprec reentrant storage.  */
65 char *
__cvt(struct _reent * data,_PRINTF_FLOAT_TYPE value,int ndigits,int flags,char * sign,int * decpt,int ch,int * length,char * buf)66 __cvt (struct _reent *data, _PRINTF_FLOAT_TYPE value, int ndigits, int flags,
67        char *sign, int *decpt, int ch, int *length, char *buf)
68 {
69   int mode, dsgn;
70   char *digits, *bp, *rve;
71   union double_union tmp;
72 
73   tmp.d = value;
74   /* This will check for "< 0" and "-0.0".  */
75   if (word0 (tmp) & Sign_bit)
76     {
77       value = -value;
78       *sign = '-';
79     }
80   else
81     *sign = '\000';
82 
83   if (ch == 'f' || ch == 'F')
84     {
85       /* Ndigits after the decimal point.  */
86       mode = 3;
87     }
88   else
89     {
90       /* To obtain ndigits after the decimal point for the 'e'
91 	 and 'E' formats, round to ndigits + 1 significant figures.  */
92       if (ch == 'e' || ch == 'E')
93 	{
94 	  ndigits++;
95 	}
96       /* Ndigits significant digits.  */
97       mode = 2;
98     }
99 
100   digits = _DTOA (value, mode, ndigits, decpt, &dsgn, &rve);
101 
102   /* Print trailing zeros.  */
103   if ((ch != 'g' && ch != 'G') || flags & ALT)
104     {
105       bp = digits + ndigits;
106       if (ch == 'f' || ch == 'F')
107 	{
108 	  if (*digits == '0' && value)
109 	    *decpt = -ndigits + 1;
110 	  bp += *decpt;
111 	}
112       /* Kludge for __dtoa irregularity.  */
113       if (value == 0)
114 	rve = bp;
115       while (rve < bp)
116 	*rve++ = '0';
117     }
118   *length = rve - digits;
119   return (digits);
120 }
121 
122 /* This function is copied from exponent in vfprintf.c with support for
123    C99 formats removed.  We don't use the original function in order to
124    decouple nano implementation of formatted IO from the Newlib one.  */
125 int
__exponent(char * p0,int exp,int fmtch)126 __exponent (char *p0, int exp, int fmtch)
127 {
128   register char *p, *t;
129   char expbuf[MAXEXPLEN];
130 #define isa 0
131 
132   p = p0;
133   *p++ = isa ? 'p' - 'a' + fmtch : fmtch;
134   if (exp < 0)
135     {
136       exp = -exp;
137       *p++ = '-';
138     }
139   else
140     *p++ = '+';
141   t = expbuf + MAXEXPLEN;
142   if (exp > 9)
143     {
144       do
145 	{
146 	  *--t = to_char (exp % 10);
147 	}
148       while ((exp /= 10) > 9);
149       *--t = to_char (exp);
150       for (; t < expbuf + MAXEXPLEN; *p++ = *t++);
151     }
152   else
153     {
154       if (!isa)
155 	*p++ = '0';
156       *p++ = to_char (exp);
157     }
158   return (p - p0);
159 }
160 
161 /* Decode and print floating point number specified by "eEfgG".  */
162 int
_printf_float(struct _reent * data,struct _prt_data_t * pdata,FILE * fp,int (* pfunc)(struct _reent *,FILE *,const char *,size_t len),va_list * ap)163 _printf_float (struct _reent *data,
164 	       struct _prt_data_t *pdata,
165 	       FILE * fp,
166 	       int (*pfunc) (struct _reent *, FILE *, const char *,
167 			     size_t len), va_list * ap)
168 {
169 #define _fpvalue (pdata->_double_)
170 
171   char *decimal_point = localeconv ()->decimal_point;
172   size_t decp_len = strlen (decimal_point);
173   /* Temporary negative sign for floats.  */
174   char softsign;
175   /* Integer value of exponent.  */
176   int expt;
177   /* Character count for expstr.  */
178   int expsize = 0;
179   /* Actual number of digits returned by cvt.  */
180   int ndig = 0;
181   char *cp;
182   int n;
183   /* Field size expanded by dprec(not for _printf_float).  */
184   int realsz;
185   char code = pdata->code;
186 
187   if (pdata->flags & LONGDBL)
188     {
189       _fpvalue = (double) GET_ARG (N, *ap, _LONG_DOUBLE);
190     }
191   else
192     {
193       _fpvalue = GET_ARG (N, *ap, double);
194     }
195 
196   /* Do this before tricky precision changes.
197 
198      If the output is infinite or NaN, leading
199      zeros are not permitted.  Otherwise, scanf
200      could not read what printf wrote.  */
201   if (isinf (_fpvalue))
202     {
203       if (_fpvalue < 0)
204 	pdata->l_buf[0] = '-';
205       if (code <= 'G')		/* 'A', 'E', 'F', or 'G'.  */
206 	cp = "INF";
207       else
208 	cp = "inf";
209       pdata->size = 3;
210       pdata->flags &= ~ZEROPAD;
211       goto print_float;
212     }
213   if (isnan (_fpvalue))
214     {
215       if (signbit (_fpvalue))
216 	pdata->l_buf[0] = '-';
217       if (code <= 'G')		/* 'A', 'E', 'F', or 'G'.  */
218 	cp = "NAN";
219       else
220 	cp = "nan";
221       pdata->size = 3;
222       pdata->flags &= ~ZEROPAD;
223       goto print_float;
224     }
225 
226   if (pdata->prec == -1)
227     {
228       pdata->prec = DEFPREC;
229     }
230   else if ((code == 'g' || code == 'G') && pdata->prec == 0)
231     {
232       pdata->prec = 1;
233     }
234 
235   pdata->flags |= FPT;
236 
237   cp = __cvt (data, _fpvalue, pdata->prec, pdata->flags, &softsign,
238 	      &expt, code, &ndig, cp);
239 
240   if (code == 'g' || code == 'G')
241     {
242       if (expt <= -4 || expt > pdata->prec)
243 	/* 'e' or 'E'.  */
244 	code -= 2;
245       else
246 	code = 'g';
247     }
248   if (code <= 'e')
249     {
250       /* 'a', 'A', 'e', or 'E' fmt.  */
251       --expt;
252       expsize = __exponent (pdata->expstr, expt, code);
253       pdata->size = expsize + ndig;
254       if (ndig > 1 || pdata->flags & ALT)
255 	++pdata->size;
256     }
257   else
258     {
259       if (code == 'f')
260 	{
261 	  /* 'f' fmt.  */
262 	  if (expt > 0)
263 	    {
264 	      pdata->size = expt;
265 	      if (pdata->prec || pdata->flags & ALT)
266 		pdata->size += pdata->prec + 1;
267 	    }
268 	  else
269 	    /* "0.X".  */
270 	    pdata->size = (pdata->prec || pdata->flags & ALT)
271 	      ? pdata->prec + 2 : 1;
272 	}
273       else if (expt >= ndig)
274 	{
275 	  /* Fixed g fmt.  */
276 	  pdata->size = expt;
277 	  if (pdata->flags & ALT)
278 	    ++pdata->size;
279 	}
280       else
281 	pdata->size = ndig + (expt > 0 ? 1 : 2 - expt);
282       pdata->lead = expt;
283     }
284 
285   if (softsign)
286     pdata->l_buf[0] = '-';
287 print_float:
288   if (_printf_common (data, pdata, &realsz, fp, pfunc) == -1)
289     goto error;
290 
291   if ((pdata->flags & FPT) == 0)
292     {
293       PRINT (cp, pdata->size);
294     }
295   else
296     {
297       /* Glue together f_p fragments.  */
298       if (code >= 'f')
299 	{
300 	  /* 'f' or 'g'.  */
301 	  if (_fpvalue == 0)
302 	    {
303 	      /* Kludge for __dtoa irregularity.  */
304 	      PRINT ("0", 1);
305 	      if (expt < ndig || pdata->flags & ALT)
306 		{
307 		  PRINT (decimal_point, decp_len);
308 		  PAD (ndig - 1, pdata->zero);
309 		}
310 	    }
311 	  else if (expt <= 0)
312 	    {
313 	      PRINT ("0", 1);
314 	      if (expt || ndig || pdata->flags & ALT)
315 		{
316 		  PRINT (decimal_point, decp_len);
317 		  PAD (-expt, pdata->zero);
318 		  PRINT (cp, ndig);
319 		}
320 	    }
321 	  else
322 	    {
323 	      char *convbuf = cp;
324 	      PRINTANDPAD (cp, convbuf + ndig, pdata->lead, pdata->zero);
325 	      cp += pdata->lead;
326 	      if (expt < ndig || pdata->flags & ALT)
327 		PRINT (decimal_point, decp_len);
328 	      PRINTANDPAD (cp, convbuf + ndig, ndig - expt, pdata->zero);
329 	    }
330 	}
331       else
332 	{
333 	  /* 'a', 'A', 'e', or 'E'.  */
334 	  if (ndig > 1 || pdata->flags & ALT)
335 	    {
336 	      PRINT (cp, 1);
337 	      cp++;
338 	      PRINT (decimal_point, decp_len);
339 	      if (_fpvalue)
340 		{
341 		  PRINT (cp, ndig - 1);
342 		}
343 	      /* "0.[0..]".  */
344 	      else
345 		/* __dtoa irregularity.  */
346 		PAD (ndig - 1, pdata->zero);
347 	    }
348 	  else			/* "XeYYY".  */
349 	    PRINT (cp, 1);
350 	  PRINT (pdata->expstr, expsize);
351 	}
352     }
353 
354   /* Left-adjusting padding (always blank).  */
355   if (pdata->flags & LADJUST)
356     PAD (pdata->width - realsz, pdata->blank);
357 
358   return (pdata->width > realsz ? pdata->width : realsz);
359 error:
360   return -1;
361 
362 #undef _fpvalue
363 }
364 
365 #endif
366