Upgrade to current upstream scanf implementation.

Also add a basic test.

Change-Id: Icc0e68a5716b9579244f6eb8bac1ab5a24eda85a
diff --git a/libc/Android.mk b/libc/Android.mk
index df9ad21..0bb521f 100644
--- a/libc/Android.mk
+++ b/libc/Android.mk
@@ -80,10 +80,7 @@
     stdio/fvwrite.c \
     stdio/snprintf.c\
     stdio/sprintf.c \
-    stdio/sscanf.c \
     stdio/vfprintf.c \
-    stdio/vfscanf.c \
-    stdio/vsscanf.c \
     stdlib/atexit.c \
     stdlib/ctype_.c \
     stdlib/getenv.c \
@@ -362,15 +359,18 @@
     upstream-openbsd/lib/libc/stdio/scanf.c \
     upstream-openbsd/lib/libc/stdio/setbuf.c \
     upstream-openbsd/lib/libc/stdio/setbuffer.c \
+    upstream-openbsd/lib/libc/stdio/sscanf.c \
     upstream-openbsd/lib/libc/stdio/stdio.c \
     upstream-openbsd/lib/libc/stdio/tempnam.c \
     upstream-openbsd/lib/libc/stdio/tmpnam.c \
     upstream-openbsd/lib/libc/stdio/ungetc.c \
     upstream-openbsd/lib/libc/stdio/vasprintf.c \
+    upstream-openbsd/lib/libc/stdio/vfscanf.c \
     upstream-openbsd/lib/libc/stdio/vprintf.c \
     upstream-openbsd/lib/libc/stdio/vscanf.c \
     upstream-openbsd/lib/libc/stdio/vsnprintf.c \
     upstream-openbsd/lib/libc/stdio/vsprintf.c \
+    upstream-openbsd/lib/libc/stdio/vsscanf.c \
     upstream-openbsd/lib/libc/stdio/wbuf.c \
     upstream-openbsd/lib/libc/stdlib/atoi.c \
     upstream-openbsd/lib/libc/stdlib/atol.c \
diff --git a/libc/stdio/sscanf.c b/libc/upstream-openbsd/lib/libc/stdio/sscanf.c
similarity index 94%
rename from libc/stdio/sscanf.c
rename to libc/upstream-openbsd/lib/libc/stdio/sscanf.c
index a0bdf1c..e371ca6 100644
--- a/libc/stdio/sscanf.c
+++ b/libc/upstream-openbsd/lib/libc/stdio/sscanf.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: sscanf.c,v 1.12 2005/08/08 08:05:36 espie Exp $ */
+/*	$OpenBSD: sscanf.c,v 1.14 2011/11/08 18:30:42 guenther Exp $ */
 /*-
  * Copyright (c) 1990, 1993
  *	The Regents of the University of California.  All rights reserved.
@@ -44,6 +44,7 @@
 	return (0);
 }
 
+/* SCANFLIKE2 */
 int
 sscanf(const char *str, const char *fmt, ...)
 {
@@ -59,7 +60,7 @@
 	f._read = eofread;
 	f._lb._base = NULL;
 	va_start(ap, fmt);
-	ret = vfscanf(&f, fmt, ap);
+	ret = __svfscanf(&f, fmt, ap);
 	va_end(ap);
 	return (ret);
 }
diff --git a/libc/stdio/vfscanf.c b/libc/upstream-openbsd/lib/libc/stdio/vfscanf.c
similarity index 81%
rename from libc/stdio/vfscanf.c
rename to libc/upstream-openbsd/lib/libc/stdio/vfscanf.c
index 78f404e..c2996a9 100644
--- a/libc/stdio/vfscanf.c
+++ b/libc/upstream-openbsd/lib/libc/stdio/vfscanf.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: vfscanf.c,v 1.21 2006/01/13 21:33:28 millert Exp $ */
+/*	$OpenBSD: vfscanf.c,v 1.30 2013/04/17 17:40:35 tedu Exp $ */
 /*-
  * Copyright (c) 1990, 1993
  *	The Regents of the University of California.  All rights reserved.
@@ -32,11 +32,13 @@
  */
 
 #include <ctype.h>
+#include <wctype.h>
 #include <inttypes.h>
 #include <stdarg.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 #include "local.h"
 
 #ifdef FLOATING_POINT
@@ -49,7 +51,7 @@
  * Flags used during conversion.
  */
 #define	LONG		0x00001	/* l: long or double */
-#define	LONGDBL		0x00002	/* L: long double; unimplemented */
+#define	LONGDBL		0x00002	/* L: long double */
 #define	SHORT		0x00004	/* h: short */
 #define	SHORTSHORT	0x00008	/* hh: 8 bit integer */
 #define LLONG		0x00010	/* ll: long long (+ deprecated q: quad) */
@@ -90,15 +92,11 @@
 
 static u_char *__sccl(char *, u_char *);
 
-#if !defined(VFSCANF)
-#define VFSCANF	vfscanf
-#endif
-
 /*
- * vfscanf
+ * Internal, unlocked version of vfscanf
  */
 int
-VFSCANF(FILE *fp, const char *fmt0, __va_list ap)
+__svfscanf(FILE *fp, const char *fmt0, __va_list ap)
 {
 	u_char *fmt = (u_char *)fmt0;
 	int c;		/* character from format, or conversion */
@@ -112,12 +110,16 @@
 	int base;		/* base argument to strtoimax/strtouimax */
 	char ccltab[256];	/* character class table for %[...] */
 	char buf[BUF];		/* buffer for numeric conversions */
+#ifdef SCANF_WIDE_CHAR
+	wchar_t *wcp;		/* handy wide character pointer */
+	size_t nconv;		/* length of multibyte sequence converted */
+	mbstate_t mbs;
+#endif
 
 	/* `basefix' is used to avoid `if' tests in the integer scanner */
 	static short basefix[17] =
 		{ 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
 
-	FLOCKFILE(fp);
 	_SET_ORIENTATION(fp, -1);
 
 	nassigned = 0;
@@ -125,10 +127,8 @@
 	base = 0;		/* XXX just to keep gcc happy */
 	for (;;) {
 		c = *fmt++;
-		if (c == 0) {
-			FUNLOCKFILE(fp);
+		if (c == 0)
 			return (nassigned);
-		}
 		if (isspace(c)) {
 			while ((fp->_r > 0 || __srefill(fp) == 0) &&
 			    isspace(*fp->_p))
@@ -162,13 +162,7 @@
 			flags |= MAXINT;
 			goto again;
 		case 'L':
-			flags |=
-				(*fmt == 'd') ? LLONG :
-				(*fmt == 'i') ? LLONG :
-				(*fmt == 'o') ? LLONG :
-				(*fmt == 'u') ? LLONG :
-				(*fmt == 'x') ? LLONG :
-				LONGDBL;
+			flags |= LONGDBL;
 			goto again;
 		case 'h':
 			if (*fmt == 'h') {
@@ -245,11 +239,10 @@
 			break;
 
 #ifdef FLOATING_POINT
-		case 'E':
-		case 'G':
-		case 'e': 
-		case 'f': 
-		case 'g':
+		case 'e': case 'E':
+		case 'f': case 'F':
+		case 'g': case 'G':
+		case 'a': case 'A':
 			c = CT_FLOAT;
 			break;
 #endif
@@ -301,7 +294,6 @@
 		 * Disgusting backwards compatibility hacks.	XXX
 		 */
 		case '\0':	/* compat */
-			FUNLOCKFILE(fp);
 			return (EOF);
 
 		default:	/* compat */
@@ -346,10 +338,52 @@
 			/* scan arbitrary characters (sets NOSKIP) */
 			if (width == 0)
 				width = 1;
+#ifdef SCANF_WIDE_CHAR
+			if (flags & LONG) {
+				if ((flags & SUPPRESS) == 0)
+					wcp = va_arg(ap, wchar_t *);
+				else
+					wcp = NULL;
+				n = 0;
+				while (width != 0) {
+					if (n == MB_CUR_MAX) {
+						fp->_flags |= __SERR;
+						goto input_failure;
+					}
+					buf[n++] = *fp->_p;
+					fp->_p++;
+					fp->_r--;
+					bzero(&mbs, sizeof(mbs));
+					nconv = mbrtowc(wcp, buf, n, &mbs);
+					if (nconv == (size_t)-1) {
+						fp->_flags |= __SERR;
+						goto input_failure;
+					}
+					if (nconv == 0 && !(flags & SUPPRESS))
+						*wcp = L'\0';
+					if (nconv != (size_t)-2) {
+						nread += n;
+						width--;
+						if (!(flags & SUPPRESS))
+							wcp++;
+						n = 0;
+					}
+					if (fp->_r <= 0 && __srefill(fp)) {
+						if (n != 0) {
+							fp->_flags |= __SERR;
+							goto input_failure;
+						}
+						break;
+					}
+				}
+				if (!(flags & SUPPRESS))
+					nassigned++;
+			} else
+#endif /* SCANF_WIDE_CHAR */
 			if (flags & SUPPRESS) {
 				size_t sum = 0;
 				for (;;) {
-					if ((n = fp->_r) < (int)width) {
+					if ((n = fp->_r) < width) {
 						sum += n;
 						width -= n;
 						fp->_p += n;
@@ -381,6 +415,72 @@
 			/* scan a (nonempty) character class (sets NOSKIP) */
 			if (width == 0)
 				width = (size_t)~0;	/* `infinity' */
+#ifdef SCANF_WIDE_CHAR
+			/* take only those things in the class */
+			if (flags & LONG) {
+				wchar_t twc;
+				int nchars;
+
+				if ((flags & SUPPRESS) == 0)
+					wcp = va_arg(ap, wchar_t *);
+				else
+					wcp = &twc;
+				n = 0;
+				nchars = 0;
+				while (width != 0) {
+					if (n == MB_CUR_MAX) {
+						fp->_flags |= __SERR;
+						goto input_failure;
+					}
+					buf[n++] = *fp->_p;
+					fp->_p++;
+					fp->_r--;
+					bzero(&mbs, sizeof(mbs));
+					nconv = mbrtowc(wcp, buf, n, &mbs);
+					if (nconv == (size_t)-1) {
+						fp->_flags |= __SERR;
+						goto input_failure;
+					}
+					if (nconv == 0)
+						*wcp = L'\0';
+					if (nconv != (size_t)-2) {
+						if (wctob(*wcp) != EOF &&
+						    !ccltab[wctob(*wcp)]) {
+							while (n != 0) {
+								n--;
+								ungetc(buf[n],
+								    fp);
+							}
+							break;
+						}
+						nread += n;
+						width--;
+						if (!(flags & SUPPRESS))
+							wcp++;
+						nchars++;
+						n = 0;
+					}
+					if (fp->_r <= 0 && __srefill(fp)) {
+						if (n != 0) {
+							fp->_flags |= __SERR;
+							goto input_failure;
+						}
+						break;
+					}
+				}
+				if (n != 0) {
+					fp->_flags |= __SERR;
+					goto input_failure;
+				}
+				n = nchars;
+				if (n == 0)
+					goto match_failure;
+				if (!(flags & SUPPRESS)) {
+					*wcp = L'\0';
+					nassigned++;
+				}
+			} else
+#endif /* SCANF_WIDE_CHAR */
 			/* take only those things in the class */
 			if (flags & SUPPRESS) {
 				n = 0;
@@ -422,6 +522,60 @@
 			/* like CCL, but zero-length string OK, & no NOSKIP */
 			if (width == 0)
 				width = (size_t)~0;
+#ifdef SCANF_WIDE_CHAR
+			if (flags & LONG) {
+				wchar_t twc;
+
+				if ((flags & SUPPRESS) == 0)
+					wcp = va_arg(ap, wchar_t *);
+				else
+					wcp = &twc;
+				n = 0;
+				while (!isspace(*fp->_p) && width != 0) {
+					if (n == MB_CUR_MAX) {
+						fp->_flags |= __SERR;
+						goto input_failure;
+					}
+					buf[n++] = *fp->_p;
+					fp->_p++;
+					fp->_r--;
+					bzero(&mbs, sizeof(mbs));
+					nconv = mbrtowc(wcp, buf, n, &mbs);
+					if (nconv == (size_t)-1) {
+						fp->_flags |= __SERR;
+						goto input_failure;
+					}
+					if (nconv == 0)
+						*wcp = L'\0';
+					if (nconv != (size_t)-2) {
+						if (iswspace(*wcp)) {
+							while (n != 0) {
+								n--;
+								ungetc(buf[n],
+								    fp);
+							}
+							break;
+						}
+						nread += n;
+						width--;
+						if (!(flags & SUPPRESS))
+							wcp++;
+						n = 0;
+					}
+					if (fp->_r <= 0 && __srefill(fp)) {
+						if (n != 0) {
+							fp->_flags |= __SERR;
+							goto input_failure;
+						}
+						break;
+					}
+				}
+				if (!(flags & SUPPRESS)) {
+					*wcp = L'\0';
+					nassigned++;
+				}
+			} else
+#endif /* SCANF_WIDE_CHAR */
 			if (flags & SUPPRESS) {
 				n = 0;
 				while (!isspace(*fp->_p)) {
@@ -681,16 +835,18 @@
 				(void) ungetc(c, fp);
 			}
 			if ((flags & SUPPRESS) == 0) {
-				double res;
-
 				*p = '\0';
-				res = strtod(buf, (char **) NULL);
-				if (flags & LONGDBL)
+				if (flags & LONGDBL) {
+					long double res = strtold(buf,
+					    (char **)NULL);
 					*va_arg(ap, long double *) = res;
-				else if (flags & LONG)
+				} else if (flags & LONG) {
+					double res = strtod(buf, (char **)NULL);
 					*va_arg(ap, double *) = res;
-				else
+				} else {
+					float res = strtof(buf, (char **)NULL);
 					*va_arg(ap, float *) = res;
+				}
 				nassigned++;
 			}
 			nread += p - buf;
@@ -702,7 +858,6 @@
 	if (nassigned == 0)
 		nassigned = -1;
 match_failure:
-	FUNLOCKFILE(fp);
 	return (nassigned);
 }
 
@@ -801,3 +956,14 @@
 	}
 	/* NOTREACHED */
 }
+
+int
+vfscanf(FILE *fp, const char *fmt0, __va_list ap)
+{
+	int r;
+
+	FLOCKFILE(fp);
+	r = __svfscanf(fp, fmt0, ap);
+	FUNLOCKFILE(fp);
+	return (r);
+}
diff --git a/libc/stdio/vsscanf.c b/libc/upstream-openbsd/lib/libc/stdio/vsscanf.c
similarity index 94%
rename from libc/stdio/vsscanf.c
rename to libc/upstream-openbsd/lib/libc/stdio/vsscanf.c
index 9c4d805..71eb752 100644
--- a/libc/stdio/vsscanf.c
+++ b/libc/upstream-openbsd/lib/libc/stdio/vsscanf.c
@@ -1,4 +1,4 @@
-/*	$OpenBSD: vsscanf.c,v 1.11 2006/01/06 18:53:04 millert Exp $ */
+/*	$OpenBSD: vsscanf.c,v 1.12 2011/11/08 18:30:42 guenther Exp $ */
 /*-
  * Copyright (c) 1990, 1993
  *	The Regents of the University of California.  All rights reserved.
@@ -39,6 +39,7 @@
 static int
 eofread(void *cookie, char *buf, int len)
 {
+
 	return (0);
 }
 
@@ -54,5 +55,5 @@
 	f._bf._size = f._r = strlen(str);
 	f._read = eofread;
 	f._lb._base = NULL;
-	return (vfscanf(&f, fmt, ap));
+	return (__svfscanf(&f, fmt, ap));
 }
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index 027de34..893e057 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -372,3 +372,14 @@
   ASSERT_EQ(EOF, putc('x', fp));
   fclose(fp);
 }
+
+TEST(stdio, sscanf) {
+  char s1[123];
+  int i1;
+  double d1;
+  char s2[123];
+  ASSERT_EQ(3, sscanf("  hello 123 1.23 ", "%s %i %lf %s", s1, &i1, &d1, s2));
+  ASSERT_STREQ("hello", s1);
+  ASSERT_EQ(123, i1);
+  ASSERT_EQ(1.23, d1);
+}