mirror of
git://projects.qi-hardware.com/wernermisc.git
synced 2025-04-21 12:27:27 +03:00
qpkg/: initial commit (sneak preview only, doesn't work properly yet)
This commit is contained in:
221
qpkg/prereq.c
Normal file
221
qpkg/prereq.c
Normal file
@@ -0,0 +1,221 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
|
||||
#include "id.h"
|
||||
#include "qpkg.h"
|
||||
#include "util.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;
|
||||
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)
|
||||
{
|
||||
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->value; pkg; pkg = pkg->more) {
|
||||
if (pkg->installed || pkg->mark) {
|
||||
resolve(next_dep, dep->next, conf);
|
||||
continue;
|
||||
}
|
||||
if (!satisfies(pkg, dep))
|
||||
continue;
|
||||
if (conflicts(pkg, conf))
|
||||
continue;
|
||||
push(pkg);
|
||||
more_deps.refs = pkg->depends;
|
||||
if (next_dep) {
|
||||
more_deps.next = next_dep->next;
|
||||
next_dep->next = &more_deps;
|
||||
} else {
|
||||
more_deps.next = NULL;
|
||||
}
|
||||
more_conf.refs = pkg->conflicts;
|
||||
more_conf.next = conf;
|
||||
resolve(next_dep ? next_dep : &more_deps, dep->next,
|
||||
&more_conf);
|
||||
if (next_dep)
|
||||
next_dep->next = more_deps.next;
|
||||
pop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user