diff options
| author | Roland McGrath <roland@gnu.org> | 1995-03-24 07:44:08 +0000 |
|---|---|---|
| committer | Roland McGrath <roland@gnu.org> | 1995-03-24 07:44:08 +0000 |
| commit | a04e740593c921d45de28bb5ccafe46f9f772769 (patch) | |
| tree | b82eea004d5ff062879e8c4b68b9b6bc61c2708a | |
| parent | 3ef21326e9f4644ce4058b7ceabd3bc6f10be374 (diff) | |
| download | glibc-a04e740593c921d45de28bb5ccafe46f9f772769.tar.xz glibc-a04e740593c921d45de28bb5ccafe46f9f772769.zip | |
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-- | ChangeLog | 15 | ||||
| -rw-r--r-- | stdio/Makefile | 2 | ||||
| -rw-r--r-- | stdio/printf-parse.h | 388 | ||||
| -rw-r--r-- | stdio/printf-prs.c | 205 | ||||
| -rw-r--r-- | stdio/vfprintf.c | 1002 | ||||
| -rw-r--r-- | sysdeps/unix/start.c | 22 |
6 files changed, 919 insertions, 715 deletions
@@ -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 64b0598111..955d7f1fcd 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 0000000000..0f6e9e287b --- /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 2f55dd3157..811a9cb7eb 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 c2462178ed..056ea32bee 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 |
