ls-tree.con commit [PATCH] Optimize diff-tree -[CM] --stdin (f0c6b2a)
   1/*
   2 * GIT - The information manager from hell
   3 *
   4 * Copyright (C) Linus Torvalds, 2005
   5 */
   6#include "cache.h"
   7
   8static int line_termination = '\n';
   9static int recursive = 0;
  10
  11struct path_prefix {
  12        struct path_prefix *prev;
  13        const char *name;
  14};
  15
  16#define DEBUG(fmt, ...) 
  17
  18static int string_path_prefix(char *buff, size_t blen, struct path_prefix *prefix)
  19{
  20        int len = 0;
  21        if (prefix) {
  22                if (prefix->prev) {
  23                        len = string_path_prefix(buff,blen,prefix->prev);
  24                        buff += len;
  25                        blen -= len;
  26                        if (blen > 0) {
  27                                *buff = '/';
  28                                len++;
  29                                buff++;
  30                                blen--;
  31                        }
  32                }
  33                strncpy(buff,prefix->name,blen);
  34                return len + strlen(prefix->name);
  35        }
  36
  37        return 0;
  38}
  39
  40static void print_path_prefix(struct path_prefix *prefix)
  41{
  42        if (prefix) {
  43                if (prefix->prev) {
  44                        print_path_prefix(prefix->prev);
  45                        putchar('/');
  46                }
  47                fputs(prefix->name, stdout);
  48        }
  49}
  50
  51/*
  52 * return:
  53 *      -1 if prefix is *not* a subset of path
  54 *       0 if prefix == path
  55 *       1 if prefix is a subset of path
  56 */
  57static int pathcmp(const char *path, struct path_prefix *prefix)
  58{
  59        char buff[PATH_MAX];
  60        int len,slen;
  61
  62        if (prefix == NULL)
  63                return 1;
  64
  65        len = string_path_prefix(buff, sizeof buff, prefix);
  66        slen = strlen(path);
  67
  68        if (slen < len)
  69                return -1;
  70
  71        if (strncmp(path,buff,len) == 0) {
  72                if (slen == len)
  73                        return 0;
  74                else
  75                        return 1;
  76        }
  77
  78        return -1;
  79}       
  80
  81/*
  82 * match may be NULL, or a *sorted* list of paths
  83 */
  84static void list_recursive(void *buffer,
  85                           const char *type,
  86                           unsigned long size,
  87                           struct path_prefix *prefix,
  88                           char **match, int matches)
  89{
  90        struct path_prefix this_prefix;
  91        this_prefix.prev = prefix;
  92
  93        if (strcmp(type, "tree"))
  94                die("expected a 'tree' node");
  95
  96        if (matches)
  97                recursive = 1;
  98
  99        while (size) {
 100                int namelen = strlen(buffer)+1;
 101                void *eltbuf = NULL;
 102                char elttype[20];
 103                unsigned long eltsize;
 104                unsigned char *sha1 = buffer + namelen;
 105                char *path = strchr(buffer, ' ') + 1;
 106                unsigned int mode;
 107                const char *matched = NULL;
 108                int mtype = -1;
 109                int mindex;
 110
 111                if (size < namelen + 20 || sscanf(buffer, "%o", &mode) != 1)
 112                        die("corrupt 'tree' file");
 113                buffer = sha1 + 20;
 114                size -= namelen + 20;
 115
 116                this_prefix.name = path;
 117                for ( mindex = 0; mindex < matches; mindex++) {
 118                        mtype = pathcmp(match[mindex],&this_prefix);
 119                        if (mtype >= 0) {
 120                                matched = match[mindex];
 121                                break;
 122                        }
 123                }
 124
 125                /*
 126                 * If we're not matching, or if this is an exact match,
 127                 * print out the info
 128                 */
 129                if (!matches || (matched != NULL && mtype == 0)) {
 130                        printf("%06o %s %s\t", mode,
 131                               S_ISDIR(mode) ? "tree" : "blob",
 132                               sha1_to_hex(sha1));
 133                        print_path_prefix(&this_prefix);
 134                        putchar(line_termination);
 135                }
 136
 137                if (! recursive || ! S_ISDIR(mode))
 138                        continue;
 139
 140                if (matches && ! matched)
 141                        continue;
 142
 143                if (! (eltbuf = read_sha1_file(sha1, elttype, &eltsize)) ) {
 144                        error("cannot read %s", sha1_to_hex(sha1));
 145                        continue;
 146                }
 147
 148                /* If this is an exact directory match, we may have
 149                 * directory files following this path. Match on them.
 150                 * Otherwise, we're at a pach subcomponent, and we need
 151                 * to try to match again.
 152                 */
 153                if (mtype == 0)
 154                        mindex++;
 155
 156                list_recursive(eltbuf, elttype, eltsize, &this_prefix, &match[mindex], matches-mindex);
 157                free(eltbuf);
 158        }
 159}
 160
 161static int qcmp(const void *a, const void *b)
 162{
 163        return strcmp(*(char **)a, *(char **)b);
 164}
 165
 166static int list(unsigned char *sha1,char **path)
 167{
 168        void *buffer;
 169        unsigned long size;
 170        int npaths;
 171
 172        for (npaths = 0; path[npaths] != NULL; npaths++)
 173                ;
 174
 175        qsort(path,npaths,sizeof(char *),qcmp);
 176
 177        buffer = read_object_with_reference(sha1, "tree", &size, NULL);
 178        if (!buffer)
 179                die("unable to read sha1 file");
 180        list_recursive(buffer, "tree", size, NULL, path, npaths);
 181        free(buffer);
 182        return 0;
 183}
 184
 185static const char *ls_tree_usage = "git-ls-tree [-r] [-z] <key> [paths...]";
 186
 187int main(int argc, char **argv)
 188{
 189        unsigned char sha1[20];
 190
 191        while (1 < argc && argv[1][0] == '-') {
 192                switch (argv[1][1]) {
 193                case 'z':
 194                        line_termination = 0;
 195                        break;
 196                case 'r':
 197                        recursive = 1;
 198                        break;
 199                default:
 200                        usage(ls_tree_usage);
 201                }
 202                argc--; argv++;
 203        }
 204
 205        if (argc < 2)
 206                usage(ls_tree_usage);
 207        if (get_sha1(argv[1], sha1) < 0)
 208                usage(ls_tree_usage);
 209        if (list(sha1, &argv[2]) < 0)
 210                die("list failed");
 211        return 0;
 212}