about summary refs log tree commit diff
path: root/stdio-common
diff options
context:
space:
mode:
Diffstat (limited to 'stdio-common')
-rw-r--r--stdio-common/vfscanf.c47
1 files changed, 31 insertions, 16 deletions
diff --git a/stdio-common/vfscanf.c b/stdio-common/vfscanf.c
index 46f0658ef1..48aa059b9d 100644
--- a/stdio-common/vfscanf.c
+++ b/stdio-common/vfscanf.c
@@ -45,6 +45,8 @@ Cambridge, MA 02139, USA.  */
 # define GROUP		0x080	/* ': group numbers */
 # define MALLOC		0x100	/* a: malloc strings */
 
+# define TYPEMOD	(LONG|LONGDBL|SHORT)
+
 
 #ifdef USE_IN_LIBIO
 # include <libioP.h>
@@ -114,7 +116,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
   register int flags;		/* Modifiers for current format element.  */
 
   /* Status for reading F-P nums.  */
-  char got_dot, got_e;
+  char got_dot, got_e, negative;
   /* If a [...] is a [^...].  */
   char not_in;
   /* Base for integral numbers.  */
@@ -307,7 +309,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 	  {
 	  case 'h':
 	    /* int's are short int's.  */
-	    if (flags & (LONG|LONGDBL))
+	    if (flags & TYPEMOD)
 	      /* Signal illegal format element.  */
 	      conv_error ();
 	    flags |= SHORT;
@@ -328,12 +330,15 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 	  case 'q':
 	  case 'L':
 	    /* double's are long double's, and int's are long long int's.  */
-	    if (flags & (LONG|SHORT))
+	    if (flags & TYPEMOD)
 	      /* Signal illegal format element.  */
 	      conv_error ();
 	    flags |= LONGDBL;
 	    break;
 	  case 'a':
+	    if (flags & TYPEMOD)
+	      /* Signal illegal format element.  */
+	      conv_error ();
 	    /* String conversions (%s, %[) take a `char **'
 	       arg and fill it in with a malloc'd pointer.  */
 	    flags |= MALLOC;
@@ -363,8 +368,18 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 	  break;
 
 	case 'n':	/* Answer number of assignments done.  */
+	  /* Corrigendum 1 to ISO C 1990 describes the allowed flags
+	     with the 'n' conversion specifier.  */
 	  if (!(flags & SUPPRESS))
-	    *ARG (int *) = read_in - 1;	/* Don't count the read-ahead.  */
+	    /* Don't count the read-ahead.  */
+	    if (flags & LONGDBL)
+	      *ARG (long long int *) = read_in - 1;
+	    else if (flags & LONG)
+	      *ARG (long int *) = read_in - 1;
+	    else if (flags & SHORT)
+	      *ARG (short int *) = read_in - 1;
+	    else
+	      *ARG (int *) = read_in - 1;
 	  break;
 
 	case 'c':	/* Match characters.  */
@@ -439,7 +454,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 skip any input.  */  \
+				 so at least we don't skip any input.  */     \
 			      (*strptr)[strsize] = '\0';		      \
 			      ++done;					      \
 			      conv_error ();				      \
@@ -512,7 +527,6 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 	    {
 	      if (width > 0)
 		--width;
-	      ADDW ('0');
 
 	      (void) inchar ();
 
@@ -612,13 +626,15 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 	  /* Check for a sign.  */
 	  if (c == '-' || c == '+')
 	    {
-	      ADDW (c);
+	      negative = c == '-';
 	      if (inchar () == EOF)
 		/* EOF is only an input error before we read any chars.  */
 		conv_error ();
 	      if (width > 0)
 		--width;
 	    }
+	  else
+	    negative = 0;
 
 	  got_dot = got_e = 0;
 	  do
@@ -628,7 +644,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 	      else if (got_e && wp[wpsize - 1] == 'e'
 		       && (c == '-' || c == '+'))
 		ADDW (c);
-	      else if (!got_e && tolower (c) == 'e')
+	      else if (wpsize > 0 && !got_e && tolower (c) == 'e')
 		{
 		  ADDW ('e');
 		  got_e = got_dot = 1;
@@ -644,12 +660,10 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 		break;
 	      if (width > 0)
 		--width;
-	    } while (inchar () != EOF && width != 0);
+	    }
+	  while (inchar () != EOF && width != 0);
 
 	  if (wpsize == 0)
-	    conv_error();
-	  if (wp[wpsize - 1] == '-' || wp[wpsize - 1] == '+'
-	      || wp[wpsize - 1] == 'e')
 	    conv_error ();
 
 	  /* Convert the number.  */
@@ -658,19 +672,19 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 	    {
 	      long double d = __strtold_internal (wp, &tw, flags & GROUP);
 	      if (!(flags & SUPPRESS) && tw != wp)
-		*ARG (long double *) = d;
+		*ARG (long double *) = negative ? -d : d;
 	    }
 	  else if (flags & LONG)
 	    {
 	      double d = __strtod_internal (wp, &tw, flags & GROUP);
 	      if (!(flags & SUPPRESS) && tw != wp)
-		*ARG (double *) = d;
+		*ARG (double *) = negative ? -d : d;
 	    }
 	  else
 	    {
 	      float d = __strtof_internal (wp, &tw, flags & GROUP);
 	      if (!(flags & SUPPRESS) && tw != wp)
-		*ARG (float *) = d;
+		*ARG (float *) = negative ? -d : d;
 	    }
 
 	  if (tw == wp)
@@ -738,7 +752,8 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 	      STRING_ADD_CHAR (c);
 	      if (width > 0)
 		--width;
-	    } while (inchar () != EOF && width != 0);
+	    }
+	  while (inchar () != EOF && width != 0);
 	  if (read_in == num.ul)
 	    conv_error ();