Split up unpack_sha1_file() some more

Make a separate helper for parsing the header of an object file
(really carefully) and for unpacking the rest. This means that
anybody who uses the "unpack_sha1_header()" interface can easily
look at the header and decide to unpack the rest too, without
doing any extra work.
diff --git a/cache.h b/cache.h
index 69b63ba..aa74bcc 100644
--- a/cache.h
+++ b/cache.h
@@ -152,6 +152,7 @@
 /* Read and unpack a sha1 file into memory, write memory to a sha1 file */
 extern void * map_sha1_file(const unsigned char *sha1, unsigned long *size);
 extern int unpack_sha1_header(z_stream *stream, void *map, unsigned long mapsize, void *buffer, unsigned long size);
+extern int parse_sha1_header(char *hdr, char *type, unsigned long *sizep);
 extern void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned long *size);
 extern void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size);
 extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
diff --git a/sha1_file.c b/sha1_file.c
index bc3d70f..af39e88 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -320,33 +320,87 @@
 	return inflate(stream, 0);
 }
 
-void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned long *size)
+void *unpack_sha1_rest(z_stream *stream, void *buffer, unsigned long size)
 {
-	int ret, bytes;
-	z_stream stream;
-	char buffer[8192];
-	unsigned char *buf;
+	int bytes = strlen(buffer) + 1;
+	char *buf = xmalloc(1+size);
 
-	ret = unpack_sha1_header(&stream, map, mapsize, buffer, sizeof(buffer));
-	if (ret < Z_OK || sscanf(buffer, "%10s %lu", type, size) != 2)
-		return NULL;
-
-	bytes = strlen(buffer) + 1;
-	buf = xmalloc(1+*size);
-
-	memcpy(buf, buffer + bytes, stream.total_out - bytes);
-	bytes = stream.total_out - bytes;
-	if (bytes < *size && ret == Z_OK) {
-		stream.next_out = buf + bytes;
-		stream.avail_out = *size - bytes;
-		while (inflate(&stream, Z_FINISH) == Z_OK)
+	memcpy(buf, buffer + bytes, stream->total_out - bytes);
+	bytes = stream->total_out - bytes;
+	if (bytes < size) {
+		stream->next_out = buf + bytes;
+		stream->avail_out = size - bytes;
+		while (inflate(stream, Z_FINISH) == Z_OK)
 			/* nothing */;
 	}
-	buf[*size] = 0;
-	inflateEnd(&stream);
+	buf[size] = 0;
+	inflateEnd(stream);
 	return buf;
 }
 
+/*
+ * We used to just use "sscanf()", but that's actually way
+ * too permissive for what we want to check. So do an anal
+ * object header parse by hand.
+ */
+int parse_sha1_header(char *hdr, char *type, unsigned long *sizep)
+{
+	int i;
+	unsigned long size;
+
+	/*
+	 * The type can be at most ten bytes (including the 
+	 * terminating '\0' that we add), and is followed by
+	 * a space. 
+	 */
+	i = 10;
+	for (;;) {
+		char c = *hdr++;
+		if (c == ' ')
+			break;
+		if (!--i)
+			return -1;
+		*type++ = c;
+	}
+	*type = 0;
+
+	/*
+	 * The length must follow immediately, and be in canonical
+	 * decimal format (ie "010" is not valid).
+	 */
+	size = *hdr++ - '0';
+	if (size > 9)
+		return -1;
+	if (size) {
+		for (;;) {
+			unsigned long c = *hdr - '0';
+			if (c > 9)
+				break;
+			hdr++;
+			size = size * 10 + c;
+		}
+	}
+	*sizep = size;
+
+	/*
+	 * The length must be followed by a zero byte
+	 */
+	return *hdr ? -1 : 0;
+}
+
+void * unpack_sha1_file(void *map, unsigned long mapsize, char *type, unsigned long *size)
+{
+	int ret;
+	z_stream stream;
+	char hdr[8192];
+
+	ret = unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr));
+	if (ret < Z_OK || parse_sha1_header(hdr, type, size) < 0)
+		return NULL;
+
+	return unpack_sha1_rest(&stream, hdr, *size);
+}
+
 void * read_sha1_file(const unsigned char *sha1, char *type, unsigned long *size)
 {
 	unsigned long mapsize;