diff options
Diffstat (limited to 'stdio-common/vfprintf.c')
| -rw-r--r-- | stdio-common/vfprintf.c | 2351 |
1 files changed, 7 insertions, 2344 deletions
diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c index ae412e4b84..13a10db99b 100644 --- a/stdio-common/vfprintf.c +++ b/stdio-common/vfprintf.c @@ -15,2350 +15,13 @@ License along with the GNU C Library; if not, see <http://www.gnu.org/licenses/>. */ -#include <array_length.h> -#include <ctype.h> -#include <limits.h> -#include <printf.h> -#include <stdarg.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <wchar.h> -#include <libc-lock.h> -#include <sys/param.h> -#include <_itoa.h> -#include <locale/localeinfo.h> -#include <stdio.h> -#include <scratch_buffer.h> +#include <libio/libioP.h> -/* This code is shared between the standard stdio implementation found - in GNU C library and the libio implementation originally found in - GNU libg++. - - Beside this it is also shared between the normal and wide character - implementation as defined in ISO/IEC 9899:1990/Amendment 1:1995. */ - -#include <libioP.h> - -/* In some cases we need extra space for all the output which is not - counted in the width of the string. We assume 32 characters is - enough. */ -#define EXTSIZ 32 -#define ARGCHECK(S, Format) \ - do \ - { \ - /* Check file argument for consistence. */ \ - CHECK_FILE (S, -1); \ - if (S->_flags & _IO_NO_WRITES) \ - { \ - S->_flags |= _IO_ERR_SEEN; \ - __set_errno (EBADF); \ - return -1; \ - } \ - if (Format == NULL) \ - { \ - __set_errno (EINVAL); \ - return -1; \ - } \ - } while (0) -#define UNBUFFERED_P(S) ((S)->_flags & _IO_UNBUFFERED) - -#define done_add(val) \ - do { \ - unsigned int _val = val; \ - assert ((unsigned int) done < (unsigned int) INT_MAX); \ - if (__glibc_unlikely (INT_MAX - done < _val)) \ - { \ - done = -1; \ - __set_errno (EOVERFLOW); \ - goto all_done; \ - } \ - done += _val; \ - } while (0) - -#ifndef COMPILE_WPRINTF -# define vfprintf _IO_vfprintf_internal -# define CHAR_T char -# define UCHAR_T unsigned char -# define INT_T int -typedef const char *THOUSANDS_SEP_T; -# define L_(Str) Str -# define ISDIGIT(Ch) ((unsigned int) ((Ch) - '0') < 10) -# define STR_LEN(Str) strlen (Str) - -# define PUT(F, S, N) _IO_sputn ((F), (S), (N)) -# define PAD(Padchar) \ - do { \ - if (width > 0) \ - { \ - ssize_t written = _IO_padn (s, (Padchar), width); \ - if (__glibc_unlikely (written != width)) \ - { \ - done = -1; \ - goto all_done; \ - } \ - done_add (written); \ - } \ - } while (0) -# define PUTC(C, F) _IO_putc_unlocked (C, F) -# define ORIENT if (_IO_vtable_offset (s) == 0 && _IO_fwide (s, -1) != -1)\ - return -1 -#else -# define vfprintf _IO_vfwprintf -# define CHAR_T wchar_t -/* This is a hack!!! There should be a type uwchar_t. */ -# define UCHAR_T unsigned int /* uwchar_t */ -# define INT_T wint_t -typedef wchar_t THOUSANDS_SEP_T; -# define L_(Str) L##Str -# define ISDIGIT(Ch) ((unsigned int) ((Ch) - L'0') < 10) -# define STR_LEN(Str) __wcslen (Str) - -# include <_itowa.h> - -# define PUT(F, S, N) _IO_sputn ((F), (S), (N)) -# define PAD(Padchar) \ - do { \ - if (width > 0) \ - { \ - ssize_t written = _IO_wpadn (s, (Padchar), width); \ - if (__glibc_unlikely (written != width)) \ - { \ - done = -1; \ - goto all_done; \ - } \ - done_add (written); \ - } \ - } while (0) -# define PUTC(C, F) _IO_putwc_unlocked (C, F) -# define ORIENT if (_IO_fwide (s, 1) != 1) return -1 - -# undef _itoa -# define _itoa(Val, Buf, Base, Case) _itowa (Val, Buf, Base, Case) -# define _itoa_word(Val, Buf, Base, Case) _itowa_word (Val, Buf, Base, Case) -# undef EOF -# define EOF WEOF -#endif - -#include "_i18n_number.h" - -/* Include the shared code for parsing the format string. */ -#include "printf-parse.h" - - -#define outchar(Ch) \ - do \ - { \ - const INT_T outc = (Ch); \ - if (PUTC (outc, s) == EOF || done == INT_MAX) \ - { \ - done = -1; \ - goto all_done; \ - } \ - ++done; \ - } \ - while (0) - -#define outstring(String, Len) \ - do \ - { \ - assert ((size_t) done <= (size_t) INT_MAX); \ - if ((size_t) PUT (s, (String), (Len)) != (size_t) (Len)) \ - { \ - done = -1; \ - goto all_done; \ - } \ - if (__glibc_unlikely (INT_MAX - done < (Len))) \ - { \ - done = -1; \ - __set_errno (EOVERFLOW); \ - goto all_done; \ - } \ - done += (Len); \ - } \ - while (0) - -/* For handling long_double and longlong we use the same flag. If - `long' and `long long' are effectively the same type define it to - zero. */ -#if LONG_MAX == LONG_LONG_MAX -# define is_longlong 0 -#else -# define is_longlong is_long_double -#endif - -/* If `long' and `int' is effectively the same type we don't have to - handle `long separately. */ -#if INT_MAX == LONG_MAX -# define is_long_num 0 -#else -# define is_long_num is_long -#endif - - -/* Global constants. */ -static const CHAR_T null[] = L_("(null)"); - -/* Size of the work_buffer variable (in characters, not bytes. */ -enum { WORK_BUFFER_SIZE = 1000 / sizeof (CHAR_T) }; - -/* This table maps a character into a number representing a class. In - each step there is a destination label for each class. */ -static const uint8_t jump_table[] = - { - /* ' ' */ 1, 0, 0, /* '#' */ 4, - 0, /* '%' */ 14, 0, /* '\''*/ 6, - 0, 0, /* '*' */ 7, /* '+' */ 2, - 0, /* '-' */ 3, /* '.' */ 9, 0, - /* '0' */ 5, /* '1' */ 8, /* '2' */ 8, /* '3' */ 8, - /* '4' */ 8, /* '5' */ 8, /* '6' */ 8, /* '7' */ 8, - /* '8' */ 8, /* '9' */ 8, 0, 0, - 0, 0, 0, 0, - 0, /* 'A' */ 26, 0, /* 'C' */ 25, - 0, /* 'E' */ 19, /* F */ 19, /* 'G' */ 19, - 0, /* 'I' */ 29, 0, 0, - /* 'L' */ 12, 0, 0, 0, - 0, 0, 0, /* 'S' */ 21, - 0, 0, 0, 0, - /* 'X' */ 18, 0, /* 'Z' */ 13, 0, - 0, 0, 0, 0, - 0, /* 'a' */ 26, 0, /* 'c' */ 20, - /* 'd' */ 15, /* 'e' */ 19, /* 'f' */ 19, /* 'g' */ 19, - /* 'h' */ 10, /* 'i' */ 15, /* 'j' */ 28, 0, - /* 'l' */ 11, /* 'm' */ 24, /* 'n' */ 23, /* 'o' */ 17, - /* 'p' */ 22, /* 'q' */ 12, 0, /* 's' */ 21, - /* 't' */ 27, /* 'u' */ 16, 0, 0, - /* 'x' */ 18, 0, /* 'z' */ 13 - }; - -#define NOT_IN_JUMP_RANGE(Ch) ((Ch) < L_(' ') || (Ch) > L_('z')) -#define CHAR_CLASS(Ch) (jump_table[(INT_T) (Ch) - L_(' ')]) -#define LABEL(Name) do_##Name -#ifdef SHARED - /* 'int' is enough and it saves some space on 64 bit systems. */ -# define JUMP_TABLE_TYPE const int -# define JUMP_TABLE_BASE_LABEL do_form_unknown -# define REF(Name) &&do_##Name - &&JUMP_TABLE_BASE_LABEL -# define JUMP(ChExpr, table) \ - do \ - { \ - int offset; \ - void *ptr; \ - spec = (ChExpr); \ - offset = NOT_IN_JUMP_RANGE (spec) ? REF (form_unknown) \ - : table[CHAR_CLASS (spec)]; \ - ptr = &&JUMP_TABLE_BASE_LABEL + offset; \ - goto *ptr; \ - } \ - while (0) -#else -# define JUMP_TABLE_TYPE const void *const -# define REF(Name) &&do_##Name -# define JUMP(ChExpr, table) \ - do \ - { \ - const void *ptr; \ - spec = (ChExpr); \ - ptr = NOT_IN_JUMP_RANGE (spec) ? REF (form_unknown) \ - : table[CHAR_CLASS (spec)]; \ - goto *ptr; \ - } \ - while (0) -#endif - -#define STEP0_3_TABLE \ - /* Step 0: at the beginning. */ \ - static JUMP_TABLE_TYPE step0_jumps[30] = \ - { \ - REF (form_unknown), \ - REF (flag_space), /* for ' ' */ \ - REF (flag_plus), /* for '+' */ \ - REF (flag_minus), /* for '-' */ \ - REF (flag_hash), /* for '<hash>' */ \ - REF (flag_zero), /* for '0' */ \ - REF (flag_quote), /* for '\'' */ \ - REF (width_asterics), /* for '*' */ \ - REF (width), /* for '1'...'9' */ \ - REF (precision), /* for '.' */ \ - REF (mod_half), /* for 'h' */ \ - REF (mod_long), /* for 'l' */ \ - REF (mod_longlong), /* for 'L', 'q' */ \ - REF (mod_size_t), /* for 'z', 'Z' */ \ - REF (form_percent), /* for '%' */ \ - REF (form_integer), /* for 'd', 'i' */ \ - REF (form_unsigned), /* for 'u' */ \ - REF (form_octal), /* for 'o' */ \ - REF (form_hexa), /* for 'X', 'x' */ \ - REF (form_float), /* for 'E', 'e', 'F', 'f', 'G', 'g' */ \ - REF (form_character), /* for 'c' */ \ - REF (form_string), /* for 's', 'S' */ \ - REF (form_pointer), /* for 'p' */ \ - REF (form_number), /* for 'n' */ \ - REF (form_strerror), /* for 'm' */ \ - REF (form_wcharacter), /* for 'C' */ \ - REF (form_floathex), /* for 'A', 'a' */ \ - REF (mod_ptrdiff_t), /* for 't' */ \ - REF (mod_intmax_t), /* for 'j' */ \ - REF (flag_i18n), /* for 'I' */ \ - }; \ - /* Step 1: after processing width. */ \ - static JUMP_TABLE_TYPE step1_jumps[30] = \ - { \ - REF (form_unknown), \ - REF (form_unknown), /* for ' ' */ \ - REF (form_unknown), /* for '+' */ \ - REF (form_unknown), /* for '-' */ \ - REF (form_unknown), /* for '<hash>' */ \ - REF (form_unknown), /* for '0' */ \ - REF (form_unknown), /* for '\'' */ \ - REF (form_unknown), /* for '*' */ \ - REF (form_unknown), /* for '1'...'9' */ \ - REF (precision), /* for '.' */ \ - REF (mod_half), /* for 'h' */ \ - REF (mod_long), /* for 'l' */ \ - REF (mod_longlong), /* for 'L', 'q' */ \ - REF (mod_size_t), /* for 'z', 'Z' */ \ - REF (form_percent), /* for '%' */ \ - REF (form_integer), /* for 'd', 'i' */ \ - REF (form_unsigned), /* for 'u' */ \ - REF (form_octal), /* for 'o' */ \ - REF (form_hexa), /* for 'X', 'x' */ \ - REF (form_float), /* for 'E', 'e', 'F', 'f', 'G', 'g' */ \ - REF (form_character), /* for 'c' */ \ - REF (form_string), /* for 's', 'S' */ \ - REF (form_pointer), /* for 'p' */ \ - REF (form_number), /* for 'n' */ \ - REF (form_strerror), /* for 'm' */ \ - REF (form_wcharacter), /* for 'C' */ \ - REF (form_floathex), /* for 'A', 'a' */ \ - REF (mod_ptrdiff_t), /* for 't' */ \ - REF (mod_intmax_t), /* for 'j' */ \ - REF (form_unknown) /* for 'I' */ \ - }; \ - /* Step 2: after processing precision. */ \ - static JUMP_TABLE_TYPE step2_jumps[30] = \ - { \ - REF (form_unknown), \ - REF (form_unknown), /* for ' ' */ \ - REF (form_unknown), /* for '+' */ \ - REF (form_unknown), /* for '-' */ \ - REF (form_unknown), /* for '<hash>' */ \ - REF (form_unknown), /* for '0' */ \ - REF (form_unknown), /* for '\'' */ \ - REF (form_unknown), /* for '*' */ \ - REF (form_unknown), /* for '1'...'9' */ \ - REF (form_unknown), /* for '.' */ \ - REF (mod_half), /* for 'h' */ \ - REF (mod_long), /* for 'l' */ \ - REF (mod_longlong), /* for 'L', 'q' */ \ - REF (mod_size_t), /* for 'z', 'Z' */ \ - REF (form_percent), /* for '%' */ \ - REF (form_integer), /* for 'd', 'i' */ \ - REF (form_unsigned), /* for 'u' */ \ - REF (form_octal), /* for 'o' */ \ - REF (form_hexa), /* for 'X', 'x' */ \ - REF (form_float), /* for 'E', 'e', 'F', 'f', 'G', 'g' */ \ - REF (form_character), /* for 'c' */ \ - REF (form_string), /* for 's', 'S' */ \ - REF (form_pointer), /* for 'p' */ \ - REF (form_number), /* for 'n' */ \ - REF (form_strerror), /* for 'm' */ \ - REF (form_wcharacter), /* for 'C' */ \ - REF (form_floathex), /* for 'A', 'a' */ \ - REF (mod_ptrdiff_t), /* for 't' */ \ - REF (mod_intmax_t), /* for 'j' */ \ - REF (form_unknown) /* for 'I' */ \ - }; \ - /* Step 3a: after processing first 'h' modifier. */ \ - static JUMP_TABLE_TYPE step3a_jumps[30] = \ - { \ - REF (form_unknown), \ - REF (form_unknown), /* for ' ' */ \ - REF (form_unknown), /* for '+' */ \ - REF (form_unknown), /* for '-' */ \ - REF (form_unknown), /* for '<hash>' */ \ - REF (form_unknown), /* for '0' */ \ - REF (form_unknown), /* for '\'' */ \ - REF (form_unknown), /* for '*' */ \ - REF (form_unknown), /* for '1'...'9' */ \ - REF (form_unknown), /* for '.' */ \ - REF (mod_halfhalf), /* for 'h' */ \ - REF (form_unknown), /* for 'l' */ \ - REF (form_unknown), /* for 'L', 'q' */ \ - REF (form_unknown), /* for 'z', 'Z' */ \ - REF (form_percent), /* for '%' */ \ - REF (form_integer), /* for 'd', 'i' */ \ - REF (form_unsigned), /* for 'u' */ \ - REF (form_octal), /* for 'o' */ \ - REF (form_hexa), /* for 'X', 'x' */ \ - REF (form_unknown), /* for 'E', 'e', 'F', 'f', 'G', 'g' */ \ - REF (form_unknown), /* for 'c' */ \ - REF (form_unknown), /* for 's', 'S' */ \ - REF (form_unknown), /* for 'p' */ \ - REF (form_number), /* for 'n' */ \ - REF (form_unknown), /* for 'm' */ \ - REF (form_unknown), /* for 'C' */ \ - REF (form_unknown), /* for 'A', 'a' */ \ - REF (form_unknown), /* for 't' */ \ - REF (form_unknown), /* for 'j' */ \ - REF (form_unknown) /* for 'I' */ \ - }; \ - /* Step 3b: after processing first 'l' modifier. */ \ - static JUMP_TABLE_TYPE step3b_jumps[30] = \ - { \ - REF (form_unknown), \ - REF (form_unknown), /* for ' ' */ \ - REF (form_unknown), /* for '+' */ \ - REF (form_unknown), /* for '-' */ \ - REF (form_unknown), /* for '<hash>' */ \ - REF (form_unknown), /* for '0' */ \ - REF (form_unknown), /* for '\'' */ \ - REF (form_unknown), /* for '*' */ \ - REF (form_unknown), /* for '1'...'9' */ \ - REF (form_unknown), /* for '.' */ \ - REF (form_unknown), /* for 'h' */ \ - REF (mod_longlong), /* for 'l' */ \ - REF (form_unknown), /* for 'L', 'q' */ \ - REF (form_unknown), /* for 'z', 'Z' */ \ - REF (form_percent), /* for '%' */ \ - REF (form_integer), /* for 'd', 'i' */ \ - REF (form_unsigned), /* for 'u' */ \ - REF (form_octal), /* for 'o' */ \ - REF (form_hexa), /* for 'X', 'x' */ \ - REF (form_float), /* for 'E', 'e', 'F', 'f', 'G', 'g' */ \ - REF (form_character), /* for 'c' */ \ - REF (form_string), /* for 's', 'S' */ \ - REF (form_pointer), /* for 'p' */ \ - REF (form_number), /* for 'n' */ \ - REF (form_strerror), /* for 'm' */ \ - REF (form_wcharacter), /* for 'C' */ \ - REF (form_floathex), /* for 'A', 'a' */ \ - REF (form_unknown), /* for 't' */ \ - REF (form_unknown), /* for 'j' */ \ - REF (form_unknown) /* for 'I' */ \ - } - -#define STEP4_TABLE \ - /* Step 4: processing format specifier. */ \ - static JUMP_TABLE_TYPE step4_jumps[30] = \ - { \ - REF (form_unknown), \ - REF (form_unknown), /* for ' ' */ \ - REF (form_unknown), /* for '+' */ \ - REF (form_unknown), /* for '-' */ \ - REF (form_unknown), /* for '<hash>' */ \ - REF (form_unknown), /* for '0' */ \ - REF (form_unknown), /* for '\'' */ \ - REF (form_unknown), /* for '*' */ \ - REF (form_unknown), /* for '1'...'9' */ \ - REF (form_unknown), /* for '.' */ \ - REF (form_unknown), /* for 'h' */ \ - REF (form_unknown), /* for 'l' */ \ - REF (form_unknown), /* for 'L', 'q' */ \ - REF (form_unknown), /* for 'z', 'Z' */ \ - REF (form_percent), /* for '%' */ \ - REF (form_integer), /* for 'd', 'i' */ \ - REF (form_unsigned), /* for 'u' */ \ - REF (form_octal), /* for 'o' */ \ - REF (form_hexa), /* for 'X', 'x' */ \ - REF (form_float), /* for 'E', 'e', 'F', 'f', 'G', 'g' */ \ - REF (form_character), /* for 'c' */ \ - REF (form_string), /* for 's', 'S' */ \ - REF (form_pointer), /* for 'p' */ \ - REF (form_number), /* for 'n' */ \ - REF (form_strerror), /* for 'm' */ \ - REF (form_wcharacter), /* for 'C' */ \ - REF (form_floathex), /* for 'A', 'a' */ \ - REF (form_unknown), /* for 't' */ \ - REF (form_unknown), /* for 'j' */ \ - REF (form_unknown) /* for 'I' */ \ - } - - -#define process_arg(fspec) \ - /* Start real work. We know about all flags and modifiers and \ - now process the wanted format specifier. */ \ - LABEL (form_percent): \ - /* Write a literal "%". */ \ - outchar (L_('%')); \ - break; \ - \ - LABEL (form_integer): \ - /* Signed decimal integer. */ \ - base = 10; \ - \ - if (is_longlong) \ - { \ - long long int signed_number; \ - \ - if (fspec == NULL) \ - signed_number = va_arg (ap, long long int); \ - else \ - signed_number = args_value[fspec->data_arg].pa_long_long_int; \ - \ - is_negative = signed_number < 0; \ - number.longlong = is_negative ? (- signed_number) : signed_number; \ - \ - goto LABEL (longlong_number); \ - } \ - else \ - { \ - long int signed_number; \ - \ - if (fspec == NULL) \ - { \ - if (is_long_num) \ - signed_number = va_arg (ap, long int); \ - else if (is_char) \ - signed_number = (signed char) va_arg (ap, unsigned int); \ - else if (!is_short) \ - signed_number = va_arg (ap, int); \ - else \ - signed_number = (short int) va_arg (ap, unsigned int); \ - } \ - else \ - if (is_long_num) \ - signed_number = args_value[fspec->data_arg].pa_long_int; \ - else if (is_char) \ - signed_number = (signed char) \ - args_value[fspec->data_arg].pa_u_int; \ - else if (!is_short) \ - signed_number = args_value[fspec->data_arg].pa_int; \ - else \ - signed_number = (short int) \ - args_value[fspec->data_arg].pa_u_int; \ - \ - is_negative = signed_number < 0; \ - number.word = is_negative ? (- signed_number) : signed_number; \ - \ - goto LABEL (number); \ - } \ - /* NOTREACHED */ \ - \ - LABEL (form_unsigned): \ - /* Unsigned decimal integer. */ \ - base = 10; \ - goto LABEL (unsigned_number); \ - /* NOTREACHED */ \ - \ - LABEL (form_octal): \ - /* Unsigned octal integer. */ \ - base = 8; \ - goto LABEL (unsigned_number); \ - /* NOTREACHED */ \ - \ - LABEL (form_hexa): \ - /* Unsigned hexadecimal integer. */ \ - base = 16; \ - \ - LABEL (unsigned_number): /* Unsigned number of base BASE. */ \ - \ - /* ISO specifies the `+' and ` ' flags only for signed \ - conversions. */ \ - is_negative = 0; \ - showsign = 0; \ - space = 0; \ - \ - if (is_longlong) \ - { \ - if (fspec == NULL) \ - number.longlong = va_arg (ap, unsigned long long int); \ - else \ - number.longlong = args_value[fspec->data_arg].pa_u_long_long_int; \ - \ - LABEL (longlong_number): \ - if (prec < 0) \ - /* Supply a default precision if none was given. */ \ - prec = 1; \ - else \ - /* We have to take care for the '0' flag. If a precision \ - is given it must be ignored. */ \ - pad = L_(' '); \ - \ - /* If the precision is 0 and the number is 0 nothing has to \ - be written for the number, except for the 'o' format in \ - alternate form. */ \ - if (prec == 0 && number.longlong == 0) \ - { \ - string = workend; \ - if (base == 8 && alt) \ - *--string = L_('0'); \ - } \ - else \ - { \ - /* Put the number in WORK. */ \ - string = _itoa (number.longlong, workend, base, \ - spec == L_('X')); \ - if (group && grouping) \ - string = group_number (work_buffer, string, workend, \ - grouping, thousands_sep); \ - if (use_outdigits && base == 10) \ - string = _i18n_number_rewrite (string, workend, workend); \ - } \ - /* Simplify further test for num != 0. */ \ - number.word = number.longlong != 0; \ - } \ - else \ - { \ - if (fspec == NULL) \ - { \ - if (is_long_num) \ - number.word = va_arg (ap, unsigned long int); \ - else if (is_char) \ - number.word = (unsigned char) va_arg (ap, unsigned int); \ - else if (!is_short) \ - number.word = va_arg (ap, unsigned int); \ - else \ - number.word = (unsigned short int) va_arg (ap, unsigned int); \ - } \ - else \ - if (is_long_num) \ - number.word = args_value[fspec->data_arg].pa_u_long_int; \ - else if (is_char) \ - number.word = (unsigned char) \ - args_value[fspec->data_arg].pa_u_int; \ - else if (!is_short) \ - number.word = args_value[fspec->data_arg].pa_u_int; \ - else \ - number.word = (unsigned short int) \ - args_value[fspec->data_arg].pa_u_int; \ - \ - LABEL (number): \ - if (prec < 0) \ - /* Supply a default precision if none was given. */ \ - prec = 1; \ - else \ - /* We have to take care for the '0' flag. If a precision \ - is given it must be ignored. */ \ - pad = L_(' '); \ - \ - /* If the precision is 0 and the number is 0 nothing has to \ - be written for the number, except for the 'o' format in \ - alternate form. */ \ - if (prec == 0 && number.word == 0) \ - { \ - string = workend; \ - if (base == 8 && alt) \ - *--string = L_('0'); \ |
