#include #include #include #include #include #include "id.h" #include "qpkg.h" #include "util.h" #include "prereq.h" struct list { const struct ref *refs; struct list *next; }; static struct pkg **best = NULL; static struct pkg **stack = NULL; static int n_best; /* undefined if best == NULL */ static int n_stack = 0; static int stack_max = 0; #define ID2S(id) (int) (id)->len, (id)->s static int epoch(const char **s, const struct id *id) { const char *end = id->s+id->len; int n = 0; while (*s != end && isdigit(**s)) { n = 10*n+**s-'0'; (*s)++; } if (*s == id->s || (*s != end && **s == ':')) return n; *s = id->s; return 0; } static int comp_versions(const struct id *va, const struct id *vb) { int epoch_a = 0, epoch_b = 0; const char *a = va->s; const char *b = vb->s; const char *ea = a+va->len; const char *eb = b+vb->len; epoch_a = epoch(&a, va); epoch_b = epoch(&b, vb); if (epoch_a != epoch_b) return epoch_a-epoch_b; while (1) { if (a == ea || b == eb) return (a != ea)-(b != eb); if (isdigit(*a) && isdigit(*b)) { int na = 0, nb = 0; while (a != ea && isdigit(*a)) { na = na*10+*a-'0'; a++; } while (b != eb && isdigit(*b)) { nb = nb*10+*b-'0'; b++; } if (na == nb) continue; return na-nb; } if (*a > *b) return 1; if (*a < *b) return 1; a++; b++; } } static void done(void) { int size; if (best && n_best <= n_stack) return; free(best); size = sizeof(*stack)*(n_stack+1); best = alloc_size(size); memcpy(best, stack, sizeof(*stack)*n_stack); n_best = n_stack; best[n_best] = NULL; } static void push(struct pkg *pkg) { //fprintf(stderr, "push %.*s\n", ID2S(pkg->id)); if (n_stack == stack_max) { stack_max = (stack_max+1)*2; stack = realloc(stack, sizeof(*stack)*stack_max); if (!stack) { perror("realloc"); exit(1); } } stack[n_stack++] = pkg; assert(!pkg->mark && !pkg->installed); pkg->mark = 1; } static void pop(void) { assert(n_stack); n_stack--; stack[n_stack]->mark = 0; } static int satisfies(const struct pkg *pkg, const struct ref *ref) { int cmp; if (pkg->id != ref->pkg) return 0; if (!ref->version) return 1; assert(pkg->version); cmp = comp_versions(pkg->version, ref->version); //fprintf(stderr, "%.*s <%d> %.*s\n", // ID2S(pkg->version), cmp, ID2S(ref->version)); switch (ref->relop) { case rel_eq: return !cmp; case rel_ge: return cmp >= 0; case rel_lt: return cmp < 0; default: abort(); } } static int conflicts(const struct pkg *pkg, const struct list *conf) { /* @@@ */ return 0; } static void resolve(struct list *next_dep, const struct ref *dep, struct list *conf) { static int level = 0; struct list more_deps; struct list more_conf = { .next = conf }; struct pkg *pkg; while (!dep) { if (!next_dep) { done(); return; } dep = next_dep->refs; next_dep = next_dep->next; } for (pkg = dep->pkg->jrb->val; pkg; pkg = pkg->more) { if (best && n_stack == n_best) return; #if 0 fprintf(stderr, "%*s", level, ""); fprintf(stderr, "%.*s %p", ID2S(pkg->id), pkg); if (pkg->version) fprintf(stderr, " %.*s", ID2S(pkg->version)); if (pkg->mark) fprintf(stderr, " +"); if (pkg->installed) fprintf(stderr, " ***"); fprintf(stderr, "\n"); #endif if (!satisfies(pkg, dep)) continue; if (pkg->installed || pkg->mark) { assert(!conflicts(pkg, conf)); resolve(next_dep, dep->next, conf); continue; } if (conflicts(pkg, conf)) continue; level++; push(pkg); more_deps.refs = pkg->depends; more_deps.next = next_dep; more_conf.refs = pkg->conflicts; more_conf.next = conf; resolve(&more_deps, dep->next, &more_conf); next_dep = more_deps.next; pop(); level--; } } struct pkg **prereq(struct pkg *pkg) { struct list deps = { .refs = pkg->depends, .next = NULL }; #if 0 /* make sure we don't return NULL if all dependencies are met */ if (!stack) { stack = alloc_type(struct pkg *); stack_max = 1; } #endif /* @@@ make list of pre-existing conflicts */ resolve(&deps, NULL, NULL); free(stack); return best; }