tree-walk.con commit Merge branch 'maint' (dd371b4)
   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        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 = buf + len;
  47        desc->size = size - len;
  48}
  49
  50const unsigned char *tree_entry_extract(struct tree_desc *desc, const char **pathp, unsigned int *modep)
  51{
  52        void *tree = desc->buf;
  53        unsigned long size = desc->size;
  54        int len = strlen(tree)+1;
  55        const unsigned char *sha1 = tree + len;
  56        const char *path = strchr(tree, ' ');
  57        unsigned int mode;
  58
  59        if (!path || size < len + 20 || sscanf(tree, "%o", &mode) != 1)
  60                die("corrupt tree file");
  61        *pathp = path+1;
  62        *modep = canon_mode(mode);
  63        return sha1;
  64}
  65
  66void traverse_trees(int n, struct tree_desc *t, const char *base, traverse_callback_t callback)
  67{
  68        struct name_entry *entry = xmalloc(n*sizeof(*entry));
  69
  70        for (;;) {
  71                struct name_entry entry[3];
  72                unsigned long mask = 0;
  73                int i, last;
  74
  75                last = -1;
  76                for (i = 0; i < n; i++) {
  77                        if (!t[i].size)
  78                                continue;
  79                        entry_extract(t+i, entry+i);
  80                        if (last >= 0) {
  81                                int cmp = entry_compare(entry+i, entry+last);
  82
  83                                /*
  84                                 * Is the new name bigger than the old one?
  85                                 * Ignore it
  86                                 */
  87                                if (cmp > 0)
  88                                        continue;
  89                                /*
  90                                 * Is the new name smaller than the old one?
  91                                 * Ignore all old ones
  92                                 */
  93                                if (cmp < 0)
  94                                        mask = 0;
  95                        }
  96                        mask |= 1ul << i;
  97                        last = i;
  98                }
  99                if (!mask)
 100                        break;
 101
 102                /*
 103                 * Update the tree entries we've walked, and clear
 104                 * all the unused name-entries.
 105                 */
 106                for (i = 0; i < n; i++) {
 107                        if (mask & (1ul << i)) {
 108                                update_tree_entry(t+i);
 109                                continue;
 110                        }
 111                        entry_clear(entry + i);
 112                }
 113                callback(n, mask, entry, base);
 114        }
 115        free(entry);
 116}
 117
 118static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char *result, unsigned *mode)
 119{
 120        int namelen = strlen(name);
 121        while (t->size) {
 122                const char *entry;
 123                const unsigned char *sha1;
 124                int entrylen, cmp;
 125
 126                sha1 = tree_entry_extract(t, &entry, mode);
 127                update_tree_entry(t);
 128                entrylen = strlen(entry);
 129                if (entrylen > namelen)
 130                        continue;
 131                cmp = memcmp(name, entry, entrylen);
 132                if (cmp > 0)
 133                        continue;
 134                if (cmp < 0)
 135                        break;
 136                if (entrylen == namelen) {
 137                        memcpy(result, sha1, 20);
 138                        return 0;
 139                }
 140                if (name[entrylen] != '/')
 141                        continue;
 142                if (!S_ISDIR(*mode))
 143                        break;
 144                if (++entrylen == namelen) {
 145                        memcpy(result, sha1, 20);
 146                        return 0;
 147                }
 148                return get_tree_entry(sha1, name + entrylen, result, mode);
 149        }
 150        return -1;
 151}
 152
 153int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned char *sha1, unsigned *mode)
 154{
 155        int retval;
 156        void *tree;
 157        struct tree_desc t;
 158
 159        tree = read_object_with_reference(tree_sha1, tree_type, &t.size, NULL);
 160        if (!tree)
 161                return -1;
 162        t.buf = tree;
 163        retval = find_tree_entry(&t, name, sha1, mode);
 164        free(tree);
 165        return retval;
 166}
 167