summaryrefslogtreecommitdiff
path: root/libc/stdio
diff options
context:
space:
mode:
Diffstat (limited to 'libc/stdio')
-rw-r--r--libc/stdio/_scanf.c68
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) || \