aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoland McGrath <roland@gnu.org>1995-03-24 07:44:08 +0000
committerRoland McGrath <roland@gnu.org>1995-03-24 07:44:08 +0000
commita04e740593c921d45de28bb5ccafe46f9f772769 (patch)
treeb82eea004d5ff062879e8c4b68b9b6bc61c2708a
parent3ef21326e9f4644ce4058b7ceabd3bc6f10be374 (diff)
downloadglibc-a04e740593c921d45de28bb5ccafe46f9f772769.zip
glibc-a04e740593c921d45de28bb5ccafe46f9f772769.tar.gz
glibc-a04e740593c921d45de28bb5ccafe46f9f772769.tar.bz2
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. * stdlib/Makefile: All the mpn stuff moved here from stdio/Makefile.
-rw-r--r--ChangeLog15
-rw-r--r--stdio/Makefile2
-rw-r--r--stdio/printf-parse.h388
-rw-r--r--stdio/printf-prs.c205
-rw-r--r--stdio/vfprintf.c1002
-rw-r--r--sysdeps/unix/start.c22
6 files changed, 919 insertions, 715 deletions
diff --git a/ChangeLog b/ChangeLog
index fed93d2..e02c200 100644
--- 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
diff --git a/stdio/Makefile b/stdio/Makefile
index 64b0598..955d7f1 100644
--- a/stdio/Makefile
+++ b/stdio/Makefile
@@ -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
index 0000000..0f6e9e2
--- /dev/null
+++ b/stdio/printf-parse.h
@@ -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;
+}
diff --git a/stdio/printf-prs.c b/stdio/printf-prs.c
index 2f55dd3..811a9cb 100644
--- a/stdio/printf-prs.c
+++ b/stdio/printf-prs.c
@@ -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);
}
diff --git a/stdio/vfprintf.c b/stdio/vfprintf.c
index c246217..056ea32 100644
--- a/stdio/vfprintf.c
+++ b/stdio/vfprintf.c
@@ -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;
}
@@ -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 */
diff --git a/sysdeps/unix/start.c b/sysdeps/unix/start.c
index 62c9bd9..c00aa5c 100644
--- a/sysdeps/unix/start.c
+++ b/sysdeps/unix/start.c
@@ -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));
}