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
 | |
| 
 |