ls-tree.con commit Merge http://www.kernel.org/pub/scm/gitk/gitk (302ebfe)
   1/*
   2 * GIT - The information manager from hell
   3 *
   4 * Copyright (C) Linus Torvalds, 2005
   5 */
   6#include "cache.h"
   7#include "blob.h"
   8#include "tree.h"
   9#include "quote.h"
  10
  11static int line_termination = '\n';
  12#define LS_RECURSIVE 1
  13#define LS_TREE_ONLY 2
  14static int ls_options = 0;
  15
  16static struct tree_entry_list root_entry;
  17
  18static void prepare_root(unsigned char *sha1)
  19{
  20        unsigned char rsha[20];
  21        unsigned long size;
  22        void *buf;
  23        struct tree *root_tree;
  24
  25        buf = read_object_with_reference(sha1, "tree", &size, rsha);
  26        free(buf);
  27        if (!buf)
  28                die("Could not read %s", sha1_to_hex(sha1));
  29
  30        root_tree = lookup_tree(rsha);
  31        if (!root_tree)
  32                die("Could not read %s", sha1_to_hex(sha1));
  33
  34        /* Prepare a fake entry */
  35        root_entry.directory = 1;
  36        root_entry.executable = root_entry.symlink = 0;
  37        root_entry.mode = S_IFDIR;
  38        root_entry.name = "";
  39        root_entry.item.tree = root_tree;
  40        root_entry.parent = NULL;
  41}
  42
  43static int prepare_children(struct tree_entry_list *elem)
  44{
  45        if (!elem->directory)
  46                return -1;
  47        if (!elem->item.tree->object.parsed) {
  48                struct tree_entry_list *e;
  49                if (parse_tree(elem->item.tree))
  50                        return -1;
  51                /* Set up the parent link */
  52                for (e = elem->item.tree->entries; e; e = e->next)
  53                        e->parent = elem;
  54        }
  55        return 0;
  56}
  57
  58static struct tree_entry_list *find_entry(const char *path, char *pathbuf)
  59{
  60        const char *next, *slash;
  61        int len;
  62        struct tree_entry_list *elem = &root_entry, *oldelem = NULL;
  63
  64        *(pathbuf) = '\0';
  65
  66        /* Find tree element, descending from root, that
  67         * corresponds to the named path, lazily expanding
  68         * the tree if possible.
  69         */
  70
  71        while (path) {
  72                /* The fact we still have path means that the caller
  73                 * wants us to make sure that elem at this point is a
  74                 * directory, and possibly descend into it.  Even what
  75                 * is left is just trailing slashes, we loop back to
  76                 * here, and this call to prepare_children() will
  77                 * catch elem not being a tree.  Nice.
  78                 */
  79                if (prepare_children(elem))
  80                        return NULL;
  81
  82                slash = strchr(path, '/');
  83                if (!slash) {
  84                        len = strlen(path);
  85                        next = NULL;
  86                }
  87                else {
  88                        next = slash + 1;
  89                        len = slash - path;
  90                }
  91                if (len) {
  92                        if (oldelem) {
  93                                pathbuf += sprintf(pathbuf, "%s/", oldelem->name);
  94                        }
  95
  96                        /* (len == 0) if the original path was "drivers/char/"
  97                         * and we have run already two rounds, having elem
  98                         * pointing at the drivers/char directory.
  99                         */
 100                        elem = elem->item.tree->entries;
 101                        while (elem) {
 102                                if ((strlen(elem->name) == len) &&
 103                                    !strncmp(elem->name, path, len)) {
 104                                        /* found */
 105                                        break;
 106                                }
 107                                elem = elem->next;
 108                        }
 109                        if (!elem)
 110                                return NULL;
 111
 112                        oldelem = elem;
 113                }
 114                path = next;
 115        }
 116
 117        return elem;
 118}
 119
 120static const char *entry_type(struct tree_entry_list *e)
 121{
 122        return (e->directory ? "tree" : "blob");
 123}
 124
 125static const char *entry_hex(struct tree_entry_list *e)
 126{
 127        return sha1_to_hex(e->directory
 128                           ? e->item.tree->object.sha1
 129                           : e->item.blob->object.sha1);
 130}
 131
 132/* forward declaration for mutually recursive routines */
 133static int show_entry(struct tree_entry_list *, int, char *pathbuf);
 134
 135static int show_children(struct tree_entry_list *e, int level, char *pathbuf)
 136{
 137        int oldlen = strlen(pathbuf);
 138
 139        if (e != &root_entry)
 140                sprintf(pathbuf + oldlen, "%s/", e->name);
 141
 142        if (prepare_children(e))
 143                die("internal error: ls-tree show_children called with non tree");
 144        e = e->item.tree->entries;
 145        while (e) {
 146                show_entry(e, level, pathbuf);
 147                e = e->next;
 148        }
 149
 150        pathbuf[oldlen] = '\0';
 151
 152        return 0;
 153}
 154
 155static int show_entry(struct tree_entry_list *e, int level, char *pathbuf)
 156{
 157        int err = 0; 
 158
 159        if (e != &root_entry) {
 160                printf("%06o %s %s      ",
 161                       e->mode, entry_type(e), entry_hex(e));
 162                write_name_quoted(pathbuf, e->name, line_termination, stdout);
 163                putchar(line_termination);
 164        }
 165
 166        if (e->directory) {
 167                /* If this is a directory, we have the following cases:
 168                 * (1) This is the top-level request (explicit path from the
 169                 *     command line, or "root" if there is no command line).
 170                 *  a. Without any flag.  We show direct children.  We do not 
 171                 *     recurse into them.
 172                 *  b. With -r.  We do recurse into children.
 173                 *  c. With -d.  We do not recurse into children.
 174                 * (2) We came here because our caller is either (1-a) or
 175                 *     (1-b).
 176                 *  a. Without any flag.  We do not show our children (which
 177                 *     are grandchildren for the original request).
 178                 *  b. With -r.  We continue to recurse into our children.
 179                 *  c. With -d.  We should not have come here to begin with.
 180                 */
 181                if (level == 0 && !(ls_options & LS_TREE_ONLY))
 182                        /* case (1)-a and (1)-b */
 183                        err = err | show_children(e, level+1, pathbuf);
 184                else if (level && ls_options & LS_RECURSIVE)
 185                        /* case (2)-b */
 186                        err = err | show_children(e, level+1, pathbuf);
 187        }
 188        return err;
 189}
 190
 191static int list_one(const char *path)
 192{
 193        int err = 0;
 194        char pathbuf[MAXPATHLEN + 1];
 195        struct tree_entry_list *e = find_entry(path, pathbuf);
 196        if (!e) {
 197                /* traditionally ls-tree does not complain about
 198                 * missing path.  We may change this later to match
 199                 * what "/bin/ls -a" does, which is to complain.
 200                 */
 201                return err;
 202        }
 203        err = err | show_entry(e, 0, pathbuf);
 204        return err;
 205}
 206
 207static int list(char **path)
 208{
 209        int i;
 210        int err = 0;
 211        for (i = 0; path[i]; i++)
 212                err = err | list_one(path[i]);
 213        return err;
 214}
 215
 216static const char ls_tree_usage[] =
 217        "git-ls-tree [-d] [-r] [-z] <tree-ish> [path...]";
 218
 219int main(int argc, char **argv)
 220{
 221        static char *path0[] = { "", NULL };
 222        char **path;
 223        unsigned char sha1[20];
 224
 225        while (1 < argc && argv[1][0] == '-') {
 226                switch (argv[1][1]) {
 227                case 'z':
 228                        line_termination = 0;
 229                        break;
 230                case 'r':
 231                        ls_options |= LS_RECURSIVE;
 232                        break;
 233                case 'd':
 234                        ls_options |= LS_TREE_ONLY;
 235                        break;
 236                default:
 237                        usage(ls_tree_usage);
 238                }
 239                argc--; argv++;
 240        }
 241
 242        if (argc < 2)
 243                usage(ls_tree_usage);
 244        if (get_sha1(argv[1], sha1) < 0)
 245                usage(ls_tree_usage);
 246
 247        path = (argc == 2) ? path0 : (argv + 2);
 248        prepare_root(sha1);
 249        if (list(path) < 0)
 250                die("list failed");
 251        return 0;
 252}