343 lines
7.9 KiB
C
343 lines
7.9 KiB
C
|
/*-
|
||
|
* 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
|
||
|
|