about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2015-05-21 15:46:46 +0100
committerFlorian Weimer <fweimer@redhat.com>2015-05-21 16:46:46 +0200
commitf8194fa6f991456bd2cf79f2cdfbec7b497eb342 (patch)
treec22bf79aade57413f67e76705761f989fc442be5
parentf0f98189d83940152e876c31724c6bb699b7e578 (diff)
downloadglibc-f8194fa6f991456bd2cf79f2cdfbec7b497eb342.tar.gz
glibc-f8194fa6f991456bd2cf79f2cdfbec7b497eb342.tar.xz
glibc-f8194fa6f991456bd2cf79f2cdfbec7b497eb342.zip
vfprintf: Introduce printf_positional function
This splits a considerable chunk of code from the main vfprintf
function.  This will make it easier to remove the use of extend_alloca
from the positional argument handling code.
-rw-r--r--ChangeLog7
-rw-r--r--stdio-common/vfprintf.c716
2 files changed, 376 insertions, 347 deletions
diff --git a/ChangeLog b/ChangeLog
index 2c606bcfbb..48311f5558 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,12 @@
 2015-05-21  Florian Weimer  <fweimer@redhat.com>
 
+	* stdio-common/vfprintf.c (vfprintf): Move local variables
+	args_malloced, specs, specs_malloced, and the code after
+	do_positional to the printf_positional function.
+	(printf_positional): New function.
+
+2015-05-21  Florian Weimer  <fweimer@redhat.com>
+
 	* stdio-common/vfprintf.c (jump_table): Move out of the vfprintf
 	function.
 	(NOT_IN_JUMP_RANGE, CHAR_CLASS, LABEL, REF, JUMP, STEP0_3_TABLE,
diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c
index 83b4b008ac..4753cbf954 100644
--- a/stdio-common/vfprintf.c
+++ b/stdio-common/vfprintf.c
@@ -1209,6 +1209,14 @@ static const uint8_t jump_table[] =
 static int buffered_vfprintf (FILE *stream, const CHAR_T *fmt, va_list)
      __THROW __attribute__ ((noinline)) internal_function;
 
+/* Handle positional format specifiers.  */
+static int printf_positional (_IO_FILE *s,
+			      const CHAR_T *format, int readonly_format,
+			      va_list ap, va_list *ap_savep, int done,
+			      int nspecs_done, const UCHAR_T *lead_str_end,
+			      CHAR_T *work_buffer, int save_errno,
+			      const char *grouping, THOUSANDS_SEP_T);
+
 /* Handle unknown format specifier.  */
 static int printf_unknown (FILE *, const struct printf_info *,
 			   const void *const *) __THROW;
@@ -1257,15 +1265,6 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
      0 if unknown.  */
   int readonly_format = 0;
 
-  /* For the argument descriptions, which may be allocated on the heap.  */
-  void *args_malloced = NULL;
-
-  /* For positional argument handling.  */
-  struct printf_spec *specs;
-
-  /* Track if we malloced the SPECS array and thus must free it.  */
-  bool specs_malloced = false;
-
   /* Orient the stream.  */
 #ifdef ORIENT
   ORIENT;
@@ -1670,232 +1669,265 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap)
   /* Unlock stream and return.  */
   goto all_done;
 
-  /* Here starts the more complex loop to handle positional parameters.  */
+  /* Hand off processing for positional parameters.  */
 do_positional:
-  {
-    /* Array with information about the needed arguments.  This has to
-       be dynamically extensible.  */
-    size_t nspecs = 0;
-    /* A more or less arbitrary start value.  */
-    size_t nspecs_size = 32 * sizeof (struct printf_spec);
-
-    specs = alloca (nspecs_size);
-    /* The number of arguments the format string requests.  This will
-       determine the size of the array needed to store the argument
-       attributes.  */
-    size_t nargs = 0;
-    size_t bytes_per_arg;
-    union printf_arg *args_value;
-    int *args_size;
-    int *args_type;
-
-    /* Positional parameters refer to arguments directly.  This could
-       also determine the maximum number of arguments.  Track the
-       maximum number.  */
-    size_t max_ref_arg = 0;
-
-    /* Just a counter.  */
-    size_t cnt;
-
-    if (__glibc_unlikely (workstart != NULL))
+  if (__glibc_unlikely (workstart != NULL))
+    {
       free (workstart);
-    workstart = NULL;
+      workstart = NULL;
+    }
+  done = printf_positional (s, format, readonly_format, ap, &ap_save,
+			    done, nspecs_done, lead_str_end, work_buffer,
+			    save_errno, grouping, thousands_sep);
 
-    if (grouping == (const char *) -1)
-      {
+ all_done:
+  if (__glibc_unlikely (workstart != NULL))
+    free (workstart);
+  /* Unlock the stream.  */
+  _IO_funlockfile (s);
+  _IO_cleanup_region_end (0);
+
+  return done;
+}
+
+static int
+printf_positional (_IO_FILE *s, const CHAR_T *format, int readonly_format,
+		   va_list ap, va_list *ap_savep, int done, int nspecs_done,
+		   const UCHAR_T *lead_str_end,
+		   CHAR_T *work_buffer, int save_errno,
+		   const char *grouping, THOUSANDS_SEP_T thousands_sep)
+{
+  /* For the argument descriptions, which may be allocated on the heap.  */
+  void *args_malloced = NULL;
+
+  /* For positional argument handling.  */
+  struct printf_spec *specs;
+
+  /* Track if we malloced the SPECS array and thus must free it.  */
+  bool specs_malloced = false;
+
+  /* Array with information about the needed arguments.  This has to
+     be dynamically extensible.  */
+  size_t nspecs = 0;
+  /* A more or less arbitrary start value.  */
+  size_t nspecs_size = 32 * sizeof (struct printf_spec);
+
+  specs = alloca (nspecs_size);
+  /* The number of arguments the format string requests.  This will
+     determine the size of the array needed to store the argument
+     attributes.  */
+  size_t nargs = 0;
+  size_t bytes_per_arg;
+  union printf_arg *args_value;
+  int *args_size;
+  int *args_type;
+
+  /* Positional parameters refer to arguments directly.  This could
+     also determine the maximum number of arguments.  Track the
+     maximum number.  */
+  size_t max_ref_arg = 0;
+
+  /* Just a counter.  */
+  size_t cnt;
+
+  CHAR_T *workstart = NULL;
+
+  if (grouping == (const char *) -1)
+    {
 #ifdef COMPILE_WPRINTF
-	thousands_sep = _NL_CURRENT_WORD (LC_NUMERIC,
-					  _NL_NUMERIC_THOUSANDS_SEP_WC);
+      thousands_sep = _NL_CURRENT_WORD (LC_NUMERIC,
+					_NL_NUMERIC_THOUSANDS_SEP_WC);
 #else
-	thousands_sep = _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP);
+      thousands_sep = _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP);
 #endif
 
-	grouping = _NL_CURRENT (LC_NUMERIC, GROUPING);
-	if (*grouping == '\0' || *grouping == CHAR_MAX)
-	  grouping = NULL;
-      }
+      grouping = _NL_CURRENT (LC_NUMERIC, GROUPING);
+      if (*grouping == '\0' || *grouping == CHAR_MAX)
+	grouping = NULL;
+    }
 
-    for (f = lead_str_end; *f != L_('\0'); f = specs[nspecs++].next_fmt)
-      {
-	if (nspecs * sizeof (*specs) >= nspecs_size)
-	  {
-	    /* Extend the array of format specifiers.  */
-	    if (nspecs_size * 2 < nspecs_size)
-	      {
-		__set_errno (ENOMEM);
-		done = -1;
-		goto all_done;
-	      }
-	    struct printf_spec *old = specs;
-	    if (__libc_use_alloca (2 * nspecs_size))
-	      specs = extend_alloca (specs, nspecs_size, 2 * nspecs_size);
-	    else
-	      {
-		nspecs_size *= 2;
-		specs = malloc (nspecs_size);
-		if (specs == NULL)
-		  {
-		    __set_errno (ENOMEM);
-		    specs = old;
-		    done = -1;
-		    goto all_done;
-		  }
-	      }
+  for (const UCHAR_T *f = lead_str_end; *f != L_('\0');
+       f = specs[nspecs++].next_fmt)
+    {
+      if (nspecs * sizeof (*specs) >= nspecs_size)
+	{
+	  /* Extend the array of format specifiers.  */
+	  if (nspecs_size * 2 < nspecs_size)
+	    {
+	      __set_errno (ENOMEM);
+	      done = -1;
+	      goto all_done;
+	    }
+	  struct printf_spec *old = specs;
+	  if (__libc_use_alloca (2 * nspecs_size))
+	    specs = extend_alloca (specs, nspecs_size, 2 * nspecs_size);
+	  else
+	    {
+	      nspecs_size *= 2;
+	      specs = malloc (nspecs_size);
+	      if (specs == NULL)
+		{
+		  __set_errno (ENOMEM);
+		  specs = old;
+		  done = -1;
+		  goto all_done;
+		}
+	    }
 
-	    /* Copy the old array's elements to the new space.  */
-	    memmove (specs, old, nspecs * sizeof (*specs));
+	  /* Copy the old array's elements to the new space.  */
+	  memmove (specs, old, nspecs * sizeof (*specs));
 
-	    /* If we had previously malloc'd space for SPECS, then
-	       release it after the copy is complete.  */
-	    if (specs_malloced)
-	      free (old);
+	  /* If we had previously malloc'd space for SPECS, then
+	     release it after the copy is complete.  */
+	  if (specs_malloced)
+	    free (old);
 
-	    /* Now set SPECS_MALLOCED if needed.  */
-	    if (!__libc_use_alloca (nspecs_size))
-	      specs_malloced = true;
-	  }
+	  /* Now set SPECS_MALLOCED if needed.  */
+	  if (!__libc_use_alloca (nspecs_size))
+	    specs_malloced = true;
+	}
 
-	/* Parse the format specifier.  */
+      /* Parse the format specifier.  */
 #ifdef COMPILE_WPRINTF
-	nargs += __parse_one_specwc (f, nargs, &specs[nspecs], &max_ref_arg);
+      nargs += __parse_one_specwc (f, nargs, &specs[nspecs], &max_ref_arg);
 #else
-	nargs += __parse_one_specmb (f, nargs, &specs[nspecs], &max_ref_arg);
+      nargs += __parse_one_specmb (f, nargs, &specs[nspecs], &max_ref_arg);
 #endif
-      }
-
-    /* Determine the number of arguments the format string consumes.  */
-    nargs = MAX (nargs, max_ref_arg);
-    /* Calculate total size needed to represent a single argument across
-       all three argument-related arrays.  */
-    bytes_per_arg = (sizeof (*args_value) + sizeof (*args_size)
-		     + sizeof (*args_type));
+    }
 
-    /* Check for potential integer overflow.  */
-    if (__glibc_unlikely (nargs > INT_MAX / bytes_per_arg))
-      {
-	 __set_errno (EOVERFLOW);
-	 done = -1;
-	 goto all_done;
-      }
+  /* Determine the number of arguments the format string consumes.  */
+  nargs = MAX (nargs, max_ref_arg);
+  /* Calculate total size needed to represent a single argument across
+     all three argument-related arrays.  */
+  bytes_per_arg = (sizeof (*args_value) + sizeof (*args_size)
+		   + sizeof (*args_type));
 
-    /* Allocate memory for all three argument arrays.  */
-    if (__libc_use_alloca (nargs * bytes_per_arg))
-	args_value = alloca (nargs * bytes_per_arg);
-    else
-      {
-	args_value = args_malloced = malloc (nargs * bytes_per_arg);
-	if (args_value == NULL)
-	  {
-	    done = -1;
-	    goto all_done;
-	  }
-      }
+  /* Check for potential integer overflow.  */
+  if (__glibc_unlikely (nargs > INT_MAX / bytes_per_arg))
+    {
+      __set_errno (EOVERFLOW);
+      done = -1;
+      goto all_done;
+    }
 
-    /* Set up the remaining two arrays to each point past the end of the
-       prior array, since space for all three has been allocated now.  */
-    args_size = &args_value[nargs].pa_int;
-    args_type = &args_size[nargs];
-    memset (args_type, s->_flags2 & _IO_FLAGS2_FORTIFY ? '\xff' : '\0',
-	    nargs * sizeof (*args_type));
+  /* Allocate memory for all three argument arrays.  */
+  if (__libc_use_alloca (nargs * bytes_per_arg))
+    args_value = alloca (nargs * bytes_per_arg);
+  else
+    {
+      args_value = args_malloced = malloc (nargs * bytes_per_arg);
+      if (args_value == NULL)
+	{
+	  done = -1;
+	  goto all_done;
+	}
+    }
 
-    /* XXX Could do sanity check here: If any element in ARGS_TYPE is
-       still zero after this loop, format is invalid.  For now we
-       simply use 0 as the value.  */
+  /* Set up the remaining two arrays to each point past the end of the
+     prior array, since space for all three has been allocated now.  */
+  args_size = &args_value[nargs].pa_int;
+  args_type = &args_size[nargs];
+  memset (args_type, s->_flags2 & _IO_FLAGS2_FORTIFY ? '\xff' : '\0',
+	  nargs * sizeof (*args_type));
 
-    /* Fill in the types of all the arguments.  */
-    for (cnt = 0; cnt < nspecs; ++cnt)
-      {
-	/* If the width is determined by an argument this is an int.  */
-	if (specs[cnt].width_arg != -1)
-	  args_type[specs[cnt].width_arg] = PA_INT;
+  /* XXX Could do sanity check here: If any element in ARGS_TYPE is
+     still zero after this loop, format is invalid.  For now we
+     simply use 0 as the value.  */
 
-	/* If the precision is determined by an argument this is an int.  */
-	if (specs[cnt].prec_arg != -1)
-	  args_type[specs[cnt].prec_arg] = PA_INT;
+  /* Fill in the types of all the arguments.  */
+  for (cnt = 0; cnt < nspecs; ++cnt)
+    {
+      /* If the width is determined by an argument this is an int.  */
+      if (specs[cnt].width_arg != -1)
+	args_type[specs[cnt].width_arg] = PA_INT;
 
-	switch (specs[cnt].ndata_args)
-	  {
-	  case 0:		/* No arguments.  */
-	    break;
-	  case 1:		/* One argument; we already have the
-				   type and size.  */
-	    args_type[specs[cnt].data_arg] = specs[cnt].data_arg_type;
-	    args_size[specs[cnt].data_arg] = specs[cnt].size;
-	    break;
-	  default:
-	    /* We have more than one argument for this format spec.
-	       We must call the arginfo function again to determine
-	       all the types.  */
-	    (void) (*__printf_arginfo_table[specs[cnt].info.spec])
-	      (&specs[cnt].info,
-	       specs[cnt].ndata_args, &args_type[specs[cnt].data_arg],
-	       &args_size[specs[cnt].data_arg]);
-	    break;
-	  }
-      }
+      /* If the precision is determined by an argument this is an int.  */
+      if (specs[cnt].prec_arg != -1)
+	args_type[specs[cnt].prec_arg] = PA_INT;
 
-    /* Now we know all the types and the order.  Fill in the argument
-       values.  */
-    for (cnt = 0; cnt < nargs; ++cnt)
-      switch (args_type[cnt])
+      switch (specs[cnt].ndata_args)
 	{
-#define T(tag, mem, type)						      \
-	case tag:							      \
-	  args_value[cnt].mem = va_arg (ap_save, type);			      \
+	case 0:		/* No arguments.  */
+	  break;
+	case 1:		/* One argument; we already have the
+			   type and size.  */
+	  args_type[specs[cnt].data_arg] = specs[cnt].data_arg_type;
+	  args_size[specs[cnt].data_arg] = specs[cnt].size;
+	  break;
+	default:
+	  /* We have more than one argument for this format spec.
+	     We must call the arginfo function again to determine
+	     all the types.  */
+	  (void) (*__printf_arginfo_table[specs[cnt].info.spec])
+	    (&specs[cnt].info,
+	     specs[cnt].ndata_args, &args_type[specs[cnt].data_arg],
+	     &args_size[specs[cnt].data_arg]);
+	  break;
+	}
+    }
+
+  /* Now we know all the types and the order.  Fill in the argument
+     values.  */
+  for (cnt = 0; cnt < nargs; ++cnt)
+    switch (args_type[cnt])
+      {
+#define T(tag, mem, type)				\
+	case tag:					\
+	  args_value[cnt].mem = va_arg (*ap_savep, type); \
 	  break
 
 	T (PA_WCHAR, pa_wchar, wint_t);
-	case PA_CHAR:				/* Promoted.  */
-	case PA_INT|PA_FLAG_SHORT:		/* Promoted.  */
+      case PA_CHAR:				/* Promoted.  */
+      case PA_INT|PA_FLAG_SHORT:		/* Promoted.  */
 #if LONG_MAX == INT_MAX
-	case PA_INT|PA_FLAG_LONG:
+      case PA_INT|PA_FLAG_LONG:
 #endif
 	T (PA_INT, pa_int, int);
 #if LONG_MAX == LONG_LONG_MAX
-	case PA_INT|PA_FLAG_LONG:
+      case PA_INT|PA_FLAG_LONG:
 #endif
 	T (PA_INT|PA_FLAG_LONG_LONG, pa_long_long_int, long long int);
 #if LONG_MAX != INT_MAX && LONG_MAX != LONG_LONG_MAX
 # error "he?"
 #endif
-	case PA_FLOAT:				/* Promoted.  */
+      case PA_FLOAT:				/* Promoted.  */
 	T (PA_DOUBLE, pa_double, double);
-	case PA_DOUBLE|PA_FLAG_LONG_DOUBLE:
-	  if (__ldbl_is_dbl)
-	    {
-	      args_value[cnt].pa_double = va_arg (ap_save, double);
-	      args_type[cnt] &= ~PA_FLAG_LONG_DOUBLE;
-	    }
-	  else
-	    args_value[cnt].pa_long_double = va_arg (ap_save, long double);
-	  break;
-	case PA_STRING:				/* All pointers are the same */
-	case PA_WSTRING:			/* All pointers are the same */
+      case PA_DOUBLE|PA_FLAG_LONG_DOUBLE:
+	if (__ldbl_is_dbl)
+	  {
+	    args_value[cnt].pa_double = va_arg (*ap_savep, double);
+	    args_type[cnt] &= ~PA_FLAG_LONG_DOUBLE;
+	  }
+	else
+	  args_value[cnt].pa_long_double = va_arg (*ap_savep, long double);
+	break;
+      case PA_STRING:				/* All pointers are the same */
+      case PA_WSTRING:			/* All pointers are the same */
 	T (PA_POINTER, pa_pointer, void *);
 #undef T
-	default:
-	  if ((args_type[cnt] & PA_FLAG_PTR) != 0)
-	    args_value[cnt].pa_pointer = va_arg (ap_save, void *);
-	  else if (__glibc_unlikely (__printf_va_arg_table != NULL)
-		   && __printf_va_arg_table[args_type[cnt] - PA_LAST] != NULL)
-	    {
-	      args_value[cnt].pa_user = alloca (args_size[cnt]);
-	      (*__printf_va_arg_table[args_type[cnt] - PA_LAST])
-		(args_value[cnt].pa_user, &ap_save);
-	    }
-	  else
-	    args_value[cnt].pa_long_double = 0.0;
-	  break;
-	case -1:
-	  /* Error case.  Not all parameters appear in N$ format
-	     strings.  We have no way to determine their type.  */
-	  assert (s->_flags2 & _IO_FLAGS2_FORTIFY);
-	  __libc_fatal ("*** invalid %N$ use detected ***\n");
-	}
+      default:
+	if ((args_type[cnt] & PA_FLAG_PTR) != 0)
+	  args_value[cnt].pa_pointer = va_arg (*ap_savep, void *);
+	else if (__glibc_unlikely (__printf_va_arg_table != NULL)
+		 && __printf_va_arg_table[args_type[cnt] - PA_LAST] != NULL)
+	  {
+	    args_value[cnt].pa_user = alloca (args_size[cnt]);
+	    (*__printf_va_arg_table[args_type[cnt] - PA_LAST])
+	      (args_value[cnt].pa_user, ap_savep);
+	  }
+	else
+	  args_value[cnt].pa_long_double = 0.0;
+	break;
+      case -1:
+	/* Error case.  Not all parameters appear in N$ format
+	   strings.  We have no way to determine their type.  */
+	assert (s->_flags2 & _IO_FLAGS2_FORTIFY);
+	__libc_fatal ("*** invalid %N$ use detected ***\n");
+      }
 
-    /* Now walk through all format specifiers and process them.  */
-    for (; (size_t) nspecs_done < nspecs; ++nspecs_done)
-      {
+  /* Now walk through all format specifiers and process them.  */
+  for (; (size_t) nspecs_done < nspecs; ++nspecs_done)
+    {
 #undef REF
 #ifdef SHARED
 # undef JUMP_TABLE_BASE_LABEL
@@ -1906,184 +1938,174 @@ do_positional:
 #endif
 #undef LABEL
 #define LABEL(Name) do2_##Name
-	STEP4_TABLE;
+      STEP4_TABLE;
 
-	int is_negative;
-	union
+      int is_negative;
+      union
+      {
+	unsigned long long int longlong;
+	unsigned long int word;
+      } number;
+      int base;
+      union printf_arg the_arg;
+      CHAR_T *string;		/* Pointer to argument string.  */
+
+      /* Fill variables from values in struct.  */
+      int alt = specs[nspecs_done].info.alt;
+      int space = specs[nspecs_done].info.space;
+      int left = specs[nspecs_done].info.left;
+      int showsign = specs[nspecs_done].info.showsign;
+      int group = specs[nspecs_done].info.group;
+      int is_long_double = specs[nspecs_done].info.is_long_double;
+      int is_short = specs[nspecs_done].info.is_short;
+      int is_char = specs[nspecs_done].info.is_char;
+      int is_long = specs[nspecs_done].info.is_long;
+      int width = specs[nspecs_done].info.width;
+      int prec = specs[nspecs_done].info.prec;
+      int use_outdigits = specs[nspecs_done].info.i18n;
+      char pad = specs[nspecs_done].info.pad;
+      CHAR_T spec = specs[nspecs_done].info.spec;
+
+      workstart = NULL;
+      CHAR_T *workend = work_buffer + WORK_BUFFER_SIZE;
+
+      /* Fill in last information.  */
+      if (specs[nspecs_done].width_arg != -1)
 	{
-	  unsigned long long int longlong;
-	  unsigned long int word;
-	} number;
-	int base;
-	union printf_arg the_arg;
-	CHAR_T *string;		/* Pointer to argument string.  */
-
-	/* Fill variables from values in struct.  */
-	int alt = specs[nspecs_done].info.alt;
-	int space = specs[nspecs_done].info.space;
-	int left = specs[nspecs_done].info.left;
-	int showsign = specs[nspecs_done].info.showsign;
-	int group = specs[nspecs_done].info.group;
-	int is_long_double = specs[nspecs_done].info.is_long_double;
-	int is_short = specs[nspecs_done].info.is_short;
-	int is_char = specs[nspecs_done].info.is_char;
-	int is_long = specs[nspecs_done].info.is_long;
-	int width = specs[nspecs_done].info.width;
-	int prec = specs[nspecs_done].info.prec;
-	int use_outdigits = specs[nspecs_done].info.i18n;
-	char pad = specs[nspecs_done].info.pad;
-	CHAR_T spec = specs[nspecs_done].info.spec;
-
-	workstart = NULL;
-	workend = work_buffer + WORK_BUFFER_SIZE;
-
-	/* Fill in last information.  */
-	if (specs[nspecs_done].width_arg != -1)
-	  {
-	    /* Extract the field width from an argument.  */
-	    specs[nspecs_done].info.width =
-	      args_value[specs[nspecs_done].width_arg].pa_int;
+	  /* Extract the field width from an argument.  */
+	  specs[nspecs_done].info.width =
+	    args_value[specs[nspecs_done].width_arg].pa_int;
 
-	    if (specs[nspecs_done].info.width < 0)
-	      /* If the width value is negative left justification is
-		 selected and the value is taken as being positive.  */
-	      {
-		specs[nspecs_done].info.width *= -1;
-		left = specs[nspecs_done].info.left = 1;
-	      }
-	    width = specs[nspecs_done].info.width;
-	  }
+	  if (specs[nspecs_done].info.width < 0)
+	    /* If the width value is negative left justification is
+	       selected and the value is taken as being positive.  */
+	    {
+	      specs[nspecs_done].info.width *= -1;
+	      left = specs[nspecs_done].info.left = 1;
+	    }
+	  width = specs[nspecs_done].info.width;
+	}
 
-	if (specs[nspecs_done].prec_arg != -1)
-	  {
-	    /* Extract the precision from an argument.  */
-	    specs[nspecs_done].info.prec =
-	      args_value[specs[nspecs_done].prec_arg].pa_int;
+      if (specs[nspecs_done].prec_arg != -1)
+	{
+	  /* Extract the precision from an argument.  */
+	  specs[nspecs_done].info.prec =
+	    args_value[specs[nspecs_done].prec_arg].pa_int;
 
-	    if (specs[nspecs_done].info.prec < 0)
-	      /* If the precision is negative the precision is
-		 omitted.  */
-	      specs[nspecs_done].info.prec = -1;
+	  if (specs[nspecs_done].info.prec < 0)
+	    /* If the precision is negative the precision is
+	       omitted.  */
+	    specs[nspecs_done].info.prec = -1;
 
-	    prec = specs[nspecs_done].info.prec;
-	  }
+	  prec = specs[nspecs_done].info.prec;
+	}
 
-	/* Maybe the buffer is too small.  */
-	if (MAX (prec, width) + 32 > WORK_BUFFER_SIZE)
-	  {
-	    if (__libc_use_alloca ((MAX (prec, width) + 32)
-				   * sizeof (CHAR_T)))
-	      workend = ((CHAR_T *) alloca ((MAX (prec, width) + 32)
-					    * sizeof (CHAR_T))
-			 + (MAX (prec, width) + 32));
-	    else
-	      {
-		workstart = (CHAR_T *) malloc ((MAX (prec, width) + 32)
-					       * sizeof (CHAR_T));
-		if (workstart == NULL)
-		  {
-		    done = -1;
-		    goto all_done;
-		  }
-		workend = workstart + (MAX (prec, width) + 32);
-	      }
-	  }
+      /* Maybe the buffer is too small.  */
+      if (MAX (prec, width) + 32 > WORK_BUFFER_SIZE)
+	{
+	  if (__libc_use_alloca ((MAX (prec, width) + 32)
+				 * sizeof (CHAR_T)))
+	    workend = ((CHAR_T *) alloca ((MAX (prec, width) + 32)
+					  * sizeof (CHAR_T))
+		       + (MAX (prec, width) + 32));
+	  else
+	    {
+	      workstart = (CHAR_T *) malloc ((MAX (prec, width) + 32)
+					     * sizeof (CHAR_T));
+	      if (workstart == NULL)
+		{
+		  done = -1;
+		  goto all_done;
+		}
+	      workend = workstart + (MAX (prec, width) + 32);
+	    }
+	}
 
-	/* Process format specifiers.  */
-	while (1)
-	  {
-	    extern printf_function **__printf_function_table;
-	    int function_done;
+      /* Process format specifiers.  */
+      while (1)
+	{
+	  extern printf_function **__printf_function_table;
+	  int function_done;
 
-	    if (spec <= UCHAR_MAX
-		&& __printf_function_table != NULL
-		&& __printf_function_table[(size_t) spec] != NULL)
-	      {
-		const void **ptr = alloca (specs[nspecs_done].ndata_args
-					   * sizeof (const void *));
+	  if (spec <= UCHAR_MAX
+	      && __printf_function_table != NULL
+	      && __printf_function_table[(size_t) spec] != NULL)
+	    {
+	      const void **ptr = alloca (specs[nspecs_done].ndata_args
+					 * sizeof (const void *));
 
-		/* Fill in an array of pointers to the argument values.  */
-		for (unsigned int i = 0; i < specs[nspecs_done].ndata_args;
-		     ++i)
-		  ptr[i] = &args_value[specs[nspecs_done].data_arg + i];
+	      /* Fill in an array of pointers to the argument values.  */
+	      for (unsigned int i = 0; i < specs[nspecs_done].ndata_args;
+		   ++i)
+		ptr[i] = &args_value[specs[nspecs_done].data_arg + i];
 
-		/* Call the function.  */
-		function_done = __printf_function_table[(size_t) spec]
-		  (s, &specs[nspecs_done].info, ptr);
+	      /* Call the function.  */
+	      function_done = __printf_function_table[(size_t) spec]
+		(s, &specs[nspecs_done].info, ptr);
 
-		if (function_done != -2)
-		  {
-		    /* If an error occurred we don't have information
-		       about # of chars.  */
-		    if (function_done < 0)
-		      {
-			/* Function has set errno.  */
-			done = -1;
-			goto all_done;
-		      }
-
-		    done_add (function_done);
-		    break;
-		  }
-	      }
+	      if (function_done != -2)
+		{
+		  /* If an error occurred we don't have information
+		     about # of chars.  */
+		  if (function_done < 0)
+		    {
+		      /* Function has set errno.  */
+		      done = -1;
+		      goto all_done;
+		    }
+
+		  done_add (function_done);
+		  break;
+		}
+	    }
 
-	    JUMP (spec, step4_jumps);
+	  JUMP (spec, step4_jumps);
 
-	    process_arg ((&specs[nspecs_done]));
-	    process_string_arg ((&specs[nspecs_done]));
+	  process_arg ((&specs[nspecs_done]));
+	  process_string_arg ((&specs[nspecs_done]));
 
 	  LABEL (form_unknown):
-	    {
-	      unsigned int i;
-	      const void **ptr;
+	  {
+	    unsigned int i;
+	    const void **ptr;
 
-	      ptr = alloca (specs[nspecs_done].ndata_args
-			    * sizeof (const void *));
+	    ptr = alloca (specs[nspecs_done].ndata_args
+			  * sizeof (const void *));
 
-	      /* Fill in an array of pointers to the argument values.  */
-	      for (i = 0; i < specs[nspecs_done].ndata_args; ++i)
-		ptr[i] = &args_value[specs[nspecs_done].data_arg + i];
+	    /* Fill in an array of pointers to the argument values.  */
+	    for (i = 0; i < specs[nspecs_done].ndata_args; ++i)
+	      ptr[i] = &args_value[specs[nspecs_done].data_arg + i];
 
-	      /* Call the function.  */
-	      function_done = printf_unknown (s, &specs[nspecs_done].info,
-					      ptr);
+	    /* Call the function.  */
+	    function_done = printf_unknown (s, &specs[nspecs_done].info,
+					    ptr);
 
-	      /* If an error occurred we don't have information about #
-		 of chars.  */
-	      if (function_done < 0)
-		{
-		  /* Function has set errno.  */
-		  done = -1;
-		  goto all_done;
-		}
+	    /* If an error occurred we don't have information about #
+	       of chars.  */
+	    if (function_done < 0)
+	      {
+		/* Function has set errno.  */
+		done = -1;
+		goto all_done;
+	      }
 
-	      done_add (function_done);
-	    }
-	    break;
+	    done_add (function_done);
 	  }
+	  break;
+	}
 
-	if (__glibc_unlikely (workstart != NULL))
-	  free (workstart);
-	workstart = NULL;
-
-	/* Write the following constant string.  */
-	outstring (specs[nspecs_done].end_of_fmt,
-		   specs[nspecs_done].next_fmt
-		   - specs[nspecs_done].end_of_fmt);
-      }
-  }
+      if (__glibc_unlikely (workstart != NULL))
+	free (workstart);
+      workstart = NULL;
 
-all_done:
-  if (specs_malloced)
-    free (specs);
-  if (__glibc_unlikely (args_malloced != NULL))
-    free (args_malloced);
+      /* Write the following constant string.  */
+      outstring (specs[nspecs_done].end_of_fmt,
+		 specs[nspecs_done].next_fmt
+		 - specs[nspecs_done].end_of_fmt);
+    }
+ all_done:
   if (__glibc_unlikely (workstart != NULL))
     free (workstart);
-  /* Unlock the stream.  */
-  _IO_funlockfile (s);
-  _IO_cleanup_region_end (0);
-
   return done;
 }