#include "cache.h"
#include "refs.h"
#include "refs/refs-internal.h"
#include "iterator.h"
int ref_iterator_advance(struct ref_iterator *ref_iterator)
{
return ref_iterator->vtable->advance(ref_iterator);
}
int ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
return ref_iterator->vtable->peel(ref_iterator, peeled);
}
int ref_iterator_abort(struct ref_iterator *ref_iterator)
{
return ref_iterator->vtable->abort(ref_iterator);
}
void base_ref_iterator_init(struct ref_iterator *iter,
struct ref_iterator_vtable *vtable,
int ordered)
{
iter->vtable = vtable;
iter->ordered = !!ordered;
iter->refname = NULL;
iter->oid = NULL;
iter->flags = 0;
}
void base_ref_iterator_free(struct ref_iterator *iter)
{
iter->vtable = NULL;
free(iter);
}
struct empty_ref_iterator {
struct ref_iterator base;
};
static int empty_ref_iterator_advance(struct ref_iterator *ref_iterator)
{
return ref_iterator_abort(ref_iterator);
}
static int empty_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
BUG("peel called for empty iterator");
}
static int empty_ref_iterator_abort(struct ref_iterator *ref_iterator)
{
base_ref_iterator_free(ref_iterator);
return ITER_DONE;
}
static struct ref_iterator_vtable empty_ref_iterator_vtable = {
empty_ref_iterator_advance,
empty_ref_iterator_peel,
empty_ref_iterator_abort
};
struct ref_iterator *empty_ref_iterator_begin(void)
{
struct empty_ref_iterator *iter = xcalloc(1, sizeof(*iter));
struct ref_iterator *ref_iterator = &iter->base;
base_ref_iterator_init(ref_iterator, &empty_ref_iterator_vtable, 1);
return ref_iterator;
}
int is_empty_ref_iterator(struct ref_iterator *ref_iterator)
{
return ref_iterator->vtable == &empty_ref_iterator_vtable;
}
struct merge_ref_iterator {
struct ref_iterator base;
struct ref_iterator *iter0, *iter1;
ref_iterator_select_fn *select;
void *cb_data;
struct ref_iterator **current;
};
static int merge_ref_iterator_advance(struct ref_iterator *ref_iterator)
{
struct merge_ref_iterator *iter =
(struct merge_ref_iterator *)ref_iterator;
int ok;
if (!iter->current) {
if ((ok = ref_iterator_advance(iter->iter0)) != ITER_OK) {
iter->iter0 = NULL;
if (ok == ITER_ERROR)
goto error;
}
if ((ok = ref_iterator_advance(iter->iter1)) != ITER_OK) {
iter->iter1 = NULL;
if (ok == ITER_ERROR)
goto error;
}
} else {
if ((ok = ref_iterator_advance(*iter->current)) != ITER_OK) {
*iter->current = NULL;
if (ok == ITER_ERROR)
goto error;
}
}
while (1) {
struct ref_iterator **secondary;
enum iterator_selection selection =
iter->select(iter->iter0, iter->iter1, iter->cb_data);
if (selection == ITER_SELECT_DONE) {
return ref_iterator_abort(ref_iterator);
} else if (selection == ITER_SELECT_ERROR) {
ref_iterator_abort(ref_iterator);
return ITER_ERROR;
}
if ((selection & ITER_CURRENT_SELECTION_MASK) == 0) {
iter->current = &iter->iter0;
secondary = &iter->iter1;
} else {
iter->current = &iter->iter1;
secondary = &iter->iter0;
}
if (selection & ITER_SKIP_SECONDARY) {
if ((ok = ref_iterator_advance(*secondary)) != ITER_OK) {
*secondary = NULL;
if (ok == ITER_ERROR)
goto error;
}
}
if (selection & ITER_YIELD_CURRENT) {
iter->base.refname = (*iter->current)->refname;
iter->base.oid = (*iter->current)->oid;
iter->base.flags = (*iter->current)->flags;
return ITER_OK;
}
}
error:
ref_iterator_abort(ref_iterator);
return ITER_ERROR;
}
static int merge_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
struct merge_ref_iterator *iter =
(struct merge_ref_iterator *)ref_iterator;
if (!iter->current) {
BUG("peel called before advance for merge iterator");
}
return ref_iterator_peel(*iter->current, peeled);
}
static int merge_ref_iterator_abort(struct ref_iterator *ref_iterator)
{
struct merge_ref_iterator *iter =
(struct merge_ref_iterator *)ref_iterator;
int ok = ITER_DONE;
if (iter->iter0) {
if (ref_iterator_abort(iter->iter0) != ITER_DONE)
ok = ITER_ERROR;
}
if (iter->iter1) {
if (ref_iterator_abort(iter->iter1) != ITER_DONE)
ok = ITER_ERROR;
}
base_ref_iterator_free(ref_iterator);
return ok;
}
static struct ref_iterator_vtable merge_ref_iterator_vtable = {
merge_ref_iterator_advance,
merge_ref_iterator_peel,
merge_ref_iterator_abort
};
struct ref_iterator *merge_ref_iterator_begin(
int ordered,
struct ref_iterator *iter0, struct ref_iterator *iter1,
ref_iterator_select_fn *select, void *cb_data)
{
struct merge_ref_iterator *iter = xcalloc(1, sizeof(*iter));
struct ref_iterator *ref_iterator = &iter->base;
base_ref_iterator_init(ref_iterator, &merge_ref_iterator_vtable, ordered);
iter->iter0 = iter0;
iter->iter1 = iter1;
iter->select = select;
iter->cb_data = cb_data;
iter->current = NULL;
return ref_iterator;
}
static enum iterator_selection overlay_iterator_select(
struct ref_iterator *front, struct ref_iterator *back,
void *cb_data)
{
int cmp;
if (!back)
return front ? ITER_SELECT_0 : ITER_SELECT_DONE;
else if (!front)
return ITER_SELECT_1;
cmp = strcmp(front->refname, back->refname);
if (cmp < 0)
return ITER_SELECT_0;
else if (cmp > 0)
return ITER_SELECT_1;
else
return ITER_SELECT_0_SKIP_1;
}
struct ref_iterator *overlay_ref_iterator_begin(
struct ref_iterator *front, struct ref_iterator *back)
{
if (is_empty_ref_iterator(front)) {
ref_iterator_abort(front);
return back;
} else if (is_empty_ref_iterator(back)) {
ref_iterator_abort(back);
return front;
} else if (!front->ordered || !back->ordered) {
BUG("overlay_ref_iterator requires ordered inputs");
}
return merge_ref_iterator_begin(1, front, back,
overlay_iterator_select, NULL);
}
struct prefix_ref_iterator {
struct ref_iterator base;
struct ref_iterator *iter0;
char *prefix;
int trim;
};
static int compare_prefix(const char *refname, const char *prefix)
{
while (*prefix) {
if (*refname != *prefix)
return ((unsigned char)*refname < (unsigned char)*prefix) ? -1 : +1;
refname++;
prefix++;
}
return 0;
}
static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
{
struct prefix_ref_iterator *iter =
(struct prefix_ref_iterator *)ref_iterator;
int ok;
while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
int cmp = compare_prefix(iter->iter0->refname, iter->prefix);
if (cmp < 0)
continue;
if (cmp > 0) {
if (iter->iter0->ordered) {
ok = ref_iterator_abort(iter->iter0);
break;
} else {
continue;
}
}
if (iter->trim) {
if (strlen(iter->iter0->refname) <= iter->trim)
BUG("attempt to trim too many characters");
iter->base.refname = iter->iter0->refname + iter->trim;
} else {
iter->base.refname = iter->iter0->refname;
}
iter->base.oid = iter->iter0->oid;
iter->base.flags = iter->iter0->flags;
return ITER_OK;
}
iter->iter0 = NULL;
if (ref_iterator_abort(ref_iterator) != ITER_DONE)
return ITER_ERROR;
return ok;
}
static int prefix_ref_iterator_peel(struct ref_iterator *ref_iterator,
struct object_id *peeled)
{
struct prefix_ref_iterator *iter =
(struct prefix_ref_iterator *)ref_iterator;
return ref_iterator_peel(iter->iter0, peeled);
}
static int prefix_ref_iterator_abort(struct ref_iterator *ref_iterator)
{
struct prefix_ref_iterator *iter =
(struct prefix_ref_iterator *)ref_iterator;
int ok = ITER_DONE;
if (iter->iter0)
ok = ref_iterator_abort(iter->iter0);
free(iter->prefix);
base_ref_iterator_free(ref_iterator);
return ok;
}
static struct ref_iterator_vtable prefix_ref_iterator_vtable = {
prefix_ref_iterator_advance,
prefix_ref_iterator_peel,
prefix_ref_iterator_abort
};
struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
const char *prefix,
int trim)
{
struct prefix_ref_iterator *iter;
struct ref_iterator *ref_iterator;
if (!*prefix && !trim)
return iter0;
iter = xcalloc(1, sizeof(*iter));
ref_iterator = &iter->base;
base_ref_iterator_init(ref_iterator, &prefix_ref_iterator_vtable, iter0->ordered);
iter->iter0 = iter0;
iter->prefix = xstrdup(prefix);
iter->trim = trim;
return ref_iterator;
}
struct ref_iterator *current_ref_iter = NULL;
int do_for_each_repo_ref_iterator(struct repository *r, struct ref_iterator *iter,
each_repo_ref_fn fn, void *cb_data)
{
int retval = 0, ok;
struct ref_iterator *old_ref_iter = current_ref_iter;
current_ref_iter = iter;
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
retval = fn(r, iter->refname, iter->oid, iter->flags, cb_data);
if (retval) {
ref_iterator_abort(iter);
goto out;
}
}
out:
current_ref_iter = old_ref_iter;
if (ok == ITER_ERROR)
return -1;
return retval;
}