2 * Copyright (c) 1990 The Regents of the University of California.
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.
32 #include "../stdlib/local.h"
33 #include "nano-vfscanf_local.h"
37 _scanf_float (struct _reent
*rptr
,
38 struct _scan_data_t
*pdata
,
39 FILE *fp
, va_list *ap
)
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
51 long leading_zeroes
= 0;
52 long zeroes
, exp_adjust
;
53 char *exp_start
= NULL
;
54 unsigned width_left
= 0;
58 if (pdata
->width
== 0 || pdata
->width
> BUF
- 1)
60 /* size_t is unsigned, hence this optimisation. */
61 if (pdata
->width
- 1 > BUF
- 2)
64 width_left
= pdata
->width
- (BUF
- 1);
65 pdata
->width
= BUF
- 1;
67 pdata
->flags
|= SIGNOK
| NDIGITS
| DPTOK
| EXPOK
;
70 for (p
= pdata
->buf
; pdata
->width
; )
73 /* This code mimicks the integer conversion code,
74 but is much simpler. */
78 if (pdata
->flags
& NDIGITS
)
80 pdata
->flags
&= ~SIGNOK
;
99 if (nancount
+ infcount
== 0)
101 pdata
->flags
&= ~(SIGNOK
| NDIGITS
);
108 if (pdata
->flags
& SIGNOK
)
110 pdata
->flags
&= ~SIGNOK
;
116 if (nancount
== 0 && zeroes
== 0
117 && (pdata
->flags
& (NDIGITS
| DPTOK
| EXPOK
)) ==
118 (NDIGITS
| DPTOK
| EXPOK
))
120 pdata
->flags
&= ~(SIGNOK
| DPTOK
| EXPOK
| NDIGITS
);
129 if (infcount
== 1 || infcount
== 4)
145 if (infcount
== 0 && zeroes
== 0
146 && (pdata
->flags
& (NDIGITS
| DPTOK
| EXPOK
)) ==
147 (NDIGITS
| DPTOK
| EXPOK
))
149 pdata
->flags
&= ~(SIGNOK
| DPTOK
| EXPOK
| NDIGITS
);
153 if (infcount
== 3 || infcount
== 5)
184 if (pdata
->flags
& DPTOK
)
186 pdata
->flags
&= ~(SIGNOK
| DPTOK
);
187 leading_zeroes
= zeroes
;
193 /* No exponent without some digits. */
194 if ((pdata
->flags
& (NDIGITS
| EXPOK
)) == EXPOK
195 || ((pdata
->flags
& EXPOK
) && zeroes
))
197 if (! (pdata
->flags
& DPTOK
))
199 exp_adjust
= zeroes
- leading_zeroes
;
203 (pdata
->flags
& ~(EXPOK
| DPTOK
)) | SIGNOK
| NDIGITS
;
217 else if (pdata
->pfn_refill (rptr
, fp
))
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
230 FIXME - we still don't handle NAN([0xdigits]). */
231 if (nancount
- 1U < 2U)
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
)
239 pdata
->pfn_ungetc (rptr
, *--p
, fp
); /* "[-+nNaA]". */
242 return MATCH_FAILURE
;
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)
249 /* "infcount && infcount < 8". */
250 if (infcount
>= 3) /* valid 'inf', but short of 'infinity'. */
251 while (infcount
-- > 3)
253 pdata
->pfn_ungetc (rptr
, *--p
, fp
); /* "[iInNtT]". */
258 while (p
> pdata
->buf
)
260 pdata
->pfn_ungetc (rptr
, *--p
, fp
); /* "[-+iInN]". */
263 return MATCH_FAILURE
;
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
)
271 if (pdata
->flags
& EXPOK
)
273 /* No digits at all. */
274 while (p
> pdata
->buf
)
276 pdata
->pfn_ungetc (rptr
, *--p
, fp
); /* "[-+.]". */
279 return MATCH_FAILURE
;
281 /* Just a bad exponent (e and maybe sign). */
284 if (c
!= 'e' && c
!= 'E')
286 pdata
->pfn_ungetc (rptr
, c
, fp
); /* "[-+]". */
290 pdata
->pfn_ungetc (rptr
, c
, fp
); /* "[eE]". */
292 if ((pdata
->flags
& SUPPRESS
) == 0)
298 if ((pdata
->flags
& (DPTOK
| EXPOK
)) == EXPOK
)
300 exp_adjust
= zeroes
- leading_zeroes
;
301 new_exp
= -exp_adjust
;
305 new_exp
= _strtol_r (rptr
, (exp_start
+ 1), NULL
, 10) - exp_adjust
;
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
);
316 /* Current _strtold routine is markedly slower than
317 _strtod_r. Only use it if we have a long double
319 fp
= _strtod_r (rptr
, pdata
->buf
, NULL
);
321 /* Do not support long double. */
322 if (pdata
->flags
& LONG
)
323 *GET_ARG (N
, *ap
, double *) = fp
;
324 else if (pdata
->flags
& LONGDBL
)
326 ldp
= GET_ARG (N
, *ap
, _LONG_DOUBLE
*);
331 flp
= GET_ARG (N
, *ap
, float *);