Support Windows-style ar files.

Apparently Windows' .lib files are also ar. Add tests.

Change-Id: Ie35f410268086b8fe6d4d1b491de3f30a46309dd
Reviewed-on: https://boringssl-review.googlesource.com/c/33348
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/util/ar/ar.go b/util/ar/ar.go
index ce5c65c..756caf5 100644
--- a/util/ar/ar.go
+++ b/util/ar/ar.go
@@ -102,7 +102,9 @@
 			}
 
 			filename := longFilenameTable[offset:]
-			if i := bytes.IndexByte(filename, '/'); i < 0 {
+			// Windows terminates filenames with NUL characters,
+			// while sysv/GNU uses /.
+			if i := bytes.IndexAny(filename, "/\x00"); i < 0 {
 				return nil, errors.New("ar: unterminated filename in table")
 			} else {
 				filename = filename[:i]
diff --git a/util/ar/ar_test.go b/util/ar/ar_test.go
index f9e23ff..ef37d79 100644
--- a/util/ar/ar_test.go
+++ b/util/ar/ar_test.go
@@ -28,7 +28,7 @@
 type arTest struct {
 	name string
 	in   string
-	out  []string
+	out  map[string]string
 	// allowPadding is true if the contents may have trailing newlines at end.
 	// On macOS, ar calls ranlib which pads all inputs up to eight bytes with
 	// newlines. Unlike ar's native padding up to two bytes, this padding is
@@ -48,8 +48,33 @@
 }
 
 var arTests = []arTest{
-	{"linux", "libsample.a", []string{"foo.c.o", "bar.cc.o"}, false},
-	{"mac", "libsample.a", []string{"foo.c.o", "bar.cc.o"}, true},
+	{
+		"linux",
+		"libsample.a",
+		map[string]string{
+			"foo.c.o": "foo.c.o",
+			"bar.cc.o": "bar.cc.o",
+		},
+		false,
+	},
+	{
+		"mac",
+		"libsample.a",
+		map[string]string{
+			"foo.c.o": "foo.c.o",
+			"bar.cc.o": "bar.cc.o",
+		},
+		true,
+	},
+	{
+		"windows",
+		"sample.lib",
+		map[string]string{
+			"CMakeFiles\\sample.dir\\foo.c.obj": "foo.c.obj",
+			"CMakeFiles\\sample.dir\\bar.cc.obj": "bar.cc.obj",
+		},
+		false,
+	},
 }
 
 func TestAR(t *testing.T) {
@@ -66,12 +91,10 @@
 				t.Fatalf("reading input failed: %s", err)
 			}
 
-			expectedFiles := make(map[string]struct{})
-			for _, file := range test.out {
-				expectedFiles[file] = struct{}{}
-				expected, err := ioutil.ReadFile(test.Path(file))
+			for file, contentsPath := range test.out {
+				expected, err := ioutil.ReadFile(test.Path(contentsPath))
 				if err != nil {
-					t.Fatalf("error reading %s: %s", file, err)
+					t.Fatalf("error reading %s: %s", contentsPath, err)
 				}
 				got, ok := ret[file]
 				if test.allowPadding {
@@ -86,7 +109,7 @@
 			}
 
 			for file, _ := range ret {
-				if _, ok := expectedFiles[file]; !ok {
+				if _, ok := test.out[file]; !ok {
 					t.Errorf("output contained unexpected file %q", file)
 				}
 			}
diff --git a/util/ar/testdata/windows/bar.cc.obj b/util/ar/testdata/windows/bar.cc.obj
new file mode 100644
index 0000000..4a315cd
--- /dev/null
+++ b/util/ar/testdata/windows/bar.cc.obj
Binary files differ
diff --git a/util/ar/testdata/windows/foo.c.obj b/util/ar/testdata/windows/foo.c.obj
new file mode 100644
index 0000000..9b4aad7
--- /dev/null
+++ b/util/ar/testdata/windows/foo.c.obj
Binary files differ
diff --git a/util/ar/testdata/windows/sample.lib b/util/ar/testdata/windows/sample.lib
new file mode 100644
index 0000000..efeebb2
--- /dev/null
+++ b/util/ar/testdata/windows/sample.lib
Binary files differ