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