OSDN Git Service

_scanf.c: Implement 'm' modifier for 'c' and '[' conversions.
authorWill Newton <will.newton@imgtec.com>
Thu, 7 May 2015 22:15:19 +0000 (01:15 +0300)
committerBernhard Reutner-Fischer <rep.dot.nop@gmail.com>
Wed, 20 May 2015 08:59:30 +0000 (10:59 +0200)
The current code implements the 'm' modifier only for 's'
conversions and would cause a segfault if it was used for 'c'
or '[' conversions. This patch extends the code to cover these
cases too.

The original version could write scanned data outside the passed buffer
because index i used in the '[' conversion handling block was clobbered.

Signed-off-by: Will Newton <will.newton@imgtec.com>
Signed-off-by: Max Filippov <jcmvbkbc@gmail.com>
Signed-off-by: Bernhard Reutner-Fischer <rep.dot.nop@gmail.com>
libc/stdio/_scanf.c
test/stdio/scanf_m.c [new file with mode: 0644]

index 6ecb3cb..a5828c3 100644 (file)
@@ -1352,7 +1352,20 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg)
                                (psfs.conv_num >= CONV_c)
 #endif /* __UCLIBC_HAS_WCHAR__ */
                        {
+                               /* We might have to handle the allocation ourselves */
+                               int len;
+                               unsigned char **ptr;
+
                                b = (psfs.store ? ((unsigned char *) psfs.cur_ptr) : buf);
+                               /* With 'm', we actually got a pointer to a pointer */
+                               ptr = (void *)b;
+
+                               if (psfs.flags & FLAG_MALLOC) {
+                                       len = 0;
+                                       b = NULL;
+                               } else
+                                       len = -1;
+
                                fail = 1;
 
                                if (psfs.conv_num == CONV_c) {
@@ -1360,32 +1373,28 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg)
                                                sc.width = 1;
                                        }
 
+                                       if (psfs.flags & FLAG_MALLOC)
+                                               b = malloc(sc.width);
+
+                                       i = 0;
                                        while (__scan_getc(&sc) >= 0) {
                                                zero_conversions = 0;
-                                               *b = sc.cc;
-                                               b += psfs.store;
+                                               b[i] = sc.cc;
+                                               i += psfs.store;
                                        }
                                        __scan_ungetc(&sc);
                                        if (sc.width > 0) {     /* Failed to read all required. */
                                                goto DONE;
                                        }
+                                       if (psfs.flags & FLAG_MALLOC)
+                                               *ptr = b;
                                        psfs.cnt += psfs.store;
                                        goto NEXT_FMT;
                                }
 
                                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;
@@ -1400,10 +1409,6 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg)
                                                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) || \
@@ -1454,13 +1459,20 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg)
 #endif /* __UCLIBC_HAS_WCHAR__ */
 
 
+                                       i = 0;
                                        while (__scan_getc(&sc) >= 0) {
                                                zero_conversions = 0;
                                                if (!scanset[sc.cc]) {
                                                        break;
                                                }
-                                               *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;
                                        }
                                }
@@ -1470,6 +1482,9 @@ int VFSCANF (FILE *__restrict fp, const Wchar *__restrict format, va_list arg)
                                if (fail) {     /* nothing stored! */
                                        goto DONE;
                                }
+                               if (psfs.flags & FLAG_MALLOC)
+                                       *ptr = b;
+                               b += i;
                                *b = 0;         /* Nul-terminate string. */
                                psfs.cnt += psfs.store;
                                goto NEXT_FMT;
diff --git a/test/stdio/scanf_m.c b/test/stdio/scanf_m.c
new file mode 100644 (file)
index 0000000..0ce78b6
--- /dev/null
@@ -0,0 +1,24 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+int main(void)
+{
+       const char *buf = "hello world";
+       char *ps = NULL, *pc = NULL;
+       char s[6], c;
+
+       /* Check that %[...]/%c work. */
+       sscanf(buf, "%[a-z] %c", s, &c);
+       /* Check that %m[...]/%mc work. */
+       sscanf(buf, "%m[a-z] %mc", &ps, &pc);
+
+       if (strcmp(ps, "hello") != 0 || *pc != 'w' ||
+           strcmp(s, "hello") != 0 || c != 'w')
+               return 1;
+
+       free(ps);
+       free(pc);
+
+       return 0;
+}