summaryrefslogtreecommitdiffstats
path: root/newlib/libc/stdio/nano-vfscanf_float.c
diff options
context:
space:
mode:
Diffstat (limited to 'newlib/libc/stdio/nano-vfscanf_float.c')
-rw-r--r--newlib/libc/stdio/nano-vfscanf_float.c342
1 files changed, 342 insertions, 0 deletions
diff --git a/newlib/libc/stdio/nano-vfscanf_float.c b/newlib/libc/stdio/nano-vfscanf_float.c
new file mode 100644
index 000000000..a81fe7f70
--- /dev/null
+++ b/newlib/libc/stdio/nano-vfscanf_float.c
@@ -0,0 +1,342 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <_ansi.h>
+#include <reent.h>
+#include <newlib.h>
+#include <ctype.h>
+#include <wctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <limits.h>
+#include <wchar.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include "local.h"
+#include "../stdlib/local.h"
+#include "nano-vfscanf_local.h"
+
+#ifdef FLOATING_POINT
+int
+_scanf_float (struct _reent *rptr,
+ struct _scan_data_t *pdata,
+ FILE *fp, va_list *ap)
+{
+ int c;
+ char *p;
+ float *flp;
+ _LONG_DOUBLE *ldp;
+
+ /* Scan a floating point number as if by strtod. */
+ /* This code used to assume that the number of digits is reasonable.
+ However, ANSI / ISO C makes no such stipulation; we have to get
+ exact results even when there is an unreasonable amount of leading
+ zeroes. */
+ long leading_zeroes = 0;
+ long zeroes, exp_adjust;
+ char *exp_start = NULL;
+ unsigned width_left = 0;
+ char nancount = 0;
+ char infcount = 0;
+#ifdef hardway
+ if (pdata->width == 0 || pdata->width > BUF - 1)
+#else
+ /* size_t is unsigned, hence this optimisation. */
+ if (pdata->width - 1 > BUF - 2)
+#endif
+ {
+ width_left = pdata->width - (BUF - 1);
+ pdata->width = BUF - 1;
+ }
+ pdata->flags |= SIGNOK | NDIGITS | DPTOK | EXPOK;
+ zeroes = 0;
+ exp_adjust = 0;
+ for (p = pdata->buf; pdata->width; )
+ {
+ c = *fp->_p;
+ /* This code mimicks the integer conversion code,
+ but is much simpler. */
+ switch (c)
+ {
+ case '0':
+ if (pdata->flags & NDIGITS)
+ {
+ pdata->flags &= ~SIGNOK;
+ zeroes++;
+ if (width_left)
+ {
+ width_left--;
+ pdata->width++;
+ }
+ goto fskip;
+ }
+ /* Fall through. */
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (nancount + infcount == 0)
+ {
+ pdata->flags &= ~(SIGNOK | NDIGITS);
+ goto fok;
+ }
+ break;
+
+ case '+':
+ case '-':
+ if (pdata->flags & SIGNOK)
+ {
+ pdata->flags &= ~SIGNOK;
+ goto fok;
+ }
+ break;
+ case 'n':
+ case 'N':
+ if (nancount == 0 && zeroes == 0
+ && (pdata->flags & (NDIGITS | DPTOK | EXPOK)) ==
+ (NDIGITS | DPTOK | EXPOK))
+ {
+ pdata->flags &= ~(SIGNOK | DPTOK | EXPOK | NDIGITS);
+ nancount = 1;
+ goto fok;
+ }
+ if (nancount == 2)
+ {
+ nancount = 3;
+ goto fok;
+ }
+ if (infcount == 1 || infcount == 4)
+ {
+ infcount++;
+ goto fok;
+ }
+ break;
+ case 'a':
+ case 'A':
+ if (nancount == 1)
+ {
+ nancount = 2;
+ goto fok;
+ }
+ break;
+ case 'i':
+ case 'I':
+ if (infcount == 0 && zeroes == 0
+ && (pdata->flags & (NDIGITS | DPTOK | EXPOK)) ==
+ (NDIGITS | DPTOK | EXPOK))
+ {
+ pdata->flags &= ~(SIGNOK | DPTOK | EXPOK | NDIGITS);
+ infcount = 1;
+ goto fok;
+ }
+ if (infcount == 3 || infcount == 5)
+ {
+ infcount++;
+ goto fok;
+ }
+ break;
+ case 'f':
+ case 'F':
+ if (infcount == 2)
+ {
+ infcount = 3;
+ goto fok;
+ }
+ break;
+ case 't':
+ case 'T':
+ if (infcount == 6)
+ {
+ infcount = 7;
+ goto fok;
+ }
+ break;
+ case 'y':
+ case 'Y':
+ if (infcount == 7)
+ {
+ infcount = 8;
+ goto fok;
+ }
+ break;
+ case '.':
+ if (pdata->flags & DPTOK)
+ {
+ pdata->flags &= ~(SIGNOK | DPTOK);
+ leading_zeroes = zeroes;
+ goto fok;
+ }
+ break;
+ case 'e':
+ case 'E':
+ /* No exponent without some digits. */
+ if ((pdata->flags & (NDIGITS | EXPOK)) == EXPOK
+ || ((pdata->flags & EXPOK) && zeroes))
+ {
+ if (! (pdata->flags & DPTOK))
+ {
+ exp_adjust = zeroes - leading_zeroes;
+ exp_start = p;
+ }
+ pdata->flags =
+ (pdata->flags & ~(EXPOK | DPTOK)) | SIGNOK | NDIGITS;
+ zeroes = 0;
+ goto fok;
+ }
+ break;
+ }
+ break;
+fok:
+ *p++ = c;
+fskip:
+ pdata->width--;
+ ++pdata->nread;
+ if (--fp->_r > 0)
+ fp->_p++;
+ else if (pdata->pfn_refill (rptr, fp))
+ /* "EOF". */
+ break;
+ }
+ if (zeroes)
+ pdata->flags &= ~NDIGITS;
+ /* We may have a 'N' or possibly even [sign] 'N' 'a' as the
+ start of 'NaN', only to run out of chars before it was
+ complete (or having encountered a non-matching char). So
+ check here if we have an outstanding nancount, and if so
+ put back the chars we did swallow and treat as a failed
+ match.
+
+ FIXME - we still don't handle NAN([0xdigits]). */
+ if (nancount - 1U < 2U)
+ {
+ /* "nancount && nancount < 3". */
+ /* Newlib's ungetc works even if we called __srefill in
+ the middle of a partial parse, but POSIX does not
+ guarantee that in all implementations of ungetc. */
+ while (p > pdata->buf)
+ {
+ pdata->pfn_ungetc (rptr, *--p, fp); /* "[-+nNaA]". */
+ --pdata->nread;
+ }
+ return MATCH_FAILURE;
+ }
+ /* Likewise for 'inf' and 'infinity'. But be careful that
+ 'infinite' consumes only 3 characters, leaving the stream
+ at the second 'i'. */
+ if (infcount - 1U < 7U)
+ {
+ /* "infcount && infcount < 8". */
+ if (infcount >= 3) /* valid 'inf', but short of 'infinity'. */
+ while (infcount-- > 3)
+ {
+ pdata->pfn_ungetc (rptr, *--p, fp); /* "[iInNtT]". */
+ --pdata->nread;
+ }
+ else
+ {
+ while (p > pdata->buf)
+ {
+ pdata->pfn_ungetc (rptr, *--p, fp); /* "[-+iInN]". */
+ --pdata->nread;
+ }
+ return MATCH_FAILURE;
+ }
+ }
+ /* If no digits, might be missing exponent digits
+ (just give back the exponent) or might be missing
+ regular digits, but had sign and/or decimal point. */
+ if (pdata->flags & NDIGITS)
+ {
+ if (pdata->flags & EXPOK)
+ {
+ /* No digits at all. */
+ while (p > pdata->buf)
+ {
+ pdata->pfn_ungetc (rptr, *--p, fp); /* "[-+.]". */
+ --pdata->nread;
+ }
+ return MATCH_FAILURE;
+ }
+ /* Just a bad exponent (e and maybe sign). */
+ c = *--p;
+ --pdata->nread;
+ if (c != 'e' && c != 'E')
+ {
+ pdata->pfn_ungetc (rptr, c, fp); /* "[-+]". */
+ c = *--p;
+ --pdata->nread;
+ }
+ pdata->pfn_ungetc (rptr, c, fp); /* "[eE]". */
+ }
+ if ((pdata->flags & SUPPRESS) == 0)
+ {
+ double fp;
+ long new_exp = 0;
+
+ *p = 0;
+ if ((pdata->flags & (DPTOK | EXPOK)) == EXPOK)
+ {
+ exp_adjust = zeroes - leading_zeroes;
+ new_exp = -exp_adjust;
+ exp_start = p;
+ }
+ else if (exp_adjust)
+ new_exp = _strtol_r (rptr, (exp_start + 1), NULL, 10) - exp_adjust;
+
+ if (exp_adjust)
+ {
+ /* If there might not be enough space for the new exponent,
+ truncate some trailing digits to make room. */
+ if (exp_start >= pdata->buf + BUF - MAX_LONG_LEN)
+ exp_start = pdata->buf + BUF - MAX_LONG_LEN - 1;
+ sprintf (exp_start, "e%ld", new_exp);
+ }
+
+ /* Current _strtold routine is markedly slower than
+ _strtod_r. Only use it if we have a long double
+ result. */
+ fp = _strtod_r (rptr, pdata->buf, NULL);
+
+ /* Do not support long double. */
+ if (pdata->flags & LONG)
+ *GET_ARG (N, *ap, double *) = fp;
+ else if (pdata->flags & LONGDBL)
+ {
+ ldp = GET_ARG (N, *ap, _LONG_DOUBLE *);
+ *ldp = fp;
+ }
+ else
+ {
+ flp = GET_ARG (N, *ap, float *);
+ if (isnan (fp))
+ *flp = nanf (NULL);
+ else
+ *flp = fp;
+ }
+ pdata->nassigned++;
+ }
+ return 0;
+}
+#endif
+