1 /*-
2  * Copyright (c) 1990 The Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * and/or other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #define _DEFAULT_SOURCE
19 #include <_ansi.h>
20 #include <newlib.h>
21 #include <ctype.h>
22 #include <wctype.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <stdint.h>
26 #include <limits.h>
27 #include <wchar.h>
28 #include <string.h>
29 #include <stdarg.h>
30 #include <errno.h>
31 #include "local.h"
32 #include "../stdlib/local.h"
33 #include "nano-vfscanf_local.h"
34 
35 #ifdef FLOATING_POINT
36 int
_scanf_float(struct _reent * rptr,struct _scan_data_t * pdata,FILE * fp,va_list * ap)37 _scanf_float (struct _reent *rptr,
38 	      struct _scan_data_t *pdata,
39 	      FILE *fp, va_list *ap)
40 {
41   int c;
42   char *p;
43   float *flp;
44   _LONG_DOUBLE *ldp;
45 
46   /* Scan a floating point number as if by strtod.  */
47   /* This code used to assume that the number of digits is reasonable.
48      However, ANSI / ISO C makes no such stipulation; we have to get
49      exact results even when there is an unreasonable amount of leading
50      zeroes.  */
51   long leading_zeroes = 0;
52   long zeroes, exp_adjust;
53   char *exp_start = NULL;
54   unsigned width_left = 0;
55   char nancount = 0;
56   char infcount = 0;
57 #ifdef hardway
58   if (pdata->width == 0 || pdata->width > BUF - 1)
59 #else
60   /* size_t is unsigned, hence this optimisation.  */
61   if (pdata->width - 1 > BUF - 2)
62 #endif
63     {
64       width_left = pdata->width - (BUF - 1);
65       pdata->width = BUF - 1;
66     }
67   pdata->flags |= SIGNOK | NDIGITS | DPTOK | EXPOK;
68   zeroes = 0;
69   exp_adjust = 0;
70   for (p = pdata->buf; pdata->width; )
71     {
72       c = *fp->_p;
73       /* This code mimicks the integer conversion code,
74 	 but is much simpler.  */
75       switch (c)
76 	{
77 	case '0':
78 	  if (pdata->flags & NDIGITS)
79 	    {
80 	      pdata->flags &= ~SIGNOK;
81 	      zeroes++;
82 	      if (width_left)
83 		{
84 		  width_left--;
85 		  pdata->width++;
86 		}
87 	      goto fskip;
88 	    }
89 	/* Fall through.  */
90 	case '1':
91 	case '2':
92 	case '3':
93 	case '4':
94 	case '5':
95 	case '6':
96 	case '7':
97 	case '8':
98 	case '9':
99 	  if (nancount + infcount == 0)
100 	    {
101 	      pdata->flags &= ~(SIGNOK | NDIGITS);
102 	      goto fok;
103 	    }
104 	  break;
105 
106 	case '+':
107 	case '-':
108 	  if (pdata->flags & SIGNOK)
109 	    {
110 	      pdata->flags &= ~SIGNOK;
111 	      goto fok;
112 	    }
113 	  break;
114 	case 'n':
115 	case 'N':
116 	  if (nancount == 0 && zeroes == 0
117 	      && (pdata->flags & (NDIGITS | DPTOK | EXPOK)) ==
118 				  (NDIGITS | DPTOK | EXPOK))
119 	    {
120 	      pdata->flags &= ~(SIGNOK | DPTOK | EXPOK | NDIGITS);
121 	      nancount = 1;
122 	      goto fok;
123 	    }
124 	  if (nancount == 2)
125 	    {
126 	      nancount = 3;
127 	      goto fok;
128 	    }
129 	  if (infcount == 1 || infcount == 4)
130 	    {
131 	      infcount++;
132 	      goto fok;
133 	    }
134 	  break;
135 	case 'a':
136 	case 'A':
137 	  if (nancount == 1)
138 	    {
139 	      nancount = 2;
140 	      goto fok;
141 	    }
142 	  break;
143 	case 'i':
144 	case 'I':
145 	  if (infcount == 0 && zeroes == 0
146 	      && (pdata->flags & (NDIGITS | DPTOK | EXPOK)) ==
147 				  (NDIGITS | DPTOK | EXPOK))
148 	    {
149 	      pdata->flags &= ~(SIGNOK | DPTOK | EXPOK | NDIGITS);
150 	      infcount = 1;
151 	      goto fok;
152 	    }
153 	  if (infcount == 3 || infcount == 5)
154 	    {
155 	      infcount++;
156 	      goto fok;
157 	    }
158 	  break;
159 	case 'f':
160 	case 'F':
161 	  if (infcount == 2)
162 	    {
163 	      infcount = 3;
164 	      goto fok;
165 	    }
166 	  break;
167 	case 't':
168 	case 'T':
169 	  if (infcount == 6)
170 	    {
171 	      infcount = 7;
172 	      goto fok;
173 	    }
174 	  break;
175 	case 'y':
176 	case 'Y':
177 	  if (infcount == 7)
178 	    {
179 	      infcount = 8;
180 	      goto fok;
181 	    }
182 	  break;
183 	case '.':
184 	  if (pdata->flags & DPTOK)
185 	    {
186 	      pdata->flags &= ~(SIGNOK | DPTOK);
187 	      leading_zeroes = zeroes;
188 	      goto fok;
189 	    }
190 	  break;
191 	case 'e':
192 	case 'E':
193 	  /* No exponent without some digits.  */
194 	  if ((pdata->flags & (NDIGITS | EXPOK)) == EXPOK
195 	      || ((pdata->flags & EXPOK) && zeroes))
196 	    {
197 	      if (! (pdata->flags & DPTOK))
198 		{
199 		  exp_adjust = zeroes - leading_zeroes;
200 		  exp_start = p;
201 	        }
202 	      pdata->flags =
203 		(pdata->flags & ~(EXPOK | DPTOK)) | SIGNOK | NDIGITS;
204 	      zeroes = 0;
205 	      goto fok;
206 	    }
207 	  break;
208 	}
209       break;
210 fok:
211       *p++ = c;
212 fskip:
213       pdata->width--;
214       ++pdata->nread;
215       if (--fp->_r > 0)
216 	fp->_p++;
217       else if (pdata->pfn_refill (rptr, fp))
218 	/* "EOF".  */
219 	break;
220     }
221   if (zeroes)
222     pdata->flags &= ~NDIGITS;
223   /* We may have a 'N' or possibly even [sign] 'N' 'a' as the
224      start of 'NaN', only to run out of chars before it was
225      complete (or having encountered a non-matching char).  So
226      check here if we have an outstanding nancount, and if so
227      put back the chars we did swallow and treat as a failed
228      match.
229 
230      FIXME - we still don't handle NAN([0xdigits]).  */
231   if (nancount - 1U < 2U)
232     {
233       /* "nancount && nancount < 3".  */
234       /* Newlib's ungetc works even if we called __srefill in
235 	 the middle of a partial parse, but POSIX does not
236 	 guarantee that in all implementations of ungetc.  */
237       while (p > pdata->buf)
238 	{
239 	  pdata->pfn_ungetc (rptr, *--p, fp); /* "[-+nNaA]".  */
240 	  --pdata->nread;
241 	}
242       return MATCH_FAILURE;
243     }
244   /* Likewise for 'inf' and 'infinity'.	 But be careful that
245      'infinite' consumes only 3 characters, leaving the stream
246      at the second 'i'.	 */
247   if (infcount - 1U < 7U)
248     {
249       /* "infcount && infcount < 8".  */
250       if (infcount >= 3) /* valid 'inf', but short of 'infinity'.  */
251 	while (infcount-- > 3)
252 	  {
253 	    pdata->pfn_ungetc (rptr, *--p, fp); /* "[iInNtT]".  */
254 	    --pdata->nread;
255 	  }
256       else
257         {
258 	  while (p > pdata->buf)
259 	    {
260 	      pdata->pfn_ungetc (rptr, *--p, fp); /* "[-+iInN]".  */
261 	      --pdata->nread;
262 	    }
263 	  return MATCH_FAILURE;
264 	}
265     }
266   /* If no digits, might be missing exponent digits
267      (just give back the exponent) or might be missing
268      regular digits, but had sign and/or decimal point.  */
269   if (pdata->flags & NDIGITS)
270     {
271       if (pdata->flags & EXPOK)
272 	{
273 	  /* No digits at all.  */
274 	  while (p > pdata->buf)
275 	    {
276 	      pdata->pfn_ungetc (rptr, *--p, fp); /* "[-+.]".  */
277 	      --pdata->nread;
278 	    }
279 	  return MATCH_FAILURE;
280 	}
281       /* Just a bad exponent (e and maybe sign).  */
282       c = *--p;
283       --pdata->nread;
284       if (c != 'e' && c != 'E')
285 	{
286 	  pdata->pfn_ungetc (rptr, c, fp); /* "[-+]".  */
287 	  c = *--p;
288 	  --pdata->nread;
289 	}
290       pdata->pfn_ungetc (rptr, c, fp); /* "[eE]".  */
291     }
292   if ((pdata->flags & SUPPRESS) == 0)
293     {
294       double fp;
295       long new_exp = 0;
296 
297       *p = 0;
298       if ((pdata->flags & (DPTOK | EXPOK)) == EXPOK)
299 	{
300 	  exp_adjust = zeroes - leading_zeroes;
301 	  new_exp = -exp_adjust;
302 	  exp_start = p;
303 	}
304       else if (exp_adjust)
305 	new_exp = strtol ((exp_start + 1), NULL, 10) - exp_adjust;
306 
307       if (exp_adjust)
308 	{
309 	  /* If there might not be enough space for the new exponent,
310 	     truncate some trailing digits to make room.  */
311 	  if (exp_start >= pdata->buf + BUF - MAX_LONG_LEN)
312 	    exp_start = pdata->buf + BUF - MAX_LONG_LEN - 1;
313 	  sprintf (exp_start, "e%ld", new_exp);
314 	}
315 
316       /* Current strtold routine is markedly slower than
317 	 strtod.  Only use it if we have a long double
318 	 result.  */
319       fp = strtod (pdata->buf, NULL);
320 
321       /* Do not support long double.  */
322       if (pdata->flags & LONG)
323 	*GET_ARG (N, *ap, double *) = fp;
324       else if (pdata->flags & LONGDBL)
325 	{
326 	  ldp = GET_ARG (N, *ap, _LONG_DOUBLE *);
327 	  *ldp = fp;
328 	}
329       else
330 	{
331 	  flp = GET_ARG (N, *ap, float *);
332 	  if (isnan (fp))
333 	    *flp = nanf ("");
334 	  else
335 	    *flp = fp;
336 	}
337       pdata->nassigned++;
338     }
339   return 0;
340 }
341 #endif
342 
343