Add sscanf %[ tests and fix the bug that fell out.

Strictly, POSIX says "If a '-' is in the scanlist and is not the first
wide character, nor the second where the first wide character is a '^',
nor the last wide character, the behavior is implementation-defined",
but it seems unreasonable for swscanf to interpret `a-c` differently
from sscanf. Make ours behave the same as each other by making swscanf
work the same as sscanf.

Bug: http://b/68672236
Test: ran tests
Change-Id: Ia84805897628d7128e901b468e02504373730e61
diff --git a/tests/stdio_test.cpp b/tests/stdio_test.cpp
index 05a19d1..f0e0ab6 100644
--- a/tests/stdio_test.cpp
+++ b/tests/stdio_test.cpp
@@ -923,33 +923,99 @@
 TEST(STDIO_TEST, sscanf_swscanf) {
   struct stuff {
     char s1[123];
-    int i1;
+    int i1, i2;
+    char cs1[3];
+    char s2[3];
+    char c1;
     double d1;
     float f1;
-    char s2[123];
+    char s3[123];
 
     void Check() {
-      ASSERT_STREQ("hello", s1);
-      ASSERT_EQ(123, i1);
-      ASSERT_DOUBLE_EQ(1.23, d1);
-      ASSERT_FLOAT_EQ(9.0f, f1);
-      ASSERT_STREQ("world", s2);
+      EXPECT_STREQ("hello", s1);
+      EXPECT_EQ(123, i1);
+      EXPECT_EQ(456, i2);
+      EXPECT_EQ('a', cs1[0]);
+      EXPECT_EQ('b', cs1[1]);
+      EXPECT_EQ('x', cs1[2]); // No terminating NUL.
+      EXPECT_STREQ("AB", s2); // Terminating NUL.
+      EXPECT_EQ('!', c1);
+      EXPECT_DOUBLE_EQ(1.23, d1);
+      EXPECT_FLOAT_EQ(9.0f, f1);
+      EXPECT_STREQ("world", s3);
     }
   } s;
 
-  memset(&s, 0, sizeof(s));
-  ASSERT_EQ(5, sscanf("  hello 123 1.23 0x1.2p3 world",
-                      "%s %i %lf %f %s",
-                      s.s1, &s.i1, &s.d1, &s.f1, s.s2));
+  memset(&s, 'x', sizeof(s));
+  ASSERT_EQ(9, sscanf("  hello 123 456abAB! 1.23 0x1.2p3 world",
+                      "%s %i%i%2c%[A-Z]%c %lf %f %s",
+                      s.s1, &s.i1, &s.i2, s.cs1, s.s2, &s.c1, &s.d1, &s.f1, s.s3));
   s.Check();
 
-  memset(&s, 0, sizeof(s));
-  ASSERT_EQ(5, swscanf(L"  hello 123 1.23 0x1.2p3 world",
-                       L"%s %i %lf %f %s",
-                       s.s1, &s.i1, &s.d1, &s.f1, s.s2));
+  memset(&s, 'x', sizeof(s));
+  ASSERT_EQ(9, swscanf(L"  hello 123 456abAB! 1.23 0x1.2p3 world",
+                       L"%s %i%i%2c%[A-Z]%c %lf %f %s",
+                       s.s1, &s.i1, &s.i2, s.cs1, s.s2, &s.c1, &s.d1, &s.f1, s.s3));
   s.Check();
 }
 
+template <typename T>
+static void CheckScanf(int sscanf_fn(const T*, const T*, ...),
+                       const T* input, const T* fmt,
+                       int expected_count, const char* expected_string) {
+  char buf[256] = {};
+  ASSERT_EQ(expected_count, sscanf_fn(input, fmt, &buf)) << fmt;
+  ASSERT_STREQ(expected_string, buf) << fmt;
+}
+
+TEST(STDIO_TEST, sscanf_ccl) {
+  // `abc` is just those characters.
+  CheckScanf(sscanf, "abcd", "%[abc]", 1, "abc");
+  // `a-c` is the range 'a' .. 'c'.
+  CheckScanf(sscanf, "abcd", "%[a-c]", 1, "abc");
+  CheckScanf(sscanf, "-d", "%[a-c]", 0, "");
+  CheckScanf(sscanf, "ac-bAd", "%[a--c]", 1, "ac-bA");
+  // `a-c-e` is equivalent to `a-e`.
+  CheckScanf(sscanf, "abcdefg", "%[a-c-e]", 1, "abcde");
+  // `e-a` is equivalent to `ae-` (because 'e' > 'a').
+  CheckScanf(sscanf, "-a-e-b", "%[e-a]", 1, "-a-e-");
+  // An initial '^' negates the set.
+  CheckScanf(sscanf, "abcde", "%[^d]", 1, "abc");
+  CheckScanf(sscanf, "abcdefgh", "%[^c-d]", 1, "ab");
+  CheckScanf(sscanf, "hgfedcba", "%[^c-d]", 1, "hgfe");
+  // The first character may be ']' or '-' without being special.
+  CheckScanf(sscanf, "[[]]x", "%[][]", 1, "[[]]");
+  CheckScanf(sscanf, "-a-x", "%[-a]", 1, "-a-");
+  // The last character may be '-' without being special.
+  CheckScanf(sscanf, "-a-x", "%[a-]", 1, "-a-");
+  // X--Y is [X--] + Y, not [X--] + [--Y] (a bug in my initial implementation).
+  CheckScanf(sscanf, "+,-/.", "%[+--/]", 1, "+,-/");
+}
+
+TEST(STDIO_TEST, swscanf_ccl) {
+  // `abc` is just those characters.
+  CheckScanf(swscanf, L"abcd", L"%[abc]", 1, "abc");
+  // `a-c` is the range 'a' .. 'c'.
+  CheckScanf(swscanf, L"abcd", L"%[a-c]", 1, "abc");
+  CheckScanf(swscanf, L"-d", L"%[a-c]", 0, "");
+  CheckScanf(swscanf, L"ac-bAd", L"%[a--c]", 1, "ac-bA");
+  // `a-c-e` is equivalent to `a-e`.
+  CheckScanf(swscanf, L"abcdefg", L"%[a-c-e]", 1, "abcde");
+  // `e-a` is equivalent to `ae-` (because 'e' > 'a').
+  CheckScanf(swscanf, L"-a-e-b", L"%[e-a]", 1, "-a-e-");
+  // An initial '^' negates the set.
+  CheckScanf(swscanf, L"abcde", L"%[^d]", 1, "abc");
+  CheckScanf(swscanf, L"abcdefgh", L"%[^c-d]", 1, "ab");
+  CheckScanf(swscanf, L"hgfedcba", L"%[^c-d]", 1, "hgfe");
+  // The first character may be ']' or '-' without being special.
+  CheckScanf(swscanf, L"[[]]x", L"%[][]", 1, "[[]]");
+  CheckScanf(swscanf, L"-a-x", L"%[-a]", 1, "-a-");
+  // The last character may be '-' without being special.
+  CheckScanf(swscanf, L"-a-x", L"%[a-]", 1, "-a-");
+  // X--Y is [X--] + Y, not [X--] + [--Y] (a bug in my initial implementation).
+  CheckScanf(swscanf, L"+,-/.", L"%[+--/]", 1, "+,-/");
+}
+
 TEST(STDIO_TEST, cantwrite_EBADF) {
   // If we open a file read-only...
   FILE* fp = fopen("/proc/version", "r");