mirror of
git://projects.qi-hardware.com/wernermisc.git
synced 2024-11-23 22:44:06 +02:00
qpkg/: initial commit (sneak preview only, doesn't work properly yet)
This commit is contained in:
commit
43b9dc1972
11
qpkg/Makefile
Normal file
11
qpkg/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
CFLAGS = -Wall -Wshadow -g
|
||||
# -O, so that we get data flow analysis, which helps to find more bugs
|
||||
|
||||
OBJS = gobble.o id.o prereq.o qpkg.o
|
||||
|
||||
all: qpkg
|
||||
|
||||
qpkg: $(OBJS)
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS)
|
398
qpkg/gobble.c
Normal file
398
qpkg/gobble.c
Normal file
@ -0,0 +1,398 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "id.h"
|
||||
#include "qpkg.h"
|
||||
#include "gobble.h"
|
||||
|
||||
|
||||
#define CHARS_AFTER_ERROR 20
|
||||
|
||||
|
||||
#define EXPECT(s) \
|
||||
do { \
|
||||
if (end-buf < sizeof(s)-1) \
|
||||
FAIL; \
|
||||
if (memcmp(buf, s, sizeof(s)-1)) \
|
||||
FAIL; \
|
||||
buf += sizeof(s)-1; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
|
||||
#define NEXT (buf == end ? '?' : *buf++)
|
||||
|
||||
|
||||
#define WHITESPACE \
|
||||
do { \
|
||||
if (buf == end) \
|
||||
FAIL; \
|
||||
if (*buf == '\n') \
|
||||
break; \
|
||||
if (!isspace(*buf)) \
|
||||
break; \
|
||||
buf++; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
|
||||
#define ISTERM(c) \
|
||||
((c) == ' ' || (c) == '\t' || (c) == '\n' || \
|
||||
(c) == ',' || (c) == ')')
|
||||
|
||||
|
||||
#define ID(tree) \
|
||||
({ \
|
||||
const char *start; \
|
||||
\
|
||||
if (buf == end) \
|
||||
FAIL; \
|
||||
start = buf; \
|
||||
while (buf != end && !ISTERM(*buf)) \
|
||||
buf++; \
|
||||
make_id(tree, start, buf-start); \
|
||||
})
|
||||
|
||||
|
||||
#define FAIL \
|
||||
do { \
|
||||
failed_at = __LINE__; \
|
||||
goto fail; \
|
||||
} \
|
||||
while (0)
|
||||
|
||||
|
||||
static void gobble_buf(const char *name, const char *buf, size_t len)
|
||||
{
|
||||
const char *end = buf+len;
|
||||
int lineno = 1;
|
||||
struct pkg *pkg = NULL; /* current package */
|
||||
struct ref **anchor = NULL;
|
||||
int i, failed_at = 0;
|
||||
|
||||
initial:
|
||||
if (buf == end)
|
||||
return;
|
||||
if (*buf == '\n') {
|
||||
lineno++;
|
||||
buf++;
|
||||
goto initial;
|
||||
}
|
||||
|
||||
/* decode the tag */
|
||||
|
||||
switch (*buf++) {
|
||||
case 'A': /* Architecture // Auto-Installed */
|
||||
switch (NEXT) {
|
||||
case 'r':
|
||||
EXPECT("chitecture:");
|
||||
/* @@@ use later */
|
||||
goto skip_data;
|
||||
case 'u':
|
||||
EXPECT("to-Installed:");
|
||||
goto skip_data;
|
||||
default:
|
||||
FAIL;
|
||||
}
|
||||
|
||||
case 'C': /* Conflicts // Conffiles */
|
||||
EXPECT("onf");
|
||||
switch (NEXT) {
|
||||
case 'l':
|
||||
EXPECT("icts:");
|
||||
goto conflicts;
|
||||
case 'f':
|
||||
EXPECT("iles:");
|
||||
goto skip_data;
|
||||
default:
|
||||
FAIL;
|
||||
}
|
||||
|
||||
case 'D': /* Depends, Description */
|
||||
EXPECT("e");
|
||||
switch (NEXT) {
|
||||
case 'p':
|
||||
EXPECT("ends:");
|
||||
goto depends;
|
||||
case 's':
|
||||
EXPECT("cription:");
|
||||
goto skip_data;
|
||||
default:
|
||||
FAIL;
|
||||
}
|
||||
|
||||
case 'F': /* Filename */
|
||||
EXPECT("ilename:");
|
||||
goto filename;
|
||||
|
||||
case 'H': /* HomePage, Homepage */
|
||||
EXPECT("ome");
|
||||
switch (NEXT) {
|
||||
case 'P':
|
||||
case 'p':
|
||||
EXPECT("age:");
|
||||
goto skip_data;
|
||||
default:
|
||||
FAIL;
|
||||
}
|
||||
|
||||
case 'I': /* Installed-Time */
|
||||
EXPECT("nstalled-Time:");
|
||||
goto skip_data;
|
||||
|
||||
case 'L': /* License */
|
||||
EXPECT("icense:");
|
||||
goto skip_data;
|
||||
|
||||
case 'M': /* Maintainer, MD5Sum */
|
||||
switch (NEXT) {
|
||||
case 'a':
|
||||
EXPECT("intainer:");
|
||||
goto skip_data;
|
||||
case 'D':
|
||||
EXPECT("5Sum:");
|
||||
goto skip_data;
|
||||
default:
|
||||
FAIL;
|
||||
}
|
||||
|
||||
case 'O': /* OE */
|
||||
EXPECT("E:");
|
||||
goto skip_data;
|
||||
|
||||
case 'P': /* Package, Priority, Provides */
|
||||
switch (NEXT) {
|
||||
case 'a':
|
||||
EXPECT("ckage:");
|
||||
goto package;
|
||||
case 'r':
|
||||
break;
|
||||
default:
|
||||
FAIL;
|
||||
}
|
||||
switch (NEXT) {
|
||||
case 'i':
|
||||
EXPECT("ority:");
|
||||
goto skip_data;
|
||||
case 'o':
|
||||
EXPECT("vides:");
|
||||
goto provides;
|
||||
default:
|
||||
FAIL;
|
||||
}
|
||||
|
||||
case 'R': /* Recommends, Replaces */
|
||||
EXPECT("e");
|
||||
switch (NEXT) {
|
||||
case 'c':
|
||||
EXPECT("ommends:");
|
||||
goto skip_data;
|
||||
case 'p':
|
||||
EXPECT("laces:");
|
||||
goto skip_data;
|
||||
default:
|
||||
FAIL;
|
||||
}
|
||||
|
||||
case 'S': /* Section, Size, Source, Suggests // Status */
|
||||
switch (NEXT) {
|
||||
case 'e':
|
||||
EXPECT("ction:");
|
||||
goto skip_data;
|
||||
case 'i':
|
||||
EXPECT("ze:");
|
||||
goto skip_data;
|
||||
case 'o':
|
||||
EXPECT("urce:");
|
||||
goto skip_data;
|
||||
case 'u':
|
||||
EXPECT("ggests:");
|
||||
goto skip_data;
|
||||
case 't':
|
||||
EXPECT("atus:");
|
||||
goto status;
|
||||
default:
|
||||
FAIL;
|
||||
}
|
||||
|
||||
case 'V': /* Version */
|
||||
EXPECT("ersion:");
|
||||
goto version;
|
||||
|
||||
default:
|
||||
FAIL;
|
||||
}
|
||||
|
||||
conflicts:
|
||||
anchor = &pkg->conflicts;
|
||||
goto list_with_version;
|
||||
|
||||
depends:
|
||||
anchor = &pkg->depends;
|
||||
goto list_with_version;
|
||||
|
||||
package:
|
||||
WHITESPACE;
|
||||
pkg = alloc_type(struct pkg);
|
||||
pkg->id = ID(packages);
|
||||
pkg->more = pkg->id->value ? pkg->id->value : NULL;
|
||||
pkg->id->value = pkg;
|
||||
pkg->version = NULL;
|
||||
pkg->filename = NULL;
|
||||
pkg->installed = 0;
|
||||
pkg->mark = 0;
|
||||
goto eol;
|
||||
|
||||
version:
|
||||
WHITESPACE;
|
||||
if (pkg->version)
|
||||
FAIL;
|
||||
pkg->version = ID(versions);
|
||||
goto eol;
|
||||
|
||||
provides:
|
||||
/* @@@ later */
|
||||
goto skip_data;
|
||||
|
||||
status:
|
||||
pkg->installed = 1;
|
||||
/* @@@ later */
|
||||
goto skip_data;
|
||||
|
||||
filename:
|
||||
WHITESPACE;
|
||||
pkg->filename = buf;
|
||||
goto skip_data;
|
||||
|
||||
eol:
|
||||
while (buf != end) {
|
||||
if (*buf == ' ' || *buf == '\t') {
|
||||
buf++;
|
||||
continue;
|
||||
}
|
||||
if (*buf++ != '\n')
|
||||
FAIL;
|
||||
lineno++;
|
||||
if (buf == end)
|
||||
return;
|
||||
if (*buf == ' ' || *buf == '\t')
|
||||
FAIL;
|
||||
goto initial;
|
||||
}
|
||||
return;
|
||||
|
||||
skip_data:
|
||||
while (buf != end) {
|
||||
if (*buf++ != '\n')
|
||||
continue;
|
||||
lineno++;
|
||||
if (buf == end)
|
||||
return;
|
||||
if (*buf != ' ' && *buf != '\t')
|
||||
goto initial;
|
||||
}
|
||||
return;
|
||||
|
||||
list_with_version:
|
||||
while (1) {
|
||||
struct ref *ref;
|
||||
|
||||
WHITESPACE;
|
||||
ref = alloc_type(struct ref);
|
||||
ref->pkg = ID(packages);
|
||||
|
||||
/*
|
||||
* Work around the Wireshark Anomaly
|
||||
*/
|
||||
if (buf != end && *buf == ')')
|
||||
buf++;
|
||||
|
||||
WHITESPACE;
|
||||
if (buf == end || *buf != '(')
|
||||
ref->version = NULL;
|
||||
else {
|
||||
buf++;
|
||||
if (buf == end)
|
||||
FAIL;
|
||||
switch (*buf++) {
|
||||
case '=':
|
||||
ref->relop = rel_eq;
|
||||
break;
|
||||
case '<':
|
||||
ref->relop = rel_lt;
|
||||
break;
|
||||
case '>':
|
||||
EXPECT("=");
|
||||
ref->relop = rel_ge;
|
||||
break;
|
||||
default:
|
||||
FAIL;
|
||||
}
|
||||
WHITESPACE;
|
||||
ref->version = ID(versions);
|
||||
EXPECT(")");
|
||||
}
|
||||
ref->next = *anchor;
|
||||
*anchor = ref;
|
||||
if (buf == end)
|
||||
return;
|
||||
if (*buf != ',')
|
||||
break;
|
||||
buf++;
|
||||
}
|
||||
anchor = NULL;
|
||||
goto eol;
|
||||
|
||||
fail:
|
||||
fprintf(stderr, "syntax derailment #%d at %s line %d: ",
|
||||
failed_at, name, lineno);
|
||||
for (i = 0; i != CHARS_AFTER_ERROR && buf != end; i++) {
|
||||
if (*buf == '\n')
|
||||
fprintf(stderr, "\\n");
|
||||
else if (isspace(*buf))
|
||||
fputc(' ', stderr);
|
||||
else if (isprint(*buf))
|
||||
fputc(*buf, stderr);
|
||||
buf++;
|
||||
}
|
||||
fprintf(stderr, "%s\n", buf == end ? "": "...");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
void gobble(const char *name)
|
||||
{
|
||||
int fd;
|
||||
struct stat st;
|
||||
void *map;
|
||||
|
||||
fd = open(name, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
perror(name);
|
||||
exit(1);
|
||||
}
|
||||
if (fstat(fd, &st) < 0) {
|
||||
perror("fstat");
|
||||
exit(1);
|
||||
}
|
||||
map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
|
||||
if (map == MAP_FAILED) {
|
||||
perror("mmap");
|
||||
exit(1);
|
||||
}
|
||||
if (posix_madvise(map, st.st_size, POSIX_MADV_WILLNEED) < 0) {
|
||||
perror("posix_madvise(POSIX_MADV_WILLNEED)");
|
||||
exit(1);
|
||||
}
|
||||
gobble_buf(name, map, st.st_size);
|
||||
if (posix_madvise(map, st.st_size, POSIX_MADV_RANDOM) < 0) {
|
||||
perror("posix_madvise(POSIX_MADV_RANDOM)");
|
||||
exit(1);
|
||||
}
|
||||
}
|
6
qpkg/gobble.h
Normal file
6
qpkg/gobble.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef GOBBLE_H
|
||||
#define GOBBLE_H
|
||||
|
||||
void gobble(const char *name);
|
||||
|
||||
#endif /* !GOBBLE_H */
|
129
qpkg/id.c
Normal file
129
qpkg/id.c
Normal file
@ -0,0 +1,129 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "util.h"
|
||||
#include "id.h"
|
||||
|
||||
|
||||
#define NODE2ID(n) ((struct id *) n)
|
||||
|
||||
|
||||
static struct id *free_id = NULL;
|
||||
|
||||
|
||||
int comp_id(const struct id *a, const struct id *b)
|
||||
{
|
||||
int len = a->len < b->len ? a->len : b->len;
|
||||
int cmp;
|
||||
|
||||
cmp = memcmp(a->s, b->s, len);
|
||||
if (cmp)
|
||||
return cmp;
|
||||
return a->len < b->len ? -1 : a->len > b->len;
|
||||
}
|
||||
|
||||
|
||||
struct tree *make_tree(int (*comp)(const struct id *a, const struct id *b))
|
||||
{
|
||||
struct tree *tree;
|
||||
|
||||
tree = alloc_type(struct tree);
|
||||
tree->comp = comp;
|
||||
tree->root = NULL;
|
||||
return tree;
|
||||
}
|
||||
|
||||
|
||||
static struct node *add_node(struct tree *tree, struct node *node)
|
||||
{
|
||||
struct node **p, *last;
|
||||
int cmp;
|
||||
|
||||
p = &tree->root;
|
||||
last = NULL;
|
||||
while (*p) {
|
||||
last = *p;
|
||||
cmp = tree->comp(NODE2ID(node), NODE2ID(*p));
|
||||
if (cmp < 0)
|
||||
p = &(*p)->left;
|
||||
else if (cmp > 0)
|
||||
p = &(*p)->right;
|
||||
else
|
||||
return *p;
|
||||
}
|
||||
*p = node;
|
||||
node->up = last;
|
||||
node->left = node->right = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
struct id *make_id(struct tree *tree, const char *s, size_t len)
|
||||
{
|
||||
struct id *id;
|
||||
struct node *node;
|
||||
|
||||
if (!free_id)
|
||||
free_id = alloc_type(struct id);
|
||||
id = free_id;
|
||||
id->s = s;
|
||||
id->len = len;
|
||||
id->value = NULL;
|
||||
node = add_node(tree, &id->node);
|
||||
if (node)
|
||||
return NODE2ID(node);
|
||||
free_id = NULL;
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
const struct id *find_id(const struct tree *tree, const char *s, size_t len)
|
||||
{
|
||||
struct id id = {
|
||||
.s = s,
|
||||
.len = len
|
||||
};
|
||||
const struct node *n = tree->root;
|
||||
int cmp;
|
||||
|
||||
while (n) {
|
||||
cmp = tree->comp(&id, NODE2ID(n));
|
||||
if (cmp < 0)
|
||||
n = n->left;
|
||||
else if (cmp > 0)
|
||||
n = n->right;
|
||||
else
|
||||
return NODE2ID(n);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
const struct id *first_id(const struct tree *tree)
|
||||
{
|
||||
const struct node *n = tree->root;
|
||||
|
||||
if (!n)
|
||||
return NULL;
|
||||
while (n->left)
|
||||
n = n->left;
|
||||
return NODE2ID(n);
|
||||
}
|
||||
|
||||
|
||||
const struct id *next_id(const struct id *id)
|
||||
{
|
||||
const struct node *n = &id->node;
|
||||
|
||||
if (n->right) {
|
||||
n = n->right;
|
||||
while (n->left)
|
||||
n = n->left;
|
||||
return NODE2ID(n);
|
||||
}
|
||||
while (n->up) {
|
||||
if (n == n->up->left)
|
||||
return NODE2ID(n->up);
|
||||
n = n->up;
|
||||
}
|
||||
return NULL;
|
||||
}
|
39
qpkg/id.h
Normal file
39
qpkg/id.h
Normal file
@ -0,0 +1,39 @@
|
||||
#ifndef ID_H
|
||||
#define ID_H
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
|
||||
struct id;
|
||||
|
||||
/*
|
||||
* @@@ basic binary trees are not a good choice. should use rb.
|
||||
* To ease future migration, we separate the node structure from the rest.
|
||||
*/
|
||||
|
||||
struct node {
|
||||
struct node *up, *left, *right;
|
||||
};
|
||||
|
||||
struct tree {
|
||||
int (*comp)(const struct id *a, const struct id *b);
|
||||
struct node *root;
|
||||
};
|
||||
|
||||
struct id {
|
||||
struct node node;
|
||||
const char *s;
|
||||
size_t len;
|
||||
void *value;
|
||||
};
|
||||
|
||||
|
||||
int comp_id(const struct id *a, const struct id *b);
|
||||
|
||||
struct tree *make_tree(int (*comp)(const struct id *a, const struct id *b));
|
||||
struct id *make_id(struct tree *tree, const char *s, size_t len);
|
||||
const struct id *find_id(const struct tree *tree, const char *s, size_t len);
|
||||
const struct id *first_id(const struct tree *tree);
|
||||
const struct id *next_id(const struct id *id);
|
||||
|
||||
#endif /* !ID_H */
|
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;
|
||||
}
|
6
qpkg/prereq.h
Normal file
6
qpkg/prereq.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef PREREQ_H
|
||||
#define PREREQ_H
|
||||
|
||||
struct pkg **prereq(struct pkg *pkg);
|
||||
|
||||
#endif /* !PREREQ_H */
|
144
qpkg/qpkg.c
Normal file
144
qpkg/qpkg.c
Normal file
@ -0,0 +1,144 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "id.h"
|
||||
#include "prereq.h"
|
||||
#include "gobble.h"
|
||||
#include "qpkg.h"
|
||||
|
||||
|
||||
struct tree *packages = NULL;
|
||||
struct tree *versions = NULL;
|
||||
|
||||
|
||||
static void list_all_packages(void)
|
||||
{
|
||||
const struct id *id;
|
||||
|
||||
for (id = first_id(packages); id; id = next_id(id)) {
|
||||
struct pkg *pkg = id->value;
|
||||
|
||||
printf("%.*s", (int) id->len, id->s);
|
||||
if (!pkg)
|
||||
printf(" (virtual)");
|
||||
else {
|
||||
if (pkg->version)
|
||||
printf(" (%.*s)",
|
||||
(int) pkg->version->len, pkg->version->s);
|
||||
if (pkg->more)
|
||||
printf(" +++");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void list_one_package(const char *name)
|
||||
{
|
||||
const struct id *id = find_id(packages, name, strlen(name));
|
||||
const struct pkg *pkg;
|
||||
|
||||
if (!id) {
|
||||
fprintf(stderr, "no such package \"%s\"\n", name);
|
||||
exit(1);
|
||||
}
|
||||
for (pkg = id->value; pkg; pkg = pkg->more)
|
||||
printf("%.*s\n", (int) pkg->version->len, pkg->version->s);
|
||||
}
|
||||
|
||||
|
||||
static void find_prereq(const char *name, const char *version)
|
||||
{
|
||||
const struct id *id = find_id(packages, name, strlen(name));
|
||||
struct pkg *pkg;
|
||||
struct pkg **pkgs;
|
||||
|
||||
if (!id) {
|
||||
fprintf(stderr, "no such package \"%s\"\n", name);
|
||||
exit(1);
|
||||
}
|
||||
pkg = id->value;
|
||||
if (!pkg) {
|
||||
fprintf(stderr, "package %s is a ghost\n", name);
|
||||
exit(1);
|
||||
}
|
||||
if (version) {
|
||||
id = find_id(versions, version, strlen(version));
|
||||
if (!id) {
|
||||
fprintf(stderr, "no such version\"%s\"\n", version);
|
||||
exit(1);
|
||||
}
|
||||
while (pkg && pkg->version != id)
|
||||
pkg = pkg->more;
|
||||
if (pkg) {
|
||||
fprintf(stderr, "no %s version\"%s\" found\n",
|
||||
name, version);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
pkgs = prereq(pkg);
|
||||
if (!pkgs) {
|
||||
fprintf(stderr, "can't resolve %s%s%s\n",
|
||||
name, version ? " version " : "", version ? version : "");
|
||||
exit(1);
|
||||
}
|
||||
while (*pkgs) {
|
||||
const char *file = (*pkgs)->filename;
|
||||
const char *end;
|
||||
|
||||
if (file) {
|
||||
end = strchr(file, '\n');
|
||||
printf("%.*s\n", (int) (end-file), file);
|
||||
}
|
||||
pkgs++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void usage(const char *name)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"usage: %s [pkg-list ...] list [pkg]\n"
|
||||
" %s [pkg-list ...] prereq pkg [version]\n"
|
||||
, name, name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int arg;
|
||||
|
||||
packages = make_tree(comp_id);
|
||||
versions = make_tree(comp_id);
|
||||
for (arg = 1; arg != argc; arg++) {
|
||||
if (!strcmp(argv[arg], "list")) {
|
||||
switch (argc-arg) {
|
||||
case 1:
|
||||
list_all_packages();
|
||||
return 0;
|
||||
case 2:
|
||||
list_one_package(argv[arg+1]);
|
||||
return 0;
|
||||
default:
|
||||
usage(*argv);
|
||||
}
|
||||
} else if (!strcmp(argv[arg], "prereq")) {
|
||||
switch (argc-arg) {
|
||||
case 2:
|
||||
find_prereq(argv[arg+1], NULL);
|
||||
return 0;
|
||||
case 3:
|
||||
find_prereq(argv[arg+1], argv[arg+2]);
|
||||
return 0;
|
||||
default:
|
||||
usage(*argv);
|
||||
}
|
||||
} else {
|
||||
gobble(argv[arg]);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
34
qpkg/qpkg.h
Normal file
34
qpkg/qpkg.h
Normal file
@ -0,0 +1,34 @@
|
||||
#ifndef QPKG_H
|
||||
#define QPKG_H
|
||||
|
||||
enum relop {
|
||||
rel_eq,
|
||||
rel_ge,
|
||||
rel_lt,
|
||||
};
|
||||
|
||||
struct pkg;
|
||||
|
||||
struct ref {
|
||||
struct id *pkg;
|
||||
struct id *version;
|
||||
enum relop relop; /* undefined if version == NULL */
|
||||
struct ref *next;
|
||||
};
|
||||
|
||||
struct pkg {
|
||||
struct id *id;
|
||||
struct id *version;
|
||||
struct ref *conflicts;
|
||||
struct ref *depends;
|
||||
const char *filename;
|
||||
int installed;
|
||||
struct pkg *more;
|
||||
int mark;
|
||||
};
|
||||
|
||||
|
||||
struct tree *packages;
|
||||
struct tree *versions;
|
||||
|
||||
#endif /* !QPKG_H */
|
27
qpkg/util.h
Normal file
27
qpkg/util.h
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* util.h - Common utility functions
|
||||
*
|
||||
* Written 2009 by Werner Almesberger
|
||||
* Copyright 2009 by Werner Almesberger
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*/
|
||||
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
|
||||
#define alloc_size(s) \
|
||||
({ void *alloc_size_tmp = malloc(s); \
|
||||
if (!alloc_size_tmp) \
|
||||
abort(); \
|
||||
alloc_size_tmp; })
|
||||
|
||||
#define alloc_type(t) ((t *) alloc_size(sizeof(t)))
|
||||
|
||||
#endif /* !UTIL_H */
|
Loading…
Reference in New Issue
Block a user