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