tree-walk.con commit git-svn: just use Digest::MD5 instead of requiring it (90c1b15)
   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                unsigned long mask = 0;
 117                int i, last;
 118
 119                last = -1;
 120                for (i = 0; i < n; i++) {
 121                        if (!t[i].size)
 122                                continue;
 123                        entry_extract(t+i, entry+i);
 124                        if (last >= 0) {
 125                                int cmp = entry_compare(entry+i, entry+last);
 126
 127                                /*
 128                                 * Is the new name bigger than the old one?
 129                                 * Ignore it
 130                                 */
 131                                if (cmp > 0)
 132                                        continue;
 133                                /*
 134                                 * Is the new name smaller than the old one?
 135                                 * Ignore all old ones
 136                                 */
 137                                if (cmp < 0)
 138                                        mask = 0;
 139                        }
 140                        mask |= 1ul << i;
 141                        last = i;
 142                }
 143                if (!mask)
 144                        break;
 145
 146                /*
 147                 * Update the tree entries we've walked, and clear
 148                 * all the unused name-entries.
 149                 */
 150                for (i = 0; i < n; i++) {
 151                        if (mask & (1ul << i)) {
 152                                update_tree_entry(t+i);
 153                                continue;
 154                        }
 155                        entry_clear(entry + i);
 156                }
 157                callback(n, mask, entry, base);
 158        }
 159        free(entry);
 160}
 161
 162static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char *result, unsigned *mode)
 163{
 164        int namelen = strlen(name);
 165        while (t->size) {
 166                const char *entry;
 167                const unsigned char *sha1;
 168                int entrylen, cmp;
 169
 170                sha1 = tree_entry_extract(t, &entry, mode);
 171                update_tree_entry(t);
 172                entrylen = strlen(entry);
 173                if (entrylen > namelen)
 174                        continue;
 175                cmp = memcmp(name, entry, entrylen);
 176                if (cmp > 0)
 177                        continue;
 178                if (cmp < 0)
 179                        break;
 180                if (entrylen == namelen) {
 181                        hashcpy(result, sha1);
 182                        return 0;
 183                }
 184                if (name[entrylen] != '/')
 185                        continue;
 186                if (!S_ISDIR(*mode))
 187                        break;
 188                if (++entrylen == namelen) {
 189                        hashcpy(result, sha1);
 190                        return 0;
 191                }
 192                return get_tree_entry(sha1, name + entrylen, result, mode);
 193        }
 194        return -1;
 195}
 196
 197int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned char *sha1, unsigned *mode)
 198{
 199        int retval;
 200        void *tree;
 201        struct tree_desc t;
 202        unsigned char root[20];
 203
 204        tree = read_object_with_reference(tree_sha1, tree_type, &t.size, root);
 205        if (!tree)
 206                return -1;
 207
 208        if (name[0] == '\0') {
 209                hashcpy(sha1, root);
 210                return 0;
 211        }
 212
 213        t.buf = tree;
 214        retval = find_tree_entry(&t, name, sha1, mode);
 215        free(tree);
 216        return retval;
 217}
 218