aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2015-10-15 16:37:48 +0200
committerFlorian Weimer <fweimer@redhat.com>2015-10-15 17:18:51 +0200
commit95e83974812f5c6de0483690ef47787965bb817a (patch)
treec86080b681b554ae4466370a385b4fe23237e8fd
parentb994fd793799590f70ceb9a96f135bc2390bb4f3 (diff)
downloadglibc-95e83974812f5c6de0483690ef47787965bb817a.zip
glibc-95e83974812f5c6de0483690ef47787965bb817a.tar.gz
glibc-95e83974812f5c6de0483690ef47787965bb817a.tar.bz2
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.
-rw-r--r--ChangeLog10
-rw-r--r--stdio-common/vfscanf.c356
2 files changed, 241 insertions, 125 deletions
diff --git a/ChangeLog b/ChangeLog
index 63701d1..b759929 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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]
diff --git a/stdio-common/vfscanf.c b/stdio-common/vfscanf.c
index 9d9ff20..1382eb5 100644
--- a/stdio-common/vfscanf.c
+++ b/stdio-common/vfscanf.c
@@ -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;