{"thread":{"id":"4259","subject":"[PATCH 3/8] Builtin git-tar-tree.","startedAt":"2006-05-23T08:31:15Z","lastAt":"2006-05-23T08:31:15Z","messageCount":1,"participants":["Peter Eriksen"],"isPatch":true,"patchVersion":1,"patchTotal":8},"messages":[{"id":"20526","messageId":"26427.9620176696$1148373114@news.gmane.org","threadId":"4259","inReplyTo":"11483730803527-git-send-email-","subject":"[PATCH 3/8] Builtin git-tar-tree.","fromName":"Peter Eriksen","fromEmail":"s022018@student.dtu.dk","sentAt":"2006-05-23T08:31:15Z","receivedAt":"2006-05-23T08:31:15Z","isPatch":true,"sender":{"key":"s022018@student.dtu.dk","avatar":null},"body":"From: Peter Eriksen <s022018@student.dtu.dk>\n\nSigned-off-by: Peter Eriksen <s022018@student.dtu.dk>\n\n\n---\n\n9860ed2d4a598ad100c3b4f6b07dd0a88a4547a6\n Makefile           |    8 +\n builtin-tar-tree.c |  351 ++++++++++++++++++++++++++++++++++++++++++++++++++++\n builtin.h          |    1 \n git.c              |    3 \n tar-tree.c         |  350 ----------------------------------------------------\n 5 files changed, 359 insertions(+), 354 deletions(-)\n create mode 100644 builtin-tar-tree.c\n delete mode 100644 tar-tree.c\n\n9860ed2d4a598ad100c3b4f6b07dd0a88a4547a6\ndiff --git a/Makefile b/Makefile\nindex 9b02264..966f7ee 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -161,7 +161,7 @@ PROGRAMS = \\\n \tgit-receive-pack$X git-rev-parse$X \\\n \tgit-send-pack$X git-show-branch$X git-shell$X \\\n \tgit-show-index$X git-ssh-fetch$X \\\n-\tgit-ssh-upload$X git-tar-tree$X git-unpack-file$X \\\n+\tgit-ssh-upload$X git-unpack-file$X \\\n \tgit-unpack-objects$X git-update-index$X git-update-server-info$X \\\n \tgit-upload-pack$X git-verify-pack$X git-write-tree$X \\\n \tgit-update-ref$X git-symbolic-ref$X \\\n@@ -171,7 +171,8 @@ PROGRAMS = \\\n BUILT_INS = git-log$X git-whatchanged$X git-show$X \\\n \tgit-count-objects$X git-diff$X git-push$X \\\n \tgit-grep$X git-rev-list$X git-check-ref-format$X \\\n-\tgit-init-db$X git-ls-files$X git-ls-tree$X\n+\tgit-init-db$X git-ls-files$X git-ls-tree$X \\\n+\tgit-tar-tree$X\n \n # what 'all' will build and 'install' will install, in gitexecdir\n ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS)\n@@ -220,7 +221,8 @@ LIB_OBJS = \\\n BUILTIN_OBJS = \\\n \tbuiltin-log.o builtin-help.o builtin-count.o builtin-diff.o builtin-push.o \\\n \tbuiltin-grep.o builtin-rev-list.o builtin-check-ref-format.o \\\n-\tbuiltin-init-db.o builtin-ls-files.o builtin-ls-tree.o\n+\tbuiltin-init-db.o builtin-ls-files.o builtin-ls-tree.o \\\n+        builtin-tar-tree.o\n \n GITLIBS = $(LIB_FILE) $(XDIFF_LIB)\n LIBS = $(GITLIBS) -lz\ndiff --git a/builtin-tar-tree.c b/builtin-tar-tree.c\nnew file mode 100644\nindex 0000000..6ada04c\n--- /dev/null\n+++ b/builtin-tar-tree.c\n@@ -0,0 +1,351 @@\n+/*\n+ * Copyright (c) 2005, 2006 Rene Scharfe\n+ */\n+#include <time.h>\n+#include \"cache.h\"\n+#include \"tree-walk.h\"\n+#include \"commit.h\"\n+#include \"strbuf.h\"\n+#include \"tar.h\"\n+#include \"builtin.h\"\n+\n+#define RECORDSIZE\t(512)\n+#define BLOCKSIZE\t(RECORDSIZE * 20)\n+\n+static const char tar_tree_usage[] = \"git-tar-tree <key> [basedir]\";\n+\n+static char block[BLOCKSIZE];\n+static unsigned long offset;\n+\n+static time_t archive_time;\n+\n+/* tries hard to write, either succeeds or dies in the attempt */\n+static void reliable_write(void *buf, unsigned long size)\n+{\n+\twhile (size > 0) {\n+\t\tlong ret = xwrite(1, buf, size);\n+\t\tif (ret < 0) {\n+\t\t\tif (errno == EPIPE)\n+\t\t\t\texit(0);\n+\t\t\tdie(\"git-tar-tree: %s\", strerror(errno));\n+\t\t} else if (!ret) {\n+\t\t\tdie(\"git-tar-tree: disk full?\");\n+\t\t}\n+\t\tsize -= ret;\n+\t\tbuf += ret;\n+\t}\n+}\n+\n+/* writes out the whole block, but only if it is full */\n+static void write_if_needed(void)\n+{\n+\tif (offset == BLOCKSIZE) {\n+\t\treliable_write(block, BLOCKSIZE);\n+\t\toffset = 0;\n+\t}\n+}\n+\n+/* acquire the next record from the buffer; user must call write_if_needed() */\n+static char *get_record(void)\n+{\n+\tchar *p = block + offset;\n+\tmemset(p, 0, RECORDSIZE);\n+\toffset += RECORDSIZE;\n+\treturn p;\n+}\n+\n+/*\n+ * The end of tar archives is marked by 1024 nul bytes and after that\n+ * follows the rest of the block (if any).\n+ */\n+static void write_trailer(void)\n+{\n+\tget_record();\n+\twrite_if_needed();\n+\tget_record();\n+\twrite_if_needed();\n+\twhile (offset) {\n+\t\tget_record();\n+\t\twrite_if_needed();\n+\t}\n+}\n+\n+/*\n+ * queues up writes, so that all our write(2) calls write exactly one\n+ * full block; pads writes to RECORDSIZE\n+ */\n+static void write_blocked(void *buf, unsigned long size)\n+{\n+\tunsigned long tail;\n+\n+\tif (offset) {\n+\t\tunsigned long chunk = BLOCKSIZE - offset;\n+\t\tif (size < chunk)\n+\t\t\tchunk = size;\n+\t\tmemcpy(block + offset, buf, chunk);\n+\t\tsize -= chunk;\n+\t\toffset += chunk;\n+\t\tbuf += chunk;\n+\t\twrite_if_needed();\n+\t}\n+\twhile (size >= BLOCKSIZE) {\n+\t\treliable_write(buf, BLOCKSIZE);\n+\t\tsize -= BLOCKSIZE;\n+\t\tbuf += BLOCKSIZE;\n+\t}\n+\tif (size) {\n+\t\tmemcpy(block + offset, buf, size);\n+\t\toffset += size;\n+\t}\n+\ttail = offset % RECORDSIZE;\n+\tif (tail)  {\n+\t\tmemset(block + offset, 0, RECORDSIZE - tail);\n+\t\toffset += RECORDSIZE - tail;\n+\t}\n+\twrite_if_needed();\n+}\n+\n+static void strbuf_append_string(struct strbuf *sb, const char *s)\n+{\n+\tint slen = strlen(s);\n+\tint total = sb->len + slen;\n+\tif (total > sb->alloc) {\n+\t\tsb->buf = xrealloc(sb->buf, total);\n+\t\tsb->alloc = total;\n+\t}\n+\tmemcpy(sb->buf + sb->len, s, slen);\n+\tsb->len = total;\n+}\n+\n+/*\n+ * pax extended header records have the format \"%u %s=%s\\n\".  %u contains\n+ * the size of the whole string (including the %u), the first %s is the\n+ * keyword, the second one is the value.  This function constructs such a\n+ * string and appends it to a struct strbuf.\n+ */\n+static void strbuf_append_ext_header(struct strbuf *sb, const char *keyword,\n+                                     const char *value, unsigned int valuelen)\n+{\n+\tchar *p;\n+\tint len, total, tmp;\n+\n+\t/* \"%u %s=%s\\n\" */\n+\tlen = 1 + 1 + strlen(keyword) + 1 + valuelen + 1;\n+\tfor (tmp = len; tmp > 9; tmp /= 10)\n+\t\tlen++;\n+\n+\ttotal = sb->len + len;\n+\tif (total > sb->alloc) {\n+\t\tsb->buf = xrealloc(sb->buf, total);\n+\t\tsb->alloc = total;\n+\t}\n+\n+\tp = sb->buf;\n+\tp += sprintf(p, \"%u %s=\", len, keyword);\n+\tmemcpy(p, value, valuelen);\n+\tp += valuelen;\n+\t*p = '\\n';\n+\tsb->len = total;\n+}\n+\n+static unsigned int ustar_header_chksum(const struct ustar_header *header)\n+{\n+\tchar *p = (char *)header;\n+\tunsigned int chksum = 0;\n+\twhile (p < header->chksum)\n+\t\tchksum += *p++;\n+\tchksum += sizeof(header->chksum) * ' ';\n+\tp += sizeof(header->chksum);\n+\twhile (p < (char *)header + sizeof(struct ustar_header))\n+\t\tchksum += *p++;\n+\treturn chksum;\n+}\n+\n+static int get_path_prefix(const struct strbuf *path, int maxlen)\n+{\n+\tint i = path->len;\n+\tif (i > maxlen)\n+\t\ti = maxlen;\n+\twhile (i > 0 && path->buf[i] != '/')\n+\t\ti--;\n+\treturn i;\n+}\n+\n+static void write_entry(const unsigned char *sha1, struct strbuf *path,\n+                        unsigned int mode, void *buffer, unsigned long size)\n+{\n+\tstruct ustar_header header;\n+\tstruct strbuf ext_header;\n+\n+\tmemset(&header, 0, sizeof(header));\n+\text_header.buf = NULL;\n+\text_header.len = ext_header.alloc = 0;\n+\n+\tif (!sha1) {\n+\t\t*header.typeflag = TYPEFLAG_GLOBAL_HEADER;\n+\t\tmode = 0100666;\n+\t\tstrcpy(header.name, \"pax_global_header\");\n+\t} else if (!path) {\n+\t\t*header.typeflag = TYPEFLAG_EXT_HEADER;\n+\t\tmode = 0100666;\n+\t\tsprintf(header.name, \"%s.paxheader\", sha1_to_hex(sha1));\n+\t} else {\n+\t\tif (S_ISDIR(mode)) {\n+\t\t\t*header.typeflag = TYPEFLAG_DIR;\n+\t\t\tmode |= 0777;\n+\t\t} else if (S_ISLNK(mode)) {\n+\t\t\t*header.typeflag = TYPEFLAG_LNK;\n+\t\t\tmode |= 0777;\n+\t\t} else if (S_ISREG(mode)) {\n+\t\t\t*header.typeflag = TYPEFLAG_REG;\n+\t\t\tmode |= (mode & 0100) ? 0777 : 0666;\n+\t\t} else {\n+\t\t\terror(\"unsupported file mode: 0%o (SHA1: %s)\",\n+\t\t\t      mode, sha1_to_hex(sha1));\n+\t\t\treturn;\n+\t\t}\n+\t\tif (path->len > sizeof(header.name)) {\n+\t\t\tint plen = get_path_prefix(path, sizeof(header.prefix));\n+\t\t\tint rest = path->len - plen - 1;\n+\t\t\tif (plen > 0 && rest <= sizeof(header.name)) {\n+\t\t\t\tmemcpy(header.prefix, path->buf, plen);\n+\t\t\t\tmemcpy(header.name, path->buf + plen + 1, rest);\n+\t\t\t} else {\n+\t\t\t\tsprintf(header.name, \"%s.data\",\n+\t\t\t\t        sha1_to_hex(sha1));\n+\t\t\t\tstrbuf_append_ext_header(&ext_header, \"path\",\n+\t\t\t\t                         path->buf, path->len);\n+\t\t\t}\n+\t\t} else\n+\t\t\tmemcpy(header.name, path->buf, path->len);\n+\t}\n+\n+\tif (S_ISLNK(mode) && buffer) {\n+\t\tif (size > sizeof(header.linkname)) {\n+\t\t\tsprintf(header.linkname, \"see %s.paxheader\",\n+\t\t\t        sha1_to_hex(sha1));\n+\t\t\tstrbuf_append_ext_header(&ext_header, \"linkpath\",\n+\t\t\t                         buffer, size);\n+\t\t} else\n+\t\t\tmemcpy(header.linkname, buffer, size);\n+\t}\n+\n+\tsprintf(header.mode, \"%07o\", mode & 07777);\n+\tsprintf(header.size, \"%011lo\", S_ISREG(mode) ? size : 0);\n+\tsprintf(header.mtime, \"%011lo\", archive_time);\n+\n+\t/* XXX: should we provide more meaningful info here? */\n+\tsprintf(header.uid, \"%07o\", 0);\n+\tsprintf(header.gid, \"%07o\", 0);\n+\tstrncpy(header.uname, \"git\", 31);\n+\tstrncpy(header.gname, \"git\", 31);\n+\tsprintf(header.devmajor, \"%07o\", 0);\n+\tsprintf(header.devminor, \"%07o\", 0);\n+\n+\tmemcpy(header.magic, \"ustar\", 6);\n+\tmemcpy(header.version, \"00\", 2);\n+\n+\tsprintf(header.chksum, \"%07o\", ustar_header_chksum(&header));\n+\n+\tif (ext_header.len > 0) {\n+\t\twrite_entry(sha1, NULL, 0, ext_header.buf, ext_header.len);\n+\t\tfree(ext_header.buf);\n+\t}\n+\twrite_blocked(&header, sizeof(header));\n+\tif (S_ISREG(mode) && buffer && size > 0)\n+\t\twrite_blocked(buffer, size);\n+}\n+\n+static void write_global_extended_header(const unsigned char *sha1)\n+{\n+\tstruct strbuf ext_header;\n+\text_header.buf = NULL;\n+\text_header.len = ext_header.alloc = 0;\n+\tstrbuf_append_ext_header(&ext_header, \"comment\", sha1_to_hex(sha1), 40);\n+\twrite_entry(NULL, NULL, 0, ext_header.buf, ext_header.len);\n+\tfree(ext_header.buf);\n+}\n+\n+static void traverse_tree(struct tree_desc *tree, struct strbuf *path)\n+{\n+\tint pathlen = path->len;\n+\n+\twhile (tree->size) {\n+\t\tconst char *name;\n+\t\tconst unsigned char *sha1;\n+\t\tunsigned mode;\n+\t\tvoid *eltbuf;\n+\t\tchar elttype[20];\n+\t\tunsigned long eltsize;\n+\n+\t\tsha1 = tree_entry_extract(tree, &name, &mode);\n+\t\tupdate_tree_entry(tree);\n+\n+\t\teltbuf = read_sha1_file(sha1, elttype, &eltsize);\n+\t\tif (!eltbuf)\n+\t\t\tdie(\"cannot read %s\", sha1_to_hex(sha1));\n+\n+\t\tpath->len = pathlen;\n+\t\tstrbuf_append_string(path, name);\n+\t\tif (S_ISDIR(mode))\n+\t\t\tstrbuf_append_string(path, \"/\");\n+\n+\t\twrite_entry(sha1, path, mode, eltbuf, eltsize);\n+\n+\t\tif (S_ISDIR(mode)) {\n+\t\t\tstruct tree_desc subtree;\n+\t\t\tsubtree.buf = eltbuf;\n+\t\t\tsubtree.size = eltsize;\n+\t\t\ttraverse_tree(&subtree, path);\n+\t\t}\n+\t\tfree(eltbuf);\n+\t}\n+}\n+\n+int cmd_tar_tree(int argc, const char **argv, char** envp)\n+{\n+\tunsigned char sha1[20], tree_sha1[20];\n+\tstruct commit *commit;\n+\tstruct tree_desc tree;\n+\tstruct strbuf current_path;\n+\n+\tcurrent_path.buf = xmalloc(PATH_MAX);\n+\tcurrent_path.alloc = PATH_MAX;\n+\tcurrent_path.len = current_path.eof = 0;\n+\n+\tsetup_git_directory();\n+\tgit_config(git_default_config);\n+\n+\tswitch (argc) {\n+\tcase 3:\n+\t\tstrbuf_append_string(&current_path, argv[2]);\n+\t\tstrbuf_append_string(&current_path, \"/\");\n+\t\t/* FALLTHROUGH */\n+\tcase 2:\n+\t\tif (get_sha1(argv[1], sha1))\n+\t\t\tdie(\"Not a valid object name %s\", argv[1]);\n+\t\tbreak;\n+\tdefault:\n+\t\tusage(tar_tree_usage);\n+\t}\n+\n+\tcommit = lookup_commit_reference_gently(sha1, 1);\n+\tif (commit) {\n+\t\twrite_global_extended_header(commit->object.sha1);\n+\t\tarchive_time = commit->date;\n+\t} else\n+\t\tarchive_time = time(NULL);\n+\n+\ttree.buf = read_object_with_reference(sha1, tree_type, &tree.size,\n+\t                                      tree_sha1);\n+\tif (!tree.buf)\n+\t\tdie(\"not a reference to a tag, commit or tree object: %s\",\n+\t\t    sha1_to_hex(sha1));\n+\n+\tif (current_path.len > 0)\n+\t\twrite_entry(tree_sha1, &current_path, 040777, NULL, 0);\n+\ttraverse_tree(&tree, &current_path);\n+\twrite_trailer();\n+\tfree(current_path.buf);\n+\treturn 0;\n+}\ndiff --git a/builtin.h b/builtin.h\nindex 951f206..d210543 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -29,5 +29,6 @@ extern int cmd_check_ref_format(int argc\n extern int cmd_init_db(int argc, const char **argv, char **envp);\n extern int cmd_ls_files(int argc, const char **argv, char **envp);\n extern int cmd_ls_tree(int argc, const char **argv, char **envp);\n+extern int cmd_tar_tree(int argc, const char **argv, char **envp);\n \n #endif\ndiff --git a/git.c b/git.c\nindex 8574775..c253e60 100644\n--- a/git.c\n+++ b/git.c\n@@ -54,7 +54,8 @@ static void handle_internal_command(int \n \t\t{ \"init-db\", cmd_init_db },\n \t\t{ \"check-ref-format\", cmd_check_ref_format },\n \t\t{ \"ls-files\", cmd_ls_files },\n-\t\t{ \"ls-tree\", cmd_ls_tree }\n+\t\t{ \"ls-tree\", cmd_ls_tree },\n+\t\t{ \"tar-tree\", cmd_tar_tree }\n \t};\n \tint i;\n \ndiff --git a/tar-tree.c b/tar-tree.c\ndeleted file mode 100644\nindex 3308736..0000000\n--- a/tar-tree.c\n+++ /dev/null\n@@ -1,350 +0,0 @@\n-/*\n- * Copyright (c) 2005, 2006 Rene Scharfe\n- */\n-#include <time.h>\n-#include \"cache.h\"\n-#include \"tree-walk.h\"\n-#include \"commit.h\"\n-#include \"strbuf.h\"\n-#include \"tar.h\"\n-\n-#define RECORDSIZE\t(512)\n-#define BLOCKSIZE\t(RECORDSIZE * 20)\n-\n-static const char tar_tree_usage[] = \"git-tar-tree <key> [basedir]\";\n-\n-static char block[BLOCKSIZE];\n-static unsigned long offset;\n-\n-static time_t archive_time;\n-\n-/* tries hard to write, either succeeds or dies in the attempt */\n-static void reliable_write(void *buf, unsigned long size)\n-{\n-\twhile (size > 0) {\n-\t\tlong ret = xwrite(1, buf, size);\n-\t\tif (ret < 0) {\n-\t\t\tif (errno == EPIPE)\n-\t\t\t\texit(0);\n-\t\t\tdie(\"git-tar-tree: %s\", strerror(errno));\n-\t\t} else if (!ret) {\n-\t\t\tdie(\"git-tar-tree: disk full?\");\n-\t\t}\n-\t\tsize -= ret;\n-\t\tbuf += ret;\n-\t}\n-}\n-\n-/* writes out the whole block, but only if it is full */\n-static void write_if_needed(void)\n-{\n-\tif (offset == BLOCKSIZE) {\n-\t\treliable_write(block, BLOCKSIZE);\n-\t\toffset = 0;\n-\t}\n-}\n-\n-/* acquire the next record from the buffer; user must call write_if_needed() */\n-static char *get_record(void)\n-{\n-\tchar *p = block + offset;\n-\tmemset(p, 0, RECORDSIZE);\n-\toffset += RECORDSIZE;\n-\treturn p;\n-}\n-\n-/*\n- * The end of tar archives is marked by 1024 nul bytes and after that\n- * follows the rest of the block (if any).\n- */\n-static void write_trailer(void)\n-{\n-\tget_record();\n-\twrite_if_needed();\n-\tget_record();\n-\twrite_if_needed();\n-\twhile (offset) {\n-\t\tget_record();\n-\t\twrite_if_needed();\n-\t}\n-}\n-\n-/*\n- * queues up writes, so that all our write(2) calls write exactly one\n- * full block; pads writes to RECORDSIZE\n- */\n-static void write_blocked(void *buf, unsigned long size)\n-{\n-\tunsigned long tail;\n-\n-\tif (offset) {\n-\t\tunsigned long chunk = BLOCKSIZE - offset;\n-\t\tif (size < chunk)\n-\t\t\tchunk = size;\n-\t\tmemcpy(block + offset, buf, chunk);\n-\t\tsize -= chunk;\n-\t\toffset += chunk;\n-\t\tbuf += chunk;\n-\t\twrite_if_needed();\n-\t}\n-\twhile (size >= BLOCKSIZE) {\n-\t\treliable_write(buf, BLOCKSIZE);\n-\t\tsize -= BLOCKSIZE;\n-\t\tbuf += BLOCKSIZE;\n-\t}\n-\tif (size) {\n-\t\tmemcpy(block + offset, buf, size);\n-\t\toffset += size;\n-\t}\n-\ttail = offset % RECORDSIZE;\n-\tif (tail)  {\n-\t\tmemset(block + offset, 0, RECORDSIZE - tail);\n-\t\toffset += RECORDSIZE - tail;\n-\t}\n-\twrite_if_needed();\n-}\n-\n-static void strbuf_append_string(struct strbuf *sb, const char *s)\n-{\n-\tint slen = strlen(s);\n-\tint total = sb->len + slen;\n-\tif (total > sb->alloc) {\n-\t\tsb->buf = xrealloc(sb->buf, total);\n-\t\tsb->alloc = total;\n-\t}\n-\tmemcpy(sb->buf + sb->len, s, slen);\n-\tsb->len = total;\n-}\n-\n-/*\n- * pax extended header records have the format \"%u %s=%s\\n\".  %u contains\n- * the size of the whole string (including the %u), the first %s is the\n- * keyword, the second one is the value.  This function constructs such a\n- * string and appends it to a struct strbuf.\n- */\n-static void strbuf_append_ext_header(struct strbuf *sb, const char *keyword,\n-                                     const char *value, unsigned int valuelen)\n-{\n-\tchar *p;\n-\tint len, total, tmp;\n-\n-\t/* \"%u %s=%s\\n\" */\n-\tlen = 1 + 1 + strlen(keyword) + 1 + valuelen + 1;\n-\tfor (tmp = len; tmp > 9; tmp /= 10)\n-\t\tlen++;\n-\n-\ttotal = sb->len + len;\n-\tif (total > sb->alloc) {\n-\t\tsb->buf = xrealloc(sb->buf, total);\n-\t\tsb->alloc = total;\n-\t}\n-\n-\tp = sb->buf;\n-\tp += sprintf(p, \"%u %s=\", len, keyword);\n-\tmemcpy(p, value, valuelen);\n-\tp += valuelen;\n-\t*p = '\\n';\n-\tsb->len = total;\n-}\n-\n-static unsigned int ustar_header_chksum(const struct ustar_header *header)\n-{\n-\tchar *p = (char *)header;\n-\tunsigned int chksum = 0;\n-\twhile (p < header->chksum)\n-\t\tchksum += *p++;\n-\tchksum += sizeof(header->chksum) * ' ';\n-\tp += sizeof(header->chksum);\n-\twhile (p < (char *)header + sizeof(struct ustar_header))\n-\t\tchksum += *p++;\n-\treturn chksum;\n-}\n-\n-static int get_path_prefix(const struct strbuf *path, int maxlen)\n-{\n-\tint i = path->len;\n-\tif (i > maxlen)\n-\t\ti = maxlen;\n-\twhile (i > 0 && path->buf[i] != '/')\n-\t\ti--;\n-\treturn i;\n-}\n-\n-static void write_entry(const unsigned char *sha1, struct strbuf *path,\n-                        unsigned int mode, void *buffer, unsigned long size)\n-{\n-\tstruct ustar_header header;\n-\tstruct strbuf ext_header;\n-\n-\tmemset(&header, 0, sizeof(header));\n-\text_header.buf = NULL;\n-\text_header.len = ext_header.alloc = 0;\n-\n-\tif (!sha1) {\n-\t\t*header.typeflag = TYPEFLAG_GLOBAL_HEADER;\n-\t\tmode = 0100666;\n-\t\tstrcpy(header.name, \"pax_global_header\");\n-\t} else if (!path) {\n-\t\t*header.typeflag = TYPEFLAG_EXT_HEADER;\n-\t\tmode = 0100666;\n-\t\tsprintf(header.name, \"%s.paxheader\", sha1_to_hex(sha1));\n-\t} else {\n-\t\tif (S_ISDIR(mode)) {\n-\t\t\t*header.typeflag = TYPEFLAG_DIR;\n-\t\t\tmode |= 0777;\n-\t\t} else if (S_ISLNK(mode)) {\n-\t\t\t*header.typeflag = TYPEFLAG_LNK;\n-\t\t\tmode |= 0777;\n-\t\t} else if (S_ISREG(mode)) {\n-\t\t\t*header.typeflag = TYPEFLAG_REG;\n-\t\t\tmode |= (mode & 0100) ? 0777 : 0666;\n-\t\t} else {\n-\t\t\terror(\"unsupported file mode: 0%o (SHA1: %s)\",\n-\t\t\t      mode, sha1_to_hex(sha1));\n-\t\t\treturn;\n-\t\t}\n-\t\tif (path->len > sizeof(header.name)) {\n-\t\t\tint plen = get_path_prefix(path, sizeof(header.prefix));\n-\t\t\tint rest = path->len - plen - 1;\n-\t\t\tif (plen > 0 && rest <= sizeof(header.name)) {\n-\t\t\t\tmemcpy(header.prefix, path->buf, plen);\n-\t\t\t\tmemcpy(header.name, path->buf + plen + 1, rest);\n-\t\t\t} else {\n-\t\t\t\tsprintf(header.name, \"%s.data\",\n-\t\t\t\t        sha1_to_hex(sha1));\n-\t\t\t\tstrbuf_append_ext_header(&ext_header, \"path\",\n-\t\t\t\t                         path->buf, path->len);\n-\t\t\t}\n-\t\t} else\n-\t\t\tmemcpy(header.name, path->buf, path->len);\n-\t}\n-\n-\tif (S_ISLNK(mode) && buffer) {\n-\t\tif (size > sizeof(header.linkname)) {\n-\t\t\tsprintf(header.linkname, \"see %s.paxheader\",\n-\t\t\t        sha1_to_hex(sha1));\n-\t\t\tstrbuf_append_ext_header(&ext_header, \"linkpath\",\n-\t\t\t                         buffer, size);\n-\t\t} else\n-\t\t\tmemcpy(header.linkname, buffer, size);\n-\t}\n-\n-\tsprintf(header.mode, \"%07o\", mode & 07777);\n-\tsprintf(header.size, \"%011lo\", S_ISREG(mode) ? size : 0);\n-\tsprintf(header.mtime, \"%011lo\", archive_time);\n-\n-\t/* XXX: should we provide more meaningful info here? */\n-\tsprintf(header.uid, \"%07o\", 0);\n-\tsprintf(header.gid, \"%07o\", 0);\n-\tstrncpy(header.uname, \"git\", 31);\n-\tstrncpy(header.gname, \"git\", 31);\n-\tsprintf(header.devmajor, \"%07o\", 0);\n-\tsprintf(header.devminor, \"%07o\", 0);\n-\n-\tmemcpy(header.magic, \"ustar\", 6);\n-\tmemcpy(header.version, \"00\", 2);\n-\n-\tsprintf(header.chksum, \"%07o\", ustar_header_chksum(&header));\n-\n-\tif (ext_header.len > 0) {\n-\t\twrite_entry(sha1, NULL, 0, ext_header.buf, ext_header.len);\n-\t\tfree(ext_header.buf);\n-\t}\n-\twrite_blocked(&header, sizeof(header));\n-\tif (S_ISREG(mode) && buffer && size > 0)\n-\t\twrite_blocked(buffer, size);\n-}\n-\n-static void write_global_extended_header(const unsigned char *sha1)\n-{\n-\tstruct strbuf ext_header;\n-\text_header.buf = NULL;\n-\text_header.len = ext_header.alloc = 0;\n-\tstrbuf_append_ext_header(&ext_header, \"comment\", sha1_to_hex(sha1), 40);\n-\twrite_entry(NULL, NULL, 0, ext_header.buf, ext_header.len);\n-\tfree(ext_header.buf);\n-}\n-\n-static void traverse_tree(struct tree_desc *tree, struct strbuf *path)\n-{\n-\tint pathlen = path->len;\n-\n-\twhile (tree->size) {\n-\t\tconst char *name;\n-\t\tconst unsigned char *sha1;\n-\t\tunsigned mode;\n-\t\tvoid *eltbuf;\n-\t\tchar elttype[20];\n-\t\tunsigned long eltsize;\n-\n-\t\tsha1 = tree_entry_extract(tree, &name, &mode);\n-\t\tupdate_tree_entry(tree);\n-\n-\t\teltbuf = read_sha1_file(sha1, elttype, &eltsize);\n-\t\tif (!eltbuf)\n-\t\t\tdie(\"cannot read %s\", sha1_to_hex(sha1));\n-\n-\t\tpath->len = pathlen;\n-\t\tstrbuf_append_string(path, name);\n-\t\tif (S_ISDIR(mode))\n-\t\t\tstrbuf_append_string(path, \"/\");\n-\n-\t\twrite_entry(sha1, path, mode, eltbuf, eltsize);\n-\n-\t\tif (S_ISDIR(mode)) {\n-\t\t\tstruct tree_desc subtree;\n-\t\t\tsubtree.buf = eltbuf;\n-\t\t\tsubtree.size = eltsize;\n-\t\t\ttraverse_tree(&subtree, path);\n-\t\t}\n-\t\tfree(eltbuf);\n-\t}\n-}\n-\n-int main(int argc, char **argv)\n-{\n-\tunsigned char sha1[20], tree_sha1[20];\n-\tstruct commit *commit;\n-\tstruct tree_desc tree;\n-\tstruct strbuf current_path;\n-\n-\tcurrent_path.buf = xmalloc(PATH_MAX);\n-\tcurrent_path.alloc = PATH_MAX;\n-\tcurrent_path.len = current_path.eof = 0;\n-\n-\tsetup_git_directory();\n-\tgit_config(git_default_config);\n-\n-\tswitch (argc) {\n-\tcase 3:\n-\t\tstrbuf_append_string(&current_path, argv[2]);\n-\t\tstrbuf_append_string(&current_path, \"/\");\n-\t\t/* FALLTHROUGH */\n-\tcase 2:\n-\t\tif (get_sha1(argv[1], sha1))\n-\t\t\tdie(\"Not a valid object name %s\", argv[1]);\n-\t\tbreak;\n-\tdefault:\n-\t\tusage(tar_tree_usage);\n-\t}\n-\n-\tcommit = lookup_commit_reference_gently(sha1, 1);\n-\tif (commit) {\n-\t\twrite_global_extended_header(commit->object.sha1);\n-\t\tarchive_time = commit->date;\n-\t} else\n-\t\tarchive_time = time(NULL);\n-\n-\ttree.buf = read_object_with_reference(sha1, tree_type, &tree.size,\n-\t                                      tree_sha1);\n-\tif (!tree.buf)\n-\t\tdie(\"not a reference to a tag, commit or tree object: %s\",\n-\t\t    sha1_to_hex(sha1));\n-\n-\tif (current_path.len > 0)\n-\t\twrite_entry(tree_sha1, &current_path, 040777, NULL, 0);\n-\ttraverse_tree(&tree, &current_path);\n-\twrite_trailer();\n-\tfree(current_path.buf);\n-\treturn 0;\n-}\n-- \n1.3.3.g288c\n"}]}