Modify sha1_loose_object_info() to support 'cat-file --literally'
by accepting flags and also make changes to copy the type to
object_info::typename.

Add parse_sha1_header_extended() which acts as a wrapper around
parse_sha1_header() allowing for more information to be obtained
based on the given flags.

Add unpack_sha1_header_literally() to unpack sha1 headers of
unknown/corrupt objects which have a unknown sha1 header size.
This was written by Junio C Hamano but tested by me.

Helped-by: Junio C Hamano <gits...@pobox.com>
Helped-by: Eric Sunshine <sunsh...@sunshineco.com>
Signed-off-by: Karthik Nayak <karthik....@gmail.com>
---
 sha1_file.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 97 insertions(+), 24 deletions(-)

diff --git a/sha1_file.c b/sha1_file.c
index 69a60ec..e31e9e2 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -1564,6 +1564,36 @@ int unpack_sha1_header(git_zstream *stream, unsigned 
char *map, unsigned long ma
        return git_inflate(stream, 0);
 }
 
+static int unpack_sha1_header_literally(git_zstream *stream, unsigned char 
*map,
+                                       unsigned long mapsize,
+                                       struct strbuf *header)
+{
+       unsigned char buffer[32], *cp;
+       unsigned long bufsiz = sizeof(buffer);
+       int status;
+
+       /* Get the data stream */
+       memset(stream, 0, sizeof(*stream));
+       stream->next_in = map;
+       stream->avail_in = mapsize;
+       stream->next_out = buffer;
+       stream->avail_out = bufsiz;
+
+       git_inflate_init(stream);
+
+       do {
+               status = git_inflate(stream, 0);
+               strbuf_add(header, buffer, stream->next_out - buffer);
+               for (cp = buffer; cp < stream->next_out; cp++)
+                       if (!*cp)
+                               /* Found the NUL at the end of the header */
+                               return 0;
+               stream->next_out = buffer;
+               stream->avail_out = bufsiz;
+       } while (status == Z_OK);
+       return -1;
+}
+
 static void *unpack_sha1_rest(git_zstream *stream, void *buffer, unsigned long 
size, const unsigned char *sha1)
 {
        int bytes = strlen(buffer) + 1;
@@ -1609,32 +1639,24 @@ static void *unpack_sha1_rest(git_zstream *stream, void 
*buffer, unsigned long s
        return NULL;
 }
 
-/*
- * 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(const char *hdr, unsigned long *sizep)
+int parse_sha1_header_extended(const char *hdr, struct object_info *oi,
+                              int flags)
 {
-       char type[10];
-       int i;
+       struct strbuf typename = STRBUF_INIT;
        unsigned long size;
+       int type;
 
        /*
         * The type can be at most ten bytes (including the
         * terminating '\0' that we add), and is followed by
         * a space.
         */
-       i = 0;
        for (;;) {
                char c = *hdr++;
                if (c == ' ')
                        break;
-               type[i++] = c;
-               if (i >= sizeof(type))
-                       return -1;
+               strbuf_addch(&typename, c);
        }
-       type[i] = 0;
 
        /*
         * The length must follow immediately, and be in canonical
@@ -1652,12 +1674,45 @@ int parse_sha1_header(const char *hdr, unsigned long 
*sizep)
                        size = size * 10 + c;
                }
        }
-       *sizep = size;
+
+       type = type_from_string_gently(typename.buf, -1, 1);
+       if (oi->sizep)
+               *oi->sizep = size;
+       if (oi->typename)
+               strbuf_addstr(oi->typename, typename.buf);
+       if (oi->typep)
+               *oi->typep = type;
+       strbuf_release(&typename);
+
+       /*
+        * Set type to 0 if its an unknown object and
+        * we're obtaining the type using '--literally'
+        * option.
+        */
+       if ((flags & LOOKUP_LITERALLY) && (type == -1))
+               type = 0;
+       else if (type == -1)
+               die("invalid object type");
 
        /*
         * The length must be followed by a zero byte
         */
-       return *hdr ? -1 : type_from_string(type);
+       return *hdr ? -1 : type;
+}
+
+/*
+ * 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. Calls the extended function.
+ */
+int parse_sha1_header(const char *hdr, unsigned long *sizep)
+{
+       struct object_info oi;
+
+       oi.sizep = sizep;
+       oi.typename = NULL;
+       oi.typep = NULL;
+       return parse_sha1_header_extended(hdr, &oi, LOOKUP_REPLACE_OBJECT);
 }
 
 static void *unpack_sha1_file(void *map, unsigned long mapsize, enum 
object_type *type, unsigned long *size, const unsigned char *sha1)
@@ -2524,13 +2579,15 @@ struct packed_git *find_sha1_pack(const unsigned char 
*sha1,
 }
 
 static int sha1_loose_object_info(const unsigned char *sha1,
-                                 struct object_info *oi)
+                                 struct object_info *oi,
+                                 int flags)
 {
-       int status;
+       int status = 0;
        unsigned long mapsize, size;
        void *map;
        git_zstream stream;
        char hdr[32];
+       struct strbuf hdrbuf = STRBUF_INIT;
 
        if (oi->delta_base_sha1)
                hashclr(oi->delta_base_sha1);
@@ -2557,17 +2614,29 @@ static int sha1_loose_object_info(const unsigned char 
*sha1,
                return -1;
        if (oi->disk_sizep)
                *oi->disk_sizep = mapsize;
-       if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) < 0)
-               status = error("unable to unpack %s header",
-                              sha1_to_hex(sha1));
-       else if ((status = parse_sha1_header(hdr, &size)) < 0)
-               status = error("unable to parse %s header", sha1_to_hex(sha1));
-       else if (oi->sizep)
+       if ((flags & LOOKUP_LITERALLY)) {
+               if (unpack_sha1_header_literally(&stream, map, mapsize, 
&hdrbuf) < 0)
+                       status = error("unable to unpack %s header with 
--literally",
+                                      sha1_to_hex(sha1));
+               else if ((status = parse_sha1_header_extended(hdrbuf.buf, oi, 
flags)) < 0)
+                       status = error("unable to parse %s header", 
sha1_to_hex(sha1));
+       } else {
+               if (unpack_sha1_header(&stream, map, mapsize, hdr, sizeof(hdr)) 
< 0)
+                       status = error("unable to unpack %s header",
+                                      sha1_to_hex(sha1));
+               else if ((status = parse_sha1_header(hdr, &size)) < 0)
+                       status = error("unable to parse %s header", 
sha1_to_hex(sha1));
+       }
+       if (oi->sizep)
                *oi->sizep = size;
        git_inflate_end(&stream);
        munmap(map, mapsize);
        if (oi->typep)
                *oi->typep = status;
+       if (oi->typename && !(oi->typename->len))
+               strbuf_addstr(oi->typename, typename(status));
+       if (hdrbuf.buf)
+               strbuf_release(&hdrbuf);
        return 0;
 }
 
@@ -2588,13 +2657,15 @@ int sha1_object_info_extended(const unsigned char 
*sha1, struct object_info *oi,
                        *(oi->disk_sizep) = 0;
                if (oi->delta_base_sha1)
                        hashclr(oi->delta_base_sha1);
+               if (oi->typename)
+                       strbuf_addstr(oi->typename, typename(co->type));
                oi->whence = OI_CACHED;
                return 0;
        }
 
        if (!find_pack_entry(real, &e)) {
                /* Most likely it's a loose object. */
-               if (!sha1_loose_object_info(real, oi)) {
+               if (!sha1_loose_object_info(real, oi, flags)) {
                        oi->whence = OI_LOOSE;
                        return 0;
                }
@@ -2618,6 +2689,8 @@ int sha1_object_info_extended(const unsigned char *sha1, 
struct object_info *oi,
                oi->u.packed.is_delta = (rtype == OBJ_REF_DELTA ||
                                         rtype == OBJ_OFS_DELTA);
        }
+       if (oi->typename)
+               strbuf_addstr(oi->typename, typename(rtype));
 
        return 0;
 }
-- 
2.3.1.307.gf3db8a5

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to