| 1 | From 8cfb43de636faa401634340d1a18404844f9ba5a Mon Sep 17 00:00:00 2001 |
| 2 | From: Mike Frysinger <vapier@gentoo.org> |
| 3 | Date: Sun, 6 May 2012 03:50:44 -0400 |
| 4 | Subject: [PATCH] stdio: implement assignment-allocation "m" character |
| 5 | |
| 6 | The latest POSIX spec introduces a "m" character to allocate buffers for |
| 7 | the user when using scanf type functions. This is like the old glibc "a" |
| 8 | flag, but now standardized. With packages starting to use these, we need |
| 9 | to implement it. |
| 10 | |
| 11 | for example: |
| 12 | char *s; |
| 13 | sscanf("foo", "%ms", &s); |
| 14 | printf("%s\n", s); |
| 15 | free(s); |
| 16 | This will automatically allocate storage for "s", read in "foo" to it, |
| 17 | and then display it. |
| 18 | |
| 19 | I'm not terribly familiar with the stdio layer, so this could be wrong. |
| 20 | But it seems to work for me. |
| 21 | |
| 22 | Signed-off-by: Mike Frysinger <vapier@gentoo.org> |
| 23 | Signed-off-by: Felix Fietkau <nbd@openwrt.org> |
| 24 | --- |
| 25 | extra/Configs/Config.in | 13 ---------- |
| 26 | libc/stdio/_scanf.c | 68 ++++++++++++++++++++++++++++--------------------- |
| 27 | 2 files changed, 39 insertions(+), 42 deletions(-) |
| 28 | |
| 29 | --- a/extra/Configs/Config.in |
| 30 | +++ b/extra/Configs/Config.in |
| 31 | @@ -1597,19 +1597,6 @@ config UCLIBC_PRINTF_SCANF_POSITIONAL_AR |
| 32 | |
| 33 | Most people will answer 9. |
| 34 | |
| 35 | - |
| 36 | -config UCLIBC_HAS_SCANF_GLIBC_A_FLAG |
| 37 | - bool "Support glibc's 'a' flag for scanf string conversions (not implemented)" |
| 38 | - help |
| 39 | - NOTE!!! Currently Not Implemented!!! Just A Place Holder!! NOTE!!! |
| 40 | - NOTE!!! Conflicts with an ANSI/ISO C99 scanf flag!! NOTE!!! |
| 41 | - |
| 42 | - Answer Y to enable support for glibc's 'a' flag for the scanf string |
| 43 | - conversions '%s', '%[', '%ls', '%l[', and '%S'. This is used to |
| 44 | - auto-allocate sufficient memory to hold the data retrieved. |
| 45 | - |
| 46 | - Most people will answer N. |
| 47 | - |
| 48 | choice |
| 49 | prompt "Stdio buffer size" |
| 50 | default UCLIBC_HAS_STDIO_BUFSIZ_4096 |
| 51 | --- a/libc/stdio/_scanf.c |
| 52 | +++ b/libc/stdio/_scanf.c |
| 53 | @@ -77,14 +77,6 @@ |
| 54 | #include <bits/uClibc_fpmax.h> |
| 55 | #endif /* __UCLIBC_HAS_FLOATS__ */ |
| 56 | |
| 57 | -#ifdef __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ |
| 58 | -#ifdef L_vfscanf |
| 59 | -/* only emit this once */ |
| 60 | -#warning Forcing undef of __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ until implemented! |
| 61 | -#endif |
| 62 | -#undef __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ |
| 63 | -#endif |
| 64 | - |
| 65 | #undef __STDIO_HAS_VSSCANF |
| 66 | #if defined(__STDIO_BUFFERS) || !defined(__UCLIBC_HAS_WCHAR__) || defined(__UCLIBC_HAS_GLIBC_CUSTOM_STREAMS__) |
| 67 | #define __STDIO_HAS_VSSCANF 1 |
| 68 | @@ -433,8 +425,9 @@ libc_hidden_def(vswscanf) |
| 69 | |
| 70 | |
| 71 | /* float layout 0123456789012345678901 repeat n for "l[" */ |
| 72 | -#define SPEC_CHARS "npxXoudifFeEgGaACSncs[" |
| 73 | -/* npxXoudif eEgG CS cs[ */ |
| 74 | +#define SPEC_CHARS "npxXoudifFeEgGaACSnmcs[" |
| 75 | +/* npxXoudif eEgG CS cs[ */ |
| 76 | +/* NOTE: the 'm' flag must come before any convs that support it */ |
| 77 | |
| 78 | /* NOTE: Ordering is important! In particular, CONV_LEFTBRACKET |
| 79 | * must immediately precede CONV_c. */ |
| 80 | @@ -444,7 +437,7 @@ enum { |
| 81 | CONV_p, |
| 82 | CONV_x, CONV_X, CONV_o, CONV_u, CONV_d, CONV_i, |
| 83 | CONV_f, CONV_F, CONV_e, CONV_E, CONV_g, CONV_G, CONV_a, CONV_A, |
| 84 | - CONV_C, CONV_S, CONV_LEFTBRACKET, CONV_c, CONV_s, CONV_leftbracket, |
| 85 | + CONV_C, CONV_S, CONV_LEFTBRACKET, CONV_m, CONV_c, CONV_s, CONV_leftbracket, |
| 86 | CONV_percent, CONV_whitespace /* not in SPEC_* and no flags */ |
| 87 | }; |
| 88 | |
| 89 | @@ -474,7 +467,7 @@ enum { |
| 90 | FLAG_SURPRESS = 0x10, /* MUST BE 1ST!! See DO_FLAGS. */ |
| 91 | FLAG_THOUSANDS = 0x20, |
| 92 | FLAG_I18N = 0x40, /* only works for d, i, u */ |
| 93 | - FLAG_MALLOC = 0x80, /* only works for s, S, and [ (and l[)*/ |
| 94 | + FLAG_MALLOC = 0x80, /* only works for c, s, S, and [ (and l[)*/ |
| 95 | }; |
| 96 | |
| 97 | |
| 98 | @@ -491,7 +484,7 @@ enum { |
| 99 | /* fFeEgGaA */ (0x0c|FLAG_SURPRESS|FLAG_THOUSANDS|FLAG_I18N), \ |
| 100 | /* C */ ( 0|FLAG_SURPRESS), \ |
| 101 | /* S and l[ */ ( 0|FLAG_SURPRESS|FLAG_MALLOC), \ |
| 102 | - /* c */ (0x04|FLAG_SURPRESS), \ |
| 103 | + /* c */ (0x04|FLAG_SURPRESS|FLAG_MALLOC), \ |
| 104 | /* s and [ */ (0x04|FLAG_SURPRESS|FLAG_MALLOC), \ |
| 105 | } |
| 106 | |
| 107 | @@ -904,17 +897,17 @@ int attribute_hidden __psfs_parse_spec(r |
| 108 | if (*psfs->fmt == *p) { |
| 109 | int p_m_spec_chars = p - spec_chars; |
| 110 | |
| 111 | -#ifdef __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ |
| 112 | -#error implement gnu a flag |
| 113 | - if ((*p == 'a') |
| 114 | - && ((psfs->fmt[1] == '[') || ((psfs->fmt[1]|0x20) == 's')) |
| 115 | - ) { /* Assumes ascii for 's' and 'S' test. */ |
| 116 | - psfs->flags |= FLAG_MALLOC; |
| 117 | + if (*p == 'm' && |
| 118 | + (psfs->fmt[1] == '[' || psfs->fmt[1] == 'c' || |
| 119 | + /* Assumes ascii for 's' and 'S' test. */ |
| 120 | + (psfs->fmt[1] | 0x20) == 's')) |
| 121 | + { |
| 122 | + if (psfs->store) |
| 123 | + psfs->flags |= FLAG_MALLOC; |
| 124 | ++psfs->fmt; |
| 125 | ++p; |
| 126 | - continue; /* The related conversions follow 'a'. */ |
| 127 | + continue; /* The related conversions follow 'm'. */ |
| 128 | } |
| 129 | -#endif /* __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ */ |
| 130 | |
| 131 | for (p = spec_ranges; p_m_spec_chars > *p ; ++p) {} |
| 132 | if (((psfs->dataargtype >> 8) | psfs->flags) |
| 133 | @@ -1265,12 +1258,6 @@ int VFSCANF (FILE *__restrict fp, const |
| 134 | while (*wf && __isascii(*wf) && (b < buf + sizeof(buf) - 1)) { |
| 135 | *b++ = *wf++; |
| 136 | } |
| 137 | -#ifdef __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ |
| 138 | -#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. |
| 139 | - if ((*b == 'a') && ((*wf == '[') || ((*wf|0x20) == 's'))) { |
| 140 | - goto DONE; /* Spec was excessively long. */ |
| 141 | - } |
| 142 | -#endif /* __UCLIBC_HAS_SCANF_GLIBC_A_FLAG__ */ |
| 143 | *b = 0; |
| 144 | if (b == buf) { /* Bad conversion specifier! */ |
| 145 | goto DONE; |
| 146 | @@ -1390,13 +1377,36 @@ int VFSCANF (FILE *__restrict fp, const |
| 147 | } |
| 148 | |
| 149 | if (psfs.conv_num == CONV_s) { |
| 150 | + /* We might have to handle the allocation ourselves */ |
| 151 | + int len; |
| 152 | + /* With 'm', we actually got a pointer to a pointer */ |
| 153 | + unsigned char **ptr = (void *)b; |
| 154 | + |
| 155 | + i = 0; |
| 156 | + if (psfs.flags & FLAG_MALLOC) { |
| 157 | + len = 0; |
| 158 | + b = NULL; |
| 159 | + } else |
| 160 | + len = -1; |
| 161 | + |
| 162 | /* Yes, believe it or not, a %s conversion can store nuls. */ |
| 163 | while ((__scan_getc(&sc) >= 0) && !isspace(sc.cc)) { |
| 164 | zero_conversions = 0; |
| 165 | - *b = sc.cc; |
| 166 | - b += psfs.store; |
| 167 | + if (i == len) { |
| 168 | + /* Pick a size that won't trigger a lot of |
| 169 | + * mallocs early on ... */ |
| 170 | + len += 256; |
| 171 | + b = realloc(b, len + 1); |
| 172 | + } |
| 173 | + b[i] = sc.cc; |
| 174 | + i += psfs.store; |
| 175 | fail = 0; |
| 176 | } |
| 177 | + |
| 178 | + if (psfs.flags & FLAG_MALLOC) |
| 179 | + *ptr = b; |
| 180 | + /* The code below takes care of terminating NUL */ |
| 181 | + b += i; |
| 182 | } else { |
| 183 | #ifdef __UCLIBC_HAS_WCHAR__ |
| 184 | assert((psfs.conv_num == CONV_LEFTBRACKET) || \ |
| 185 | |