int git_path_make_relative(git_buf *path, const char *parent) { const char *p, *q, *p_dirsep, *q_dirsep; size_t plen = path->size, newlen, alloclen, depth = 1, i, offset; for (p_dirsep = p = path->ptr, q_dirsep = q = parent; *p && *q; p++, q++) { if (*p == '/' && *q == '/') { p_dirsep = p; q_dirsep = q; } else if (*p != *q) break; } /* need at least 1 common path segment */ if ((p_dirsep == path->ptr || q_dirsep == parent) && (*p_dirsep != '/' || *q_dirsep != '/')) { giterr_set(GITERR_INVALID, "%s is not a parent of %s", parent, path->ptr); return GIT_ENOTFOUND; } if (*p == '/' && !*q) p++; else if (!*p && *q == '/') q++; else if (!*p && !*q) return git_buf_clear(path), 0; else { p = p_dirsep + 1; q = q_dirsep + 1; } plen -= (p - path->ptr); if (!*q) return git_buf_set(path, p, plen); for (; (q = strchr(q, '/')) && *(q + 1); q++) depth++; GITERR_CHECK_ALLOC_MULTIPLY(&newlen, depth, 3); GITERR_CHECK_ALLOC_ADD(&newlen, newlen, plen); GITERR_CHECK_ALLOC_ADD(&alloclen, newlen, 1); /* save the offset as we might realllocate the pointer */ offset = p - path->ptr; if (git_buf_try_grow(path, alloclen, 1, 0) < 0) return -1; p = path->ptr + offset; memmove(path->ptr + (depth * 3), p, plen + 1); for (i = 0; i < depth; i++) memcpy(path->ptr + (i * 3), "../", 3); path->size = newlen; return 0; }
static int tree_iterator__push_frame(tree_iterator *ti) { int error = 0; tree_iterator_frame *head = ti->head, *tf = NULL; size_t i, n_entries = 0, alloclen; if (head->current >= head->n_entries || !head->entries[head->current]->tree) return GIT_ITEROVER; for (i = head->current; i < head->next; ++i) n_entries += git_tree_entrycount(head->entries[i]->tree); GITERR_CHECK_ALLOC_MULTIPLY(&alloclen, sizeof(tree_iterator_entry *), n_entries); GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, sizeof(tree_iterator_frame)); tf = git__calloc(1, alloclen); GITERR_CHECK_ALLOC(tf); tf->n_entries = n_entries; tf->up = head; head->down = tf; ti->head = tf; for (i = head->current, n_entries = 0; i < head->next; ++i) { git_tree *tree = head->entries[i]->tree; size_t j, max_j = git_tree_entrycount(tree); for (j = 0; j < max_j; ++j) { tree_iterator_entry *entry = git_pool_malloc(&ti->pool, 1); GITERR_CHECK_ALLOC(entry); entry->parent = head->entries[i]; entry->te = git_tree_entry_byindex(tree, j); entry->tree = NULL; tf->entries[n_entries++] = entry; } } /* if ignore_case, sort entries case insensitively */ if (iterator__ignore_case(ti)) git__tsort_r( (void **)tf->entries, tf->n_entries, tree_iterator__ci_cmp, tf); /* pick tf->current based on "start" (or start at zero) */ if (head->startlen > 0) { git__bsearch_r((void **)tf->entries, tf->n_entries, head, tree_iterator__search_cmp, ti, &tf->current); while (tf->current && !tree_iterator__search_cmp(head, tf->entries[tf->current-1], ti)) tf->current--; if ((tf->start = strchr(head->start, '/')) != NULL) { tf->start++; tf->startlen = strlen(tf->start); } } ti->path_has_filename = ti->entry_is_current = false; if ((error = tree_iterator__set_next(ti, tf)) < 0) return error; /* autoexpand as needed */ if (!iterator__include_trees(ti) && tree_iterator__at_tree(ti)) return tree_iterator__push_frame(ti); return 0; }