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