Fri Mar 24 02:35:37 1995 Roland McGrath <roland@churchy.gnu.ai.mit.edu>
authorRoland McGrath <roland@gnu.org>
Fri, 24 Mar 1995 07:44:08 +0000 (07:44 +0000)
committerRoland McGrath <roland@gnu.org>
Fri, 24 Mar 1995 07:44:08 +0000 (07:44 +0000)
* stdio/printf-parse.h: New file, mostly written by drepper.
* stdio/vfprintf.c: Rewritten, mostly by drepper.
* stdio/printf-prs.c: Rewritten.
* stdio/Makefile (distribute): Add printf-parse.h.

Thu Mar 23 22:03:44 1995  Roland McGrath  <roland@churchy.gnu.ai.mit.edu>

* sysdeps/unix/start.c [! NO_UNDERSCORES]: Don't declare _start
  with asm name.  Just do a ".set start, __start".

* malloc/realloc.c: Call _free_internal instead of free.

* stdlib/Makefile: All the mpn stuff moved here from stdio/Makefile.

ChangeLog
stdio/Makefile
stdio/printf-parse.h [new file with mode: 0644]
stdio/printf-prs.c
stdio/vfprintf.c
sysdeps/unix/start.c

index fed93d2..e02c200 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+Fri Mar 24 02:35:37 1995  Roland McGrath  <roland@churchy.gnu.ai.mit.edu>
+
+       * stdio/printf-parse.h: New file, mostly written by drepper.
+       * stdio/vfprintf.c: Rewritten, mostly by drepper.
+       * stdio/printf-prs.c: Rewritten.
+       * stdio/Makefile (distribute): Add printf-parse.h.
+
+Thu Mar 23 22:03:44 1995  Roland McGrath  <roland@churchy.gnu.ai.mit.edu>
+
+       * sysdeps/unix/start.c [! NO_UNDERSCORES]: Don't declare _start
+       with asm name.  Just do a ".set start, __start".
+
+       * malloc/realloc.c: Call _free_internal instead of free.
+
 Tue Mar 21 00:14:27 1995  Roland McGrath  <roland@churchy.gnu.ai.mit.edu>
 
        * locale/loadlocale.c (_nl_load_locale): If LOCALE/LC_* is a
@@ -9,6 +23,7 @@ Mon Mar 20 03:19:23 1995  Roland McGrath  <roland@churchy.gnu.ai.mit.edu>
        stdio/gmp.h, stdio/longlong.h, stdio/mp_clz_tab.c,
        stdio/gen-mpn-copy: Files moved to stdlib.
        * stdio/Makefile: All mpn stuff moved to stdlib/Makefile.
+       * stdlib/Makefile: All the mpn stuff moved here from stdio/Makefile.
        * stdio/printf_fp.c: Use ../stdlib to find fpioconst.h and gmp
        headers.
        * stdlib/strtod.c: Don't use ../stdio to find fpioconst.h and gmp
index 64b0598..955d7f1 100644 (file)
@@ -48,7 +48,7 @@ routines      :=                                                            \
        memstream obstream                                                    \
        internals sysd-stdio pipestream stdio_init libc_fatal
 aux    := errlist siglist defs glue
-distribute := _itoa.h 
+distribute := _itoa.h printf-parse.h
 
 tests := tst-printf tstscanf test_rdwr test-popen tstgetln test-fseek \
         temptest tst-fileno test-fwrite \
diff --git a/stdio/printf-parse.h b/stdio/printf-parse.h
new file mode 100644 (file)
index 0000000..0f6e9e2
--- /dev/null
@@ -0,0 +1,388 @@
+/* Internal header for parsing printf format strings.
+Copyright (C) 1995 Free Software Foundation, Inc.
+This file is part of th GNU C Library.
+
+The GNU C Library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Library General Public License as
+published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version.
+
+The GNU C Library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Library General Public License for more details.
+
+You should have received a copy of the GNU Library General Public
+License along with the GNU C Library; see the file COPYING.LIB.  If
+not, write to the Free Software Foundation, Inc., 675 Mass Ave,
+Cambridge, MA 02139, USA.  */
+
+#include <ctype.h>
+#include <printf.h>
+#include <string.h>
+#include <stddef.h>
+
+#define NDEBUG 1
+#include <assert.h>
+
+#define MAX(a,b)       ({typeof(a) _a = (a); typeof(b) _b = (b);             \
+                         _a > _b ? _a : _b; })
+#define MIN(a,b)       ({typeof(a) _a = (a); typeof(b) _b = (b);             \
+                         _a < _b ? _a : _b; })
+
+struct printf_spec
+  {
+    /* Information parsed from the format spec.  */ 
+    struct printf_info info;
+
+    /* Pointers into the format string for the end of this format
+       spec and the next (or to the end of the string if no more).  */
+    const char *end_of_fmt, *next_fmt;
+
+    /* Position of arguments for precision and width, or -1 if `info' has
+       the constant value.  */
+    int prec_arg, width_arg;
+
+    int data_arg;              /* Position of data argument.  */
+    int data_arg_type;         /* Type of first argument.  */
+    /* Number of arguments consumed by this format specifier.  */
+    size_t ndata_args;
+  };
+
+
+/* The various kinds off arguments that can be passed to printf.  */
+union printf_arg
+  {
+    unsigned char pa_char;
+    short int pa_short_int;
+    int pa_int;
+    long int pa_long_int;
+    long long int pa_long_long_int;
+    unsigned short int pa_u_short_int;
+    unsigned int pa_u_int;
+    unsigned long int pa_u_long_int;
+    unsigned long long int pa_u_long_long_int;
+    float pa_float;
+    double pa_double;
+    long double pa_long_double;
+    const char *pa_string;
+    void *pa_pointer;
+  };
+
+
+/* Read a simple integer from a string and update the string pointer.
+   It is assumed that the first character is a digit.  */
+static inline unsigned int
+read_int (const char * *pstr)
+{
+  unsigned int retval = **pstr - '0';
+
+  while (isdigit (*++(*pstr)))
+    {
+      retval *= 10;
+      retval += **pstr - '0';
+    }
+
+  return retval;
+}
+
+
+
+/* Find the next spec in FORMAT, or the end of the string.  Returns
+   a pointer into FORMAT, to a '%' or a '\0'.  */
+static inline const char *
+find_spec (const char *format)
+{
+  while (*format != '\0' && *format != '%')
+    {
+      int len;
+
+      if (isascii (*format) || (len = mblen (format, MB_CUR_MAX)) <= 0)
+       ++format;
+      else
+       format += len;
+    }
+  return format;
+}
+
+
+/* This is defined in reg-printf.c.  */
+extern printf_arginfo_function **__printf_arginfo_table;
+
+
+/* FORMAT must point to a '%' at the beginning of a spec.  Fills in *SPEC
+   with the parsed details.  POSN is the number of arguments already
+   consumed.  At most MAXTYPES - POSN types are filled in TYPES.  Return
+   the number of args consumed by this spec; *MAX_REF_ARG is updated so it
+   remains the highest argument index used.  */
+static inline size_t
+parse_one_spec (const char *format, size_t posn, struct printf_spec *spec,
+               size_t *max_ref_arg)
+{
+  unsigned int n;
+  size_t nargs = 0;
+
+  /* Skip the '%'.  */
+  ++format;
+
+  /* Clear information structure.  */
+  spec->data_arg = -1;
+  spec->info.alt = 0;
+  spec->info.space = 0;
+  spec->info.left = 0;
+  spec->info.showsign = 0;
+  spec->info.group = 0;
+  spec->info.pad = ' ';
+
+  /* Test for positional argument.  */
+  if (isdigit (*format))
+    {
+      const char *begin = format;
+
+      n = read_int (&format);
+
+      if (n > 0 && *format == '$')
+       /* Is positional parameter.  */
+       {
+         ++format;             /* Skip the '$'.  */
+         spec->data_arg = n - 1;
+         *max_ref_arg = MAX (*max_ref_arg, n);
+       }
+      else
+       /* Oops; that was actually the width and/or 0 padding flag.
+          Step back and read it again.  */
+       format = begin;
+    }
+
+  /* Check for spec modifiers.  */
+  while (*format == ' ' || *format == '+' || *format == '-' ||
+        *format == '#' || *format == '0' || *format == '\'')
+    switch (*format++)
+      {
+      case ' ':
+       /* Output a space in place of a sign, when there is no sign.  */
+       spec->info.space = 1;
+       break;
+      case '+':
+       /* Always output + or - for numbers.  */
+       spec->info.showsign = 1;
+       break;
+      case '-':
+       /* Left-justify things.  */
+       spec->info.left = 1;
+       break;
+      case '#':
+       /* Use the "alternate form":
+          Hex has 0x or 0X, FP always has a decimal point.  */
+       spec->info.alt = 1;
+       break;
+      case '0':
+       /* Pad with 0s.  */
+       spec->info.pad = '0';
+       break;
+      case '\'':
+       /* Show grouping in numbers if the locale information
+          indicates any.  */
+       spec->info.group = 1;
+       break;
+      }
+  if (spec->info.left)
+    spec->info.pad = ' ';
+
+  /* Get the field width.  */
+  spec->width_arg = -1;
+  spec->info.width = 0;
+  if (*format == '*')
+    {
+      /* The field width is given in an argument.
+        A negative field width indicates left justification.  */
+      const char *begin = ++format;
+
+      if (isdigit (*format))
+       {
+         /* The width argument might be found in a positional parameter.  */
+         n = read_int (&format);
+
+         if (n > 0 && *format == '$')
+           {
+             spec->width_arg = n - 1;
+             *max_ref_arg = MAX (*max_ref_arg, n);
+             ++format;         /* Skip '$'.  */
+           }
+       }
+
+      if (spec->width_arg < 0)
+       {
+         /* Not in a positional parameter.  Consume one argument.  */
+         spec->width_arg = posn++;
+         ++nargs;
+         format = begin;       /* Step back and reread.  */
+       }
+    }
+  else if (isdigit (*format))
+    /* Constant width specification.  */
+    spec->info.width = read_int (&format);
+
+  /* Get the precision.  */
+  spec->prec_arg = -1;
+  /* -1 means none given; 0 means explicit 0.  */
+  spec->info.prec = -1;
+  if (*format == '.')
+    {
+      ++format;
+      if (*format == '*')
+       {
+         /* The precision is given in an argument.  */
+         const char *begin = ++format;
+
+         if (isdigit (*format))
+           {
+             n = read_int (&format);
+
+             if (n > 0 && *format == '$')
+               {
+                 spec->prec_arg = n - 1;
+                 *max_ref_arg = MAX (*max_ref_arg, n);
+                 ++format;
+               }
+           }
+
+         if (spec->prec_arg < 0)
+           {
+             /* Not in a positional parameter.  */
+             spec->prec_arg = posn++;
+             ++nargs;
+             format = begin;
+           }
+       }
+      else if (isdigit (*format))
+       spec->info.prec = read_int (&format);
+      else
+       /* "%.?" is treated like "%.0?".  */
+       spec->info.prec = 0;
+
+      /* If there was a precision specified, ignore the 0 flag and always
+        pad with spaces.  */
+      spec->info.pad = ' ';
+    }
+
+  /* Check for type modifiers.  */
+#define is_longlong is_long_double
+  spec->info.is_long_double = 0;
+  spec->info.is_short = 0;
+  spec->info.is_long = 0;
+
+  while (*format == 'h' || *format == 'l' || *format == 'L' ||
+        *format == 'Z' || *format == 'q')
+    switch (*format++)
+      {
+      case 'h':
+       /* int's are short int's.  */
+       spec->info.is_short = 1;
+       break;
+      case 'l':
+       if (spec->info.is_long)
+         /* A double `l' is equivalent to an `L'.  */
+         spec->info.is_longlong = 1;
+       else
+         /* int's are long int's.  */
+         spec->info.is_long = 1;
+       break;
+      case 'L':
+       /* double's are long double's, and int's are long long int's.  */
+       spec->info.is_long_double = 1;
+       break;
+      case 'Z':
+       /* int's are size_t's.  */
+       assert (sizeof(size_t) <= sizeof(unsigned long long int));
+       spec->info.is_longlong = sizeof(size_t) > sizeof(unsigned long int);
+       spec->info.is_long = sizeof(size_t) > sizeof(unsigned int);
+       break;
+      case 'q':
+       /* 4.4 uses this for long long.  */
+       spec->info.is_longlong = 1;
+       break;
+      }
+
+  /* Get the format specification.  */
+  spec->info.spec = *format++;
+  if (__printf_arginfo_table != NULL &&
+      __printf_arginfo_table[spec->info.spec] != NULL)
+    /* We don't try to get the types for all arguments if the format
+       uses more than one.  The normal case is covered though.  */
+    spec->ndata_args = (*__printf_arginfo_table[spec->info.spec])
+      (&spec->info, 1, &spec->data_arg_type);
+  else
+    {
+      /* Find the data argument types of a built-in spec.  */
+      spec->ndata_args = 1;
+
+      switch (spec->info.spec)
+       {
+       case 'i':
+       case 'd':
+       case 'u':
+       case 'o':
+       case 'X':
+       case 'x':
+         if (spec->info.is_longlong)
+           spec->data_arg_type = PA_INT|PA_FLAG_LONG_LONG;
+         else if (spec->info.is_long)
+           spec->data_arg_type = PA_INT|PA_FLAG_LONG;
+         else if (spec->info.is_short)
+           spec->data_arg_type = PA_INT|PA_FLAG_SHORT;
+         else
+           spec->data_arg_type = PA_INT;
+         break;
+       case 'e':
+       case 'E':
+       case 'f':
+       case 'g':
+       case 'G':
+         if (spec->info.is_long_double)
+           spec->data_arg_type = PA_DOUBLE|PA_FLAG_LONG_DOUBLE;
+         else
+           spec->data_arg_type = PA_DOUBLE;
+         break;
+       case 'c':
+         spec->data_arg_type = PA_CHAR;
+         break;
+       case 's':
+         spec->data_arg_type = PA_STRING;
+         break;
+       case 'p':
+         spec->data_arg_type = PA_POINTER;
+         break;
+       case 'n':
+         spec->data_arg_type = PA_INT|PA_FLAG_PTR;
+         break;
+
+       case 'm':
+       default:
+         /* An unknown spec will consume no args.  */
+         spec->ndata_args = 0;
+         break;
+       }
+
+      if (spec->data_arg == -1 && spec->ndata_args > 0) 
+       {
+         /* There are args consumed, but no positional spec.
+            Use the next sequential arg position.  */
+         spec->data_arg = posn;
+         posn += spec->ndata_args;
+         nargs += spec->ndata_args;
+       }
+    }
+
+  if (spec->info.spec == '\0')
+    /* Format ended before this spec was complete.  */
+    spec->end_of_fmt = spec->next_fmt = format - 1;
+  else
+    {
+      /* Find the next format spec.  */
+      spec->end_of_fmt = format;
+      spec->next_fmt = find_spec (format);
+    }
+
+  return nargs;
+}
index 2f55dd3..811a9cb 100644 (file)
@@ -16,196 +16,57 @@ License along with the GNU C Library; see the file COPYING.LIB.  If
 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
 Cambridge, MA 02139, USA.  */
 
-#include <ansidecl.h>
 #include <stdio.h>
 #include <printf.h>
-#include <limits.h>
+#include <stdlib.h>
 #include <string.h>
-#include <ctype.h>
 
-#ifdef __GNUC__
-#define        HAVE_LONGLONG
-#endif
+#include "printf-parse.h"
 
-extern printf_arginfo_function *__printf_arginfo_table[];
 
 size_t
-DEFUN(parse_printf_format, (fmt, n, argtypes),
-      CONST char *fmt AND size_t n AND int *argtypes)
+parse_printf_format (fmt, n, argtypes)
+      const char *fmt;
+      size_t n;
+      int *argtypes;
 {
-  register CONST char *f;
-  size_t need = 0;
+  size_t nargs;                        /* Number of arguments.  */
+  size_t max_ref_arg;          /* Highest index used in a positional arg.  */
+  struct printf_spec spec;
 
-  for (f = strchr (fmt, '%'); f != NULL; f = strchr (f, '%'))
-    {
-      struct printf_info info;
-      printf_arginfo_function *arginfo;
-
-      ++f;
+  nargs = 0;
+  max_ref_arg = 0;
 
-      info.space = info.showsign = info.left = info.alt = info.group = 0;
-      info.pad = ' ';
-      while (*f == ' ' || *f == '+' || *f == '-' || *f == '#' || *f == '0' ||
-            *f == '\'')
-       switch (*f++)
-         {
-         case ' ':
-           info.space = 1;
-           break;
-         case '+':
-           info.showsign = 1;
-           break;
-         case '-':
-           info.left = 1;
-           break;
-         case '#':
-           info.alt = 1;
-           break;
-         case '\'':
-           info.group = 1;
-           break;
-         case '0':
-           info.pad = '0';
-           break;
-         }
-      if (info.left)
-       info.pad = ' ';
+  /* Search for format specifications.  */
+  for (fmt = find_spec (fmt); *fmt != '\0'; fmt = spec.next_fmt)
+    {
+      /* Parse this spec.  */
+      nargs += parse_one_spec (fmt, nargs, &spec, &max_ref_arg);
 
-      /* Get the field width.  */
-      if (*f == '*')
-       {
-         if (++need < n)
-           *argtypes++ = PA_INT;
-         info.width = INT_MIN;
-         ++f;
-       }
-      else
-       {
-         info.width = 0;
-         while (isdigit(*f))
-           {
-             info.width *= 10;
-             info.width += *f++ - '0';
-           }
-       }
+      /* If the width is determined by an argument this is an int.  */
+      if (spec.width_arg != -1 && spec.width_arg < n)
+       argtypes[spec.width_arg] = PA_INT;
 
-      /* Get the precision.  */
-      /* -1 means none given; 0 means explicit 0.  */
-      info.prec = -1;
-      if (*f == '.')
-       {
-         ++f;
-         if (*f == '*')
-           {
-             /* The precision is given in an argument.  */
-             if (++need < n)
-               *argtypes++ = PA_INT;
-             info.prec = INT_MIN;
-             ++f;
-           }
-         else if (isdigit(*f))
-           {
-             info.prec = 0;
-             while (*f != '\0' && isdigit(*f))
-               {
-                 info.prec *= 10;
-                 info.prec += *f++ - '0';
-               }
-           }
-       }
+      /* If the precision is determined by an argument this is an int.  */
+      if (spec.prec_arg != -1 && spec.prec_arg < n)
+       argtypes[spec.prec_arg] = PA_INT;
 
-      /* Check for type modifiers.  */
-      info.is_short = info.is_long = info.is_long_double = 0;
-      while (*f == 'h' || *f == 'l' || *f == 'L')
-       switch (*f++)
+      if (spec.data_arg < n)
+       switch (spec.ndata_args)
          {
-         case 'h':
-           /* int's are short int's.  */
-           info.is_short = 1;
+         case 0:               /* No arguments.  */
            break;
-         case 'l':
-#ifdef HAVE_LONGLONG
-           if (info.is_long)
-             /* A double `l' is equivalent to an `L'.  */
-             info.is_long_double = 1;
-           else
-#endif
-             /* int's are long int's.  */
-             info.is_long = 1;
+         case 1:               /* One argument; we already have the type.  */
+           argtypes[spec.data_arg] = spec.data_arg_type;
            break;
-         case 'L':
-           /* double's are long double's, and int's are long long int's.  */
-           info.is_long_double = 1;
+         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[spec.info.spec])
+             (&spec.info, n - spec.data_arg, &argtypes[spec.data_arg]);
            break;
          }
-
-      if (*f == '\0')
-       return need;
-
-      info.spec = *f++;
-
-      arginfo = __printf_arginfo_table[info.spec];
-      if (arginfo != NULL)
-       {
-         size_t nargs
-           = (*arginfo) (&info, need > n ? 0 : n - need, argtypes);
-         need += nargs;
-         argtypes += nargs;
-       }
-      else
-       {
-         int type;
-         switch (info.spec)
-           {
-           case 'i':
-           case 'd':
-           case 'u':
-           case 'o':
-           case 'X':
-           case 'x':
-             type = PA_INT;
-             break;
-
-           case 'e':
-           case 'E':
-           case 'f':
-           case 'g':
-           case 'G':
-             type = PA_DOUBLE;
-             break;
-
-           case 'c':
-             type = PA_CHAR;
-             break;
-
-           case 's':
-             type = PA_STRING;
-             break;
-
-           case 'p':
-             type = PA_POINTER;
-             break;
-
-           case 'n':
-             type = PA_INT | PA_FLAG_PTR;
-             break;
-
-           default:
-             /* No arg for an unknown spec.  */
-             continue;
-           }
-
-         if (info.is_long_double)
-           type |= PA_FLAG_LONG_DOUBLE;
-         if (info.is_long)
-           type |= PA_FLAG_LONG;
-         if (info.is_short)
-           type |= PA_FLAG_SHORT;
-
-         if (++need < n)
-           *argtypes++ = type;
-       }
     }
 
-  return need;
+  return MAX (nargs, max_ref_arg);
 }
index c246217..056ea32 100644 (file)
@@ -16,20 +16,23 @@ License along with the GNU C Library; see the file COPYING.LIB.  If
 not, write to the Free Software Foundation, Inc., 675 Mass Ave,
 Cambridge, MA 02139, USA.  */
 
-#include <ansidecl.h>
-#include "../locale/localeinfo.h"
 #include <ctype.h>
 #include <errno.h>
 #include <float.h>
 #include <limits.h>
 #include <math.h>
+#include <printf.h>
 #include <stdarg.h>
 #include <stdlib.h>
 #include <string.h>
 #include <printf.h>
-#include <assert.h>
 #include <stddef.h>
 #include "_itoa.h"
+#include "../locale/localeinfo.h"
+
+/* Include the shared code for parsing the format string.  */
+#include "printf-parse.h"
+
 
 /* This function from the GNU C library is also used in libio.
    To compile for use in libio, compile with -DUSE_IN_LIBIO.  */
@@ -38,20 +41,21 @@ Cambridge, MA 02139, USA.  */
 /* This code is for use in libio.  */
 #include <libioP.h>
 #define PUT(f, s, n)   _IO_sputn (f, s, n)
-#define PAD(padchar)   \
-       (width > 0 ? width += _IO_padn (s, padchar, width) : 0)
-#define PUTC(c, f)     _IO_putc(c, f)
+#define PAD(padchar)                                                         \
+  if (specs[cnt].info.width > 0)                                             \
+    done += _IO_padn (s, padchar, specs[cnt].info.width)
+#define PUTC(c, f)     _IO_putc (c, f)
 #define vfprintf       _IO_vfprintf
 #define size_t         _IO_size_t
 #define FILE           _IO_FILE
 #define va_list                _IO_va_list
 #undef BUFSIZ
 #define BUFSIZ         _IO_BUFSIZ
-#define ARGCHECK(s, format) \
+#define ARGCHECK(s, format)                                                  \
   do                                                                         \
     {                                                                        \
       /* Check file argument for consistence.  */                            \
-      CHECK_FILE(s, -1);                                                     \
+      CHECK_FILE (s, -1);                                                    \
       if (s->_flags & _IO_NO_WRITES || format == NULL)                       \
        {                                                                     \
          MAYBE_SET_EINVAL;                                                   \
@@ -64,8 +68,11 @@ Cambridge, MA 02139, USA.  */
 #include <stdio.h>
 #define PUTC(c, f)     putc (c, f)
 #define PUT(f, s, n)   fwrite (s, 1, n, f)
-ssize_t __printf_pad __P ((FILE *, char pad, int n));
-#define PAD(padchar)   __printf_pad (s, padchar, width)
+ssize_t __printf_pad __P ((FILE *, char pad, size_t n));
+#define PAD(padchar)                                                         \
+  if (specs[cnt].info.width > 0)                                             \
+    { if (__printf_pad (s, padchar, specs[cnt].info.width) == -1)            \
+       return -1; else done += specs[cnt].info.width; }
 #define ARGCHECK(s, format) \
   do                                                                         \
     {                                                                        \
@@ -88,14 +95,13 @@ ssize_t __printf_pad __P ((FILE *, char pad, int n));
 #define        outchar(x)                                                            \
   do                                                                         \
     {                                                                        \
-      register CONST int outc = (x);                                         \
-      if (putc(outc, s) == EOF)                                                      \
+      register const int outc = (x);                                         \
+      if (putc (outc, s) == EOF)                                             \
        return -1;                                                            \
       else                                                                   \
        ++done;                                                               \
     } while (0)
 
-/* Advances STRING after writing LEN chars of it.  */
 #define outstring(string, len)                                               \
   do                                                                         \
     {                                                                        \
@@ -104,580 +110,525 @@ ssize_t __printf_pad __P ((FILE *, char pad, int n));
          if (PUT (s, string, len) != len)                                    \
            return -1;                                                        \
          done += len;                                                        \
-         string += len;                                                      \
        }                                                                     \
       else                                                                   \
-       while (len-- > 0)                                                     \
-         outchar (*string++);                                                \
+       {                                                                     \
+         register const char *cp = string;                                   \
+         register int l = len;                                               \
+         while (l-- > 0)                                                     \
+           outchar (*cp++);                                                  \
+       }                                                                     \
     } while (0)
 
 /* Helper function to provide temporary buffering for unbuffered streams.  */
 static int buffered_vfprintf __P ((FILE *stream, const char *fmt, va_list));
 
-/* Cast the next arg, of type ARGTYPE, into CASTTYPE, and put it in VAR.  */
-#define        castarg(var, argtype, casttype) \
-  var = (casttype) va_arg(args, argtype)
-/* Get the next arg, of type TYPE, and put it in VAR.  */
-#define        nextarg(var, type)      castarg(var, type, type)
-
 static printf_function printf_unknown;
 
 extern printf_function **__printf_function_table;
 
-#ifdef __GNUC__
-#define        HAVE_LONGLONG
-#define        LONGLONG        long long
-#else
-#define        LONGLONG        long
-#endif
-
 static char *group_number __P ((char *, char *, const char *, wchar_t));
 
+
 int
-DEFUN(vfprintf, (s, format, args),
-      register FILE *s AND CONST char *format AND va_list args)
+vfprintf (s, format, ap)
+    register FILE *s;
+    const char *format;
+    va_list ap;
 {
   /* The character used as thousands separator.  */
   wchar_t thousands_sep;
 
   /* The string describing the size of groups of digits.  */
-  const char *grouping; 
+  const char *grouping;
+
+  /* Array with information about the needed arguments.  This has to be
+     dynamically extendable.  */
+  size_t nspecs;
+  size_t nspecs_max;
+  struct printf_spec *specs;
+
+  /* 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;
+  int *args_type;
+  union printf_arg *args_value;
+
+  /* Positional parameters refer to arguments directly.  This could also
+     determine the maximum number of arguments.  Track the maximum number.  */
+  size_t max_ref_arg;
 
-  /* Pointer into the format string.  */
-  register CONST char *f;
+  /* End of leading constant string.  */
+  const char *lead_str_end;
 
   /* Number of characters written.  */
   register size_t done = 0;
 
+  /* Running pointer through format string.  */
+  const char *f;
+
+  /* Just a counter.  */
+  int cnt;
+
   ARGCHECK (s, format);
 
   if (UNBUFFERED_P (s))
     /* Use a helper function which will allocate a local temporary buffer
        for the stream and then call us again.  */
-    return buffered_vfprintf (s, format, args);
+    return buffered_vfprintf (s, format, ap);
 
   /* Reset multibyte characters to their initial state.  */
   (void) mblen ((char *) NULL, 0);
 
-  /* Figure out the thousands seperator character.  */
+  /* Figure out the thousands separator character.  */
   if (mbtowc (&thousands_sep, _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP),
-             strlen (_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP))) <= 0)
+              strlen (_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP))) <= 0)
     thousands_sep = (wchar_t) *_NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP);
   grouping = _NL_CURRENT (LC_NUMERIC, GROUPING);
-  if (*grouping == '\0' || thousands_sep == L'\0')
+  if (*grouping == '\0' || *grouping == CHAR_MAX || thousands_sep == L'\0')
     grouping = NULL;
 
-  f = format;
-  while (*f != '\0')
+  nspecs_max = 32;             /* A more or less arbitrary start value.  */
+  specs = alloca (nspecs_max * sizeof (struct printf_spec));
+  nspecs = 0;
+  nargs = 0;
+  max_ref_arg = 0;
+
+  /* Find the first format specifier.  */
+  lead_str_end = find_spec (format);
+
+  for (f = lead_str_end; *f != '\0'; f = specs[nspecs++].next_fmt)
     {
-      /* Type modifiers.  */
-      char is_short, is_long, is_long_double;
-#ifdef HAVE_LONGLONG
-      /* We use the `L' modifier for `long long int'.  */
-#define        is_longlong     is_long_double
-#else
-#define        is_longlong     0
-#endif
-      /* Format spec modifiers.  */
-      char space, showsign, left, alt, group;
-
-      /* Padding character: ' ' or '0'.  */
-      char pad;
-      /* Width of a field.  */
-      register int width;
-      /* Precision of a field.  */
-      int prec;
-
-      /* Decimal integer is negative.  */
-      char is_neg;
-
-      /* Current character of the format.  */
-      char fc;
-
-      /* Base of a number to be written.  */
-      int base;
-      /* Integral values to be written.  */
-      unsigned LONGLONG int num;
-      LONGLONG int signed_num;
-
-      /* String to be written.  */
-      CONST char *str;
-      char errorbuf[1024];     /* Buffer sometimes used by %m.  */
-
-      /* Auxiliary function to do output.  */
-      printf_function *function;
-
-      if (!isascii(*f))
+      if (nspecs >= nspecs_max)
        {
-         /* Non-ASCII, may be a multibyte.  */
-         int len = mblen (f, strlen (f));
-         if (len > 0)
+         /* Extend the array of format specifiers.  */
+         struct printf_spec *old = specs;
+
+         nspecs_max *= 2;
+         specs = alloca (nspecs_max * sizeof (struct printf_spec));
+         if (specs == &old[nspecs])
+           /* Stack grows up, OLD was the last thing allocated; extend it.  */
+           nspecs_max += nspecs_max / 2;
+         else
            {
-             outstring (f, len);
-             continue;
+             /* Copy the old array's elements to the new space.  */
+             memcpy (specs, old, nspecs * sizeof (struct printf_spec));
+             if (old == &specs[nspecs])
+               /* Stack grows down, OLD was just below the new SPECS.
+                  We can use that space when the new space runs out.  */
+               nspecs_max += nspecs_max / 2;
            }
        }
 
-      if (*f != '%')
-       {
-         /* This isn't a format spec, so write everything out until the
-            next one.  To properly handle multibyte characters, we cannot
-            just search for a '%'.  Since multibyte characters are hairy
-            (and dealt with above), if we hit any byte above 127 (only
-            those can start a multibyte character) we just punt back to
-            that code.  */
-         do
-           outchar (*f++);
-         while (*f != '\0' && *f != '%' && isascii (*f));
-         continue;
-       }
+      /* Parse the format specifier.  */
+      nargs += parse_one_spec (f, nargs, &specs[nspecs], &max_ref_arg);
+    }
+
+  /* Determine the number of arguments the format string consumes.  */
+  nargs = MAX (nargs, max_ref_arg);
+
+  /* Allocate memory for the argument descriptions.  */
+  args_type = alloca (nargs * sizeof (int));
+  args_value = alloca (nargs * sizeof (union printf_arg));
+
+  /* XXX Could do sanity check here:
+     Initialize args_type elts to zero.
+     If any is still zero after this loop, format is invalid.  */
+
+  /* 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;
 
-      ++f;
+      /* 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;
 
-      /* Check for "%%".  Note that although the ANSI standard lists
-        '%' as a conversion specifier, it says "The complete format
-        specification shall be `%%'," so we can avoid all the width
-        and precision processing.  */
-      if (*f == '%')
+      switch (specs[cnt].ndata_args)
        {
-         ++f;
-         outchar('%');
-         continue;
+       case 0:                 /* No arguments.  */
+         break;
+       case 1:                 /* One argument; we already have the type.  */
+         args_type[specs[cnt].data_arg] = specs[cnt].data_arg_type;
+         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]);
+         break;
        }
+    }
 
-      /* Check for spec modifiers.  */
-      space = showsign = left = alt = group = 0;
-      pad = ' ';
-      while (*f == ' ' || *f == '+' || *f == '-' || *f == '#' || *f == '0' ||
-            *f == '\'')
-       switch (*f++)
-         {
-         case ' ':
-           /* Output a space in place of a sign, when there is no sign.  */
-           space = 1;
-           break;
-         case '+':
-           /* Always output + or - for numbers.  */
-           showsign = 1;
-           break;
-         case '-':
-           /* Left-justify things.  */
-           left = 1;
-           break;
-         case '#':
-           /* Use the "alternate form":
-              Hex has 0x or 0X, FP always has a decimal point.  */
-           alt = 1;
-           break;
-         case '0':
-           /* Pad with 0s.  */
-           pad = '0';
-           break;
-         case '\'':
-           /* Show grouping in numbers if the locale information
-              indicates any.  */
-           group = 1;
-           break;
-         }
-      if (left)
-       pad = ' ';
-
-      /* Get the field width.  */
-      width = 0;
-      if (*f == '*')
+  /* 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, type);                              \
+       break
+
+       T (PA_CHAR, pa_char, int); /* Promoted.  */
+       T (PA_INT|PA_FLAG_SHORT, pa_short_int, int); /* Promoted.  */
+       T (PA_INT, pa_int, int);
+       T (PA_INT|PA_FLAG_LONG, pa_long_int, long int);
+       T (PA_INT|PA_FLAG_LONG_LONG, pa_long_long_int, long long int);
+       T (PA_FLOAT, pa_float, double); /* Promoted.  */
+       T (PA_DOUBLE, pa_double, double);
+       T (PA_DOUBLE|PA_FLAG_LONG_DOUBLE, pa_long_double, long double);
+       T (PA_STRING, pa_string, const char *);
+       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, void *);
+       break;
+      }
+
+  /* Write the literal text before the first format.  */
+  outstring (format, lead_str_end - format);
+
+  /* Now walk through all format specifiers and process them.  */
+  for (cnt = 0; cnt < nspecs; ++cnt)
+    {
+      printf_function *function; /* Auxiliary function to do output.  */
+      int is_neg;              /* Decimal integer is negative.  */
+      int base;                        /* Base of a number to be written.  */
+      unsigned long long int num; /* Integral number to be written.  */
+      const char *str;         /* String to be written.  */
+      char errorbuf[1024];      /* Buffer sometimes used by %m.  */
+
+      if (specs[cnt].width_arg != -1)
        {
-         /* The field width is given in an argument.
-            A negative field width indicates left justification.  */
-         nextarg(width, int);
-         if (width < 0)
+         /* Extract the field width from an argument.  */
+         specs[cnt].info.width = args_value[specs[cnt].width_arg].pa_int;
+
+         if (specs[cnt].info.width < 0)
+           /* If the width value is negative left justification is selected
+              and the value is taken as being positive.  */
            {
-             width = - width;
-             left = 1;
+             specs[cnt].info.width = -specs[cnt].info.width;
+             specs[cnt].info.left = 1;
            }
-         ++f;
        }
-      else
-       while (isdigit (*f))
-         {
-           width *= 10;
-           width += *f++ - '0';
-         }
 
-      /* Get the precision.  */
-      /* -1 means none given; 0 means explicit 0.  */
-      prec = -1;
-      if (*f == '.')
+      if (specs[cnt].prec_arg != -1)
        {
-         ++f;
-         if (*f == '*')
-           {
-             /* The precision is given in an argument.  */
-             nextarg(prec, int);
-             /* Avoid idiocy.  */
-             if (prec < 0)
-               prec = -1;
-             ++f;
-           }
-         else if (isdigit (*f))
-           {
-             prec = *f++ - '0';
-             while (*f != '\0' && isdigit (*f))
-               {
-                 prec *= 10;
-                 prec += *f++ - '0';
-               }
-           }
-         else
-           /* "%.?" is treated like "%.0?".  */
-           prec = 0;
+         /* Extract the precision from an argument.  */
+         specs[cnt].info.prec = args_value[specs[cnt].prec_arg].pa_int;
+
+         if (specs[cnt].info.prec < 0)
+           /* If the precision is negative the precision is omitted.  */
+           specs[cnt].info.prec = -1;
        }
 
-      /* If there was a precision specified, ignore the 0 flag and always
-        pad with spaces.  */
-      if (prec != -1)
-       pad = ' ';
+      /* Check for a user-defined handler for this spec.  */
+      function = (__printf_function_table == NULL ? NULL :
+                  __printf_function_table[specs[cnt].info.spec]);
 
-      /* Check for type modifiers.  */
-      is_short = is_long = is_long_double = 0;
-      while (*f == 'h' || *f == 'l' || *f == 'L' || *f == 'q' || *f == 'Z')
-       switch (*f++)
-         {
-         case 'h':
-           /* int's are short int's.  */
-           is_short = 1;
-           break;
-         case 'l':
-#ifdef HAVE_LONGLONG
-           if (is_long)
-             /* A double `l' is equivalent to an `L'.  */
-             is_longlong = 1;
-           else
-#endif
-             /* int's are long int's.  */
-             is_long = 1;
-           break;
-         case 'L':
-           /* double's are long double's, and int's are long long int's.  */
-           is_long_double = 1;
-           break;
+      if (function != NULL)
+      use_function:            /* Built-in formats with helpers use this.  */
+       {
+         int function_done;
+         unsigned int i;
+         const void *ptr[specs[cnt].ndata_args];
 
-         case 'Z':
-           /* int's are size_t's.  */
-#ifdef HAVE_LONGLONG
-           assert (sizeof(size_t) <= sizeof(unsigned long long int));
-           is_longlong = sizeof(size_t) > sizeof(unsigned long int);
-#endif
-           is_long = sizeof(size_t) > sizeof(unsigned int);
-           break;
+         /* Fill in an array of pointers to the argument values.  */
+         for (i = 0; i < specs[cnt].ndata_args; ++i)
+           ptr[i] = &args_value[specs[cnt].data_arg + i];
 
-         case 'q':
-           /* 4.4 uses this for long long.  */
-#ifdef HAVE_LONGLONG
-           is_longlong = 1;
-#else
-           is_long = 1;
-#endif
-           break;
-         }
+         /* Call the function.  */
+         function_done = (*function) (s, &specs[cnt].info, ptr);
 
-      /* Format specification.  */
-      fc = *f++;
-      function = (__printf_function_table == NULL ? NULL :
-                 __printf_function_table[fc]);
-      if (function == NULL)
-       switch (fc)
+         /* If an error occured don't do any further work.  */
+         if (function_done < 0)
+           return -1;
+
+         done += function_done;
+       }
+      else
+       switch (specs[cnt].info.spec)
          {
+         case '%':
+           /* Write a literal "%".  */
+           outchar ('%');
+           break;
          case 'i':
          case 'd':
-           /* Decimal integer.  */
-           base = 10;
-           if (is_longlong)
-             nextarg(signed_num, LONGLONG int);
-           else if (is_long)
-             nextarg(signed_num, long int);
-           else if (!is_short)
-             castarg(signed_num, int, long int);
-           else
-             castarg(signed_num, int, short int);
-
-           is_neg = signed_num < 0;
-           num = is_neg ? (- signed_num) : signed_num;
-           goto number;
+           {
+             long long int signed_num;
+
+             /* Decimal integer.  */
+             base = 10;
+             if (specs[cnt].info.is_longlong)
+               signed_num = args_value[specs[cnt].data_arg].pa_long_long_int;
+             else if (specs[cnt].info.is_long)
+               signed_num = args_value[specs[cnt].data_arg].pa_long_int;
+             else if (!specs[cnt].info.is_short)
+               signed_num = args_value[specs[cnt].data_arg].pa_int;
+             else
+               signed_num = args_value[specs[cnt].data_arg].pa_short_int;
+
+             is_neg = signed_num < 0;
+             num = is_neg ? (- signed_num) : signed_num;
+             goto number;
+           }
 
          case 'u':
            /* Decimal unsigned integer.  */
-           base = 10;
-           goto unsigned_number;
+            base = 10;
+            goto unsigned_number;
 
          case 'o':
-           /* Octal unsigned integer.  */
-           base = 8;
-           goto unsigned_number;
+            /* Octal unsigned integer.  */
+            base = 8;
+            goto unsigned_number;
 
-         case 'X':
-           /* Hexadecimal unsigned integer.  */
-         case 'x':
-           /* Hex with lower-case digits.  */
-
-           base = 16;
+          case 'X':
+            /* Hexadecimal unsigned integer.  */
+          case 'x':
+            /* Hex with lower-case digits.  */
+            base = 16;
 
          unsigned_number:
-           /* Unsigned number of base BASE.  */
-
-           if (is_longlong)
-             castarg(num, LONGLONG int, unsigned LONGLONG int);
-           else if (is_long)
-             castarg(num, long int, unsigned long int);
-           else if (!is_short)
-             castarg(num, int, unsigned int);
-           else
-             castarg(num, int, unsigned short int);
-
-           /* ANSI only specifies the `+' and
-              ` ' flags for signed conversions.  */
-           is_neg = showsign = space = 0;
+            /* Unsigned number of base BASE.  */
+
+            if (specs[cnt].info.is_longlong)
+             num = args_value[specs[cnt].data_arg].pa_u_long_long_int;
+            else if (specs[cnt].info.is_long)
+             num = args_value[specs[cnt].data_arg].pa_u_long_int;
+            else if (!specs[cnt].info.is_short)
+             num = args_value[specs[cnt].data_arg].pa_u_int;
+            else
+             num = args_value[specs[cnt].data_arg].pa_u_short_int;
+
+            /* ANSI only specifies the `+' and
+               ` ' flags for signed conversions.  */
+            is_neg = 0;
+           specs[cnt].info.showsign = 0;
+           specs[cnt].info.space = 0;
 
          number:
            /* Number of base BASE.  */
-           {
-             char work[BUFSIZ];
-             char *CONST workend = &work[sizeof(work) - 1];
-             register char *w;
-
-             /* Supply a default precision if none was given.  */
-             if (prec == -1)
-               prec = 1;
-
-             /* Put the number in WORK.  */
-             w = _itoa (num, workend + 1, base, fc == 'X') - 1;
-             if (group && grouping)
-               w = group_number (w, workend, grouping, thousands_sep);
-             width -= workend - w;
-             prec -= workend - w;
-
-             if (alt && base == 8 && prec <= 0)
-               {
-                 *w-- = '0';
-                 --width;
-               }
-
-             if (prec > 0)
-               {
-                 width -= prec;
-                 while (prec-- > 0)
-                   *w-- = '0';
-               }
-
-             if (alt && base == 16)
-               width -= 2;
-
-             if (is_neg || showsign || space)
-               --width;
-
-             if (!left && pad == ' ')
-               PAD (' ');
-
-             if (is_neg)
-               outchar('-');
-             else if (showsign)
-               outchar('+');
-             else if (space)
-               outchar(' ');
-
-             if (alt && base == 16)
-               {
-                 outchar ('0');
-                 outchar (fc);
-               }
-
-             if (!left && pad == '0')
-               PAD ('0');
-
-             /* Write the number.  */
-             while (++w <= workend)
-               outchar(*w);
-
-             if (left)
-               PAD (' ');
-           }
-           break;
-
-         case 'e':
-         case 'E':
-         case 'f':
-         case 'g':
-         case 'G':
-           {
-             /* Floating-point number.  */
-             extern printf_function __printf_fp;
-             function = __printf_fp;
-             goto use_function;
-           }
-
-         case 'c':
-           /* Character.  */
-           nextarg(num, int);
-           if (!left)
-             {
-               --width;
-               PAD (' ');
-             }
-           outchar ((unsigned char) num);
-           if (left)
-             PAD (' ');
-           break;
-
-         case 's':
-           {
-             static CONST char null[] = "(null)";
-             size_t len;
-
-             nextarg(str, CONST char *);
+            {
+              char work[BUFSIZ];
+              char *const workend = &work[sizeof(work) - 1];
+              register char *w;
+
+              /* Supply a default precision if none was given.  */
+              if (specs[cnt].info.prec == -1)
+                specs[cnt].info.prec = 1;
+
+              /* Put the number in WORK.  */
+              w = _itoa (num, workend + 1, base, specs[cnt].info.spec == 'X');
+             w -= 1;
+              if (specs[cnt].info.group && grouping)
+                w = group_number (w, workend, grouping, thousands_sep);
+              specs[cnt].info.width -= workend - w;
+              specs[cnt].info.prec -= workend - w;
+
+              if (num != 0 && specs[cnt].info.alt && base == 8
+                 && specs[cnt].info.prec <= 0)
+                {
+                 /* Add octal marker.  */
+                  *w-- = '0';
+                  --specs[cnt].info.width;
+                }
+
+              if (specs[cnt].info.prec > 0)
+                {
+                 /* Add zeros to the precision.  */
+                  specs[cnt].info.width -= specs[cnt].info.prec;
+                  while (specs[cnt].info.prec-- > 0)
+                    *w-- = '0';
+                }
+
+              if (num != 0 && specs[cnt].info.alt && base == 16)
+               /* Account for 0X hex marker.  */
+                specs[cnt].info.width -= 2;
+
+              if (is_neg || specs[cnt].info.showsign || specs[cnt].info.space)
+                --specs[cnt].info.width;
+
+              if (!specs[cnt].info.left && specs[cnt].info.pad == ' ')
+                PAD (' ');
+
+              if (is_neg)
+                outchar ('-');
+              else if (specs[cnt].info.showsign)
+                outchar ('+');
+              else if (specs[cnt].info.space)
+                outchar (' ');
+
+              if (num != 0 && specs[cnt].info.alt && base == 16)
+                {
+                  outchar ('0');
+                  outchar (specs[cnt].info.spec);
+                }
+
+              if (!specs[cnt].info.left && specs[cnt].info.pad == '0')
+                PAD ('0');
+
+              /* Write the number.  */
+              while (++w <= workend)
+                outchar (*w);
+
+              if (specs[cnt].info.left)
+                PAD (' ');
+            }
+            break;
+
+          case 'e':
+          case 'E':
+          case 'f':
+          case 'g':
+          case 'G':
+            {
+              /* Floating-point number.  This is handled by printf_fp.c.  */
+              extern printf_function __printf_fp;
+              function = __printf_fp;
+              goto use_function;
+            }
+
+          case 'c':
+            /* Character.  */
+            if (!specs[cnt].info.left)
+              {
+                --specs[cnt].info.width;
+                PAD (' ');
+              }
+            outchar ((unsigned char) args_value[specs[cnt].data_arg].pa_char);
+            if (specs[cnt].info.left)
+              PAD (' ');
+            break;
+
+          case 's':
+            {
+              static const char null[] = "(null)";
+              size_t len;
+
+             str = args_value[specs[cnt].data_arg].pa_string;
 
            string:
 
-             if (str == NULL)
-               /* Write "(null)" if there's space.  */
-               if (prec == -1 || prec >= (int) sizeof(null) - 1)
-                 {
-                   str = null;
-                   len = sizeof(null) - 1;
-                 }
-               else
-                 {
-                   str = "";
-                   len = 0;
-                 }
-             else
-               len = strlen(str);
-
-             if (prec != -1 && (size_t) prec < len)
-               len = prec;
-             width -= len;
-
-             if (!left)
-               PAD (' ');
-             outstring (str, len);
-             if (left)
-               PAD (' ');
-           }
-           break;
-
-         case 'p':
-           /* Generic pointer.  */
-           {
-             CONST PTR ptr;
-             nextarg(ptr, CONST PTR);
-             if (ptr != NULL)
+              if (str == NULL)
                {
-                 /* If the pointer is not NULL, write it as a %#x spec.  */
-                 base = 16;
-                 fc = 'x';
-                 alt = 1;
-                 num = (unsigned LONGLONG int) (unsigned long int) ptr;
-                 is_neg = 0;
-                 group = 0;
-                 goto number;
+                 /* Write "(null)" if there's space.  */
+                 if (specs[cnt].info.prec == -1
+                     || specs[cnt].info.prec >= (int) sizeof (null) - 1)
+                   {
+                     str = null;
+                     len = sizeof (null) - 1;
+                   }
+                 else
+                   {
+                     str = "";
+                     len = 0;
+                   }
                }
-             else
-               {
-                 /* Write "(nil)" for a nil pointer.  */
-                 static CONST char nil[] = "(nil)";
-                 register CONST char *p;
-
-                 width -= sizeof (nil) - 1;
-                 if (!left)
-                   PAD (' ');
-                 for (p = nil; *p != '\0'; ++p)
-                   outchar (*p);
-                 if (left)
-                   PAD (' ');
-               }
-           }
-           break;
-
-         case 'n':
-           /* Answer the count of characters written.  */
-           if (is_longlong)
-             {
-               LONGLONG int *p;
-               nextarg(p, LONGLONG int *);
-               *p = done;
-             }
-           else if (is_long)
-             {
-               long int *p;
-               nextarg(p, long int *);
-               *p = done;
-             }
-           else if (!is_short)
-             {
-               int *p;
-               nextarg(p, int *);
-               *p = done;
-             }
-           else
-             {
-               short int *p;
-               nextarg(p, short int *);
-               *p = done;
-             }
-           break;
-
-         case 'm':
-           {
-             extern char *_strerror_internal __P ((int, char buf[1024]));
-             str = _strerror_internal (errno, errorbuf);
-             goto string;
-           }
-
-         default:
-           /* Unrecognized format specifier.  */
-           function = printf_unknown;
-           goto use_function;
+              else
+                len = strlen (str);
+
+              if (specs[cnt].info.prec != -1
+                 && (size_t) specs[cnt].info.prec < len)
+               /* Limit the length to the precision.  */
+                len = specs[cnt].info.prec;
+              specs[cnt].info.width -= len;
+
+              if (!specs[cnt].info.left)
+                PAD (' ');
+              outstring (str, len);
+              if (specs[cnt].info.left)
+                PAD (' ');
+            }
+            break;
+
+          case 'p':
+            /* Generic pointer.  */
+            {
+              const void *ptr;
+              ptr = args_value[specs[cnt].data_arg].pa_pointer;
+              if (ptr != NULL)
+                {
+                  /* If the pointer is not NULL, write it as a %#x spec.  */
+                  base = 16;
+                  num = (unsigned long long int) (unsigned long int) ptr;
+                  is_neg = 0;
+                  specs[cnt].info.alt = 1;
+                 specs[cnt].info.spec = 'x';
+                  specs[cnt].info.group = 0;
+                  goto number;
+                }
+              else
+                {
+                  /* Write "(nil)" for a nil pointer.  */
+                  str = "(nil)";
+                 /* Make sure the full string "(nil)" is printed.  */
+                 if (specs[cnt].info.prec < 5)
+                   specs[cnt].info.prec = 5;
+                  goto string;
+                }
+            }
+            break;
+
+          case 'n':
+            /* Answer the count of characters written.  */
+            if (specs[cnt].info.is_longlong)
+             *(long long int *) 
+               args_value[specs[cnt].data_arg].pa_pointer = done;
+            else if (specs[cnt].info.is_long)
+             *(long int *) 
+               args_value[specs[cnt].data_arg].pa_pointer = done;
+            else if (!specs[cnt].info.is_short)
+             *(int *) 
+               args_value[specs[cnt].data_arg].pa_pointer = done;
+            else
+             *(short int *) 
+               args_value[specs[cnt].data_arg].pa_pointer = done;
+            break;
+
+          case 'm':
+            {
+              extern char *_strerror_internal __P ((int, char buf[1024]));
+              str = _strerror_internal (errno, errorbuf);
+              goto string;
+            }
+
+          default:
+            /* Unrecognized format specifier.  */
+            function = printf_unknown;
+            goto use_function;
          }
-      else
-      use_function:
-       {
-         int function_done;
-         struct printf_info info;
-
-         info.prec = prec;
-         info.width = width;
-         info.spec = fc;
-         info.is_long_double = is_long_double;
-         info.is_short = is_short;
-         info.is_long = is_long;
-         info.alt = alt;
-         info.space = space;
-         info.left = left;
-         info.showsign = showsign;
-         info.group = group;
-         info.pad = pad;
-
-         function_done = (*function) (s, &info, &args);
-         if (function_done < 0)
-           return -1;
 
-         done += function_done;
-       }
+      /* Write the following constant string.  */
+      outstring (specs[cnt].end_of_fmt,
+                specs[cnt].next_fmt - specs[cnt].end_of_fmt);
     }
 
   return done;
 }
 
 
+/* Handle an unknown format specifier.  This prints out a canonicalized
+   representation of the format spec itself.  */
+
 static int
-DEFUN(printf_unknown, (s, info, arg),
-      FILE *s AND CONST struct printf_info *info AND va_list *arg)
+printf_unknown (s, info, args)
+  FILE *s;
+  const struct printf_info *info;
+  const void **const args;
 {
   int done = 0;
   char work[BUFSIZ];
-  char *CONST workend = &work[sizeof(work) - 1];
+  char *const workend = &work[sizeof(work) - 1];
   register char *w;
-  register int prec = info->prec, width = info->width;
 
-  outchar('%');
+  outchar ('%');
 
   if (info->alt)
     outchar ('#');
@@ -692,29 +643,23 @@ DEFUN(printf_unknown, (s, info, arg),
   if (info->pad == '0')
     outchar ('0');
 
-  w = workend;
-  while (width > 0)
+  if (info->width != 0)
     {
-      *w-- = '0' + (width % 10);
-      width /= 10;
+      w = _itoa (info->width, workend + 1, 10, 0);
+      while (++w <= workend)
+       outchar (*w);
     }
-  while (++w <= workend)
-    outchar(*w);
 
   if (info->prec != -1)
     {
-      outchar('.');
-      w = workend;
-      while (prec > 0)
-       {
-         *w-- = '0' + (prec % 10);
-         prec /= 10;
-       }
+      outchar ('.');
+      w = _itoa (info->prec, workend + 1, 10, 0);
       while (++w <= workend)
-       outchar(*w);
+       outchar (*w);
     }
 
-  outchar(info->spec);
+  if (info->spec != '\0')
+    outchar (info->spec);
 
   return done;
 }
@@ -768,7 +713,6 @@ group_number (char *w, char *workend, const char *grouping,
            }
        }
     }
-
   return w;
 }
 \f
@@ -781,7 +725,9 @@ struct helper_file
   };
 
 static int
-DEFUN(_IO_helper_overflow, (s, c), _IO_FILE *s AND int c)
+_IO_helper_overflow (s, c)
+  _IO_FILE *s;
+  int c;
 {
   _IO_FILE *target = ((struct helper_file*) s)->_put_stream;
   int used = s->_IO_write_ptr - s->_IO_write_base;
@@ -815,8 +761,10 @@ static const struct _IO_jump_t _IO_helper_jumps =
   };
 
 static int
-DEFUN(buffered_vfprintf, (s, format, args),
-      register _IO_FILE *s AND char CONST *format AND _IO_va_list args)
+buffered_vfprintf (s, format, args)
+  register _IO_FILE *s;
+  char const *format;
+  _IO_va_list args;
 {
   char buf[_IO_BUFSIZ];
   struct helper_file helper;
@@ -847,8 +795,10 @@ DEFUN(buffered_vfprintf, (s, format, args),
 #else /* !USE_IN_LIBIO */
 
 static int
-DEFUN(buffered_vfprintf, (s, format, args),
-      register FILE *s AND char CONST *format AND va_list args)
+buffered_vfprintf (s, format, args)
+  register FILE *s;
+  char const *format;
+  va_list args;
 {
   char buf[BUFSIZ];
   int result;
@@ -882,27 +832,21 @@ ssize_t
 __printf_pad (s, pad, count)
      FILE *s;
      char pad;
-     int count;
+     size_t count;
 {
-  CONST char *padptr;
-  register int i;
-  size_t written = 0, w;
+  const char *padptr;
+  register size_t i;
 
   padptr = pad == ' ' ? blanks : zeroes;
 
   for (i = count; i >= PADSIZE; i -= PADSIZE)
-    {
-      w = PUT(s, padptr, PADSIZE);
-      written += w;
-      if (w != PADSIZE)
-       return written;
-    }
+    if (PUT (s, padptr, PADSIZE) != PADSIZE)
+      return -1;
   if (i > 0)
-    {
-      w = PUT(s, padptr, i);
-      written += w;
-    }
-  return written;
+    if (PUT (s, padptr, i) != i)
+      return -1;
+
+  return count;
 }
 #undef PADSIZE
 #endif /* USE_IN_LIBIO */
index 62c9bd9..c00aa5c 100644 (file)
@@ -48,13 +48,6 @@ static void start1();
 
 #ifndef        HAVE__start
 
-#if !defined (NO_UNDERSCORES) && defined (__GNUC__)
-/* Declare _start with an explicit assembly symbol name of `start'
-   (note no leading underscore).  This is the name vendor crt0.o's
-   tend to use, and thus the name most linkers expect.  */
-void _start (void) asm ("start");
-#endif
-
 /* N.B.: It is important that this be the first function.
    This file is the first thing in the text section.  */
 void
@@ -63,17 +56,20 @@ DEFUN_VOID(_start)
   start1();
 }
 
-#if !defined (NO_UNDERSCORES) && defined (HAVE_WEAK_SYMBOLS)
-/* Make an alias called `start' (no leading underscore,
-   so it can't conflict with C symbols) for `_start'.  */
-asm (".weak start; start = _start");
+#ifndef NO_UNDERSCORES
+/* Make an alias called `start' (no leading underscore, so it can't
+   conflict with C symbols) for `_start'.  This is the name vendor crt0.o's
+   tend to use, and thus the name most linkers expect.  */
+void _start (void) asm ("start");
+#endif
+asm (".set start, __start");
 #endif
 
 #endif
 
 /* ARGSUSED */
 static void
-start1(ARG_DUMMIES argc, argp)
+start1 (ARG_DUMMIES argc, argp)
      DECL_DUMMIES
      int argc;
      char *argp;
@@ -94,5 +90,5 @@ start1(ARG_DUMMIES argc, argp)
   __libc_init (argc, argv, __environ);
 
   /* Call the user program.  */
-  exit(main(argc, argv, __environ));
+  exit (main (argc, argv, __environ));
 }