vfscanf: Use struct scratch_buffer instead of extend_alloca

A custom character buffer is added in this commit, in the form of
struct char_buffer.  The char_buffer_add function replaces the
ADDW macro (which has grown with each successive security fix).
The char_buffer_add slow path is moved out-of-line, reducing
code size.

	* stdio-common/vfscanf.c (MEMCPY): Remove macro.
	(struct char_buffer): New type.
	(char_buffer_start, char_buffer_size, char_buffer_error)
	(char_buffer_rewind, char_buffer_add): New functions.
	(ADDW): Remove macro, replaced by the char_buffer_add function.
	(_IO_vfscanf_internal): Rewrite using struct char_buffer instead
	of extend_alloca.  Make control flow more explicit.
This commit is contained in:
Florian Weimer 2015-10-15 16:37:48 +02:00
parent b994fd7937
commit 95e8397481
2 changed files with 241 additions and 125 deletions

View File

@ -1,3 +1,13 @@
2015-10-15 Florian Weimer <fweimer@redhat.com>
* stdio-common/vfscanf.c (MEMCPY): Remove macro.
(struct char_buffer): New type.
(char_buffer_start, char_buffer_size, char_buffer_error)
(char_buffer_rewind, char_buffer_add): New functions.
(ADDW): Remove macro, replaced by the char_buffer_add function.
(_IO_vfscanf_internal): Rewrite using struct char_buffer instead
of extend_alloca. Make control flow more explicit.
2015-10-15 H.J. Lu <hongjiu.lu@intel.com>
[BZ #19137]

View File

@ -29,6 +29,7 @@
#include <wctype.h>
#include <libc-lock.h>
#include <locale/localeinfo.h>
#include <scratch_buffer.h>
#ifdef __GNUC__
# define HAVE_LONGLONG
@ -87,7 +88,6 @@
? ++read_in \
: (size_t) (inchar_errno = errno)), 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)
@ -118,7 +118,6 @@
(void) (c != EOF \
? ++read_in \
: (size_t) (inchar_errno = errno)), c))
# define MEMCPY(d, s, n) memcpy (d, s, n)
# define ISSPACE(Ch) __isspace_l (Ch, loc)
# define ISDIGIT(Ch) __isdigit_l (Ch, loc)
# define ISXDIGIT(Ch) __isxdigit_l (Ch, loc)
@ -192,6 +191,78 @@ struct ptrs_to_free
char **ptrs[32];
};
struct char_buffer {
CHAR_T *current;
CHAR_T *end;
struct scratch_buffer scratch;
};
/* Returns a pointer to the first CHAR_T object in the buffer. Only
valid if char_buffer_add (BUFFER, CH) has been called and
char_buffer_error (BUFFER) is false. */
static inline CHAR_T *
char_buffer_start (const struct char_buffer *buffer)
{
return (CHAR_T *) buffer->scratch.data;
}
/* Returns the number of CHAR_T objects in the buffer. Only valid if
char_buffer_error (BUFFER) is false. */
static inline size_t
char_buffer_size (const struct char_buffer *buffer)
{
return buffer->current - char_buffer_start (buffer);
}
/* Reinitializes BUFFER->current and BUFFER->end to cover the entire
scratch buffer. */
static inline void
char_buffer_rewind (struct char_buffer *buffer)
{
buffer->current = char_buffer_start (buffer);
buffer->end = buffer->current + buffer->scratch.length / sizeof (CHAR_T);
}
/* Returns true if a previous call to char_buffer_add (BUFFER, CH)
failed. */
static inline bool
char_buffer_error (const struct char_buffer *buffer)
{
return __glibc_unlikely (buffer->current == NULL);
}
/* Slow path for char_buffer_add. */
static void
char_buffer_add_slow (struct char_buffer *buffer, CHAR_T ch)
{
if (char_buffer_error (buffer))
return;
size_t offset = buffer->end - (CHAR_T *) buffer->scratch.data;
if (!scratch_buffer_grow_preserve (&buffer->scratch))
{
buffer->current = NULL;
buffer->end = NULL;
return;
}
char_buffer_rewind (buffer);
buffer->current += offset;
*buffer->current++ = ch;
}
/* Adds CH to BUFFER. This function does not report any errors, check
for them with char_buffer_error. */
static inline void
char_buffer_add (struct char_buffer *buffer, CHAR_T ch)
__attribute__ ((always_inline));
static inline void
char_buffer_add (struct char_buffer *buffer, CHAR_T ch)
{
if (__glibc_unlikely (buffer->current == buffer->end))
char_buffer_add_slow (buffer, ch);
else
*buffer->current++ = ch;
}
/* Read formatted input from S according to the format string
FORMAT, using the argument list in ARG.
Return the number of assignments made, or -1 for an input error. */
@ -262,46 +333,8 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
int skip_space = 0;
/* 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. */
bool use_malloc = false;
#define ADDW(Ch) \
do \
{ \
if (__glibc_unlikely (wpsize == wpmax)) \
{ \
CHAR_T *old = wp; \
bool fits = __glibc_likely (wpmax <= SIZE_MAX / sizeof (CHAR_T) / 2); \
size_t wpneed = MAX (UCHAR_MAX + 1, 2 * wpmax); \
size_t newsize = fits ? wpneed * sizeof (CHAR_T) : SIZE_MAX; \
if (!__libc_use_alloca (newsize)) \
{ \
wp = realloc (use_malloc ? wp : NULL, newsize); \
if (wp == NULL) \
{ \
if (use_malloc) \
free (old); \
done = EOF; \
goto errout; \
} \
if (! use_malloc) \
MEMCPY (wp, old, wpsize); \
wpmax = wpneed; \
use_malloc = true; \
} \
else \
{ \
size_t s = wpmax * sizeof (CHAR_T); \
wp = (CHAR_T *) extend_alloca (wp, s, newsize); \
wpmax = s / sizeof (CHAR_T); \
if (old != NULL) \
MEMCPY (wp, old, wpsize); \
} \
} \
wp[wpsize++] = (Ch); \
} \
while (0)
struct char_buffer charbuf;
scratch_buffer_init (&charbuf.scratch);
#ifdef __va_copy
__va_copy (arg, argptr);
@ -449,7 +482,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
argpos = 0;
/* Prepare temporary buffer. */
wpsize = 0;
char_buffer_rewind (&charbuf);
/* Check for a positional parameter specification. */
if (ISDIGIT ((UCHAR_T) *f))
@ -1374,7 +1407,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
/* Check for a sign. */
if (c == L_('-') || c == L_('+'))
{
ADDW (c);
char_buffer_add (&charbuf, c);
if (width > 0)
--width;
c = inchar ();
@ -1386,7 +1419,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
if (width > 0)
--width;
ADDW (c);
char_buffer_add (&charbuf, c);
c = inchar ();
if (width != 0 && TOLOWER (c) == L_('x'))
@ -1641,7 +1674,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
while ((unsigned char) *cmpp == c && avail >= 0)
{
ADDW (c);
char_buffer_add (&charbuf, c);
if (*++cmpp == '\0')
break;
else
@ -1652,12 +1685,19 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
}
}
if (char_buffer_error (&charbuf))
{
__set_errno (ENOMEM);
done = EOF;
goto errout;
}
if (*cmpp != '\0')
{
/* We are pushing all read characters back. */
if (cmpp > thousands)
{
wpsize -= cmpp - thousands;
charbuf.current -= cmpp - thousands;
ungetc (c, s);
while (--cmpp > thousands)
ungetc_not_eof ((unsigned char) *cmpp, s);
@ -1670,14 +1710,14 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
width = avail;
/* The last thousands character will be added back by
the ADDW below. */
--wpsize;
the char_buffer_add below. */
--charbuf.current;
#endif
}
else
break;
ADDW (c);
char_buffer_add (&charbuf, c);
if (width > 0)
--width;
@ -1707,7 +1747,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
while ((unsigned char) *cmpp == c && avail >= 0)
{
ADDW (c);
char_buffer_add (&charbuf, c);
if (*++cmpp == '\0')
break;
else
@ -1718,12 +1758,19 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
}
}
if (char_buffer_error (&charbuf))
{
__set_errno (ENOMEM);
done = EOF;
goto errout;
}
if (*cmpp != '\0')
{
/* We are pushing all read characters back. */
if (cmpp > thousands)
{
wpsize -= cmpp - thousands;
charbuf.current -= cmpp - thousands;
ungetc (c, s);
while (--cmpp > thousands)
ungetc_not_eof ((unsigned char) *cmpp, s);
@ -1736,26 +1783,35 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
width = avail;
/* The last thousands character will be added back by
the ADDW below. */
--wpsize;
the char_buffer_add below. */
--charbuf.current;
#endif
}
else
break;
}
ADDW (c);
char_buffer_add (&charbuf, c);
if (width > 0)
--width;
c = inchar ();
}
if (wpsize == 0
|| (wpsize == 1 && (wp[0] == L_('+') || wp[0] == L_('-'))))
if (char_buffer_error (&charbuf))
{
__set_errno (ENOMEM);
done = EOF;
goto errout;
}
if (char_buffer_size (&charbuf) == 0
|| (char_buffer_size (&charbuf) == 1
&& (char_buffer_start (&charbuf)[0] == L_('+')
|| char_buffer_start (&charbuf)[0] == L_('-'))))
{
/* There was no number. If we are supposed to read a pointer
we must recognize "(nil)" as well. */
if (__builtin_expect (wpsize == 0
if (__builtin_expect (char_buffer_size (&charbuf) == 0
&& (flags & READ_POINTER)
&& (width < 0 || width >= 5)
&& c == '('
@ -1765,7 +1821,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
&& inchar () == L_(')'), 1))
/* We must produce the value of a NULL pointer. A single
'0' digit is enough. */
ADDW (L_('0'));
char_buffer_add (&charbuf, L_('0'));
else
{
/* The last read character is not part of the number
@ -1780,22 +1836,32 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
ungetc (c, s);
/* Convert the number. */
ADDW (L_('\0'));
char_buffer_add (&charbuf, L_('\0'));
if (char_buffer_error (&charbuf))
{
__set_errno (ENOMEM);
done = EOF;
goto errout;
}
if (need_longlong && (flags & LONGDBL))
{
if (flags & NUMBER_SIGNED)
num.q = __strtoll_internal (wp, &tw, base, flags & GROUP);
num.q = __strtoll_internal
(char_buffer_start (&charbuf), &tw, base, flags & GROUP);
else
num.uq = __strtoull_internal (wp, &tw, base, flags & GROUP);
num.uq = __strtoull_internal
(char_buffer_start (&charbuf), &tw, base, flags & GROUP);
}
else
{
if (flags & NUMBER_SIGNED)
num.l = __strtol_internal (wp, &tw, base, flags & GROUP);
num.l = __strtol_internal
(char_buffer_start (&charbuf), &tw, base, flags & GROUP);
else
num.ul = __strtoul_internal (wp, &tw, base, flags & GROUP);
num.ul = __strtoul_internal
(char_buffer_start (&charbuf), &tw, base, flags & GROUP);
}
if (__glibc_unlikely (wp == tw))
if (__glibc_unlikely (char_buffer_start (&charbuf) == tw))
conv_error ();
if (!(flags & SUPPRESS))
@ -1864,42 +1930,42 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
if (TOLOWER (c) == L_('n'))
{
/* Maybe "nan". */
ADDW (c);
char_buffer_add (&charbuf, c);
if (__builtin_expect (width == 0
|| inchar () == EOF
|| TOLOWER (c) != L_('a'), 0))
conv_error ();
if (width > 0)
--width;
ADDW (c);
char_buffer_add (&charbuf, c);
if (__builtin_expect (width == 0
|| inchar () == EOF
|| TOLOWER (c) != L_('n'), 0))
conv_error ();
if (width > 0)
--width;
ADDW (c);
char_buffer_add (&charbuf, c);
/* It is "nan". */
goto scan_float;
}
else if (TOLOWER (c) == L_('i'))
{
/* Maybe "inf" or "infinity". */
ADDW (c);
char_buffer_add (&charbuf, c);
if (__builtin_expect (width == 0
|| inchar () == EOF
|| TOLOWER (c) != L_('n'), 0))
conv_error ();
if (width > 0)
--width;
ADDW (c);
char_buffer_add (&charbuf, c);
if (__builtin_expect (width == 0
|| inchar () == EOF
|| TOLOWER (c) != L_('f'), 0))
conv_error ();
if (width > 0)
--width;
ADDW (c);
char_buffer_add (&charbuf, c);
/* It is as least "inf". */
if (width != 0 && inchar () != EOF)
{
@ -1908,35 +1974,35 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
if (width > 0)
--width;
/* Now we have to read the rest as well. */
ADDW (c);
char_buffer_add (&charbuf, c);
if (__builtin_expect (width == 0
|| inchar () == EOF
|| TOLOWER (c) != L_('n'), 0))
conv_error ();
if (width > 0)
--width;
ADDW (c);
char_buffer_add (&charbuf, c);
if (__builtin_expect (width == 0
|| inchar () == EOF
|| TOLOWER (c) != L_('i'), 0))
conv_error ();
if (width > 0)
--width;
ADDW (c);
char_buffer_add (&charbuf, c);
if (__builtin_expect (width == 0
|| inchar () == EOF
|| TOLOWER (c) != L_('t'), 0))
conv_error ();
if (width > 0)
--width;
ADDW (c);
char_buffer_add (&charbuf, c);
if (__builtin_expect (width == 0
|| inchar () == EOF
|| TOLOWER (c) != L_('y'), 0))
conv_error ();
if (width > 0)
--width;
ADDW (c);
char_buffer_add (&charbuf, c);
}
else
/* Never mind. */
@ -1948,14 +2014,14 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
exp_char = L_('e');
if (width != 0 && c == L_('0'))
{
ADDW (c);
char_buffer_add (&charbuf, c);
c = inchar ();
if (width > 0)
--width;
if (width != 0 && TOLOWER (c) == L_('x'))
{
/* It is a number in hexadecimal format. */
ADDW (c);
char_buffer_add (&charbuf, c);
flags |= HEXA_FLOAT;
exp_char = L_('p');
@ -1972,23 +2038,29 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
while (1)
{
if (char_buffer_error (&charbuf))
{
__set_errno (ENOMEM);
done = EOF;
goto errout;
}
if (ISDIGIT (c))
{
ADDW (c);
char_buffer_add (&charbuf, c);
got_digit = 1;
}
else if (!got_e && (flags & HEXA_FLOAT) && ISXDIGIT (c))
{
ADDW (c);
char_buffer_add (&charbuf, c);
got_digit = 1;
}
else if (got_e && wp[wpsize - 1] == exp_char
else if (got_e && charbuf.current[-1] == exp_char
&& (c == L_('-') || c == L_('+')))
ADDW (c);
char_buffer_add (&charbuf, c);
else if (got_digit && !got_e
&& (CHAR_T) TOLOWER (c) == exp_char)
{
ADDW (exp_char);
char_buffer_add (&charbuf, exp_char);
got_e = got_dot = 1;
}
else
@ -1996,11 +2068,11 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
#ifdef COMPILE_WSCANF
if (! got_dot && c == decimal)
{
ADDW (c);
char_buffer_add (&charbuf, c);
got_dot = 1;
}
else if ((flags & GROUP) != 0 && ! got_dot && c == thousands)
ADDW (c);
char_buffer_add (&charbuf, c);
else
{
/* The last read character is not part of the number
@ -2029,7 +2101,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
{
/* Add all the characters. */
for (cmpp = decimal; *cmpp != '\0'; ++cmpp)
ADDW ((unsigned char) *cmpp);
char_buffer_add (&charbuf, (unsigned char) *cmpp);
if (width > 0)
width = avail;
got_dot = 1;
@ -2066,7 +2138,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
{
/* Add all the characters. */
for (cmpp = thousands; *cmpp != '\0'; ++cmpp)
ADDW ((unsigned char) *cmpp);
char_buffer_add (&charbuf, (unsigned char) *cmpp);
if (width > 0)
width = avail;
}
@ -2088,13 +2160,20 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
--width;
}
if (char_buffer_error (&charbuf))
{
__set_errno (ENOMEM);
done = EOF;
goto errout;
}
wctrans_t map;
if (__builtin_expect ((flags & I18N) != 0, 0)
/* Hexadecimal floats make no sense, fixing localized
digits with ASCII letters. */
&& !(flags & HEXA_FLOAT)
/* Minimum requirement. */
&& (wpsize == 0 || got_dot)
&& (char_buffer_size (&charbuf) == 0 || got_dot)
&& (map = __wctrans ("to_inpunct")) != NULL)
{
/* Reget the first character. */
@ -2113,20 +2192,23 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
for localized FP numbers, then we may have localized
digits. Note, we test GOT_DOT above. */
#ifdef COMPILE_WSCANF
if (wpsize == 0 || (wpsize == 1 && wcdigits[11] == decimal))
if (char_buffer_size (&charbuf) == 0
|| (char_buffer_size (&charbuf) == 1
&& wcdigits[11] == decimal))
#else
char mbdigits[12][MB_LEN_MAX + 1];
mbstate_t state;
memset (&state, '\0', sizeof (state));
bool match_so_far = wpsize == 0;
bool match_so_far = char_buffer_size (&charbuf) == 0;
size_t mblen = __wcrtomb (mbdigits[11], wcdigits[11], &state);
if (mblen != (size_t) -1)
{
mbdigits[11][mblen] = '\0';
match_so_far |= (wpsize == strlen (decimal)
&& strcmp (decimal, mbdigits[11]) == 0);
match_so_far |=
(char_buffer_size (&charbuf) == strlen (decimal)
&& strcmp (decimal, mbdigits[11]) == 0);
}
else
{
@ -2135,7 +2217,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
from a file. */
if (decimal_len <= MB_LEN_MAX)
{
match_so_far |= wpsize == decimal_len;
match_so_far |= char_buffer_size (&charbuf) == decimal_len;
memcpy (mbdigits[11], decimal, decimal_len + 1);
}
else
@ -2190,13 +2272,19 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
conversion is done correctly. */
while (1)
{
if (got_e && wp[wpsize - 1] == exp_char
if (char_buffer_error (&charbuf))
{
__set_errno (ENOMEM);
done = EOF;
goto errout;
}
if (got_e && charbuf.current[-1] == exp_char
&& (c == L_('-') || c == L_('+')))
ADDW (c);
else if (wpsize > 0 && !got_e
char_buffer_add (&charbuf, c);
else if (char_buffer_size (&charbuf) > 0 && !got_e
&& (CHAR_T) TOLOWER (c) == exp_char)
{
ADDW (exp_char);
char_buffer_add (&charbuf, exp_char);
got_e = got_dot = 1;
}
else
@ -2210,15 +2298,15 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
if (c == wcdigits[n])
{
if (n < 10)
ADDW (L_('0') + n);
char_buffer_add (&charbuf, L_('0') + n);
else if (n == 11 && !got_dot)
{
ADDW (decimal);
char_buffer_add (&charbuf, decimal);
got_dot = 1;
}
else if (n == 10 && have_locthousands
&& ! got_dot)
ADDW (thousands);
char_buffer_add (&charbuf, thousands);
else
/* The last read character is not part
of the number anymore. */
@ -2245,13 +2333,14 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
width = avail;
if (n < 10)
ADDW (L_('0') + n);
char_buffer_add (&charbuf, L_('0') + n);
else if (n == 11 && !got_dot)
{
/* Add all the characters. */
for (cmpp = decimal; *cmpp != '\0';
++cmpp)
ADDW ((unsigned char) *cmpp);
char_buffer_add (&charbuf,
(unsigned char) *cmpp);
got_dot = 1;
}
@ -2261,7 +2350,8 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
/* Add all the characters. */
for (cmpp = thousands; *cmpp != '\0';
++cmpp)
ADDW ((unsigned char) *cmpp);
char_buffer_add (&charbuf,
(unsigned char) *cmpp);
}
else
/* The last read character is not part
@ -2305,36 +2395,53 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
#endif
}
if (char_buffer_error (&charbuf))
{
__set_errno (ENOMEM);
done = EOF;
goto errout;
}
/* Have we read any character? If we try to read a number
in hexadecimal notation and we have read only the `0x'
prefix this is an error. */
if (__builtin_expect (wpsize == 0
|| ((flags & HEXA_FLOAT) && wpsize == 2), 0))
if (__glibc_unlikely (char_buffer_size (&charbuf) == 0
|| ((flags & HEXA_FLOAT)
&& char_buffer_size (&charbuf) == 2)))
conv_error ();
scan_float:
/* Convert the number. */
ADDW (L_('\0'));
char_buffer_add (&charbuf, L_('\0'));
if (char_buffer_error (&charbuf))
{
__set_errno (ENOMEM);
done = EOF;
goto errout;
}
if ((flags & LONGDBL) && !__ldbl_is_dbl)
{
long double d = __strtold_internal (wp, &tw, flags & GROUP);
if (!(flags & SUPPRESS) && tw != wp)
long double d = __strtold_internal
(char_buffer_start (&charbuf), &tw, flags & GROUP);
if (!(flags & SUPPRESS) && tw != char_buffer_start (&charbuf))
*ARG (long double *) = negative ? -d : d;
}
else if (flags & (LONG | LONGDBL))
{
double d = __strtod_internal (wp, &tw, flags & GROUP);
if (!(flags & SUPPRESS) && tw != wp)
double d = __strtod_internal
(char_buffer_start (&charbuf), &tw, flags & GROUP);
if (!(flags & SUPPRESS) && tw != char_buffer_start (&charbuf))
*ARG (double *) = negative ? -d : d;
}
else
{
float d = __strtof_internal (wp, &tw, flags & GROUP);
if (!(flags & SUPPRESS) && tw != wp)
float d = __strtof_internal
(char_buffer_start (&charbuf), &tw, flags & GROUP);
if (!(flags & SUPPRESS) && tw != char_buffer_start (&charbuf))
*ARG (float *) = negative ? -d : d;
}
if (__glibc_unlikely (tw == wp))
if (__glibc_unlikely (tw == char_buffer_start (&charbuf)))
conv_error ();
if (!(flags & SUPPRESS))
@ -2380,12 +2487,13 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
#else
/* Fill WP with byte flags indexed by character.
We will use this flag map for matching input characters. */
if (wpmax < UCHAR_MAX + 1)
if (!scratch_buffer_set_array_size
(&charbuf.scratch, UCHAR_MAX + 1, 1))
{
wpmax = UCHAR_MAX + 1;
wp = (char *) alloca (wpmax);
done = EOF;
goto errout;
}
memset (wp, '\0', UCHAR_MAX + 1);
memset (charbuf.scratch.data, '\0', UCHAR_MAX + 1);
fc = *f;
if (fc == ']' || fc == '-')
@ -2393,7 +2501,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
/* If ] or - appears before any char in the set, it is not
the terminator or separator, but the first char in the
set. */
wp[fc] = 1;
((char *)charbuf.scratch.data)[fc] = 1;
++f;
}
@ -2404,11 +2512,11 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
/* Add all characters from the one before the '-'
up to (but not including) the next format char. */
for (fc = (unsigned char) f[-2]; fc < (unsigned char) *f; ++fc)
wp[fc] = 1;
((char *)charbuf.scratch.data)[fc] = 1;
}
else
/* Add the character to the flag map. */
wp[fc] = 1;
((char *)charbuf.scratch.data)[fc] = 1;
if (__glibc_unlikely (fc == '\0'))
conv_error();
@ -2537,7 +2645,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
do
{
if (wp[c] == not_in)
if (((char *) charbuf.scratch.data)[c] == not_in)
{
ungetc_not_eof (c, s);
break;
@ -2765,7 +2873,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
#else
do
{
if (wp[c] == not_in)
if (((char *) charbuf.scratch.data)[c] == not_in)
{
ungetc_not_eof (c, s);
break;
@ -2905,9 +3013,7 @@ _IO_vfscanf_internal (_IO_FILE *s, const char *format, _IO_va_list argptr,
/* Unlock stream. */
UNLOCK_STREAM (s);
if (use_malloc)
free (wp);
scratch_buffer_free (&charbuf.scratch);
if (errp != NULL)
*errp |= errval;