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.c94
1 files changed, 56 insertions, 38 deletions
diff --git a/stdio-common/vfscanf.c b/stdio-common/vfscanf.c
index b0e48df4f8..76c9936abe 100644
--- a/stdio-common/vfscanf.c
+++ b/stdio-common/vfscanf.c
@@ -132,6 +132,8 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
   int base;
   /* Signedness for integral numbers.  */
   int number_signed;
+  /* Decimal point character.  */
+  wchar_t decimal;
   /* Integral holding variables.  */
   union
     {
@@ -144,9 +146,24 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
   register char *str, **strptr;
   size_t strsize;
   /* Workspace.  */
-  char work[200];
-  char *w;			/* Pointer into WORK.  */
-  wchar_t decimal;		/* Decimal point character.  */
+  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.  */
+#define ADDW(Ch)							    \
+  do									    \
+    {									    \
+      if (wpsize == wpmax)						    \
+	{								    \
+	  char *old = wp;						    \
+	  wpmax = 200 > 2 * wpmax ? 200 : 2 * wpmax;			    \
+	  wp = (char *) alloca (wpmax);					    \
+	  if (wpsize > 0)						    \
+	    memcpy (wp, old, wpsize);					    \
+	}								    \
+      wp[wpsize++] = (Ch);						    \
+    }									    \
+  while (0)
 
   ARGCHECK (s, format);
 
@@ -338,7 +355,6 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 	conv_error ();
 
       /* Find the conversion specifier.  */
-      w = work;
       fc = *f++;
       if (fc != '[' && fc != 'c' && fc != 'n')
 	/* Eat whitespace.  */
@@ -490,7 +506,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 	  /* Check for a sign.  */
 	  if (c == '-' || c == '+')
 	    {
-	      *w++ = c;
+	      ADDW (c);
 	      if (width > 0)
 		--width;
 	      (void) inchar ();
@@ -501,7 +517,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 	    {
 	      if (width > 0)
 		--width;
-	      *w++ = '0';
+	      ADDW ('0');
 
 	      (void) inchar ();
 
@@ -523,40 +539,40 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 	  if (base == 0)
 	    base = 10;
 
-	  /* Read the number into WORK.  */
+	  /* Read the number into workspace.  */
 	  do
 	    {
 	      if (base == 16 ? !isxdigit (c) :
 		  (!isdigit (c) || c - '0' >= base))
 		break;
-	      *w++ = c;
+	      ADDW (c);
 	      if (width > 0)
 		--width;
 	    }
 	  while (inchar () != EOF && width != 0);
 
-	  if (w == work ||
-	      (w - work == 1 && (work[0] == '+' || work[0] == '-')))
+	  if (wpsize == 0 ||
+	      (wpsize == 1 && (wp[0] == '+' || wp[0] == '-')))
 	    /* There was no number.  */
 	    conv_error ();
 
 	  /* Convert the number.  */
-	  *w = '\0';
+	  ADDW ('\0');
 	  if (is_longlong)
 	    {
 	      if (number_signed)
-		num.q = __strtoq_internal (work, &w, base, group_flag);
+		num.q = __strtoq_internal (wp, &tw, base, group_flag);
 	      else
-		num.uq = __strtouq_internal (work, &w, base, group_flag);
+		num.uq = __strtouq_internal (wp, &tw, base, group_flag);
 	    }
 	  else
 	    {
 	      if (number_signed)
-		num.l = __strtol_internal (work, &w, base, group_flag);
+		num.l = __strtol_internal (wp, &tw, base, group_flag);
 	      else
-		num.ul = __strtoul_internal (work, &w, base, group_flag);
+		num.ul = __strtoul_internal (wp, &tw, base, group_flag);
 	    }
-	  if (w == work)
+	  if (wp == tw)
 	    conv_error ();
 
 	  if (do_assign)
@@ -599,7 +615,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 	  /* Check for a sign.  */
 	  if (c == '-' || c == '+')
 	    {
-	      *w++ = c;
+	      ADDW (c);
 	      if (inchar () == EOF)
 		/* EOF is only an input error before we read any chars.  */
 		conv_error ();
@@ -611,17 +627,18 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 	  do
 	    {
 	      if (isdigit (c))
-		*w++ = c;
-	      else if (got_e && w[-1] == 'e' && (c == '-' || c == '+'))
-		*w++ = c;
+		ADDW (c);
+	      else if (got_e && wp[wpsize - 1] == 'e'
+		       && (c == '-' || c == '+'))
+		ADDW (c);
 	      else if (!got_e && tolower (c) == 'e')
 		{
-		  *w++ = 'e';
+		  ADDW ('e');
 		  got_e = got_dot = 1;
 		}
 	      else if (c == decimal && !got_dot)
 		{
-		  *w++ = c;
+		  ADDW (c);
 		  got_dot = 1;
 		}
 	      else
@@ -630,33 +647,34 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 		--width;
 	    } while (inchar () != EOF && width != 0);
 
-	  if (w == work)
+	  if (wpsize == 0)
 	    conv_error();
-	  if (w[-1] == '-' || w[-1] == '+' || w[-1] == 'e')
+	  if (wp[wpsize - 1] == '-' || wp[wpsize - 1] == '+'
+	      || wp[wpsize - 1] == 'e')
 	    conv_error ();
 
 	  /* Convert the number.  */
-	  *w = '\0';
+	  ADDW ('\0');
 	  if (is_long_double)
 	    {
-	      long double d = __strtold_internal (work, &w, group_flag);
-	      if (do_assign && w != work)
+	      long double d = __strtold_internal (wp, &tw, group_flag);
+	      if (do_assign && tw != wp)
 		*ARG (long double *) = d;
 	    }
 	  else if (is_long)
 	    {
-	      double d = __strtod_internal (work, &w, group_flag);
-	      if (do_assign && w != work)
+	      double d = __strtod_internal (wp, &tw, group_flag);
+	      if (do_assign && tw != wp)
 		*ARG (double *) = d;
 	    }
 	  else
 	    {
-	      float d = __strtof_internal (work, &w, group_flag);
-	      if (do_assign && w != work)
+	      float d = __strtof_internal (wp, &tw, group_flag);
+	      if (do_assign && tw != wp)
 		*ARG (float *) = d;
 	    }
 
-	  if (w == work)
+	  if (tw == wp)
 	    conv_error ();
 
 	  if (do_assign)
@@ -680,23 +698,23 @@ __vfscanf (FILE *s, const char *format, va_list argptr)
 	  while ((fc = *f++) != '\0' && fc != ']')
 	    {
 	      if (fc == '-' && *f != '\0' && *f != ']' &&
-		  w > work && w[-1] <= *f)
+		  wpsize > 0 && wp[wpsize - 1] <= *f)
 		/* Add all characters from the one before the '-'
 		   up to (but not including) the next format char.  */
-		for (fc = w[-1] + 1; fc < *f; ++fc)
-		  *w++ = fc;
+		for (fc = wp[wpsize - 1] + 1; fc < *f; ++fc)
+		  ADDW (fc);
 	      else
 		/* Add the character to the list.  */
-		*w++ = fc;
+		ADDW (fc);
 	    }
 	  if (fc == '\0')
 	    conv_error();
 
-	  *w = '\0';
+	  ADDW ('\0');
 	  num.ul = read_in;
 	  do
 	    {
-	      if ((strchr (work, c) == NULL) != not_in)
+	      if ((strchr (wp, c) == NULL) != not_in)
 		break;
 	      STRING_ADD_CHAR (c);
 	      if (width > 0)