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