diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c
index 2cf7c8a..c1aaa11 100644
--- a/stdio-common/vfprintf.c
+++ b/stdio-common/vfprintf.c
@@ -29,7 +29,6 @@
 #include <_itoa.h>
 #include <locale/localeinfo.h>
 #include <stdio.h>
-#include <scratch_buffer.h>
 
 /* This code is shared between the standard stdio implementation found
    in GNU C library and the libio implementation originally found in
@@ -1695,6 +1694,13 @@ do_positional:
   return done;
 }
 
+/* Parsed format specifiers.  */
+#define DYNARRAY_STRUCT specs_array
+#define DYNARRAY_ELEMENT struct printf_spec
+#define DYNARRAY_PREFIX specs_array_
+#define DYNARRAY_INITIAL_SIZE 15
+#include <malloc/dynarray-skeleton.c>
+
 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,
@@ -1705,15 +1711,9 @@ printf_positional (_IO_FILE *s, const CHAR_T *format, int readonly_format,
   /* For the argument descriptions, which may be allocated on the heap.  */
   void *args_malloced = NULL;
 
-  /* For positional argument handling.  */
-  struct scratch_buffer specsbuf;
-  scratch_buffer_init (&specsbuf);
-  struct printf_spec *specs = specsbuf.data;
-  size_t specs_limit = specsbuf.length / sizeof (specs[0]);
-
-  /* Array with information about the needed arguments.  This has to
-     be dynamically extensible.  */
-  size_t nspecs = 0;
+  /* Parsed format specifiers found in the format string.  */
+  struct specs_array specs;
+  specs_array_init (&specs);
 
   /* The number of arguments the format string requests.  This will
      determine the size of the array needed to store the argument
@@ -1748,26 +1748,22 @@ printf_positional (_IO_FILE *s, const CHAR_T *format, int readonly_format,
 	grouping = NULL;
     }
 
-  for (const UCHAR_T *f = lead_str_end; *f != L_('\0');
-       f = specs[nspecs++].next_fmt)
+  for (const UCHAR_T *f = lead_str_end; *f != L_('\0'); )
     {
-      if (nspecs == specs_limit)
+      struct printf_spec *new_spec = specs_array_emplace (&specs);
+      if (new_spec == NULL)
 	{
-	  if (!scratch_buffer_grow_preserve (&specsbuf))
-	    {
-	      done = -1;
-	      goto all_done;
-	    }
-	  specs = specsbuf.data;
-	  specs_limit = specsbuf.length / sizeof (specs[0]);
+	  done = -1;
+	  goto all_done;
 	}
 
       /* Parse the format specifier.  */
 #ifdef COMPILE_WPRINTF
-      nargs += __parse_one_specwc (f, nargs, &specs[nspecs], &max_ref_arg);
+      nargs += __parse_one_specwc (f, nargs, new_spec, &max_ref_arg);
 #else
-      nargs += __parse_one_specmb (f, nargs, &specs[nspecs], &max_ref_arg);
+      nargs += __parse_one_specmb (f, nargs, new_spec, &max_ref_arg);
 #endif
+      f = new_spec->next_fmt;
     }
 
   /* Determine the number of arguments the format string consumes.  */
@@ -1810,33 +1806,34 @@ printf_positional (_IO_FILE *s, const CHAR_T *format, int readonly_format,
      simply use 0 as the value.  */
 
   /* Fill in the types of all the arguments.  */
+  size_t nspecs = specs_array_size (&specs);
   for (cnt = 0; cnt < nspecs; ++cnt)
     {
+      struct printf_spec *spec = specs_array_at (&specs, 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;
+      if (spec->width_arg != -1)
+	args_type[spec->width_arg] = PA_INT;
 
       /* 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;
+      if (spec->prec_arg != -1)
+	args_type[spec->prec_arg] = PA_INT;
 
-      switch (specs[cnt].ndata_args)
+      switch (spec->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;
+	  args_type[spec->data_arg] = spec->data_arg_type;
+	  args_size[spec->data_arg] = spec->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]);
+	  (void) (*__printf_arginfo_table[spec->info.spec])
+	    (&spec->info, spec->ndata_args, &args_type[spec->data_arg],
+	     &args_size[spec->data_arg]);
 	  break;
 	}
     }
@@ -1916,53 +1913,52 @@ printf_positional (_IO_FILE *s, const CHAR_T *format, int readonly_format,
       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;
+      struct printf_spec *pspec = specs_array_at (&specs, nspecs_done);
+      int alt = pspec->info.alt;
+      int space = pspec->info.space;
+      int left = pspec->info.left;
+      int showsign = pspec->info.showsign;
+      int group = pspec->info.group;
+      int is_long_double = pspec->info.is_long_double;
+      int is_short = pspec->info.is_short;
+      int is_char = pspec->info.is_char;
+      int is_long = pspec->info.is_long;
+      int width = pspec->info.width;
+      int prec = pspec->info.prec;
+      int use_outdigits = pspec->info.i18n;
+      char pad = pspec->info.pad;
+      CHAR_T spec = pspec->info.spec;
 
       workstart = NULL;
       CHAR_T *workend = work_buffer + WORK_BUFFER_SIZE;
 
       /* Fill in last information.  */
-      if (specs[nspecs_done].width_arg != -1)
+      if (pspec->width_arg != -1)
 	{
 	  /* Extract the field width from an argument.  */
-	  specs[nspecs_done].info.width =
-	    args_value[specs[nspecs_done].width_arg].pa_int;
+	  pspec->info.width = args_value[pspec->width_arg].pa_int;
 
-	  if (specs[nspecs_done].info.width < 0)
+	  if (pspec->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;
+	      pspec->info.width *= -1;
+	      left = pspec->info.left = 1;
 	    }
-	  width = specs[nspecs_done].info.width;
+	  width = pspec->info.width;
 	}
 
-      if (specs[nspecs_done].prec_arg != -1)
+      if (pspec->prec_arg != -1)
 	{
 	  /* Extract the precision from an argument.  */
-	  specs[nspecs_done].info.prec =
-	    args_value[specs[nspecs_done].prec_arg].pa_int;
+	  pspec->info.prec = args_value[pspec->prec_arg].pa_int;
 
-	  if (specs[nspecs_done].info.prec < 0)
+	  if (pspec->info.prec < 0)
 	    /* If the precision is negative the precision is
 	       omitted.  */
-	    specs[nspecs_done].info.prec = -1;
+	    pspec->info.prec = -1;
 
-	  prec = specs[nspecs_done].info.prec;
+	  prec = pspec->info.prec;
 	}
 
       /* Maybe the buffer is too small.  */
@@ -1996,17 +1992,17 @@ printf_positional (_IO_FILE *s, const CHAR_T *format, int readonly_format,
 	      && __printf_function_table != NULL
 	      && __printf_function_table[(size_t) spec] != NULL)
 	    {
-	      const void **ptr = alloca (specs[nspecs_done].ndata_args
+	      const void **ptr = alloca (pspec->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;
+	      for (unsigned int i = 0; i < pspec->ndata_args;
 		   ++i)
-		ptr[i] = &args_value[specs[nspecs_done].data_arg + i];
+		ptr[i] = &args_value[pspec->data_arg + i];
 
 	      /* Call the function.  */
 	      function_done = __printf_function_table[(size_t) spec]
-		(s, &specs[nspecs_done].info, ptr);
+		(s, &pspec->info, ptr);
 
 	      if (function_done != -2)
 		{
@@ -2026,24 +2022,22 @@ printf_positional (_IO_FILE *s, const CHAR_T *format, int readonly_format,
 
 	  JUMP (spec, step4_jumps);
 
-	  process_arg ((&specs[nspecs_done]));
-	  process_string_arg ((&specs[nspecs_done]));
+	  process_arg (pspec);
+	  process_string_arg (pspec);
 
 	  LABEL (form_unknown):
 	  {
 	    unsigned int i;
 	    const void **ptr;
 
-	    ptr = alloca (specs[nspecs_done].ndata_args
-			  * sizeof (const void *));
+	    ptr = alloca (pspec->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];
+	    for (i = 0; i < pspec->ndata_args; ++i)
+	      ptr[i] = &args_value[pspec->data_arg + i];
 
 	    /* Call the function.  */
-	    function_done = printf_unknown (s, &specs[nspecs_done].info,
-					    ptr);
+	    function_done = printf_unknown (s, &pspec->info, ptr);
 
 	    /* If an error occurred we don't have information about #
 	       of chars.  */
@@ -2064,16 +2058,14 @@ printf_positional (_IO_FILE *s, const CHAR_T *format, int readonly_format,
       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);
+      outstring (pspec->end_of_fmt, pspec->next_fmt - pspec->end_of_fmt);
     }
  all_done:
   if (__glibc_unlikely (args_malloced != NULL))
     free (args_malloced);
   if (__glibc_unlikely (workstart != NULL))
     free (workstart);
-  scratch_buffer_free (&specsbuf);
+  specs_array_free (&specs);
   return done;
 }
 
