1869baede56da54f964e4300d0b4148e66999cab
   1#include "cache.h"
   2#include "tree-walk.h"
   3#include "tree.h"
   4
   5void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1)
   6{
   7        unsigned long size = 0;
   8        void *buf = NULL;
   9
  10        if (sha1) {
  11                buf = read_object_with_reference(sha1, tree_type, &size, NULL);
  12                if (!buf)
  13                        die("unable to read tree %s", sha1_to_hex(sha1));
  14        }
  15        desc->size = size;
  16        desc->buf = buf;
  17        return buf;
  18}
  19
  20static int entry_compare(struct name_entry *a, struct name_entry *b)
  21{
  22        return base_name_compare(
  23                        a->path, tree_entry_len(a->path, a->sha1), a->mode,
  24                        b->path, tree_entry_len(b->path, b->sha1), b->mode);
  25}
  26
  27static void entry_clear(struct name_entry *a)
  28{
  29        memset(a, 0, sizeof(*a));
  30}
  31
  32static void entry_extract(struct tree_desc *t, struct name_entry *a)
  33{
  34        a->sha1 = tree_entry_extract(t, &a->path, &a->mode);
  35}
  36
  37void update_tree_entry(struct tree_desc *desc)
  38{
  39        const void *buf = desc->buf;
  40        unsigned long size = desc->size;
  41        int len = strlen(buf) + 1 + 20;
  42
  43        if (size < len)
  44                die("corrupt tree file");
  45        desc->buf = (char *) buf + len;
  46        desc->size = size - len;
  47}
  48
  49static const char *get_mode(const char *str, unsigned int *modep)
  50{
  51        unsigned char c;
  52        unsigned int mode = 0;
  53
  54        while ((c = *str++) != ' ') {
  55                if (c < '0' || c > '7')
  56                        return NULL;
  57                mode = (mode << 3) + (c - '0');
  58        }
  59        *modep = mode;
  60        return str;
  61}
  62
  63const unsigned char *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned int *modep)
  64{
  65        const void *tree = desc->buf;
  66        unsigned long size = desc->size;
  67        int len = strlen(tree)+1;
  68        const unsigned char *sha1 = (unsigned char *) tree + len;
  69        const char *path;
  70        unsigned int mode;
  71
  72        path = get_mode(tree, &mode);
  73        if (!path || size < len + 20)
  74                die("corrupt tree file");
  75        *pathp = path;
  76        *modep = canon_mode(mode);
  77        return sha1;
  78}
  79
  80int tree_entry(struct tree_desc *desc, struct name_entry *entry)
  81{
  82        const void *tree = desc->buf;
  83        const char *path;
  84        unsigned long len, size = desc->size;
  85
  86        if (!size)
  87                return 0;
  88
  89        path = get_mode(tree, &entry->mode);
  90        if (!path)
  91                die("corrupt tree file");
  92
  93        entry->path = path;
  94        len = strlen(path);
  95
  96        path += len + 1;
  97        entry->sha1 = (const unsigned char *) path;
  98
  99        path += 20;
 100        len = path - (char *) tree;
 101        if (len > size)
 102                die("corrupt tree file");
 103
 104        desc->buf = path;
 105        desc->size = size - len;
 106        return 1;
 107}
 108
 109void traverse_trees(int n, struct tree_desc *t, const char *base, traverse_callback_t callback)
 110{
 111        struct name_entry *entry = xmalloc(n*sizeof(*entry));
 112
 113        for (;;) {
 114                unsigned long mask = 0;
 115                int i, last;
 116
 117                last = -1;
 118                for (i = 0; i < n; i++) {
 119                        if (!t[i].size)
 120                                continue;
 121                        entry_extract(t+i, entry+i);
 122                        if (last >= 0) {
 123                                int cmp = entry_compare(entry+i, entry+last);
 124
 125                                /*
 126                                 * Is the new name bigger than the old one?
 127                                 * Ignore it
 128                                 */
 129                                if (cmp > 0)
 130                                        continue;
 131                                /*
 132                                 * Is the new name smaller than the old one?
 133                                 * Ignore all old ones
 134                                 */
 135                                if (cmp < 0)
 136                                        mask = 0;
 137                        }
 138                        mask |= 1ul << i;
 139                        last = i;
 140                }
 141                if (!mask)
 142                        break;
 143
 144                /*
 145                 * Update the tree entries we've walked, and clear
 146                 * all the unused name-entries.
 147                 */
 148                for (i = 0; i < n; i++) {
 149                        if (mask & (1ul << i)) {
 150                                update_tree_entry(t+i);
 151                                continue;
 152                        }
 153                        entry_clear(entry + i);
 154                }
 155                callback(n, mask, entry, base);
 156        }
 157        free(entry);
 158}
 159
 160static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char *result, unsigned *mode)
 161{
 162        int namelen = strlen(name);
 163        while (t->size) {
 164                const char *entry;
 165                const unsigned char *sha1;
 166                int entrylen, cmp;
 167
 168                sha1 = tree_entry_extract(t, &entry, mode);
 169                update_tree_entry(t);
 170                entrylen = tree_entry_len(entry, sha1);
 171                if (entrylen > namelen)
 172                        continue;
 173                cmp = memcmp(name, entry, entrylen);
 174                if (cmp > 0)
 175                        continue;
 176                if (cmp < 0)
 177                        break;
 178                if (entrylen == namelen) {
 179                        hashcpy(result, sha1);
 180                        return 0;
 181                }
 182                if (name[entrylen] != '/')
 183                        continue;
 184                if (!S_ISDIR(*mode))
 185                        break;
 186                if (++entrylen == namelen) {
 187                        hashcpy(result, sha1);
 188                        return 0;
 189                }
 190                return get_tree_entry(sha1, name + entrylen, result, mode);
 191        }
 192        return -1;
 193}
 194
 195int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned char *sha1, unsigned *mode)
 196{
 197        int retval;
 198        void *tree;
 199        struct tree_desc t;
 200        unsigned char root[20];
 201
 202        tree = read_object_with_reference(tree_sha1, tree_type, &t.size, root);
 203        if (!tree)
 204                return -1;
 205
 206        if (name[0] == '\0') {
 207                hashcpy(sha1, root);
 208                return 0;
 209        }
 210
 211        t.buf = tree;
 212        retval = find_tree_entry(&t, name, sha1, mode);
 213        free(tree);
 214        return retval;
 215}
 216