+ if (last) {
+ if (last->data)
+ free(last->data);
+ last->data = dat;
+ last->len = datlen;
+ memcpy(last->sha1, sha1, sizeof(sha1));
+ }
+ return 0;
+}
+
+static const char *get_mode(const char *str, unsigned int *modep)
+{
+ unsigned char c;
+ unsigned int mode = 0;
+
+ while ((c = *str++) != ' ') {
+ if (c < '0' || c > '7')
+ return NULL;
+ mode = (mode << 3) + (c - '0');
+ }
+ *modep = mode;
+ return str;
+}
+
+static void load_tree(struct tree_entry *root)
+{
+ struct object_entry *myoe;
+ struct tree_content *t;
+ unsigned long size;
+ char *buf;
+ const char *c;
+ char type[20];
+
+ root->tree = t = new_tree_content(8);
+ if (!memcmp(root->sha1, null_sha1, 20))
+ return;
+
+ myoe = find_object(root->sha1);
+ if (myoe) {
+ die("FIXME");
+ } else {
+ buf = read_sha1_file(root->sha1, type, &size);
+ if (!buf || strcmp(type, tree_type))
+ die("Can't load existing tree %s", sha1_to_hex(root->sha1));
+ }
+
+ c = buf;
+ while (c != (buf + size)) {
+ struct tree_entry *e = new_tree_entry();
+
+ if (t->entry_count == t->entry_capacity)
+ root->tree = t = grow_tree_content(t, 8);
+ t->entries[t->entry_count++] = e;
+
+ e->tree = NULL;
+ c = get_mode(c, &e->mode);
+ if (!c)
+ die("Corrupt mode in %s", sha1_to_hex(root->sha1));
+ e->name = to_atom(c, strlen(c));
+ c += e->name->str_len + 1;
+ memcpy(e->sha1, c, sizeof(e->sha1));
+ c += 20;
+ }
+ free(buf);
+}
+
+static int tecmp (const void *_a, const void *_b)
+{
+ struct tree_entry *a = *((struct tree_entry**)_a);
+ struct tree_entry *b = *((struct tree_entry**)_b);
+ return base_name_compare(
+ a->name->str_dat, a->name->str_len, a->mode,
+ b->name->str_dat, b->name->str_len, b->mode);
+}
+
+static void store_tree(struct tree_entry *root)
+{
+ struct tree_content *t = root->tree;
+ unsigned int i;
+ size_t maxlen;
+ char *buf, *c;
+
+ if (memcmp(root->sha1, null_sha1, 20))
+ return;
+
+ maxlen = 0;
+ for (i = 0; i < t->entry_count; i++) {
+ maxlen += t->entries[i]->name->str_len + 34;
+ if (t->entries[i]->tree)
+ store_tree(t->entries[i]);
+ }
+
+ qsort(t->entries, t->entry_count, sizeof(t->entries[0]), tecmp);
+ buf = c = xmalloc(maxlen);
+ for (i = 0; i < t->entry_count; i++) {
+ struct tree_entry *e = t->entries[i];
+ c += sprintf(c, "%o", e->mode);
+ *c++ = ' ';
+ strcpy(c, e->name->str_dat);
+ c += e->name->str_len + 1;
+ memcpy(c, e->sha1, 20);
+ c += 20;
+ }
+ store_object(OBJ_TREE, buf, c - buf, NULL, root->sha1);
+ free(buf);
+}
+
+static int tree_content_set(
+ struct tree_entry *root,
+ const char *p,
+ const unsigned char *sha1,
+ const unsigned int mode)
+{
+ struct tree_content *t = root->tree;
+ const char *slash1;
+ unsigned int i, n;
+ struct tree_entry *e;
+
+ slash1 = strchr(p, '/');
+ if (slash1)
+ n = slash1 - p;
+ else
+ n = strlen(p);
+
+ for (i = 0; i < t->entry_count; i++) {
+ e = t->entries[i];
+ if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) {
+ if (!slash1) {
+ if (e->mode == mode && !memcmp(e->sha1, sha1, 20))
+ return 0;
+ e->mode = mode;
+ memcpy(e->sha1, sha1, 20);
+ if (e->tree) {
+ release_tree_content(e->tree);
+ e->tree = NULL;
+ }
+ memcpy(root->sha1, null_sha1, 20);
+ return 1;
+ }
+ if (!S_ISDIR(e->mode)) {
+ e->tree = new_tree_content(8);
+ e->mode = 040000;
+ }
+ if (!e->tree)
+ load_tree(e);
+ if (tree_content_set(e, slash1 + 1, sha1, mode)) {
+ memcpy(root->sha1, null_sha1, 20);
+ return 1;
+ }
+ return 0;
+ }
+ }
+
+ if (t->entry_count == t->entry_capacity)
+ root->tree = t = grow_tree_content(t, 8);
+ e = new_tree_entry();
+ e->name = to_atom(p, n);
+ t->entries[t->entry_count++] = e;
+ if (slash1) {
+ e->tree = new_tree_content(8);
+ e->mode = 040000;
+ tree_content_set(e, slash1 + 1, sha1, mode);
+ } else {
+ e->tree = NULL;
+ e->mode = mode;
+ memcpy(e->sha1, sha1, 20);
+ }
+ memcpy(root->sha1, null_sha1, 20);
+ return 1;
+}
+
+static int tree_content_remove(struct tree_entry *root, const char *p)
+{
+ struct tree_content *t = root->tree;
+ const char *slash1;
+ unsigned int i, n;
+ struct tree_entry *e;
+
+ slash1 = strchr(p, '/');
+ if (slash1)
+ n = slash1 - p;
+ else
+ n = strlen(p);
+
+ for (i = 0; i < t->entry_count; i++) {
+ e = t->entries[i];
+ if (e->name->str_len == n && !strncmp(p, e->name->str_dat, n)) {
+ if (!slash1 || !S_ISDIR(e->mode))
+ goto del_entry;
+ if (!e->tree)
+ load_tree(e);
+ if (tree_content_remove(e, slash1 + 1)) {
+ if (!e->tree->entry_count)
+ goto del_entry;
+ memcpy(root->sha1, null_sha1, 20);
+ return 1;
+ }
+ return 0;
+ }
+ }
+ return 0;
+
+del_entry:
+ for (i++; i < t->entry_count; i++)
+ t->entries[i-1] = t->entries[i];
+ t->entry_count--;
+ release_tree_entry(e);
+ memcpy(root->sha1, null_sha1, 20);