diff options
Diffstat (limited to 'stdio-common')
-rw-r--r-- | stdio-common/Makefile | 3 | ||||
-rw-r--r-- | stdio-common/_itoa.c | 4 | ||||
-rw-r--r-- | stdio-common/_itowa.c | 346 | ||||
-rw-r--r-- | stdio-common/_itowa.h | 63 | ||||
-rw-r--r-- | stdio-common/itoa-digits.c | 7 | ||||
-rw-r--r-- | stdio-common/itowa-digits.c | 27 | ||||
-rw-r--r-- | stdio-common/printf-parse.h | 71 | ||||
-rw-r--r-- | stdio-common/printf-prs.c | 4 | ||||
-rw-r--r-- | stdio-common/printf.h | 3 | ||||
-rw-r--r-- | stdio-common/printf_fp.c | 13 | ||||
-rw-r--r-- | stdio-common/printf_size.c | 3 | ||||
-rw-r--r-- | stdio-common/vfprintf.c | 585 | ||||
-rw-r--r-- | stdio-common/vfscanf.c | 1313 | ||||
-rw-r--r-- | stdio-common/vfwprintf.c | 3 | ||||
-rw-r--r-- | stdio-common/vfwscanf.c | 2 |
15 files changed, 1874 insertions, 573 deletions
diff --git a/stdio-common/Makefile b/stdio-common/Makefile index 9fb0c5d15d..74900189a8 100644 --- a/stdio-common/Makefile +++ b/stdio-common/Makefile @@ -25,11 +25,12 @@ headers := printf.h routines := \ ctermid cuserid \ - _itoa itoa-digits \ + _itoa _itowa itoa-digits itowa-digits \ vfprintf vprintf printf_fp reg-printf printf-prs printf_fphex \ printf_size fprintf printf snprintf sprintf asprintf dprintf \ vfscanf \ fscanf scanf sscanf \ + vfwprintf vfwscanf \ perror psignal \ tmpfile tmpfile64 tmpnam tmpnam_r tempnam tempname \ getline getw putw \ diff --git a/stdio-common/_itoa.c b/stdio-common/_itoa.c index 3a7cd78003..2eca838229 100644 --- a/stdio-common/_itoa.c +++ b/stdio-common/_itoa.c @@ -78,7 +78,7 @@ struct base_table_t /* Local variables. */ -static const struct base_table_t base_table[] = +const struct base_table_t _itoa_base_table[] = { #if BITS_PER_MP_LIMB == 64 /* 2 */ {SEL1(0ul) 1, 1}, @@ -171,7 +171,7 @@ _itoa (value, buflim, base, upper_case) { const char *digits = upper_case ? _itoa_upper_digits : _itoa_lower_digits; char *bp = buflim; - const struct base_table_t *brec = &base_table[base - 2]; + const struct base_table_t *brec = &_itoa_base_table[base - 2]; switch (base) { diff --git a/stdio-common/_itowa.c b/stdio-common/_itowa.c new file mode 100644 index 0000000000..430415b96b --- /dev/null +++ b/stdio-common/_itowa.c @@ -0,0 +1,346 @@ +/* Internal function for converting integers to ASCII. + Copyright (C) 1994, 1995, 1996, 1999 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Contributed by Torbjorn Granlund <tege@matematik.su.se> + and Ulrich Drepper <drepper@gnu.org>. + + 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <gmp-mparam.h> +#include <stdlib/gmp.h> +#include <stdlib/gmp-impl.h> +#include <stdlib/longlong.h> + +#include "_itowa.h" + + +/* Canonize environment. For some architectures not all values might + be defined in the GMP header files. */ +#ifndef UMUL_TIME +# define UMUL_TIME 1 +#endif +#ifndef UDIV_TIME +# define UDIV_TIME 3 +#endif + +/* Control memory layout. */ +#ifdef PACK +# undef PACK +# define PACK __attribute__ ((packed)) +#else +# define PACK +#endif + + +/* Declare local types. */ +struct base_table_t +{ +#if (UDIV_TIME > 2 * UMUL_TIME) + mp_limb_t base_multiplier; +#endif + char flag; + char post_shift; +#if BITS_PER_MP_LIMB == 32 + struct + { + char normalization_steps; + char ndigits; + mp_limb_t base PACK; +#if UDIV_TIME > 2 * UMUL_TIME + mp_limb_t base_ninv PACK; +#endif + } big; +#endif +}; + +/* To reduce the memory needed we include some fields of the tables + only conditionally. */ +#if UDIV_TIME > 2 * UMUL_TIME +# define SEL1(X) X, +# define SEL2(X) ,X +#else +# define SEL1(X) +# define SEL2(X) +#endif + +/* Factor table for the different bases. */ +extern const struct base_table_t _itoa_base_table[]; + +/* Lower-case digits. */ +extern const wchar_t _itowa_lower_digits[]; +/* Upper-case digits. */ +extern const wchar_t _itowa_upper_digits[]; + + +wchar_t * +_itowa (value, buflim, base, upper_case) + unsigned long long int value; + wchar_t *buflim; + unsigned int base; + int upper_case; +{ + const wchar_t *digits = (upper_case + ? _itowa_upper_digits : _itowa_lower_digits); + wchar_t *bp = buflim; + const struct base_table_t *brec = &_itoa_base_table[base - 2]; + + switch (base) + { +#define RUN_2N(BITS) \ + do \ + { \ + /* `unsigned long long int' always has 64 bits. */ \ + mp_limb_t work_hi = value >> (64 - BITS_PER_MP_LIMB); \ + \ + if (BITS_PER_MP_LIMB == 32) \ + { \ + if (work_hi != 0) \ + { \ + mp_limb_t work_lo; \ + int cnt; \ + \ + work_lo = value & 0xfffffffful; \ + for (cnt = BITS_PER_MP_LIMB / BITS; cnt > 0; --cnt) \ + { \ + *--bp = digits[work_lo & ((1ul << BITS) - 1)]; \ + work_lo >>= BITS; \ + } \ + if (BITS_PER_MP_LIMB % BITS != 0) \ + { \ + work_lo \ + |= ((work_hi \ + & ((1 << (BITS - BITS_PER_MP_LIMB%BITS)) \ + - 1)) \ + << BITS_PER_MP_LIMB % BITS); \ + work_hi >>= BITS - BITS_PER_MP_LIMB % BITS; \ + if (work_hi == 0) \ + work_hi = work_lo; \ + else \ + *--bp = digits[work_lo]; \ + } \ + } \ + else \ + work_hi = value & 0xfffffffful; \ + } \ + do \ + { \ + *--bp = digits[work_hi & ((1 << BITS) - 1)]; \ + work_hi >>= BITS; \ + } \ + while (work_hi != 0); \ + } \ + while (0) + case 8: + RUN_2N (3); + break; + + case 16: + RUN_2N (4); + break; + + default: + { +#if BITS_PER_MP_LIMB == 64 + mp_limb_t base_multiplier = brec->base_multiplier; + if (brec->flag) + while (value != 0) + { + mp_limb_t quo, rem, x, dummy; + + umul_ppmm (x, dummy, value, base_multiplier); + quo = (x + ((value - x) >> 1)) >> (brec->post_shift - 1); + rem = value - quo * base; + *--bp = digits[rem]; + value = quo; + } + else + while (value != 0) + { + mp_limb_t quo, rem, x, dummy; + + umul_ppmm (x, dummy, value, base_multiplier); + quo = x >> brec->post_shift; + rem = value - quo * base; + *--bp = digits[rem]; + value = quo; + } +#endif +#if BITS_PER_MP_LIMB == 32 + mp_limb_t t[3]; + int n; + + /* First convert x0 to 1-3 words in base s->big.base. + Optimize for frequent cases of 32 bit numbers. */ + if ((mp_limb_t) (value >> 32) >= 1) + { +#if UDIV_TIME > 2 * UMUL_TIME || UDIV_NEEDS_NORMALIZATION + int big_normalization_steps = brec->big.normalization_steps; + mp_limb_t big_base_norm + = brec->big.base << big_normalization_steps; +#endif + if ((mp_limb_t) (value >> 32) >= brec->big.base) + { + mp_limb_t x1hi, x1lo, r; + /* If you want to optimize this, take advantage of + that the quotient in the first udiv_qrnnd will + always be very small. It might be faster just to + subtract in a tight loop. */ + +#if UDIV_TIME > 2 * UMUL_TIME + mp_limb_t x, xh, xl; + + if (big_normalization_steps == 0) + xh = 0; + else + xh = (mp_limb_t) (value >> (64 - big_normalization_steps)); + xl = (mp_limb_t) (value >> (32 - big_normalization_steps)); + udiv_qrnnd_preinv (x1hi, r, xh, xl, big_base_norm, + brec->big.base_ninv); + + xl = ((mp_limb_t) value) << big_normalization_steps; + udiv_qrnnd_preinv (x1lo, x, r, xl, big_base_norm, + brec->big.base_ninv); + t[2] = x >> big_normalization_steps; + + if (big_normalization_steps == 0) + xh = x1hi; + else + xh = ((x1hi << big_normalization_steps) + | (x1lo >> (32 - big_normalization_steps))); + xl = x1lo << big_normalization_steps; + udiv_qrnnd_preinv (t[0], x, xh, xl, big_base_norm, + brec->big.base_ninv); + t[1] = x >> big_normalization_steps; +#elif UDIV_NEEDS_NORMALIZATION + mp_limb_t x, xh, xl; + + if (big_normalization_steps == 0) + xh = 0; + else + xh = (mp_limb_t) (value >> 64 - big_normalization_steps); + xl = (mp_limb_t) (value >> 32 - big_normalization_steps); + udiv_qrnnd (x1hi, r, xh, xl, big_base_norm); + + xl = ((mp_limb_t) value) << big_normalization_steps; + udiv_qrnnd (x1lo, x, r, xl, big_base_norm); + t[2] = x >> big_normalization_steps; + + if (big_normalization_steps == 0) + xh = x1hi; + else + xh = ((x1hi << big_normalization_steps) + | (x1lo >> 32 - big_normalization_steps)); + xl = x1lo << big_normalization_steps; + udiv_qrnnd (t[0], x, xh, xl, big_base_norm); + t[1] = x >> big_normalization_steps; +#else + udiv_qrnnd (x1hi, r, 0, (mp_limb_t) (value >> 32), + brec->big.base); + udiv_qrnnd (x1lo, t[2], r, (mp_limb_t) value, brec->big.base); + udiv_qrnnd (t[0], t[1], x1hi, x1lo, brec->big.base); +#endif + n = 3; + } + else + { +#if (UDIV_TIME > 2 * UMUL_TIME) + mp_limb_t x; + + value <<= brec->big.normalization_steps; + udiv_qrnnd_preinv (t[0], x, (mp_limb_t) (value >> 32), + (mp_limb_t) value, big_base_norm, + brec->big.base_ninv); + t[1] = x >> brec->big.normalization_steps; +#elif UDIV_NEEDS_NORMALIZATION + mp_limb_t x; + + value <<= big_normalization_steps; + udiv_qrnnd (t[0], x, (mp_limb_t) (value >> 32), + (mp_limb_t) value, big_base_norm); + t[1] = x >> big_normalization_steps; +#else + udiv_qrnnd (t[0], t[1], (mp_limb_t) (value >> 32), + (mp_limb_t) value, brec->big.base); +#endif + n = 2; + } + } + else + { + t[0] = value; + n = 1; + } + + /* Convert the 1-3 words in t[], word by word, to ASCII. */ + do + { + mp_limb_t ti = t[--n]; + int ndig_for_this_limb = 0; + +#if UDIV_TIME > 2 * UMUL_TIME + mp_limb_t base_multiplier = brec->base_multiplier; + if (brec->flag) + while (ti != 0) + { + mp_limb_t quo, rem, x, dummy; + + umul_ppmm (x, dummy, ti, base_multiplier); + quo = (x + ((ti - x) >> 1)) >> (brec->post_shift - 1); + rem = ti - quo * base; + *--bp = digits[rem]; + ti = quo; + ++ndig_for_this_limb; + } + else + while (ti != 0) + { + mp_limb_t quo, rem, x, dummy; + + umul_ppmm (x, dummy, ti, base_multiplier); + quo = x >> brec->post_shift; + rem = ti - quo * base; + *--bp = digits[rem]; + ti = quo; + ++ndig_for_this_limb; + } +#else + while (ti != 0) + { + mp_limb_t quo, rem; + + quo = ti / base; + rem = ti % base; + *--bp = digits[rem]; + ti = quo; + ++ndig_for_this_limb; + } +#endif + /* If this wasn't the most significant word, pad with zeros. */ + if (n != 0) + while (ndig_for_this_limb < brec->big.ndigits) + { + *--bp = '0'; + ++ndig_for_this_limb; + } + } + while (n != 0); +#endif + } + break; + } + + return bp; +} diff --git a/stdio-common/_itowa.h b/stdio-common/_itowa.h new file mode 100644 index 0000000000..e219f298ee --- /dev/null +++ b/stdio-common/_itowa.h @@ -0,0 +1,63 @@ +/* Internal function for converting integers to ASCII. + Copyright (C) 1994, 95, 96, 97, 98, 99 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#ifndef _ITOWA_H +#define _ITOWA_H 1 +#include <features.h> +#include <wchar.h> + +/* Convert VALUE into ASCII in base BASE (2..36). + Write backwards starting the character just before BUFLIM. + Return the address of the first (left-to-right) character in the number. + Use upper case letters iff UPPER_CASE is nonzero. */ + +extern wchar_t *_itowa __P ((unsigned long long int value, wchar_t *buflim, + unsigned int base, int upper_case)); + +static inline wchar_t * +__attribute__ ((unused)) +_itowa_word (unsigned long value, wchar_t *buflim, + unsigned int base, int upper_case) +{ + extern const wchar_t _itowa_upper_digits[], _itowa_lower_digits[]; + const wchar_t *digits = (upper_case + ? _itowa_upper_digits : _itowa_lower_digits); + wchar_t *bp = buflim; + + switch (base) + { +#define SPECIAL(Base) \ + case Base: \ + do \ + *--bp = digits[value % Base]; \ + while ((value /= Base) != 0); \ + break + + SPECIAL (10); + SPECIAL (16); + SPECIAL (8); + default: + do + *--bp = digits[value % base]; + while ((value /= base) != 0); + } + return bp; +} + +#endif /* itowa.h */ diff --git a/stdio-common/itoa-digits.c b/stdio-common/itoa-digits.c index b475bbca42..34699dbcc8 100644 --- a/stdio-common/itoa-digits.c +++ b/stdio-common/itoa-digits.c @@ -1,5 +1,5 @@ /* Digits. - Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc. + Copyright (C) 1994, 1995, 1996, 1999 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 @@ -18,9 +18,8 @@ Boston, MA 02111-1307, USA. */ /* Lower-case digits. */ -const char _itoa_lower_digits[] +const char _itoa_lower_digits[36] = "0123456789abcdefghijklmnopqrstuvwxyz"; /* Upper-case digits. */ -const char _itoa_upper_digits[] +const char _itoa_upper_digits[36] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - diff --git a/stdio-common/itowa-digits.c b/stdio-common/itowa-digits.c new file mode 100644 index 0000000000..60a85789e3 --- /dev/null +++ b/stdio-common/itowa-digits.c @@ -0,0 +1,27 @@ +/* Digits. + Copyright (C) 1994, 1995, 1996, 1999 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., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +#include <wchar.h> + +/* Lower-case digits. */ +const wchar_t _itowa_lower_digits[36] + = L"0123456789abcdefghijklmnopqrstuvwxyz"; +/* Upper-case digits. */ +const wchar_t _itowa_upper_digits[36] + = L"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; diff --git a/stdio-common/printf-parse.h b/stdio-common/printf-parse.h index a915f03f18..d62f3a835e 100644 --- a/stdio-common/printf-parse.h +++ b/stdio-common/printf-parse.h @@ -35,7 +35,7 @@ struct printf_spec /* 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; + const UCHAR_T *end_of_fmt, *next_fmt; /* Position of arguments for precision and width, or -1 if `info' has the constant value. */ @@ -90,21 +90,29 @@ read_int (const UCHAR_T * *pstr) /* 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, mbstate_t *ps) +static inline const UCHAR_T * +#ifdef COMPILE_WPRINTF +find_spec (const UCHAR_T *format) +#else +find_spec (const UCHAR_T *format, mbstate_t *ps) +#endif { - while (*format != '\0' && *format != '%') +#ifdef COMPILE_WPRINTF + return (const UCHAR_T *) __wcschrnul ((const CHAR_T *) format, L'%'); +#else + while (*format != L_('\0') && *format != L_('%')) { int len; /* Remove any hints of a wrong encoding. */ ps->count = 0; - if (isascii (*format) || (len = mbrlen (format, MB_CUR_MAX, ps)) <= 0) - ++format; - else + if (! ISASCII (*format) && (len = MBRLEN (format, MB_CUR_MAX, ps)) > 0) format += len; + else + ++format; } return format; +#endif } @@ -119,8 +127,13 @@ extern printf_function **__printf_function_table; 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 +#ifdef COMPILE_WPRINTF +parse_one_spec (const UCHAR_T *format, size_t posn, struct printf_spec *spec, + size_t *max_ref_arg) +#else parse_one_spec (const UCHAR_T *format, size_t posn, struct printf_spec *spec, size_t *max_ref_arg, mbstate_t *ps) +#endif { unsigned int n; size_t nargs = 0; @@ -342,12 +355,12 @@ parse_one_spec (const UCHAR_T *format, size_t posn, struct printf_spec *spec, switch (spec->info.spec) { - case L'i': - case L'd': - case L'u': - case L'o': - case L'X': - case L'x': + case L_('i'): + case L_('d'): + case L_('u'): + case L_('o'): + case L_('X'): + case L_('x'): #if LONG_MAX != LONG_LONG_MAX if (spec->info.is_long_double) spec->data_arg_type = PA_INT|PA_FLAG_LONG_LONG; @@ -362,38 +375,38 @@ parse_one_spec (const UCHAR_T *format, size_t posn, struct printf_spec *spec, else spec->data_arg_type = PA_INT; break; - case L'e': - case L'E': - case L'f': - case L'g': - case L'G': - case L'a': - case L'A': + case L_('e'): + case L_('E'): + case L_('f'): + case L_('g'): + case L_('G'): + case L_('a'): + case L_('A'): 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 L'c': + case L_('c'): spec->data_arg_type = PA_CHAR; break; - case L'C': + case L_('C'): spec->data_arg_type = PA_WCHAR; break; - case L's': + case L_('s'): spec->data_arg_type = PA_STRING; break; - case L'S': + case L_('S'): spec->data_arg_type = PA_WSTRING; break; - case L'p': + case L_('p'): spec->data_arg_type = PA_POINTER; break; - case L'n': + case L_('n'): spec->data_arg_type = PA_INT|PA_FLAG_PTR; break; - case L'm': + case L_('m'): default: /* An unknown spec will consume no args. */ spec->ndata_args = 0; @@ -416,7 +429,11 @@ parse_one_spec (const UCHAR_T *format, size_t posn, struct printf_spec *spec, { /* Find the next format spec. */ spec->end_of_fmt = format; +#ifdef COMPILE_WPRINTF + spec->next_fmt = find_spec (format); +#else spec->next_fmt = find_spec (format, ps); +#endif } return nargs; diff --git a/stdio-common/printf-prs.c b/stdio-common/printf-prs.c index 4f15373544..19869cad19 100644 --- a/stdio-common/printf-prs.c +++ b/stdio-common/printf-prs.c @@ -1,4 +1,4 @@ -/* Copyright (C) 1991, 1992, 1995, 1996 Free Software Foundation, Inc. +/* Copyright (C) 1991, 1992, 1995, 1996, 1999 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 @@ -29,6 +29,8 @@ # define INT_T int # define L_(Str) Str # define ISDIGIT(Ch) isdigit (Ch) +# define ISASCII(Ch) isascii (Ch) +# define MBRLEN(Cp, L, St) mbrlen (Cp, L, St) # ifdef USE_IN_LIBIO # define PUT(F, S, N) _IO_sputn (F, S, N) diff --git a/stdio-common/printf.h b/stdio-common/printf.h index 18b2f4ab8a..66ac5d0742 100644 --- a/stdio-common/printf.h +++ b/stdio-common/printf.h @@ -1,4 +1,4 @@ -/* Copyright (C) 1991, 92, 93, 95, 96, 97, 98 Free Software Foundation, Inc. +/* Copyright (C) 1991,92,93,95,96,97,98,99 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 @@ -45,6 +45,7 @@ struct printf_info unsigned int group:1; /* ' flag. */ unsigned int extra:1; /* For special use. */ unsigned int is_char:1; /* hh flag. */ + unsigned int wide:1; /* Nonzero for wide character streams. */ wchar_t pad; /* Padding character. */ }; diff --git a/stdio-common/printf_fp.c b/stdio-common/printf_fp.c index c75289a3a9..1b550abe4f 100644 --- a/stdio-common/printf_fp.c +++ b/stdio-common/printf_fp.c @@ -52,11 +52,12 @@ the GNU I/O library. */ #ifdef USE_IN_LIBIO # define PUT(f, s, n) _IO_sputn (f, s, n) -# define PAD(f, c, n) _IO_padn (f, c, n) +# define PAD(f, c, n) (wide ? _IO_wpadn (f, c, n) : _IO_padn (f, c, n)) /* We use this file GNU C library and GNU I/O library. So make names equal. */ # undef putc -# define putc(c, f) _IO_putc_unlocked (c, f) +# define putc(c, f) (wide \ + ? _IO_putwc_unlocked (c, f) : _IO_putc_unlocked (c, f)) # define size_t _IO_size_t # define FILE _IO_FILE #else /* ! USE_IN_LIBIO */ @@ -188,6 +189,9 @@ __printf_fp (FILE *fp, /* General helper (carry limb). */ mp_limb_t cy; + /* Nonzero if this is output on a wide character stream. */ + int wide = info->wide; + char hack_digit (void) { mp_limb_t hi; @@ -765,7 +769,10 @@ __printf_fp (FILE *fp, if ((expsign == 0 && exponent >= dig_max) || (expsign != 0 && exponent > 4)) { - type = isupper (info->spec) ? 'E' : 'e'; + if ('g' - 'G' == 'e' - 'E') + type = 'E' + (info->spec - 'G'); + else + type = isupper (info->spec) ? 'E' : 'e'; fracdig_max = dig_max - 1; intdig_max = 1; chars_needed = 1 + 1 + fracdig_max + 1 + 1 + 4; diff --git a/stdio-common/printf_size.c b/stdio-common/printf_size.c index 34581067dc..654675a0d7 100644 --- a/stdio-common/printf_size.c +++ b/stdio-common/printf_size.c @@ -1,5 +1,5 @@ /* Print size value using units for orders of magnitude. - Copyright (C) 1997, 1998 Free Software Foundation, Inc. + Copyright (C) 1997, 1998, 1999 Free Software Foundation, Inc. This file is part of the GNU C Library. Contributed by Ulrich Drepper <drepper@cygnus.com>, 1997. Based on a proposal by Larry McVoy <lm@sgi.com>. @@ -212,6 +212,7 @@ printf_size (FILE *fp, const struct printf_info *info, const void *const *args) fp_info.group = info->group; fp_info.extra = info->extra; fp_info.pad = info->pad; + fp_info.wide = 0; if (fp_info.left && fp_info.pad == L' ') { diff --git a/stdio-common/vfprintf.c b/stdio-common/vfprintf.c index 390ce91f71..fe145d6a3d 100644 --- a/stdio-common/vfprintf.c +++ b/stdio-common/vfprintf.c @@ -36,74 +36,16 @@ Beside this it is also shared between the normal and wide character implementation as defined in ISO/IEC 9899:1990/Amendment 1:1995. */ -#ifndef COMPILE_WPRINTF -# define CHAR_T char -# define UCHAR_T unsigned char -# define INT_T int -# define L_(Str) Str -# define ISDIGIT(Ch) isdigit (Ch) - -# ifdef USE_IN_LIBIO -# define PUT(F, S, N) _IO_sputn ((F), (S), (N)) -# define PAD(Padchar) \ - if (width > 0) \ - done += _IO_padn (s, (Padchar), width) -# else -# define PUTC(C, F) putc (C, F) -ssize_t __printf_pad __P ((FILE *, char pad, size_t n)); -# define PAD(Padchar) \ - if (width > 0) \ - { ssize_t __res = __printf_pad (s, (Padchar), width); \ - if (__res == -1) \ - { \ - done = -1; \ - goto all_done; \ - } \ - done += __res; } -# endif -#else -# define vfprintf vfwprintf -# define CHAR_T wchar_t -# define UCHAR_T uwchar_t -# define INT_T wint_t -# define L_(Str) L##Str -# define ISDIGIT(Ch) iswdigit (Ch) - -# ifdef USE_IN_LIBIO -# define PUT(F, S, N) _IO_sputn ((F), (S), (N)) -# define PAD(Padchar) \ - if (width > 0) \ - done += _IO_wpadn (s, (Padchar), width) -# else -# define PUTC(C, F) wputc (C, F) -ssize_t __wprintf_pad __P ((FILE *, wchar_t pad, size_t n)); -# define PAD(Padchar) \ - if (width > 0) \ - { ssize_t __res = __wprintf_pad (s, (Padchar), width); \ - if (__res == -1) \ - { \ - done = -1; \ - goto all_done; \ - } \ - done += __res; } -# endif -#endif - -/* Include the shared code for parsing the format string. */ -#include "printf-parse.h" - #ifdef USE_IN_LIBIO /* This code is for use in libio. */ # include <libioP.h> -# define PUTC(C, F) _IO_putc_unlocked (C, F) -# define vfprintf _IO_vfprintf # define FILE _IO_FILE # undef va_list # define va_list _IO_va_list -# undef BUFSIZ +# undef BUFSIZ # define BUFSIZ _IO_BUFSIZ -# define ARGCHECK(S, Format) \ +# define ARGCHECK(S, Format) \ do \ { \ /* Check file argument for consistence. */ \ @@ -120,11 +62,54 @@ ssize_t __wprintf_pad __P ((FILE *, wchar_t pad, size_t n)); } \ } while (0) # define UNBUFFERED_P(S) ((S)->_IO_file_flags & _IO_UNBUFFERED) + +# ifndef COMPILE_WPRINTF +# define vfprintf _IO_vfprintf +# define CHAR_T char +# define UCHAR_T unsigned char +# define INT_T int +# define L_(Str) Str +# define ISDIGIT(Ch) isdigit (Ch) +# define ISASCII(Ch) isascii (Ch) +# define MBRLEN(Cp, L, St) mbrlen (Cp, L, St) + +# define PUT(F, S, N) _IO_sputn ((F), (S), (N)) +# define PAD(Padchar) \ + if (width > 0) \ + done += _IO_padn (s, (Padchar), width) +# define PUTC(C, F) _IO_putc_unlocked (C, F) +# define ORIENT if (_IO_fwide (s, -1) != -1) return -1 +# else +# include "_itowa.h" + +# 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 +# define L_(Str) L##Str +# define ISDIGIT(Ch) iswdigit (Ch) +# define ISASCII(Ch) (((unsigned int) (Ch) & ~0x7f) == 0) +# define MBRLEN(Cp, L, St) wcslen ((const wchar_t *) (Cp)) + +# define PUT(F, S, N) _IO_sputn ((F), (S), (N)) +# define PAD(Padchar) \ + if (width > 0) \ + done += _IO_wpadn (s, (Padchar), width) +# define PUTC(C, F) _IO_putwc_unlocked (C, F) +# define ORIENT if (_IO_fwide (s, 1) != 1) return -1 + +# define _itoa(Val, Buf, Base, Case) _itowa (Val, (wchar_t *) Buf, Base, Case) +# define _itoa_word(Val, Buf, Base, Case) _itowa_word (Val, (wchar_t *) Buf, \ + Base, Case) +# undef EOF +# define EOF WEOF +# endif #else /* ! USE_IN_LIBIO */ /* This code is for use in the GNU C library. */ # include <stdio.h> # define PUT(F, S, N) fwrite (S, 1, N, F) -# define ARGCHECK(S, Format) \ +# define ARGCHECK(S, Format) \ do \ { \ /* Check file argument for consistence. */ \ @@ -153,11 +138,14 @@ extern void __flockfile (FILE *); extern void __funlockfile (FILE *); #endif /* USE_IN_LIBIO */ +/* Include the shared code for parsing the format string. */ +#include "printf-parse.h" + #define outchar(Ch) \ do \ { \ - register const int outc = (Ch); \ + register const INT_T outc = (Ch); \ if (PUTC (outc, s) == EOF) \ { \ done = -1; \ @@ -199,7 +187,7 @@ extern void __funlockfile (FILE *); /* Global variables. */ -static const char null[] = "(null)"; +static const CHAR_T null[] = L_("(null)"); /* Helper function to provide temporary buffering for unbuffered streams. */ @@ -211,7 +199,8 @@ static int printf_unknown __P ((FILE *, const struct printf_info *, const void *const *)); /* Group digits of number string. */ -static char *group_number __P ((CHAR_T *, CHAR_T *, const CHAR_T *, wchar_t)) +static UCHAR_T *group_number __P ((UCHAR_T *, UCHAR_T *, const char *, + wchar_t)) internal_function; @@ -238,11 +227,13 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) const UCHAR_T *end_of_spec; /* Buffer intermediate results. */ - char work_buffer[1000]; - char *workend; + UCHAR_T work_buffer[1000]; + UCHAR_T *workend; /* State for restartable multibyte character handling functions. */ +#ifndef COMPILE_WPRINTF mbstate_t mbstate; +#endif /* We have to save the original argument pointer. */ va_list ap_save; @@ -505,7 +496,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) now process the wanted format specifier. */ \ LABEL (form_percent): \ /* Write a literal "%". */ \ - outchar ('%'); \ + outchar (L_('%')); \ break; \ \ LABEL (form_integer): \ @@ -588,7 +579,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) else \ /* We have to take care for the '0' flag. If a precision \ is given it must be ignored. */ \ - pad = ' '; \ + 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 \ @@ -597,13 +588,13 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) { \ string = workend; \ if (base == 8 && alt) \ - *string-- = '0'; \ + *string-- = L_('0'); \ } \ else \ { \ /* Put the number in WORK. */ \ - string = _itoa (number.longlong, workend + 1, base, \ - spec == 'X'); \ + string = (UCHAR_T *) _itoa (number.longlong, workend + 1, base, \ + spec == L_('X')); \ string -= 1; \ if (group && grouping) \ string = group_number (string, workend, grouping, \ @@ -642,7 +633,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) else \ /* We have to take care for the '0' flag. If a precision \ is given it must be ignored. */ \ - pad = ' '; \ + 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 \ @@ -651,13 +642,13 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) { \ string = workend; \ if (base == 8 && alt) \ - *string-- = '0'; \ + *string-- = L_('0'); \ } \ else \ { \ /* Put the number in WORK. */ \ - string = _itoa_word (number.word, workend + 1, base, \ - spec == 'X'); \ + string = (UCHAR_T *) _itoa_word (number.word, workend + 1, \ + base, spec == L_('X')); \ string -= 1; \ if (group && grouping) \ string = group_number (string, workend, grouping, \ @@ -670,10 +661,10 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) if (prec > 0) \ /* Add zeros to the precision. */ \ while (prec-- > 0) \ - *string-- = '0'; \ + *string-- = L_('0'); \ else if (number.word != 0 && alt && base == 8) \ /* Add octal marker. */ \ - *string-- = '0'; \ + *string-- = L_('0'); \ \ if (!left) \ { \ @@ -686,41 +677,41 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) if (is_negative || showsign || space) \ --width; \ \ - if (pad == '0') \ + if (pad == L_('0')) \ { \ while (width-- > 0) \ - *string-- = '0'; \ + *string-- = L_('0'); \ \ if (number.word != 0 && alt && base == 16) \ { \ *string-- = spec; \ - *string-- = '0'; \ + *string-- = L_('0'); \ } \ \ if (is_negative) \ - *string-- = '-'; \ + *string-- = L_('-'); \ else if (showsign) \ - *string-- = '+'; \ + *string-- = L_('+'); \ else if (space) \ - *string-- = ' '; \ + *string-- = L_(' '); \ } \ else \ { \ if (number.word != 0 && alt && base == 16) \ { \ *string-- = spec; \ - *string-- = '0'; \ + *string-- = L_('0'); \ } \ \ if (is_negative) \ - *string-- = '-'; \ + *string-- = L_('-'); \ else if (showsign) \ - *string-- = '+'; \ + *string-- = L_('+'); \ else if (space) \ - *string-- = ' '; \ + *string-- = L_(' '); \ \ while (width-- > 0) \ - *string-- = ' '; \ + *string-- = L_(' '); \ } \ \ outstring (string + 1, workend - string); \ @@ -732,20 +723,20 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) if (number.word != 0 && alt && base == 16) \ { \ *string-- = spec; \ - *string-- = '0'; \ + *string-- = L_('0'); \ } \ \ if (is_negative) \ - *string-- = '-'; \ + *string-- = L_('-'); \ else if (showsign) \ - *string-- = '+'; \ + *string-- = L_('+'); \ else if (space) \ - *string-- = ' '; \ + *string-- = L_(' '); \ \ width -= workend - string; \ outstring (string + 1, workend - string); \ \ - PAD (' '); \ + PAD (L_(' ')); \ break; \ } \ \ @@ -771,7 +762,8 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) showsign: showsign, \ group: group, \ pad: pad, \ - extra: 0 }; \ + extra: 0, \ + wide: sizeof (CHAR_T) != 1 }; \ \ if (is_long_double) \ the_arg.pa_long_double = va_arg (ap, long double); \ @@ -821,7 +813,8 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) showsign: showsign, \ group: group, \ pad: pad, \ - extra: 0 }; \ + extra: 0, \ + wide: sizeof (CHAR_T) != 1 }; \ \ if (is_long_double) \ the_arg.pa_long_double = va_arg (ap, long double); \ @@ -849,6 +842,178 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) } \ break; \ \ + LABEL (form_pointer): \ + /* Generic pointer. */ \ + { \ + const void *ptr; \ + if (fspec == NULL) \ + ptr = va_arg (ap, void *); \ + else \ + ptr = args_value[fspec->data_arg].pa_pointer; \ + if (ptr != NULL) \ + { \ + /* If the pointer is not NULL, write it as a %#x spec. */ \ + base = 16; \ + number.word = (unsigned long int) ptr; \ + is_negative = 0; \ + alt = 1; \ + group = 0; \ + spec = 'x'; \ + goto LABEL (number); \ + } \ + else \ + { \ + /* Write "(nil)" for a nil pointer. */ \ + string = (UCHAR_T *) L_("(nil)"); \ + /* Make sure the full string "(nil)" is printed. */ \ + if (prec < 5) \ + prec = 5; \ + is_long = 0; /* This is no wide-char string. */ \ + goto LABEL (print_string); \ + } \ + } \ + /* NOTREACHED */ \ + \ + LABEL (form_number): \ + /* Answer the count of characters written. */ \ + if (fspec == NULL) \ + { \ + if (is_longlong) \ + *(long long int *) va_arg (ap, void *) = done; \ + else if (is_long_num) \ + *(long int *) va_arg (ap, void *) = done; \ + else if (!is_short) \ + *(int *) va_arg (ap, void *) = done; \ + else \ + *(short int *) va_arg (ap, void *) = done; \ + } \ + else \ + if (is_longlong) \ + *(long long int *) args_value[fspec->data_arg].pa_pointer = done; \ + else if (is_long_num) \ + *(long int *) args_value[fspec->data_arg].pa_pointer = done; \ + else if (!is_short) \ + *(int *) args_value[fspec->data_arg].pa_pointer = done; \ + else \ + *(short int *) args_value[fspec->data_arg].pa_pointer = done; \ + break; \ + \ + LABEL (form_strerror): \ + /* Print description of error ERRNO. */ \ + string = \ + (UCHAR_T *) __strerror_r (save_errno, (char *) work_buffer, \ + sizeof work_buffer); \ + is_long = 0; /* This is no wide-char string. */ \ + goto LABEL (print_string) + +#ifdef COMPILE_WPRINTF +# define process_string_arg(fspec) \ + LABEL (form_character): \ + /* Character. */ \ + if (is_long) \ + goto LABEL (form_wcharacter); \ + --width; /* Account for the character itself. */ \ + if (!left) \ + PAD (L' '); \ + if (fspec == NULL) \ + outchar (btowc ((unsigned char) va_arg (ap, int))); /* Promoted. */ \ + else \ + outchar (btowc ((unsigned char) args_value[fspec->data_arg].pa_char));\ + if (left) \ + PAD (L' '); \ + break; \ + \ + LABEL (form_wcharacter): \ + { \ + /* Wide character. */ \ + --width; \ + if (!left) \ + PAD (L' '); \ + if (fspec == NULL) \ + outchar (va_arg (ap, wint_t)); \ + else \ + outchar (args_value[fspec->data_arg].pa_wchar); \ + if (left) \ + PAD (L' '); \ + } \ + break; \ + \ + LABEL (form_string): \ + { \ + size_t len; \ + \ + /* The string argument could in fact be `char *' or `wchar_t *'. \ + But this should not make a difference here. */ \ + if (fspec == NULL) \ + string = (UCHAR_T *) va_arg (ap, const wchar_t *); \ + else \ + string = (UCHAR_T *) args_value[fspec->data_arg].pa_wstring; \ + \ + /* Entry point for printing other strings. */ \ + LABEL (print_string): \ + \ + if (string == NULL) \ + { \ + /* Write "(null)" if there's space. */ \ + if (prec == -1 \ + || prec >= (int) (sizeof (null) / sizeof (null[0])) - 1) \ + { \ + string = (UCHAR_T *) null; \ + len = (sizeof (null) / sizeof (null[0])) - 1; \ + } \ + else \ + { \ + string = (UCHAR_T *) L""; \ + len = 0; \ + } \ + } \ + else if (!is_long && spec != L_('S')) \ + { \ + /* This is complicated. We have to transform the multibyte \ + string into a wide character string. */ \ + const char *mbs = (const char *) string; \ + mbstate_t mbstate; \ + \ + len = prec == -1 ? strnlen (mbs, prec) : strlen (mbs); \ + \ + /* Allocate dynamically an array which definitely is long \ + enough for the wide character version. */ \ + string = (UCHAR_T *) alloca ((len + 1) * sizeof (wchar_t)); \ + \ + memset (&mbstate, '\0', sizeof (mbstate_t)); \ + len = __mbsrtowcs ((wchar_t *) string, &mbs, len + 1, &mbstate); \ + if (len == (size_t) -1) \ + { \ + /* Illegal multibyte character. */ \ + done = -1; \ + goto all_done; \ + } \ + } \ + else \ + { \ + if (prec != -1) \ + /* Search for the end of the string, but don't search past \ + the length specified by the precision. */ \ + len = __wcsnlen ((wchar_t *) string, prec); \ + else \ + len = __wcslen ((wchar_t *) string); \ + } \ + \ + if ((width -= len) < 0) \ + { \ + outstring (string, len); \ + break; \ + } \ + \ + if (!left) \ + PAD (L' '); \ + outstring (string, len); \ + if (left) \ + PAD (L' '); \ + } \ + break; +#else +# define process_string_arg(fspec) \ LABEL (form_character): \ /* Character. */ \ if (is_long) \ @@ -917,7 +1082,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) if (prec != -1) \ /* Search for the end of the string, but don't search past \ the length specified by the precision. */ \ - len = strnlen (string, prec); \ + len = __strnlen (string, prec); \ else \ len = strlen (string); \ } \ @@ -939,7 +1104,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) s2 = (const wchar_t *) string; \ string = alloca (len + 1); \ (void) __wcsrtombs (string, &s2, len + 1, &mbstate); \ - if (prec < len) \ + if (prec > 0 && prec < len) \ len = prec; \ } \ \ @@ -955,75 +1120,23 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) if (left) \ PAD (' '); \ } \ - break; \ - \ - LABEL (form_pointer): \ - /* Generic pointer. */ \ - { \ - const void *ptr; \ - if (fspec == NULL) \ - ptr = va_arg (ap, void *); \ - else \ - ptr = args_value[fspec->data_arg].pa_pointer; \ - if (ptr != NULL) \ - { \ - /* If the pointer is not NULL, write it as a %#x spec. */ \ - base = 16; \ - number.word = (unsigned long int) ptr; \ - is_negative = 0; \ - alt = 1; \ - group = 0; \ - spec = 'x'; \ - goto LABEL (number); \ - } \ - else \ - { \ - /* Write "(nil)" for a nil pointer. */ \ - string = (char *) "(nil)"; \ - /* Make sure the full string "(nil)" is printed. */ \ - if (prec < 5) \ - prec = 5; \ - is_long = 0; /* This is no wide-char string. */ \ - goto LABEL (print_string); \ - } \ - } \ - /* NOTREACHED */ \ - \ - LABEL (form_number): \ - /* Answer the count of characters written. */ \ - if (fspec == NULL) \ - { \ - if (is_longlong) \ - *(long long int *) va_arg (ap, void *) = done; \ - else if (is_long_num) \ - *(long int *) va_arg (ap, void *) = done; \ - else if (!is_short) \ - *(int *) va_arg (ap, void *) = done; \ - else \ - *(short int *) va_arg (ap, void *) = done; \ - } \ - else \ - if (is_longlong) \ - *(long long int *) args_value[fspec->data_arg].pa_pointer = done; \ - else if (is_long_num) \ - *(long int *) args_value[fspec->data_arg].pa_pointer = done; \ - else if (!is_short) \ - *(int *) args_value[fspec->data_arg].pa_pointer = done; \ - else \ - *(short int *) args_value[fspec->data_arg].pa_pointer = done; \ - break; \ - \ - LABEL (form_strerror): \ - /* Print description of error ERRNO. */ \ - string = \ - (char *) __strerror_r (save_errno, work_buffer, sizeof work_buffer); \ - is_long = 0; /* This is no wide-char string. */ \ - goto LABEL (print_string) + break; +#endif + /* Orient the stream. */ +#ifdef ORIENT + ORIENT; +#endif /* Sanity check of arguments. */ ARGCHECK (s, format); + /* Check for correct orientation. */ + if (_IO_fwide (s, sizeof (CHAR_T) == 1 ? -1 : 1) + != (sizeof (CHAR_T) == 1 ? -1 : 1)) + /* The stream is already oriented otherwise. */ + return EOF; + if (UNBUFFERED_P (s)) /* Use a helper function which will allocate a local temporary buffer for the stream and then call us again. */ @@ -1041,11 +1154,16 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) #endif nspecs_done = 0; +#ifdef COMPILE_WPRINTF + /* Find the first format specifier. */ + f = lead_str_end = find_spec ((const UCHAR_T *) format); +#else /* Put state for processing format string in initial state. */ memset (&mbstate, '\0', sizeof (mbstate_t)); /* Find the first format specifier. */ f = lead_str_end = find_spec (format, &mbstate); +#endif /* Lock stream. */ #ifdef USE_IN_LIBIO @@ -1081,7 +1199,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) } number; int base; union printf_arg the_arg; - char *string; /* Pointer to argument string. */ + UCHAR_T *string; /* Pointer to argument string. */ int alt = 0; /* Alternate format. */ int space = 0; /* Use space prefix if no sign is needed. */ int left = 0; /* Left-justify output. */ @@ -1093,10 +1211,10 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) int is_char = 0; /* Argument is promoted (unsigned) char. */ int width = 0; /* Width of output; 0 means none specified. */ int prec = -1; /* Precision of output; -1 means none specified. */ - char pad = ' '; /* Padding character. */ + UCHAR_T pad = L_(' ');/* Padding character. */ CHAR_T spec; - workend = &work_buffer[sizeof (work_buffer) - 1]; + workend = &work_buffer[sizeof (work_buffer) / sizeof (CHAR_T) - 1]; /* Get current character in format string. */ JUMP (*++f, step0_jumps); @@ -1172,10 +1290,11 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) left = 1; } - if (width + 32 >= sizeof (work_buffer)) + if (width + 32 >= sizeof (work_buffer) / sizeof (work_buffer[0])) /* We have to use a special buffer. The "32" is just a safe bet for all the output which is not counted in the width. */ - workend = alloca (width + 32) + (width + 31); + workend = ((UCHAR_T *) alloca ((width + 32) * sizeof (CHAR_T)) + + (width + 31)); } JUMP (*f, step1_jumps); @@ -1183,10 +1302,11 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) LABEL (width): width = read_int (&f); - if (width + 32 >= sizeof (work_buffer)) + if (width + 32 >= sizeof (work_buffer) / sizeof (work_buffer[0])) /* We have to use a special buffer. The "32" is just a safe bet for all the output which is not counted in the width. */ - workend = alloca (width + 32) + (width + 31); + workend = ((UCHAR_T *) alloca ((width + 32) * sizeof (CHAR_T)) + + (width + 31)); if (*f == L_('$')) /* Oh, oh. The argument comes from a positional parameter. */ goto do_positional; @@ -1213,7 +1333,8 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) prec = read_int (&f); else prec = 0; - if (prec > width && prec + 32 > sizeof (work_buffer)) + if (prec > width + && prec + 32 > sizeof (work_buffer) / sizeof (work_buffer[0])) workend = alloca (spec + 32) + (spec + 31); JUMP (*f, step2_jumps); @@ -1258,6 +1379,7 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) while (1) { process_arg (((struct printf_spec *) NULL)); + process_string_arg (((struct printf_spec *) NULL)); LABEL (form_unknown): if (spec == L_('\0')) @@ -1276,7 +1398,11 @@ vfprintf (FILE *s, const CHAR_T *format, va_list ap) ++nspecs_done; /* Look for next format specifier. */ +#ifdef COMPILE_WPRINTF + f = find_spec ((end_of_spec = ++f)); +#else f = find_spec ((end_of_spec = ++f), &mbstate); +#endif /* Write the following constant string. */ outstring (end_of_spec, f - end_of_spec); @@ -1301,7 +1427,7 @@ do_positional: attributes. */ size_t nargs = 0; int *args_type; - union printf_arg *args_value; + union printf_arg *args_value = NULL; /* Positional parameters refer to arguments directly. This could also determine the maximum number of arguments. Track the @@ -1329,7 +1455,7 @@ do_positional: grouping = NULL; } - for (f = lead_str_end; *f != '\0'; f = specs[nspecs++].next_fmt) + for (f = lead_str_end; *f != L_('\0'); f = specs[nspecs++].next_fmt) { if (nspecs >= nspecs_max) { @@ -1356,8 +1482,12 @@ do_positional: } /* Parse the format specifier. */ +#ifdef COMPILE_WPRINTF + nargs += parse_one_spec (f, nargs, &specs[nspecs], &max_ref_arg); +#else nargs += parse_one_spec (f, nargs, &specs[nspecs], &max_ref_arg, &mbstate); +#endif } /* Determine the number of arguments the format string consumes. */ @@ -1449,7 +1579,7 @@ do_positional: } number; int base; union printf_arg the_arg; - char *string; /* Pointer to argument string. */ + UCHAR_T *string; /* Pointer to argument string. */ /* Fill variables from values in struct. */ int alt = specs[nspecs_done].info.alt; @@ -1498,8 +1628,10 @@ do_positional: } /* Maybe the buffer is too small. */ - if (MAX (prec, width) + 32 > sizeof (work_buffer)) - workend = alloca (MAX (prec, width) + 32) + (MAX (prec, width) + 31); + if (MAX (prec, width) + 32 > sizeof (work_buffer) / sizeof (UCHAR_T)) + workend = ((UCHAR_T *) alloca ((MAX (prec, width) + 32) + * sizeof (UCHAR_T)) + + (MAX (prec, width) + 31)); /* Process format specifiers. */ while (1) @@ -1507,6 +1639,7 @@ do_positional: JUMP (spec, step4_jumps); process_arg ((&specs[nspecs_done])); + process_string_arg ((&specs[nspecs_done])); LABEL (form_unknown): { @@ -1564,21 +1697,6 @@ all_done: return done; } - -#ifdef USE_IN_LIBIO -# undef vfprintf -# ifdef strong_alias -/* This is for glibc. */ -strong_alias (_IO_vfprintf, vfprintf); -# else -# if defined __ELF__ || defined __GNU_LIBRARY__ -# include <gnu-stabs.h> -# ifdef weak_alias -weak_alias (_IO_vfprintf, vfprintf); -# endif -# endif -# endif -#endif /* Handle an unknown format specifier. This prints out a canonicalized representation of the format spec itself. */ @@ -1588,24 +1706,25 @@ printf_unknown (FILE *s, const struct printf_info *info, { int done = 0; - char work_buffer[MAX (info->width, info->spec) + 32]; - char *const workend = &work_buffer[sizeof (work_buffer) - 1]; - register char *w; + CHAR_T work_buffer[MAX (info->width, info->spec) + 32]; + CHAR_T *const workend = &work_buffer[sizeof (work_buffer) / sizeof (CHAR_T) + - 1]; + register CHAR_T *w; - outchar ('%'); + outchar (L_('%')); if (info->alt) - outchar ('#'); + outchar (L_('#')); if (info->group) - outchar ('\''); + outchar (L_('\'')); if (info->showsign) - outchar ('+'); + outchar (L_('+')); else if (info->space) - outchar (' '); + outchar (L_(' ')); if (info->left) - outchar ('-'); + outchar (L_('-')); if (info->pad == '0') - outchar ('0'); + outchar (L_('0')); if (info->width != 0) { @@ -1622,7 +1741,7 @@ printf_unknown (FILE *s, const struct printf_info *info, outchar (*w++); } - if (info->spec != '\0') + if (info->spec != L_('\0')) outchar (info->spec); all_done: @@ -1631,13 +1750,13 @@ printf_unknown (FILE *s, const struct printf_info *info, /* 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 * +static UCHAR_T * internal_function -group_number (CHAR_T *w, CHAR_T *rear_ptr, const CHAR_T *grouping, +group_number (UCHAR_T *w, UCHAR_T *rear_ptr, const char *grouping, wchar_t thousands_sep) { int len; - char *src, *s; + UCHAR_T *src, *s; /* We treat all negative values like CHAR_MAX. */ @@ -1648,8 +1767,9 @@ group_number (CHAR_T *w, CHAR_T *rear_ptr, const CHAR_T *grouping, len = *grouping; /* Copy existing string so that nothing gets overwritten. */ - src = (char *) alloca (rear_ptr - w); - s = (char *) __mempcpy (src, w + 1, rear_ptr - w) - 1; + src = (UCHAR_T *) alloca ((rear_ptr - w) * sizeof (UCHAR_T)); + s = (UCHAR_T *) __mempcpy (src, w + 1, + (rear_ptr - w) * sizeof (UCHAR_T)) - 1; w = rear_ptr; /* Process all characters in the string. */ @@ -1699,12 +1819,22 @@ static int _IO_helper_overflow (_IO_FILE *s, int c) { _IO_FILE *target = ((struct helper_file*) s)->_put_stream; +#ifdef COMPILE_WPRINTF + int used = s->_wide_data->_IO_write_ptr - s->_wide_data->_IO_write_base; + if (used) + { + _IO_size_t written = _IO_sputn (target, s->_wide_data->_IO_write_base, + used); + s->_wide_data->_IO_write_ptr -= written; + } +#else 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; } +#endif return PUTC (c, s); } @@ -1735,16 +1865,18 @@ internal_function buffered_vfprintf (register _IO_FILE *s, const CHAR_T *format, _IO_va_list args) { - char buf[_IO_BUFSIZ]; + CHAR_T 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; +#ifdef COMPILE_WPRINTF + _IO_wsetp (hp, buf, buf + sizeof buf / sizeof (CHAR_T)); +#else + _IO_setp (hp, buf, buf + sizeof buf); +#endif hp->_IO_file_flags = _IO_MAGIC|_IO_NO_READS; #if _IO_JUMPS_OFFSET hp->_vtable_offset = 0; @@ -1756,14 +1888,24 @@ buffered_vfprintf (register _IO_FILE *s, const CHAR_T *format, _IO_JUMPS (hp) = (struct _IO_jump_t *) &_IO_helper_jumps; /* Now print to helper instead. */ - result = _IO_vfprintf (hp, format, args); + result = vfprintf (hp, format, args); /* Now flush anything from the helper to the S. */ +#ifdef COMPILE_WPRINTF + if ((to_flush = (hp->_wide_data->_IO_write_ptr + - hp->_wide_data->_IO_write_base)) > 0) + { + if ((int) _IO_sputn (s, hp->_wide_data->_IO_write_base, to_flush) + != to_flush) + return -1; + } +#else if ((to_flush = hp->_IO_write_ptr - hp->_IO_write_base) > 0) { if ((int) _IO_sputn (s, hp->_IO_write_base, to_flush) != to_flush) return -1; } +#endif return result; } @@ -1826,3 +1968,26 @@ __wprintf_pad (FILE *s, wchar_t pad, size_t count) } #undef PADSIZE #endif /* USE_IN_LIBIO */ + +#ifdef USE_IN_LIBIO +# undef vfprintf +# ifdef strong_alias +/* This is for glibc. */ +# ifdef COMPILE_WPRINTF +strong_alias (_IO_vfwprintf, vfwprintf); +# else +strong_alias (_IO_vfprintf, vfprintf); +# endif +# else +# if defined __ELF__ || defined __GNU_LIBRARY__ +# include <gnu-stabs.h> +# ifdef weak_alias +# ifdef COMPILE_WPRINTF +weak_alias (_IO_vfwprintf, vfwprintf); +# else +weak_alias (_IO_vfprintf, vfprintf); +# endif +# endif +# endif +# endif +#endif diff --git a/stdio-common/vfscanf.c b/stdio-common/vfscanf.c index 0339edbeca..5caf616be3 100644 --- a/stdio-common/vfscanf.c +++ b/stdio-common/vfscanf.c @@ -16,6 +16,7 @@ write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include <assert.h> #include <errno.h> #include <limits.h> #include <ctype.h> @@ -69,13 +70,56 @@ # undef va_list # define va_list _IO_va_list -# define ungetc(c, s) ((void) ((int) c == EOF \ + +# ifdef COMPILE_WPRINTF +# define ungetc(c, s) ((void) ((int) c == WEOF \ + || (--read_in, \ + _IO_sputbackwc (s, (unsigned char) c)))) +# define inchar() (c == EOF ? EOF \ + : ((c = _IO_getwc_unlocked (s)), \ + (void) (c != EOF && ++read_in), c)) + +# define MEMCPY(d, s, n) wmemcpy (d, s, n) +# define ISSPACE(Ch) iswspace (Ch) +# define ISDIGIT(Ch) iswdigit (Ch) +# define ISXDIGIT(Ch) iswxdigit (Ch) +# define UNGETC(Ch, S) ungetwc (Ch, S) +# define TOLOWER(Ch) towlower (Ch) +# define ORIENT if (_IO_fwide (s, 1) != 1) return EOF +# define __strtoll_internal __wcstoll_internal +# define __strtoull_internal __wcstoull_internal +# define __strtol_internal __wcstol_internal +# define __strtoul_internal __wcstoul_internal +# define __strtold_internal __wcstold_internal +# define __strtod_internal __wcstod_internal +# define __strtof_internal __wcstof_internal + +# define L_(Str) L##Str +# define CHAR_T wchar_t +# define UCHAR_T unsigned int +# define WINT_T wint_t +# else +# define ungetc(c, s) ((void) ((int) c == EOF \ || (--read_in, \ _IO_sputbackc (s, (unsigned char) c)))) -# define inchar() (c == EOF ? EOF \ +# define inchar() (c == EOF ? EOF \ : ((c = _IO_getc_unlocked (s)), \ (void) (c != EOF && ++read_in), c)) -# define encode_error() do { \ +# define MEMCPY(d, s, n) memcpy (d, s, n) +# define ISSPACE(Ch) isspace (Ch) +# define ISDIGIT(Ch) isdigit (Ch) +# define ISXDIGIT(Ch) isxdigit (Ch) +# define UNGETC(Ch, S) ungetc (Ch, S) +# define TOLOWER(Ch) tolower (Ch) +# define ORIENT if (_IO_fwide (s, -1) != -1) return EOF + +# define L_(Str) Str +# define CHAR_T char +# define UCHAR_T unsigned char +# define WINT_T int +# endif + +# define encode_error() do { \ if (errp != NULL) *errp |= 4; \ _IO_funlockfile (s); \ __libc_cleanup_end (0); \ @@ -94,7 +138,7 @@ __libc_cleanup_end (0); \ return done ?: EOF; \ } while (0) -# define memory_error() do { \ +# define memory_error() do { \ _IO_funlockfile (s); \ __set_errno (ENOMEM); \ __libc_cleanup_end (0); \ @@ -180,30 +224,39 @@ FORMAT, using the argument list in ARG. Return the number of assignments made, or -1 for an input error. */ #ifdef USE_IN_LIBIO +# ifdef COMPILE_WPRINTF +int +_IO_vfwscanf (s, format, argptr, errp) + _IO_FILE *s; + const wchar_t *format; + _IO_va_list argptr; + int *errp; +# else int _IO_vfscanf (s, format, argptr, errp) _IO_FILE *s; const char *format; _IO_va_list argptr; int *errp; +# endif #else int __vfscanf (FILE *s, const char *format, va_list argptr) #endif { va_list arg; - register const char *f = format; - register unsigned char fc; /* Current character of the format. */ + register const CHAR_T *f = format; + register UCHAR_T fc; /* Current character of the format. */ register size_t done = 0; /* Assignments done. */ register size_t read_in = 0; /* Chars read in. */ - register int c = 0; /* Last char read. */ + register WINT_T c = 0; /* Last char read. */ register int width; /* Maximum field width. */ register int flags; /* Modifiers for current format element. */ /* Status for reading F-P nums. */ char got_dot, got_e, negative; /* If a [...] is a [^...]. */ - char not_in; + CHAR_T not_in; #define exp_char not_in /* Base for integral numbers. */ int base; @@ -236,8 +289,8 @@ __vfscanf (FILE *s, const char *format, va_list argptr) /* Nonzero if we are reading a pointer. */ int read_pointer; /* Workspace. */ - char *tw; /* Temporary pointer. */ - char *wp = NULL; /* Workspace. */ + CHAR_T *tw; /* Temporary pointer. */ + CHAR_T *wp = NULL; /* Workspace. */ size_t wpmax = 0; /* Maximal size of workspace. */ size_t wpsize; /* Currently used bytes in workspace. */ #define ADDW(Ch) \ @@ -245,11 +298,11 @@ __vfscanf (FILE *s, const char *format, va_list argptr) { \ if (wpsize == wpmax) \ { \ - char *old = wp; \ + CHAR_T *old = wp; \ wpmax = UCHAR_MAX > 2 * wpmax ? UCHAR_MAX : 2 * wpmax; \ - wp = (char *) alloca (wpmax); \ + wp = (CHAR_T *) alloca (wpmax * sizeof (wchar_t)); \ if (old != NULL) \ - memcpy (wp, old, wpsize); \ + MEMCPY (wp, old, wpsize); \ } \ wp[wpsize++] = (Ch); \ } \ @@ -261,6 +314,10 @@ __vfscanf (FILE *s, const char *format, va_list argptr) arg = (va_list) argptr; #endif +#ifdef ORIENT + ORIENT; +#endif + ARGCHECK (s, format); /* Figure out the decimal point character. */ @@ -280,8 +337,10 @@ __vfscanf (FILE *s, const char *format, va_list argptr) LOCK_STREAM (s); +#ifndef COMPILE_WPRINTF /* From now on we use `state' to convert the format string. */ memset (&state, '\0', sizeof (state)); +#endif /* Run through the format string. */ while (*f != '\0') @@ -320,6 +379,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr) # endif #endif +#ifndef COMPILE_WPRINTF if (!isascii (*f)) { /* Non-ASCII, may be a multibyte. */ @@ -341,12 +401,13 @@ __vfscanf (FILE *s, const char *format, va_list argptr) continue; } } +#endif fc = *f++; if (fc != '%') { /* Remember to skip spaces. */ - if (isspace (fc)) + if (ISSPACE (fc)) { skip_space = 1; continue; @@ -363,7 +424,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr) string. Now it's time to skip all leading white space. */ if (skip_space) { - while (isspace (c)) + while (ISSPACE (c)) if (inchar () == EOF && errno == EINTR) conv_error (); skip_space = 0; @@ -371,7 +432,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr) if (c != fc) { - ungetc (c, s); + UNGETC (c, s); conv_error (); } @@ -391,12 +452,12 @@ __vfscanf (FILE *s, const char *format, va_list argptr) wpsize = 0; /* Check for a positional parameter specification. */ - if (isdigit (*f)) + if (ISDIGIT (*f)) { - argpos = *f++ - '0'; - while (isdigit (*f)) - argpos = argpos * 10 + (*f++ - '0'); - if (*f == '$') + argpos = *f++ - L_('0'); + while (ISDIGIT (*f)) + argpos = argpos * 10 + (*f++ - L_('0')); + if (*f == L_('$')) ++f; else { @@ -409,27 +470,27 @@ __vfscanf (FILE *s, const char *format, va_list argptr) } /* Check for the assignment-suppressing and the number grouping flag. */ - while (*f == '*' || *f == '\'') + while (*f == L_('*') || *f == L_('\'')) switch (*f++) { - case '*': + case L_('*'): flags |= SUPPRESS; break; - case '\'': + case L_('\''): flags |= GROUP; break; } /* We have seen width. */ - if (isdigit (*f)) + if (ISDIGIT (*f)) flags |= WIDTH; /* Find the maximum field width. */ width = 0; - while (isdigit (*f)) + while (ISDIGIT (*f)) { width *= 10; - width += *f++ - '0'; + width += *f++ - L_('0'); } got_width: if (width == 0) @@ -438,9 +499,9 @@ __vfscanf (FILE *s, const char *format, va_list argptr) /* Check for type modifiers. */ switch (*f++) { - case 'h': + case L_('h'): /* ints are short ints or chars. */ - if (*f == 'h') + if (*f == L_('h')) { ++f; flags |= CHAR; @@ -448,8 +509,8 @@ __vfscanf (FILE *s, const char *format, va_list argptr) else flags |= SHORT; break; - case 'l': - if (*f == 'l') + case L_('l'): + if (*f == L_('l')) { /* A double `l' is equivalent to an `L'. */ ++f; @@ -459,15 +520,15 @@ __vfscanf (FILE *s, const char *format, va_list argptr) /* ints are long ints. */ flags |= LONG; break; - case 'q': - case 'L': + case L_('q'): + case L_('L'): /* doubles are long doubles, and ints are long long ints. */ flags |= LONGDBL | LONG; break; - case 'a': + case L_('a'): /* The `a' is used as a flag only if followed by `s', `S' or `['. */ - if (*f != 's' && *f != 'S' && *f != '[') + if (*f != L_('s') && *f != L_('S') && *f != L_('[')) { --f; break; @@ -476,19 +537,19 @@ __vfscanf (FILE *s, const char *format, va_list argptr) arg and fill it in with a malloc'd pointer. */ flags |= MALLOC; break; - case 'z': + case L_('z'): if (need_longlong && sizeof (size_t) > sizeof (unsigned long int)) flags |= LONGDBL; else if (sizeof (size_t) > sizeof (unsigned int)) flags |= LONG; break; - case 'j': + case L_('j'): if (need_longlong && sizeof (uintmax_t) > sizeof (unsigned long int)) flags |= LONGDBL; else if (sizeof (uintmax_t) > sizeof (unsigned int)) flags |= LONG; break; - case 't': + case L_('t'): if (need_longlong && sizeof (ptrdiff_t) > sizeof (long int)) flags |= LONGDBL; else if (sizeof (ptrdiff_t) > sizeof (int)) @@ -501,12 +562,13 @@ __vfscanf (FILE *s, const char *format, va_list argptr) } /* End of the format string? */ - if (*f == '\0') + if (*f == L_('\0')) conv_error (); /* Find the conversion specifier. */ fc = *f++; - if (skip_space || (fc != '[' && fc != 'c' && fc != 'C' && fc != 'n')) + if (skip_space || (fc != L_('[') && fc != L_('c') + && fc != L_('C') && fc != L_('n'))) { /* Eat whitespace. */ int save_errno = errno; @@ -514,15 +576,15 @@ __vfscanf (FILE *s, const char *format, va_list argptr) do if (inchar () == EOF && errno == EINTR) input_error (); - while (isspace (c)); + while (ISSPACE (c)); errno = save_errno; - ungetc (c, s); + UNGETC (c, s); skip_space = 0; } switch (fc) { - case '%': /* Must match a literal '%'. */ + case L_('%'): /* Must match a literal '%'. */ c = inchar (); if (c == EOF) input_error (); @@ -533,7 +595,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr) } break; - case 'n': /* Answer number of assignments done. */ + case L_('n'): /* Answer number of assignments done. */ /* Corrigendum 1 to ISO C 1990 describes the allowed flags with the 'n' conversion specifier. */ if (!(flags & SUPPRESS)) @@ -581,7 +643,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr) } break; - case 'c': /* Match characters. */ + case L_('c'): /* Match characters. */ if ((flags & LONG) == 0) { if (!(flags & SUPPRESS)) @@ -598,6 +660,26 @@ __vfscanf (FILE *s, const char *format, va_list argptr) if (width == -1) width = 1; +#ifdef COMPILE_WPRINTF + /* We have to convert the wide character(s) into multibyte + characters and store the result. */ + memset (&state, '\0', sizeof (state)); + + do + { + size_t n; + + n = wcrtomb (!(flags & SUPPRESS) ? str : NULL, c, &state); + if (n == (size_t) -1) + /* No valid wide character. */ + input_error (); + + /* Increment the output pointer. Even if we don't + write anything. */ + str += n; + } + while (--width > 0 && inchar () != EOF); +#else if (!(flags & SUPPRESS)) { do @@ -606,6 +688,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr) } else while (--width > 0 && inchar () != EOF); +#endif if (!(flags & SUPPRESS)) ++done; @@ -613,238 +696,448 @@ __vfscanf (FILE *s, const char *format, va_list argptr) break; } /* FALLTHROUGH */ - case 'C': - /* Get UTF-8 encoded wide character. Here we assume (as in - other parts of the libc) that we only have to handle - UTF-8. */ + case L_('C'): + if (!(flags & SUPPRESS)) + { + wstr = ARG (wchar_t *); + if (str == NULL) + conv_error (); + } + + c = inchar (); + if (c == EOF) + input_error (); + +#ifdef COMPILE_WPRINTF + /* Just store the incoming wide characters. */ + if (!(flags & SUPPRESS)) + { + do + *wstr++ = c; + while (--width > 0 && inchar () != EOF); + } + else + while (--width > 0 && inchar () != EOF); +#else { - wint_t val; - size_t cnt = 0; - int first = 1; + /* We have to convert the multibyte input sequence to wide + characters. */ + char buf[MB_LEN_MAX]; + mbstate_t cstate; - if (!(flags & SUPPRESS)) - { - wstr = ARG (wchar_t *); - if (str == NULL) - conv_error (); - } + memset (&cstate, '\0', sizeof (cstate)); do { -#define NEXT_WIDE_CHAR(First) \ - c = inchar (); \ - if (c == EOF) \ - { \ - /* EOF is only an error for the first character. */ \ - if (First) \ - input_error (); \ - else \ - break; \ - } \ - val = c; \ - if (val >= 0x80) \ - { \ - if ((c & 0xc0) == 0x80 || (c & 0xfe) == 0xfe) \ - encode_error (); \ - if ((c & 0xe0) == 0xc0) \ - { \ - /* We expect two bytes. */ \ - cnt = 1; \ - val &= 0x1f; \ - } \ - else if ((c & 0xf0) == 0xe0) \ - { \ - /* We expect three bytes. */ \ - cnt = 2; \ - val &= 0x0f; \ - } \ - else if ((c & 0xf8) == 0xf0) \ - { \ - /* We expect four bytes. */ \ - cnt = 3; \ - val &= 0x07; \ - } \ - else if ((c & 0xfc) == 0xf8) \ - { \ - /* We expect five bytes. */ \ - cnt = 4; \ - val &= 0x03; \ - } \ - else \ - { \ - /* We expect six bytes. */ \ - cnt = 5; \ - val &= 0x01; \ - } \ - \ - do \ - { \ - c = inchar (); \ - if (c == EOF \ - || (c & 0xc0) == 0x80 || (c & 0xfe) == 0xfe) \ - encode_error (); \ - val <<= 6; \ - val |= c & 0x3f; \ - } \ - while (--cnt > 0); \ - } \ - \ - if (!(flags & SUPPRESS)) \ - *wstr++ = val; \ - First = 0 - - NEXT_WIDE_CHAR (first); - } - while (--width > 0); + size_t cnt; - if (!(flags & SUPPRESS)) - ++done; + /* This is what we present the mbrtowc function first. */ + buf[0] = c; + cnt = 1; + + while (1) + { + size_t n; + + n = __mbrtowc (!(flags & SUPPRESS) ? wstr : NULL, + buf, cnt, &cstate); + + if (n == (size_t) -2) + { + /* Possibly correct character, just not enough + input. */ + assert (cnt < MB_CUR_MAX); + + if (inchar () == EOF) + encode_error (); + + buf[cnt++] = c; + continue; + } + + if (n != cnt) + encode_error (); + + /* We have a match. */ + break; + } + + /* Advance the result pointer. */ + ++wstr; + } + while (--width > 0 && inchar () != EOF); } - break; +#endif - case 's': /* Read a string. */ - if (flags & LONG) - /* We have to process a wide character string. */ - goto wide_char_string; + if (!(flags & SUPPRESS)) + ++done; + break; + + case L_('s'): /* Read a string. */ + if (!(flags & LONG)) + { #define STRING_ARG(Str, Type) \ - if (!(flags & SUPPRESS)) \ - { \ - if (flags & MALLOC) \ + do if (!(flags & SUPPRESS)) \ { \ - /* The string is to be stored in a malloc'd buffer. */ \ - strptr = ARG (char **); \ - if (strptr == NULL) \ + if (flags & MALLOC) \ + { \ + /* The string is to be stored in a malloc'd buffer. */ \ + strptr = ARG (char **); \ + if (strptr == NULL) \ + conv_error (); \ + /* Allocate an initial buffer. */ \ + strsize = 100; \ + *strptr = (char *) malloc (strsize * sizeof (Type)); \ + Str = (Type *) *strptr; \ + } \ + else \ + Str = ARG (Type *); \ + if (Str == NULL) \ conv_error (); \ - /* Allocate an initial buffer. */ \ - strsize = 100; \ - *strptr = malloc (strsize * sizeof (Type)); \ - Str = (Type *) *strptr; \ - } \ - else \ - Str = ARG (Type *); \ - if (Str == NULL) \ - conv_error (); \ - } - STRING_ARG (str, char); + } while (0) + STRING_ARG (str, char); - c = inchar (); - if (c == EOF) - input_error (); + c = inchar (); + if (c == EOF) + input_error (); - do - { - if (isspace (c)) +#ifdef COMPILE_WPRINTF + memset (&state, '\0', sizeof (state)); +#endif + + do { - ungetc (c, s); - break; - } -#define STRING_ADD_CHAR(Str, c, Type) \ - if (!(flags & SUPPRESS)) \ - { \ - *Str++ = c; \ - if ((flags & MALLOC) && (char *) Str == *strptr + strsize) \ - { \ - /* Enlarge the buffer. */ \ - Str = realloc (*strptr, strsize * 2 * sizeof (Type)); \ - if (Str == NULL) \ - { \ - /* Can't allocate that much. Last-ditch effort. */\ - Str = realloc (*strptr, \ - (strsize + 1) * sizeof (Type)); \ - if (Str == NULL) \ - { \ - /* We lose. Oh well. \ - Terminate the string and stop converting, \ - so at least we don't skip any input. */ \ - ((Type *) (*strptr))[strsize] = '\0'; \ - ++done; \ - conv_error (); \ - } \ - else \ - { \ - *strptr = (char *) Str; \ - Str = ((Type *) *strptr) + strsize; \ - ++strsize; \ - } \ - } \ - else \ - { \ - *strptr = (char *) Str; \ - Str = ((Type *) *strptr) + strsize; \ - strsize *= 2; \ - } \ - } \ + if (ISSPACE (c)) + { + UNGETC (c, s); + break; + } + +#ifdef COMPILE_WPRINTF + /* This is quite complicated. We have to convert the + wide characters into multibyte characters and then + store them. */ + { + size_t n; + + if (!(flags & SUPPRESS) && (flags & MALLOC) + && str + MB_CUR_MAX >= *strptr + strsize) + { + /* We have to enlarge the buffer if the `a' flag + was given. */ + str = (char *) realloc (*strptr, strsize * 2); + if (str == NULL) + { + /* Can't allocate that much. Last-ditch + effort. */ + str = (char *) realloc (*strptr, strsize + 1); + if (str == NULL) + { + /* We lose. Oh well. Terminate the + string and stop converting, + so at least we don't skip any input. */ + ((char *) (*strptr))[strsize - 1] = '\0'; + ++done; + conv_error (); + } + else + { + *strptr = (char *) str; + str += strsize; + ++strsize; + } + } + else + { + *strptr = (char *) str; + str += strsize; + strsize *= 2; + } + } + + n = wcrtomb (!(flags & SUPPRESS) ? str : NULL, c, &state); + if (n == (size_t) -1) + encode_error (); + + assert (n <= MB_CUR_MAX); + str += n; + } +#else + /* This is easy. */ + if (!(flags & SUPPRESS)) + { + *str++ = c; + if ((flags & MALLOC) + && (char *) str == *strptr + strsize) + { + /* Enlarge the buffer. */ + str = (char *) realloc (*strptr, 2 * strsize); + if (str == NULL) + { + /* Can't allocate that much. Last-ditch + effort. */ + str = (char *) realloc (*strptr, strsize + 1); + if (str == NULL) + { + /* We lose. Oh well. Terminate the + string and stop converting, + so at least we don't skip any input. */ + ((char *) (*strptr))[strsize - 1] = '\0'; + ++done; + conv_error (); + } + else + { + *strptr = (char *) str; + str += strsize; + ++strsize; + } + } + else + { + *strptr = (char *) str; + str += strsize; + strsize *= 2; + } + } + } +#endif } - STRING_ADD_CHAR (str, c, char); - } while ((width <= 0 || --width > 0) && inchar () != EOF); + while ((width <= 0 || --width > 0) && inchar () != EOF); - if (!(flags & SUPPRESS)) - { - *str = '\0'; - ++done; + if (!(flags & SUPPRESS)) + { +#ifdef COMPILE_WPRINTF + /* We have to emit the code to get into the intial + state. */ + char buf[MB_LEN_MAX]; + size_t n = wcrtomb (buf, L'\0', &state); + if (n > 0 && (flags & MALLOC) + && str + n >= *strptr + strsize) + { + /* Enlarge the buffer. */ + str = (char *) realloc (*strptr, + (str + n + 1) - *strptr); + if (str == NULL) + { + /* We lose. Oh well. Terminate the string + and stop converting, so at least we don't + skip any input. */ + ((char *) (*strptr))[strsize - 1] = '\0'; + ++done; + conv_error (); + } + else + { + *strptr = (char *) str; + str = ((char *) *strptr) + strsize; + strsize = (str + n + 1) - *strptr; + } + } + + str = __mempcpy (str, buf, n); +#endif + *str = '\0'; + + if ((flags & MALLOC) && str - *strptr != strsize) + { + char *cp = (char *) realloc (*strptr, str - *strptr); + if (cp != NULL) + *strptr = cp; + } + + ++done; + } + break; } - break; + /* FALLTHROUGH */ - case 'S': - /* Wide character string. */ - wide_char_string: + case L_('S'): { - wint_t val; - int first = 1; +#ifndef COMPILE_WPRINTF + mbstate_t cstate; +#endif + + /* Wide character string. */ STRING_ARG (wstr, wchar_t); + c = inchar (); + if (c == EOF) + input_error (); + +#ifndef COMPILE_WPRINTF + memset (&cstate, '\0', sizeof (cstate)); +#endif + do { - size_t cnt = 0; - NEXT_WIDE_CHAR (first); - - if (__iswspace (val)) + if (ISSPACE (c)) { - /* XXX We would have to push back the whole wide char - with possibly many bytes. But since scanf does - not make a difference for white space characters - we can simply push back a simple <SP> which is - guaranteed to be in the [:space:] class. */ - ungetc (' ', s); + UNGETC (c, s); break; } - STRING_ADD_CHAR (wstr, val, wchar_t); - first = 0; +#ifdef COMPILE_WPRINTF + /* This is easy. */ + if (!(flags & SUPPRESS)) + { + *wstr++ = c; + if ((flags & MALLOC) + && wstr == (wchar_t *) *strptr + strsize) + { + /* Enlarge the buffer. */ + wstr = (wchar_t *) realloc (*strptr, + (2 * strsize) + * sizeof (wchar_t)); + if (wstr == NULL) + { + /* Can't allocate that much. Last-ditch + effort. */ + wstr = (wchar_t *) realloc (*strptr, + (strsize + + sizeof (wchar_t))); + if (wstr == NULL) + { + /* We lose. Oh well. Terminate the string + and stop converting, so at least we don't + skip any input. */ + ((wchar_t *) (*strptr))[strsize - 1] = L'\0'; + ++done; + conv_error (); + } + else + { + *strptr = (char *) wstr; + wstr += strsize; + ++strsize; + } + } + else + { + *strptr = (char *) wstr; + wstr += strsize; + strsize *= 2; + } + } + } +#else + { + char buf[MB_LEN_MAX]; + size_t cnt; + + buf[0] = c; + cnt = 1; + + while (1) + { + size_t n; + + n = __mbrtowc (!(flags & SUPPRESS) ? wstr : NULL, + buf, cnt, &cstate); + + if (n == (size_t) -2) + { + /* Possibly correct character, just not enough + input. */ + assert (cnt < MB_CUR_MAX); + + if (inchar () == EOF) + encode_error (); + + buf[cnt++] = c; + continue; + } + + if (n != cnt) + encode_error (); + + /* We have a match. */ + break; + } + + if (!(flags & SUPPRESS) && (flags & MALLOC) + && wstr == (wchar_t *) *strptr + strsize) + { + /* Enlarge the buffer. */ + wstr = (wchar_t *) realloc (*strptr, + (2 * strsize + * sizeof (wchar_t))); + if (wstr == NULL) + { + /* Can't allocate that much. Last-ditch effort. */ + wstr = (wchar_t *) realloc (*strptr, + ((strsize + 1) + * sizeof (wchar_t))); + if (wstr == NULL) + { + /* We lose. Oh well. Terminate the + string and stop converting, so at + least we don't skip any input. */ + ((wchar_t *) (*strptr))[strsize - 1] = L'\0'; + ++done; + conv_error (); + } + else + { + *strptr = (char *) wstr; + wstr += strsize; + ++strsize; + } + } + else + { + *strptr = (char *) wstr; + wstr += strsize; + strsize *= 2; + } + } + } +#endif } - while (width <= 0 || --width > 0); + while ((width <= 0 || --width > 0) && inchar () != EOF); if (!(flags & SUPPRESS)) { - *wstr = L'\0'; + *wstr++ = L'\0'; + + if ((flags & MALLOC) && wstr - (wchar_t *) *strptr != strsize) + { + wchar_t *cp = (wchar_t *) realloc (*strptr, + ((wstr + - (wchar_t *) *strptr) + * sizeof(wchar_t))); + if (cp != NULL) + *strptr = (char *) cp; + } + ++done; } } break; - case 'x': /* Hexadecimal integer. */ - case 'X': /* Ditto. */ + case L_('x'): /* Hexadecimal integer. */ + case L_('X'): /* Ditto. */ base = 16; number_signed = 0; goto number; - case 'o': /* Octal integer. */ + case L_('o'): /* Octal integer. */ base = 8; number_signed = 0; goto number; - case 'u': /* Unsigned decimal integer. */ + case L_('u'): /* Unsigned decimal integer. */ base = 10; number_signed = 0; goto number; - case 'd': /* Signed decimal integer. */ + case L_('d'): /* Signed decimal integer. */ base = 10; number_signed = 1; goto number; - case 'i': /* Generic number. */ + case L_('i'): /* Generic number. */ base = 0; number_signed = 1; @@ -854,7 +1147,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr) input_error (); /* Check for a sign. */ - if (c == '-' || c == '+') + if (c == L_('-') || c == L_('+')) { ADDW (c); if (width > 0) @@ -863,7 +1156,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr) } /* Look for a leading indication of base. */ - if (width != 0 && c == '0') + if (width != 0 && c == L_('0')) { if (width > 0) --width; @@ -871,7 +1164,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr) ADDW (c); c = inchar (); - if (width != 0 && _tolower (c) == 'x') + if (width != 0 && TOLOWER (c) == L_('x')) { if (base == 0) base = 16; @@ -892,8 +1185,8 @@ __vfscanf (FILE *s, const char *format, va_list argptr) /* Read the number into workspace. */ while (c != EOF && width != 0) { - if (base == 16 ? !isxdigit (c) : - ((!isdigit (c) || c - '0' >= base) && + if (base == 16 ? !ISXDIGIT (c) : + ((!ISDIGIT (c) || c - L_('0') >= base) && !((flags & GROUP) && base == 10 && c == thousands))) break; ADDW (c); @@ -904,34 +1197,34 @@ __vfscanf (FILE *s, const char *format, va_list argptr) } if (wpsize == 0 || - (wpsize == 1 && (wp[0] == '+' || wp[0] == '-'))) + (wpsize == 1 && (wp[0] == L_('+') || wp[0] == L_('-')))) { /* There was no number. If we are supposed to read a pointer we must recognize "(nil)" as well. */ if (wpsize == 0 && read_pointer && (width < 0 || width >= 0) && c == '(' - && _tolower (inchar ()) == 'n' - && _tolower (inchar ()) == 'i' - && _tolower (inchar ()) == 'l' - && inchar () == ')') + && TOLOWER (inchar ()) == L_('n') + && TOLOWER (inchar ()) == L_('i') + && TOLOWER (inchar ()) == L_('l') + && inchar () == L_(')')) /* We must produce the value of a NULL pointer. A single '0' digit is enough. */ - ADDW ('0'); + ADDW (L_('0')); else { /* The last read character is not part of the number anymore. */ - ungetc (c, s); + UNGETC (c, s); conv_error (); } } else /* The just read character is not part of the number anymore. */ - ungetc (c, s); + UNGETC (c, s); /* Convert the number. */ - ADDW ('\0'); + ADDW (L_('\0')); if (need_longlong && (flags & LONGDBL)) { if (number_signed) @@ -982,28 +1275,28 @@ __vfscanf (FILE *s, const char *format, va_list argptr) } break; - case 'e': /* Floating-point numbers. */ - case 'E': - case 'f': - case 'g': - case 'G': - case 'a': - case 'A': + case L_('e'): /* Floating-point numbers. */ + case L_('E'): + case L_('f'): + case L_('g'): + case L_('G'): + case L_('a'): + case L_('A'): c = inchar (); if (c == EOF) input_error (); /* Check for a sign. */ - if (c == '-' || c == '+') + if (c == L_('-') || c == L_('+')) { - negative = c == '-'; + negative = c == L_('-'); if (inchar () == EOF) /* EOF is only an input error before we read any chars. */ conv_error (); - if (! isdigit (c) && c != decimal) + if (! ISDIGIT (c) && c != decimal) { /* This is no valid number. */ - ungetc (c, s); + UNGETC (c, s); input_error (); } if (width > 0) @@ -1013,69 +1306,69 @@ __vfscanf (FILE *s, const char *format, va_list argptr) negative = 0; /* Take care for the special arguments "nan" and "inf". */ - if (_tolower (c) == 'n') + if (TOLOWER (c) == L_('n')) { /* Maybe "nan". */ ADDW (c); - if (inchar () == EOF || _tolower (c) != 'a') + if (inchar () == EOF || TOLOWER (c) != L_('a')) input_error (); ADDW (c); - if (inchar () == EOF || _tolower (c) != 'n') + if (inchar () == EOF || TOLOWER (c) != L_('n')) input_error (); ADDW (c); /* It is "nan". */ goto scan_float; } - else if (_tolower (c) == 'i') + else if (TOLOWER (c) == L_('i')) { /* Maybe "inf" or "infinity". */ ADDW (c); - if (inchar () == EOF || _tolower (c) != 'n') + if (inchar () == EOF || TOLOWER (c) != L_('n')) input_error (); ADDW (c); - if (inchar () == EOF || _tolower (c) != 'f') + if (inchar () == EOF || TOLOWER (c) != L_('f')) input_error (); ADDW (c); /* It is as least "inf". */ if (inchar () != EOF) { - if (_tolower (c) == 'i') + if (TOLOWER (c) == L_('i')) { /* Now we have to read the rest as well. */ ADDW (c); - if (inchar () == EOF || _tolower (c) != 'n') + if (inchar () == EOF || TOLOWER (c) != L_('n')) input_error (); ADDW (c); - if (inchar () == EOF || _tolower (c) != 'i') + if (inchar () == EOF || TOLOWER (c) != L_('i')) input_error (); ADDW (c); - if (inchar () == EOF || _tolower (c) != 't') + if (inchar () == EOF || TOLOWER (c) != L_('t')) input_error (); ADDW (c); - if (inchar () == EOF || _tolower (c) != 'y') + if (inchar () == EOF || TOLOWER (c) != L_('y')) input_error (); ADDW (c); } else /* Never mind. */ - ungetc (c, s); + UNGETC (c, s); } goto scan_float; } is_hexa = 0; - exp_char = 'e'; - if (c == '0') + exp_char = L_('e'); + if (c == L_('0')) { ADDW (c); c = inchar (); - if (_tolower (c) == 'x') + if (TOLOWER (c) == L_('x')) { /* It is a number in hexadecimal format. */ ADDW (c); is_hexa = 1; - exp_char = 'p'; + exp_char = L_('p'); /* Grouping is not allowed. */ flags &= ~GROUP; @@ -1086,14 +1379,14 @@ __vfscanf (FILE *s, const char *format, va_list argptr) got_dot = got_e = 0; do { - if (isdigit (c)) + if (ISDIGIT (c)) ADDW (c); - else if (!got_e && is_hexa && isxdigit (c)) + else if (!got_e && is_hexa && ISXDIGIT (c)) ADDW (c); else if (got_e && wp[wpsize - 1] == exp_char - && (c == '-' || c == '+')) + && (c == L_('-') || c == L_('+'))) ADDW (c); - else if (wpsize > 0 && !got_e && _tolower (c) == exp_char) + else if (wpsize > 0 && !got_e && TOLOWER (c) == exp_char) { ADDW (exp_char); got_e = got_dot = 1; @@ -1109,7 +1402,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr) { /* The last read character is not part of the number anymore. */ - ungetc (c, s); + UNGETC (c, s); break; } if (width > 0) @@ -1125,7 +1418,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr) scan_float: /* Convert the number. */ - ADDW ('\0'); + ADDW (L_('\0')); if (flags & LONGDBL) { long double d = __strtold_internal (wp, &tw, flags & GROUP); @@ -1152,22 +1445,13 @@ __vfscanf (FILE *s, const char *format, va_list argptr) ++done; break; - case '[': /* Character class. */ + case L_('['): /* Character class. */ if (flags & LONG) - { - STRING_ARG (wstr, wchar_t); - c = '\0'; /* This is to keep gcc quiet. */ - } + STRING_ARG (wstr, wchar_t); else - { - STRING_ARG (str, char); + STRING_ARG (str, char); - c = inchar (); - if (c == EOF) - input_error (); - } - - if (*f == '^') + if (*f == L_('^')) { ++f; not_in = 1; @@ -1175,6 +1459,29 @@ __vfscanf (FILE *s, const char *format, va_list argptr) else not_in = 0; + if (width < 0) + /* There is no width given so there is also no limit on the + number of characters we read. Therefore we set width to + a very high value to make the algorithm easier. */ + width = INT_MAX; + +#ifdef COMPILE_WPRINTF + /* Find the beginning and the end of the scanlist. We are not + creating a lookup table since it would have to be too large. + Instead we search each time through the string. This is not + a constant lookup time but who uses this feature deserves to + be punished. */ + tw = (wchar_t *) f; /* Marks the beginning. */ + + if (*f == ']' || *f == '-') + ++f; + + while ((fc = *f++) != L'\0' && fc != L']'); + + if (fc == L'\0') + conv_error (); + wp = (wchar_t *) f - 1; +#else /* Fill WP with byte flags indexed by character. We will use this flag map for matching input characters. */ if (wpmax < UCHAR_MAX) @@ -1182,7 +1489,7 @@ __vfscanf (FILE *s, const char *format, va_list argptr) wpmax = UCHAR_MAX; wp = (char *) alloca (wpmax); } - memset (wp, 0, UCHAR_MAX); + memset (wp, '\0', UCHAR_MAX); fc = *f; if (fc == ']' || fc == '-') @@ -1194,85 +1501,433 @@ __vfscanf (FILE *s, const char *format, va_list argptr) ++f; } + tw = (char *) f; while ((fc = *f++) != '\0' && fc != ']') - { - if (fc == '-' && *f != '\0' && *f != ']' && - (unsigned char) f[-2] <= (unsigned char) *f) - { - /* Add all characters from the one before the '-' - up to (but not including) the next format char. */ - for (fc = f[-2]; fc < *f; ++fc) - wp[fc] = 1; - } - else - /* Add the character to the flag map. */ - wp[fc] = 1; - } + if (fc == '-' && *f != '\0' && *f != ']' && f - 2 != tw + && (unsigned char) f[-2] <= (unsigned char) *f) + { + /* Add all characters from the one before the '-' + up to (but not including) the next format char. */ + for (fc = f[-2]; fc < *f; ++fc) + wp[fc] = 1; + } + else + /* Add the character to the flag map. */ + wp[fc] = 1; + if (fc == '\0') - { - if (!(flags & LONG)) - ungetc (c, s); - conv_error(); - } + conv_error(); +#endif if (flags & LONG) { - wint_t val; - int first = 1; + size_t now = read_in; +#ifdef COMPILE_WPRINTF + do + { + wchar_t *runp; + + if (inchar () == WEOF) + break; + + /* Test whether it's in the scanlist. */ + runp = tw; + while (runp < wp) + { + if (runp[0] == L'-' && runp[1] != '\0' && runp[1] != ']' + && runp != tw + && (unsigned int) runp[-1] <= (unsigned int) runp[1]) + { + /* Match against all characters in between the + first and last character of the sequence. */ + wchar_t wc; + + for (wc = runp[-1] + 1; wc < runp[1]; ++wc) + if (wc == c) + break; + + if (wc == runp[1] && !not_in) + break; + if (wc == runp[1] && not_in) + { + /* The current character is not in the + scanset. */ + ungetwc (c, s); + goto out; + } + } + else + { + if (*runp == runp[1] && !not_in) + break; + if (*runp != runp[1] && not_in) + { + ungetwc (c ,s); + goto out; + } + } + + ++runp; + } + + if (!(flags & SUPPRESS)) + { + *wstr++ = c; + + if ((flags & MALLOC) + && wstr == (wchar_t *) *strptr + strsize) + { + /* Enlarge the buffer. */ + wstr = (wchar_t *) realloc (*strptr, + (2 * strsize) + * sizeof (wchar_t)); + if (wstr == NULL) + { + /* Can't allocate that much. Last-ditch + effort. */ + wstr = (wchar_t *) + realloc (*strptr, (strsize + + sizeof (wchar_t))); + if (wstr == NULL) + { + /* We lose. Oh well. Terminate the string + and stop converting, so at least we don't + skip any input. */ + ((wchar_t *) (*strptr))[strsize - 1] = L'\0'; + ++done; + conv_error (); + } + else + { + *strptr = (char *) wstr; + wstr += strsize; + ++strsize; + } + } + else + { + *strptr = (char *) wstr; + wstr += strsize; + strsize *= 2; + } + } + } + } + while (--width > 0); + out: +#else + char buf[MB_LEN_MAX]; + size_t cnt = 0; + mbstate_t cstate; + + memset (&cstate, '\0', sizeof (cstate)); do { - size_t cnt = 0; - NEXT_WIDE_CHAR (first); - if (val <= 255 && wp[val] == not_in) + again: + if (inchar () == EOF) + break; + + if (wp[c] == not_in) { - ungetc (val, s); + ungetc (c, s); break; } - STRING_ADD_CHAR (wstr, val, wchar_t); - if (width > 0) - --width; - first = 0; + + /* This is easy. */ + if (!(flags & SUPPRESS)) + { + size_t n; + + /* Convert it into a wide character. */ + n = __mbrtowc (wstr, buf, cnt, &cstate); + + if (n == (size_t) -2) + { + /* Possibly correct character, just not enough + input. */ + assert (cnt < MB_CUR_MAX); + goto again; + } + + if (n != cnt) + encode_error (); + + ++wstr; + if ((flags & MALLOC) + && wstr == (wchar_t *) *strptr + strsize) + { + /* Enlarge the buffer. */ + wstr = (wchar_t *) realloc (*strptr, + (2 * strsize + * sizeof (wchar_t))); + if (wstr == NULL) + { + /* Can't allocate that much. Last-ditch + effort. */ + wstr = (wchar_t *) + realloc (*strptr, ((strsize + 1) + * sizeof (wchar_t))); + if (wstr == NULL) + { + /* We lose. Oh well. Terminate the + string and stop converting, + so at least we don't skip any input. */ + ((wchar_t *) (*strptr))[strsize - 1] = L'\0'; + ++done; + conv_error (); + } + else + { + *strptr = (char *) wstr; + wstr += strsize; + ++strsize; + } + } + else + { + *strptr = (char *) wstr; + wstr += strsize; + strsize *= 2; + } + } + } } - while (width != 0); + while (--width > 0); + + if (cnt != 0) + /* We stopped in the middle of recognizing another + character. That's a problem. */ + encode_error (); +#endif - if (first) + if (now == read_in) + /* We haven't succesfully read any character. */ conv_error (); if (!(flags & SUPPRESS)) { - *wstr = L'\0'; + *wstr++ = L'\0'; + + if ((flags & MALLOC) + && wstr - (wchar_t *) *strptr != strsize) + { + wchar_t *cp = (wchar_t *) + realloc (*strptr, ((wstr - (wchar_t *) *strptr) + * sizeof(wchar_t))); + if (cp != NULL) + *strptr = (char *) cp; + } + ++done; } } else { - num.ul = read_in - 1; /* -1 because we already read one char. */ + size_t now = read_in; +#ifdef COMPILE_WPRINTF + + memset (&state, '\0', sizeof (state)); + + do + { + wchar_t *runp; + size_t n; + + if (inchar () == WEOF) + break; + + /* Test whether it's in the scanlist. */ + runp = tw; + while (runp < wp) + { + if (runp[0] == L'-' && runp[1] != '\0' && runp[1] != ']' + && runp != tw + && (unsigned int) runp[-1] <= (unsigned int) runp[1]) + { + /* Match against all characters in between the + first and last character of the sequence. */ + wchar_t wc; + + for (wc = runp[-1] + 1; wc < runp[1]; ++wc) + if (wc == c) + break; + + if (wc == runp[1] && !not_in) + break; + if (wc == runp[1] && not_in) + { + /* The current character is not in the + scanset. */ + ungetwc (c, s); + goto out2; + } + } + else + { + if (*runp == runp[1] && !not_in) + break; + if (*runp != runp[1] && not_in) + { + ungetwc (c ,s); + goto out2; + } + } + + ++runp; + } + + if (!(flags & SUPPRESS)) + { + if ((flags & MALLOC) + && str + MB_CUR_MAX >= *strptr + strsize) + { + /* Enlarge the buffer. */ + str = (char *) realloc (*strptr, 2 * strsize); + if (str == NULL) + { + /* Can't allocate that much. Last-ditch + effort. */ + str = (char *) realloc (*strptr, strsize + 1); + if (str == NULL) + { + /* We lose. Oh well. Terminate the string + and stop converting, so at least we don't + skip any input. */ + (*strptr)[strsize - 1] = '\0'; + ++done; + conv_error (); + } + else + { + *strptr = str; + str += strsize; + ++strsize; + } + } + else + { + *strptr = str; + str += strsize; + strsize *= 2; + } + } + } + + n = wcrtomb (!(flags & SUPPRESS) ? str : NULL, c, &state); + if (n == (size_t) -1) + encode_error (); + + assert (n <= MB_CUR_MAX); + str += n; + } + while (--width > 0); + out2: +#else do { + if (inchar () == EOF) + break; + if (wp[c] == not_in) { ungetc (c, s); break; } - STRING_ADD_CHAR (str, c, char); - if (width > 0) - --width; + + /* This is easy. */ + if (!(flags & SUPPRESS)) + { + *str++ = c; + if ((flags & MALLOC) + && (char *) str == *strptr + strsize) + { + /* Enlarge the buffer. */ + str = (char *) realloc (*strptr, 2 * strsize); + if (str == NULL) + { + /* Can't allocate that much. Last-ditch + effort. */ + str = (char *) realloc (*strptr, strsize + 1); + if (str == NULL) + { + /* We lose. Oh well. Terminate the + string and stop converting, + so at least we don't skip any input. */ + ((char *) (*strptr))[strsize - 1] = '\0'; + ++done; + conv_error (); + } + else + { + *strptr = (char *) str; + str += strsize; + ++strsize; + } + } + else + { + *strptr = (char *) str; + str += strsize; + strsize *= 2; + } + } + } } - while (width != 0 && inchar () != EOF); + while (--width > 0); +#endif - if (read_in == num.ul) + if (now == read_in) + /* We haven't succesfully read any character. */ conv_error (); if (!(flags & SUPPRESS)) { +#ifdef COMPILE_WPRINTF + /* We have to emit the code to get into the intial + state. */ + char buf[MB_LEN_MAX]; + size_t n = wcrtomb (buf, L'\0', &state); + if (n > 0 && (flags & MALLOC) + && str + n >= *strptr + strsize) + { + /* Enlarge the buffer. */ + str = (char *) realloc (*strptr, + (str + n + 1) - *strptr); + if (str == NULL) + { + /* We lose. Oh well. Terminate the string + and stop converting, so at least we don't + skip any input. */ + ((char *) (*strptr))[strsize - 1] = '\0'; + ++done; + conv_error (); + } + else + { + *strptr = (char *) str; + str = ((char *) *strptr) + strsize; + strsize = (str + n + 1) - *strptr; + } + } + + str = __mempcpy (str, buf, n); +#endif *str = '\0'; + + if ((flags & MALLOC) && str - *strptr != strsize) + { + char *cp = (char *) realloc (*strptr, str - *strptr); + if (cp != NULL) + *strptr = cp; + } + ++done; } } break; - case 'p': /* Generic pointer. */ + case L_('p'): /* Generic pointer. */ base = 16; /* A PTR must be the same size as a `long int'. */ flags &= ~(SHORT|LONGDBL); @@ -1305,11 +1960,23 @@ __vfscanf (FILE *s, const char *format, va_list argptr) } #ifdef USE_IN_LIBIO +# ifdef COMPILE_WPRINTF +int +__vfwscanf (FILE *s, const wchar_t *format, va_list argptr) +{ + return _IO_vfwscanf (s, format, argptr, NULL); +} +# else int __vfscanf (FILE *s, const char *format, va_list argptr) { return _IO_vfscanf (s, format, argptr, NULL); } +# endif #endif +#ifdef COMPILE_WPRINTF +weak_alias (__vfwscanf, vfwscanf) +#else weak_alias (__vfscanf, vfscanf) +#endif diff --git a/stdio-common/vfwprintf.c b/stdio-common/vfwprintf.c new file mode 100644 index 0000000000..2c3cd06fad --- /dev/null +++ b/stdio-common/vfwprintf.c @@ -0,0 +1,3 @@ +#include <wctype.h> +#define COMPILE_WPRINTF 1 +#include "vfprintf.c" diff --git a/stdio-common/vfwscanf.c b/stdio-common/vfwscanf.c new file mode 100644 index 0000000000..62220bdccc --- /dev/null +++ b/stdio-common/vfwscanf.c @@ -0,0 +1,2 @@ +#define COMPILE_WPRINTF 1 +#include "vfscanf.c" |