about summary refs log tree commit diff
path: root/stdio-common/vfscanf.c
diff options
context:
space:
mode:
Diffstat (limited to 'stdio-common/vfscanf.c')
-rw-r--r--stdio-common/vfscanf.c97
1 files changed, 56 insertions, 41 deletions
diff --git a/stdio-common/vfscanf.c b/stdio-common/vfscanf.c
index 76c9936abe..df68260cdd 100644
--- a/stdio-common/vfscanf.c
+++ b/stdio-common/vfscanf.c
@@ -34,10 +34,6 @@ Cambridge, MA 02139, USA.  */
 #define	LONGLONG	long
 #endif
 
-#ifdef USE_IN_LIBIO
-# include <libioP.h>
-# include <libio.h>
-
 /* Those are flags in the conversion format. */
 # define LONG		0x01	/* l: long or double */
 # define LONGDBL	0x02	/* L: long long or long double */
@@ -48,11 +44,15 @@ Cambridge, MA 02139, USA.  */
 # define WIDTH		0x40	/* width */
 
 
+#ifdef USE_IN_LIBIO
+# include <libioP.h>
+# include <libio.h>
+
 # define va_list	_IO_va_list
 # define ungetc(c, s)	_IO_ungetc (c, s)
-# define inchar()	((c = _IO_getc(s)), ++read_in, c)
+# define inchar()	((c = _IO_getc (s)), ++read_in, c)
 # define conv_error()	return ((errp != NULL && (*errp |= 2)), \
-				(c == EOF || _IO_ungetc(c, s)), done)
+				(c == EOF || _IO_ungetc (c, s)), done)
 
 # define input_error()	return ((errp != NULL && (*errp |= 1)), \
 				done == 0 ? EOF : done)
@@ -69,8 +69,8 @@ Cambridge, MA 02139, USA.  */
        }								     \
     } while (0)
 #else
-# define inchar()	((c = getc(s)) == EOF ? EOF : (++read_in, c))
-# define conv_error()	return (ungetc(c, s), done)
+# define inchar()	((c = getc (s)), ++read_in, c)
+# define conv_error()	return (ungetc (c, s), done)
 # define input_error()	return (done == 0 ? EOF : done)
 # define memory_error()	return ((errno = ENOMEM), EOF)
 # define ARGCHECK(s, format)						     \
@@ -111,9 +111,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
   register int do_assign;	/* Whether to do an assignment.  */
   register int width;		/* Maximum field width.  */
   int group_flag;		/* %' modifier flag.  */
-#ifdef USE_IN_LIBIO
   int flags;			/* Trace flags for current format element.  */
-#endif
 
   /* Type modifiers.  */
   int is_short, is_long, is_long_double;
@@ -145,11 +143,15 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
   /* Character-buffer pointer.  */
   register char *str, **strptr;
   size_t strsize;
+  /* We must not react on white spaces immediately because they can
+     possibly be matched even if in the input stream no character is
+     available anymore.  */
+  int skip_space = 0;
   /* Workspace.  */
   char *tw;			/* Temporary pointer.  */
   char *wp = NULL;		/* Workspace.  */
-  size_t wpsize = 0;		/* Currently used bytes in workspace.  */
   size_t wpmax = 0;		/* Maximal size of workspace.  */
+  size_t wpsize;		/* Currently used bytes in workspace.  */
 #define ADDW(Ch)							    \
   do									    \
     {									    \
@@ -158,7 +160,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 	  char *old = wp;						    \
 	  wpmax = 200 > 2 * wpmax ? 200 : 2 * wpmax;			    \
 	  wp = (char *) alloca (wpmax);					    \
-	  if (wpsize > 0)						    \
+	  if (old != NULL)						    \
 	    memcpy (wp, old, wpsize);					    \
 	}								    \
       wp[wpsize++] = (Ch);						    \
@@ -220,27 +222,37 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
       fc = *f++;
       if (fc != '%')
 	{
+	  /* Remember to skip spaces.  */
+	  if (isspace (fc))
+	    {
+	      skip_space = 1;
+	      continue;
+	    }
+
 	  /* Characters other than format specs must just match.  */
 	  if (c == EOF)
 	    input_error ();
-	  if (isspace (fc))
+
+	  /* We saw an white space as the last character in the format
+	     string.  Now it's time to skip all leading white
+	     spaces.  */
+	  if (skip_space)
 	    {
-	      /* Whitespace characters match any amount of whitespace.  */
 	      while (isspace (c))
-		inchar ();
-	      continue;
+		(void) inchar ();
+	      skip_space = 0;
 	    }
+
 	  else if (c == fc)
 	    (void) inchar ();
 	  else
 	    conv_error ();
+
 	  continue;
 	}
 
-#ifdef USE_IN_LIBIO
-      /* That is the start of the coversion string. */
+      /* This is the start of the conversion string. */
       flags = 0;
-#endif
 
       /* Initialize state of modifiers.  */
       argpos = 0;
@@ -248,6 +260,9 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
       group_flag = 0;
       is_short = is_long = is_long_double = malloc_string = 0;
 
+      /* Prepare temporary buffer.  */
+      wpsize = 0;
+
       /* Check for a positional parameter specification.  */
       if (isdigit (*f))
 	{
@@ -270,9 +285,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 	switch (*f++)
 	  {
 	  case '*':
-#ifdef USE_IN_LIBIO
 	    flags = SUPPRESS;
-#endif
 	    do_assign = 0;
 	    break;
 	  case '\'':
@@ -280,11 +293,9 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 	    break;
 	  }
 
-#ifdef USE_IN_LIBIO
       /* We have seen width. */
       if (isdigit (*f))
 	flags |= WIDTH;
-#endif
 
       /* Find the maximum field width.  */
       width = 0;
@@ -303,44 +314,36 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 	  {
 	  case 'h':
 	    /* int's are short int's.  */
-#ifdef USE_IN_LIBIO
 	    if (flags & ~(SUPPRESS | WIDTH))
 	      /* Signal illegal format element.  */
 	      conv_error ();
 	    flags |= SHORT;
-#endif
 	    is_short = 1;
 	    break;
 	  case 'l':
 	    if (is_long)
 	      {
 		/* A double `l' is equivalent to an `L'.  */
-#ifdef USE_IN_LIBIO
-		if ((flags & ~(SUPPRESS | WIDTH)) && (flags & LONGDBL))
+		if ((flags & ~(SUPPRESS | WIDTH)))
 		  conv_error ();
 		flags &= ~LONG;
 		flags |= LONGDBL;
-#endif
 		is_longlong = 1;
 	      }
 	    else
 	      {
 		/* int's are long int's.  */
-#ifdef USE_IN_LIBIO
 		flags |= LONG;
-#endif
 		is_long = 1;
 	      }
 	    break;
 	  case 'q':
 	  case 'L':
 	    /* double's are long double's, and int's are long long int's.  */
-#ifdef USE_IN_LIBIO
 	    if (flags & ~(SUPPRESS | WIDTH))
 	      /* Signal illegal format element.  */
 	      conv_error ();
 	    flags |= LONGDBL;
-#endif
 	    is_long_double = 1;
 	    break;
 	  case 'a':
@@ -356,15 +359,20 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 
       /* Find the conversion specifier.  */
       fc = *f++;
-      if (fc != '[' && fc != 'c' && fc != 'n')
-	/* Eat whitespace.  */
-	while (isspace (c))
-	  (void) inchar ();
+      if (skip_space || (fc != '[' && fc != 'c' && fc != 'n'))
+	{
+	  /* Eat whitespace.  */
+	  while (isspace (c))
+	    (void) inchar ();
+	  skip_space = 0;
+	}
+
       switch (fc)
 	{
 	case '%':	/* Must match a literal '%'.  */
 	  if (c != fc)
 	    conv_error ();
+	  inchar ();
 	  break;
 
 	case 'n':	/* Answer number of assignments done.  */
@@ -444,7 +452,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 			    {						      \
 			      /* We lose.  Oh well.			      \
 				 Terminate the string and stop converting,    \
-				 so at least we don't swallow any input.  */  \
+				 so at least we don't skip any input.  */  \
 			      (*strptr)[strsize] = '\0';		      \
 			      ++done;					      \
 			      conv_error ();				      \
@@ -513,7 +521,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 	    }
 
 	  /* Look for a leading indication of base.  */
-	  if (c == '0')
+	  if (width != 0 && c == '0')
 	    {
 	      if (width > 0)
 		--width;
@@ -521,7 +529,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 
 	      (void) inchar ();
 
-	      if (tolower (c) == 'x')
+	      if (width != 0 && tolower (c) == 'x')
 		{
 		  if (base == 0)
 		    base = 16;
@@ -540,7 +548,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 	    base = 10;
 
 	  /* Read the number into workspace.  */
-	  do
+	  while (c != EOF && width != 0)
 	    {
 	      if (base == 16 ? !isxdigit (c) :
 		  (!isdigit (c) || c - '0' >= base))
@@ -548,8 +556,9 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 	      ADDW (c);
 	      if (width > 0)
 		--width;
+
+	      (void) inchar ();
 	    }
-	  while (inchar () != EOF && width != 0);
 
 	  if (wpsize == 0 ||
 	      (wpsize == 1 && (wp[0] == '+' || wp[0] == '-')))
@@ -739,6 +748,12 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 	}
     }
 
+  /* The last thing we saw int the format string was a white space.
+     Consume the last white spaces.  */
+  if (skip_space)
+    while (isspace (c))
+      (void) inchar ();
+
   return ((c == EOF || ungetc (c, s)), done);
 }