diff options
Diffstat (limited to 'stdio/vfprintf.c')
-rw-r--r-- | stdio/vfprintf.c | 858 |
1 files changed, 0 insertions, 858 deletions
diff --git a/stdio/vfprintf.c b/stdio/vfprintf.c deleted file mode 100644 index 63a5148463..0000000000 --- a/stdio/vfprintf.c +++ /dev/null @@ -1,858 +0,0 @@ -/* Copyright (C) 1991, 1992, 1993, 1994, 1995 Free Software Foundation, Inc. -This file is part of the 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 <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 <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. */ - -#ifdef USE_IN_LIBIO -/* This code is for use in libio. */ -#include <libioP.h> -#define PUT(f, s, n) _IO_sputn (f, s, n) -#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) \ - do \ - { \ - /* Check file argument for consistence. */ \ - CHECK_FILE (s, -1); \ - if (s->_flags & _IO_NO_WRITES || format == NULL) \ - { \ - MAYBE_SET_EINVAL; \ - return -1; \ - } \ - } while (0) -#define UNBUFFERED_P(s) ((s)->_IO_file_flags & _IO_UNBUFFERED) -#else /* ! USE_IN_LIBIO */ -/* This code is for use in the GNU C library. */ -#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, 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 \ - { \ - /* Check file argument for consistence. */ \ - if (!__validfp(s) || !s->__mode.__write || format == NULL) \ - { \ - errno = EINVAL; \ - return -1; \ - } \ - if (!s->__seen) \ - { \ - if (__flshfp (s, EOF) == EOF) \ - return -1; \ - } \ - } while (0) -#define UNBUFFERED_P(s) ((s)->__buffer == NULL) -#endif /* USE_IN_LIBIO */ - - -#define outchar(x) \ - do \ - { \ - register const int outc = (x); \ - if (putc (outc, s) == EOF) \ - return -1; \ - else \ - ++done; \ - } while (0) - -#define outstring(string, len) \ - do \ - { \ - if (len > 20) \ - { \ - if (PUT (s, string, len) != len) \ - return -1; \ - done += len; \ - } \ - else \ - { \ - 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)); - -static printf_function printf_unknown; - -extern printf_function **__printf_function_table; - -static char *group_number __P ((char *, char *, const char *, wchar_t)); - - -int -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; - - /* 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; - - /* 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, ap); - - /* Reset multibyte characters to their initial state. */ - (void) mblen ((char *) NULL, 0); - - /* Figure out the thousands separator character. */ - if (mbtowc (&thousands_sep, _NL_CURRENT (LC_NUMERIC, THOUSANDS_SEP), - 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' || *grouping == CHAR_MAX || thousands_sep == L'\0') - grouping = NULL; - - 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) - { - if (nspecs >= nspecs_max) - { - /* 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 - { - /* 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; - } - } - - /* 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; - - /* 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; - - switch (specs[cnt].ndata_args) - { - 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; - } - } - - /* 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) - { - /* 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. */ - { - specs[cnt].info.width = -specs[cnt].info.width; - specs[cnt].info.left = 1; - } - } - - if (specs[cnt].prec_arg != -1) - { - /* 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; - } - - /* Check for a user-defined handler for this spec. */ - function = (__printf_function_table == NULL ? NULL : - __printf_function_table[specs[cnt].info.spec]); - - 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]; - - /* 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]; - - /* Call the function. */ - function_done = (*function) (s, &specs[cnt].info, ptr); - - /* 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': - { - 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; - - case 'o': - /* Octal unsigned integer. */ - base = 8; - goto unsigned_number; - - case 'X': - /* Hexadecimal unsigned integer. */ - case 'x': - /* Hex with lower-case digits. */ - base = 16; - - unsigned_number: - /* 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 (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 (specs[cnt].info.prec == -1 - || specs[cnt].info.prec >= (int) sizeof (null) - 1) - { - str = null; - len = sizeof (null) - 1; - } - else - { - str = ""; - len = 0; - } - } - else if (specs[cnt].info.prec != -1) - { - /* Search for the end of the string, but don't search - past the length specified by the precision. */ - const char *end = memchr (str, '\0', specs[cnt].info.prec); - if (end) - len = end - str; - else - len = specs[cnt].info.prec; - } - else - len = strlen (str); - - 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, size_t)); - str = _strerror_internal (errno, errorbuf, sizeof errorbuf); - goto string; - } - - default: - /* Unrecognized format specifier. */ - function = printf_unknown; - goto use_function; - } - - /* 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 -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]; - register char *w; - - outchar ('%'); - - if (info->alt) - outchar ('#'); - if (info->group) - outchar ('\''); - if (info->showsign) - outchar ('+'); - else if (info->space) - outchar (' '); - if (info->left) - outchar ('-'); - if (info->pad == '0') - outchar ('0'); - - if (info->width != 0) - { - w = _itoa (info->width, workend + 1, 10, 0); - while (++w <= workend) - outchar (*w); - } - - if (info->prec != -1) - { - outchar ('.'); - w = _itoa (info->prec, workend + 1, 10, 0); - while (++w <= workend) - outchar (*w); - } - - if (info->spec != '\0') - outchar (info->spec); - - return done; -} - -/* Group the digits according to the grouping rules of the current locale. - The interpretation of GROUPING is as in `struct lconv' from <locale.h>. */ - -static char * -group_number (char *w, char *workend, const char *grouping, - wchar_t thousands_sep) -{ - int len; - char *src, *s; - - /* We treat all negative values like CHAR_MAX. */ - - if (*grouping == CHAR_MAX || *grouping < 0) - /* No grouping should be done. */ - return w; - - len = *grouping; - - /* Copy existing string so that nothing gets overwritten. */ - src = (char *) alloca (workend - w); - memcpy (src, w + 1, workend - w); - s = &src[workend - w - 1]; - w = workend; - - /* Process all characters in the string. */ - while (s >= src) - { - *w-- = *s--; - - if (--len == 0 && s >= src) - { - /* A new group begins. */ - *w-- = thousands_sep; - - len = *grouping++; - if (*grouping == '\0') - /* The previous grouping repeats ad infinitum. */ - --grouping; - else if (*grouping == CHAR_MAX || *grouping < 0) - { - /* No further grouping to be done. - Copy the rest of the number. */ - do - *w-- = *s--; - while (s >= src); - break; - } - } - } - return w; -} - -#ifdef USE_IN_LIBIO -/* Helper "class" for `fprintf to unbuffered': creates a temporary buffer. */ -struct helper_file - { - struct _IO_FILE_plus _f; - _IO_FILE *_put_stream; - }; - -static int -_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; - if (used) - { - _IO_size_t written = _IO_sputn (target, s->_IO_write_base, used); - s->_IO_write_ptr -= written; - } - return _IO_putc (c, s); -} - -static const struct _IO_jump_t _IO_helper_jumps = - { - _IO_helper_overflow, - _IO_default_underflow, - _IO_default_xsputn, - _IO_default_xsgetn, - _IO_default_read, - _IO_default_write, - _IO_default_doallocate, - _IO_default_pbackfail, - _IO_default_setbuf, - _IO_default_sync, - _IO_default_finish, - _IO_default_close, - _IO_default_stat, - _IO_default_seek, - _IO_default_seekoff, - _IO_default_seekpos, - _IO_default_uflow - }; - -static int -buffered_vfprintf (s, format, args) - register _IO_FILE *s; - char const *format; - _IO_va_list args; -{ - char buf[_IO_BUFSIZ]; - struct helper_file helper; - register _IO_FILE *hp = (_IO_FILE *) &helper; - int result, to_flush; - - /* Initialize helper. */ - helper._put_stream = s; - hp->_IO_write_base = buf; - hp->_IO_write_ptr = buf; - hp->_IO_write_end = buf + sizeof buf; - hp->_IO_file_flags = _IO_MAGIC|_IO_NO_READS; - hp->_jumps = (struct _IO_jump_t *) &_IO_helper_jumps; - - /* Now print to helper instead. */ - result = _IO_vfprintf (hp, format, args); - - /* Now flush anything from the helper to the S. */ - if ((to_flush = hp->_IO_write_ptr - hp->_IO_write_base) > 0) - { - if (_IO_sputn (s, hp->_IO_write_base, to_flush) != to_flush) - return -1; - } - - return result; -} - -#else /* !USE_IN_LIBIO */ - -static int -buffered_vfprintf (s, format, args) - register FILE *s; - char const *format; - va_list args; -{ - char buf[BUFSIZ]; - int result; - - s->__bufp = s->__buffer = buf; - s->__bufsize = sizeof buf; - s->__put_limit = s->__buffer + s->__bufsize; - s->__get_limit = s->__buffer; - - /* Now use buffer to print. */ - result = vfprintf (s, format, args); - - if (fflush (s) == EOF) - result = -1; - s->__buffer = s->__bufp = s->__get_limit = s->__put_limit = NULL; - s->__bufsize = 0; - - return result; -} - - -/* Pads string with given number of a specified character. - This code is taken from iopadn.c of the GNU I/O library. */ -#define PADSIZE 16 -static const char blanks[PADSIZE] = -{' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; -static const char zeroes[PADSIZE] = -{'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; - -ssize_t -__printf_pad (s, pad, count) - FILE *s; - char pad; - size_t count; -{ - const char *padptr; - register size_t i; - - padptr = pad == ' ' ? blanks : zeroes; - - for (i = count; i >= PADSIZE; i -= PADSIZE) - if (PUT (s, padptr, PADSIZE) != PADSIZE) - return -1; - if (i > 0) - if (PUT (s, padptr, i) != i) - return -1; - - return count; -} -#undef PADSIZE -#endif /* USE_IN_LIBIO */ |