diff options
Diffstat (limited to 'libc')
-rw-r--r-- | libc/stdio/_scanf.c | 68 |
1 files changed, 39 insertions, 29 deletions
diff --git a/libc/stdio/_scanf.c b/libc/stdio/_scanf.c index f38e72b5c..952853cfe 100644 --- a/libc/stdio/_scanf.c +++ b/libc/stdio/_scanf.c @@ -77,14 +77,6 @@ #include <bits/uClibc_fpmax.h> #endif /* __UCLIBC_HAS_FLOATS__ */ -#ifdef __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ -#ifdef L_vfscanf -/* only emit this once */ -#warning Forcing undef of __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ until implemented! -#endif -#undef __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ -#endif - #undef __STDIO_HAS_VSSCANF #if defined(__STDIO_BUFFERS) || !defined(__UCLIBC_HAS_WCHAR__) || defined(__UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__) #define __STDIO_HAS_VSSCANF 1 @@ -433,8 +425,9 @@ libc_hidden_def(vswscanf) /* float layout 0123456789012345678901 repeat n for "l[" */ -#define SPEC_CHARS "npxXoudifFeEgGaACSncs[" -/* npxXoudif eEgG CS cs[ */ +#define SPEC_CHARS "npxXoudifFeEgGaACSnmcs[" +/* npxXoudif eEgG CS cs[ */ +/* NOTE: the 'm' flag must come before any convs that support it */ /* NOTE: Ordering is important! In particular, CONV_LEFTBRACKET * must immediately precede CONV_c. */ @@ -444,7 +437,7 @@ enum { CONV_p, CONV_x, CONV_X, CONV_o, CONV_u, CONV_d, CONV_i, CONV_f, CONV_F, CONV_e, CONV_E, CONV_g, CONV_G, CONV_a, CONV_A, - CONV_C, CONV_S, CONV_LEFTBRACKET, CONV_c, CONV_s, CONV_leftbracket, + CONV_C, CONV_S, CONV_LEFTBRACKET, CONV_m, CONV_c, CONV_s, CONV_leftbracket, CONV_percent, CONV_whitespace /* not in SPEC_* and no flags */ }; @@ -474,7 +467,7 @@ enum { FLAG_SURPRESS = 0x10, /* MUST BE 1ST!! See DO_FLAGS. */ FLAG_THOUSANDS = 0x20, FLAG_I18N = 0x40, /* only works for d, i, u */ - FLAG_MALLOC = 0x80, /* only works for s, S, and [ (and l[)*/ + FLAG_MALLOC = 0x80, /* only works for c, s, S, and [ (and l[)*/ }; @@ -491,7 +484,7 @@ enum { /* fFeEgGaA */ (0x0c|FLAG_SURPRESS|FLAG_THOUSANDS|FLAG_I18N), \ /* C */ ( 0|FLAG_SURPRESS), \ /* S and l[ */ ( 0|FLAG_SURPRESS|FLAG_MALLOC), \ - /* c */ (0x04|FLAG_SURPRESS), \ + /* c */ (0x04|FLAG_SURPRESS|FLAG_MALLOC), \ /* s and [ */ (0x04|FLAG_SURPRESS|FLAG_MALLOC), \ } @@ -904,17 +897,17 @@ int attribute_hidden __psfs_parse_spec(register psfs_t *psfs) if (*psfs->fmt == *p) { int p_m_spec_chars = p - spec_chars; -#ifdef __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ -#error implement gnu a flag - if ((*p == 'a') - && ((psfs->fmt[1] == '[') || ((psfs->fmt[1]|0x20) == 's')) - ) { /* Assumes ascii for 's' and 'S' test. */ - psfs->flags |= FLAG_MALLOC; + if (*p == 'm' && + (psfs->fmt[1] == '[' || psfs->fmt[1] == 'c' || + /* Assumes ascii for 's' and 'S' test. */ + (psfs->fmt[1] | 0x20) == 's')) + { + if (psfs->store) + psfs->flags |= FLAG_MALLOC; ++psfs->fmt; ++p; - continue; /* The related conversions follow 'a'. */ + continue; /* The related conversions follow 'm'. */ } -#endif /* __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ */ for (p = spec_ranges; p_m_spec_chars > *p ; ++p) {} if (((psfs->dataargtype >> 8) | psfs->flags) @@ -1265,12 +1258,6 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg) while (*wf && __isascii(*wf) && (b < buf + sizeof(buf) - 1)) { *b++ = *wf++; } -#ifdef __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ -#error this is wrong... we need to ched in __psfs_parse_spec instead since this checks last char in buffer and conversion my have stopped before it. - if ((*b == 'a') && ((*wf == '[') || ((*wf|0x20) == 's'))) { - goto DONE; /* Spec was excessively long. */ - } -#endif /* __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ */ *b = 0; if (b == buf) { /* Bad conversion specifier! */ goto DONE; @@ -1390,13 +1377,36 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg) } if (psfs.conv_num == CONV_s) { + /* We might have to handle the allocation ourselves */ + int len; + /* With 'm', we actually got a pointer to a pointer */ + unsigned char **ptr = (void *)b; + + i = 0; + if (psfs.flags & FLAG_MALLOC) { + len = 0; + b = NULL; + } else + len = -1; + /* Yes, believe it or not, a %s conversion can store nuls. */ while ((__scan_getc(&sc) >= 0) && !isspace(sc.cc)) { zero_conversions = 0; - *b = sc.cc; - b += psfs.store; + if (i == len) { + /* Pick a size that won't trigger a lot of + * mallocs early on ... */ + len += 256; + b = realloc(b, len + 1); + } + b[i] = sc.cc; + i += psfs.store; fail = 0; } + + if (psfs.flags & FLAG_MALLOC) + *ptr = b; + /* The code below takes care of terminating NUL */ + b += i; } else { #ifdef __UCLIBC_HAS_WCHAR__ assert((psfs.conv_num == CONV_LEFTBRACKET) || \ |