Remove the old installability tester

Signed-off-by: Niels Thykier <niels@thykier.net>
master
Niels Thykier 11 years ago
parent 7051d5b0e9
commit ea4e4493a2

@ -5,13 +5,5 @@ Requirements:
------------- -------------
* Python 2.7 aptitude install python2.7 * Python 2.7 aptitude install python2.7
* Python APT/DPKG bindings aptitude install python2.7-apt libapt-pkg-dev dpkg-dev * Python APT/DPKG bindings aptitude install python2.7-apt
* Python dev headers aptitude install python2.7-dev
Compiling:
----------
Run "make all" in the lib directory and add a symlink called
britneymodule.so pointing to the freshly generated britneymodule.so in
the lib directory.

@ -212,7 +212,6 @@ from installability.builder import InstallabilityTesterBuilder
from excuse import Excuse from excuse import Excuse
from migrationitem import MigrationItem from migrationitem import MigrationItem
from hints import HintCollection from hints import HintCollection
from britney import buildSystem
from britney_util import (old_libraries_format, same_source, undo_changes, from britney_util import (old_libraries_format, same_source, undo_changes,
register_reverses, compute_reverse_tree, register_reverses, compute_reverse_tree,
read_nuninst, write_nuninst, write_heidi, read_nuninst, write_nuninst, write_heidi,

@ -1,33 +0,0 @@
CC = gcc
CXX = g++
CFLAGS = -Wall -W -O2 -DNDEBUG -DMDEBUG0 -g -p -fPIC
CXXFLAGS += -fPIC
all : britneymodule.so # checklib aptvercmp freelist libajdpkg.a
clean :
rm -f *.so *.o *~ Makefile.dep gmon.out
rm -f freelist aptvercmp checklib libajdpkg.a
checklib : checklib.o dpkg.o dpkg-lib.o memory3.o freelist.o assert.o
$(CC) $(CFLAGS) -o checklib -lapt-pkg $^ # -lccmalloc -ldl
aptvercmp : dpkg-lib.cpp
$(CXX) $(CFLAGS) -DTESTBIN -o aptvercmp dpkg-lib.cpp -lapt-pkg
freelist : freelist.c assert.o
$(CC) $(CFLAGS) -DTESTBIN -o $@ $^
#libajdpkg.a : dpkg.o dpkg-lib.o memory3.o freelist.o assert.o
# ar rv $@ $^
# ranlib $@
britneymodule.so : britney-py.o dpkg.o dpkg-lib.o memory3.o freelist.o assert.o
$(CC) -shared -o britneymodule.so $^ -lapt-pkg
Makefile.dep :
@gcc -MM *.c *.cpp > Makefile.dep
@echo Makefile.dep : Makefile *.c *.h >> Makefile.dep
-include Makefile.dep

@ -1,32 +0,0 @@
BUILDING
========
Install libapt-pkg-dev
testing/ $ make
testing/ $ mkdir old cur out
testing/ $ cd testing
testing/testing/ $ perl Makefile.PL
testing/testing/ $ make
Add old and new packages files into old and cur, respectively.
testing/ $ ./checklib i386 alpha
Will generate some test stuff in out/
TODO
====
Need some way of actually updating archives.
Need some way of judging differences between Packages files.
(so I can see what hasn't been updated and work out why;
so I can check that Packages.gz matches dpkg-scanpackages output)
Need some way of automatically explaining why packages aren't upgraded.
(shouldn't be hard?)
BUGS
====
out/ directory must exist for checklib, or segfault

@ -1,11 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
int _myassertbug(int line, char *file, char *err) {
fprintf(stderr, "Assertion failed: %s:%d: %s\n", file, line, err);
fprintf(stderr, "I HATE YOU!!!");
((void(*)())0)();
abort();
return 0;
}

@ -1,299 +0,0 @@
#include <python2.7/Python.h>
#include "dpkg.h"
#define MAKE_PY_LIST(L,S,E,I,V) \
do { \
L = PyList_New(0); \
if (!L) break; \
for (S; E; I) { \
PyObject *EL; \
EL = Py_BuildValue V; \
if (!EL) { \
Py_DECREF(L); \
L = NULL; \
break; \
} \
PyList_Append(L, EL); \
Py_DECREF(EL); \
} \
if (L) PyList_Sort(L); \
} while(0)
/**************************************************************************
* britney.Packages -- dpkg_packages wrapper
*******************************************/
typedef enum { DONTFREE, FREE } dpkgpackages_freeme;
typedef struct {
PyObject_HEAD
dpkg_packages *pkgs;
PyObject *ref; /* object packages are "in" */
dpkgpackages_freeme freeme; /* free pkgs when deallocing? */
} dpkgpackages;
staticforward PyTypeObject Packages_Type;
static void dpkgpackages_dealloc(dpkgpackages *self) {
if (self->freeme == FREE) free_packages(self->pkgs);
Py_XDECREF(self->ref);
self->pkgs = NULL;
self->ref = NULL;
PyObject_DEL(self);
}
static PyObject *dpkgpackages_isinstallable(dpkgpackages *self, PyObject *args)
{
char *pkgname;
int r;
if (!PyArg_ParseTuple(args, "s", &pkgname)) return NULL;
if ((r=checkinstallable2(self->pkgs, pkgname))) {
return Py_BuildValue("i", r);
} else {
return Py_BuildValue("");
}
}
static PyObject *dpkgpackages_remove_binary(dpkgpackages *self, PyObject *args) {
char *pkg_name;
(void)self; /* unused */
if (!PyArg_ParseTuple(args, "s", &pkg_name))
return NULL;
dpkg_collected_package *cpkg = lookup_packagetbl(self->pkgs->packages, pkg_name);
if (cpkg == NULL) return Py_BuildValue("i", 0);
remove_package(self->pkgs, cpkg);
return Py_BuildValue("i", 1);
}
static PyObject *dpkgpackages_add_binary(dpkgpackages *self, PyObject *args) {
char *pkg_name;
PyObject *value, *pyString;
(void)self; /* unused */
if (!PyArg_ParseTuple(args, "sO", &pkg_name, &value) ||
!PyList_Check(value)) return NULL;
/* initialize the new package */
dpkg_package *pkg;
pkg = block_malloc(sizeof(dpkg_package));
pkg->package = strdup(pkg_name);
pkg->priority = 0;
pkg->details = NULL;
pkg->depends[1] = NULL;
pkg->depends[2] = NULL;
pkg->depends[3] = NULL;
pyString = PyList_GetItem(value, 0);
if (pyString == NULL) return NULL;
pkg->version = PyString_AsString(pyString);
pyString = PyList_GetItem(value, 2);
if (pyString == NULL) return NULL;
pkg->source = PyString_AsString(pyString);
pyString = PyList_GetItem(value, 3);
if (pyString == NULL) return NULL;
pkg->source_ver = PyString_AsString(pyString);
pyString = PyList_GetItem(value, 4);
if (pyString == NULL) return NULL;
pkg->arch_all = (pyString == Py_None || strcmp(PyString_AsString(pyString), "all") ? 0 : 1);
pyString = PyList_GetItem(value, 5);
if (pyString == NULL) return NULL;
if (pyString != Py_None) {
pkg->multiarch = PyString_AsString(pyString);
} else pkg->multiarch = NULL;
pyString = PyList_GetItem(value, 6);
if (pyString == NULL) return NULL;
if (pyString != Py_None) {
pkg->depends[0] = read_dep_andor(PyString_AsString(pyString));
} else pkg->depends[0] = NULL;
pyString = PyList_GetItem(value, 7);
if (pyString == NULL) return NULL;
if (pyString != Py_None) {
pkg->conflicts = read_dep_and(PyString_AsString(pyString));
} else pkg->conflicts = NULL;
pyString = PyList_GetItem(value, 8);
if (pyString == NULL) return NULL;
if (pyString != Py_None) {
pkg->provides = read_packagenames(PyString_AsString(pyString));
} else pkg->provides = NULL;
add_package(self->pkgs, pkg);
return Py_BuildValue("i", 1);
}
static PyObject *dpkgpackages_getattr(dpkgpackages *self, char *name) {
static struct PyMethodDef dpkgsources_methods[] = {
{ "is_installable", (binaryfunc) dpkgpackages_isinstallable,
METH_VARARGS, NULL },
{ "remove_binary", (binaryfunc) dpkgpackages_remove_binary,
METH_VARARGS, NULL },
{ "add_binary", (binaryfunc) dpkgpackages_add_binary,
METH_VARARGS, NULL },
{ NULL, NULL, 0, NULL }
};
if (strcmp(name, "packages") == 0) {
PyObject *packages;
packagetbl_iter it;
MAKE_PY_LIST(packages,
it = first_packagetbl(self->pkgs->packages),
!done_packagetbl(it), it = next_packagetbl(it),
("s", it.k)
);
return packages;
}
return Py_FindMethod(dpkgsources_methods, (PyObject *)self, name);
}
static PyTypeObject Packages_Type = {
PyObject_HEAD_INIT(&PyType_Type)
0, /* ob_size (0) */
"Packages", /* type name */
sizeof(dpkgpackages), /* basicsize */
0, /* itemsize (0) */
(destructor) dpkgpackages_dealloc,
(printfunc) 0,
(getattrfunc) dpkgpackages_getattr,
(setattrfunc) 0,
(cmpfunc) 0,
(reprfunc) 0,
0, /* number methods */
0, /* sequence methods */
0, /* mapping methods */
(hashfunc) 0, /* dict[x] ?? */
(ternaryfunc) 0, /* x() */
(reprfunc) 0 /* str(x) */
};
/**************************************************************************
* britney.buildSystem() -- build a fake package system, with the only purpose of
* calling the is_installable method on the packages.
******************************************************/
static PyObject *build_system(PyObject *self, PyObject *args) {
Py_ssize_t pos = 0;
char *arch;
PyObject *pkgs, *key, *value, *pyString;
(void)self; /* unused */
if (!PyArg_ParseTuple(args, "sO", &arch, &pkgs) ||
!PyDict_Check(pkgs)) return NULL;
/* Fields and positions for the binary package:
# VERSION = 0
# SECTION = 1
# SOURCE = 2
# SOURCEVER = 3
# ARCHITECTURE = 4
# MULTIARCH = 5
# DEPENDS = 6
# CONFLICTS = 7
# PROVIDES = 8
# RDEPENDS = 9
# RCONFLICTS = 10
*/
dpkg_packages *dpkg_pkgs = new_packages(arch);
/* loop on the dictionary keys to build the packages */
while (PyDict_Next(pkgs, &pos, &key, &value)) {
/* initialize the new package */
dpkg_package *pkg;
pkg = block_malloc(sizeof(dpkg_package));
pkg->package = strdup(PyString_AsString(key));
pkg->priority = 0;
pkg->details = NULL;
pkg->depends[1] = NULL;
pkg->depends[2] = NULL;
pkg->depends[3] = NULL;
pyString = PyList_GetItem(value, 0);
if (pyString == NULL) continue;
pkg->version = PyString_AsString(pyString);
pyString = PyList_GetItem(value, 2);
if (pyString == NULL) continue;
pkg->source = PyString_AsString(pyString);
pyString = PyList_GetItem(value, 3);
if (pyString == NULL) continue;
pkg->source_ver = PyString_AsString(pyString);
pyString = PyList_GetItem(value, 4);
if (pyString == NULL) continue;
pkg->arch_all = (pyString == Py_None || strcmp(PyString_AsString(pyString), "all") ? 0 : 1);
pyString = PyList_GetItem(value, 5);
if (pyString == NULL) continue;
if (pyString != Py_None) {
pkg->multiarch = PyString_AsString(pyString);
} else pkg->multiarch = NULL;
pyString = PyList_GetItem(value, 6);
if (pyString == NULL) continue;
if (pyString != Py_None) {
pkg->depends[0] = read_dep_andor(PyString_AsString(pyString));
} else pkg->depends[0] = NULL;
pyString = PyList_GetItem(value, 7);
if (pyString == NULL) continue;
if (pyString != Py_None) {
pkg->conflicts = read_dep_and(PyString_AsString(pyString));
} else pkg->conflicts = NULL;
pyString = PyList_GetItem(value, 8);
if (pyString == NULL) continue;
if (pyString != Py_None) {
pkg->provides = read_packagenames(PyString_AsString(pyString));
} else pkg->provides = NULL;
add_package(dpkg_pkgs, pkg);
}
dpkgpackages *res;
res = PyObject_NEW(dpkgpackages, &Packages_Type);
if (res == NULL) return NULL;
res->pkgs = dpkg_pkgs;
res->freeme = FREE;
res->ref = NULL;
return (PyObject *)res;
}
/**************************************************************************
* module initialisation
***********************/
static PyMethodDef britneymethods[] = {
{ "buildSystem", build_system, METH_VARARGS, NULL },
{ NULL, NULL, 0, NULL }
};
void initbritney(void) {
Py_InitModule("britney", britneymethods);
}

@ -1,185 +0,0 @@
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include "dpkg.h"
#if 0
static void checknewsrc(sourcetbl *srcstbl, dpkg_source *cur, void *data) {
dpkg_sources *oldsrc = data;
dpkg_source *old;
old = lookup_sourcetbl(oldsrc->sources, cur->package);
if (old == NULL) {
printf("New: %s (%s)\n", cur->package, cur->version );
} else if (strcmp(old->version, cur->version) != 0) {
printf("Updated: %s (%s, was %s)\n",
cur->package, cur->version, old->version );
} else {
dpkg_source *src2;
src2 = remove_sourcetbl(srcstbl, cur->package);
assert(cur == src2);
free_source(cur);
}
}
static void checkoldsrc(sourcetbl *oldsrctbl, dpkg_source *old, void *data) {
dpkg_sources *src = data;
dpkg_source *cur;
(void)oldsrctbl;
cur = lookup_sourcetbl(src->sources, old->package);
if (cur == NULL) {
printf("Removed: %s (was %s)\n", old->package, old->version );
}
}
static void checkuptodate(sourcetbl *srctbl, dpkg_source *src, void *data) {
int i;
int remove;
ownedpackagelist **p;
dpkg_sources *srcs = data;
(void)srctbl;
remove = 0;
for (i = 0; i < srcs->n_arches; i++) {
p = &src->packages[i];
while(*p != NULL) {
if (strcmp((*p)->value->source_ver, src->version) != 0) {
if (cmpversions((*p)->value->source_ver, GT, src->version)) {
printf("ALERT: old source: ");
} else {
printf("WARN: out of date: ");
}
printf("%s %s: %s binary: %s %s from %s\n",
src->package, src->version, srcs->archname[i],
(*p)->value->package, (*p)->value->version,
(*p)->value->source_ver);
delete_ownedpackagelist(p);
} else {
p = &(*p)->next;
}
}
if (src->packages[i] == NULL) {
printf("%s missing uptodate binaries for %s\n",
src->package, srcs->archname[i]);
remove = 1;
}
}
if (remove) {
dpkg_source *src2;
src2 = remove_sourcetbl(srcs->sources, src->package);
assert(src == src2);
free_source(src);
}
}
#endif
static void upgrade(sourcetbl *srctbl, dpkg_source *src, void *data) {
static int i = 0;
dpkg_sources_note *srcsn = data;
(void)srctbl;
i++; i %= 1000;
if (can_undo(srcsn)) {
if (i % 29 == 1 || i % 31 == 1 || i % 7 == 5)
undo_change(srcsn);
if (i % 33 == 0) commit_changes(srcsn);
}
upgrade_source(data, src);
}
static void checkpkgs(packagetbl *pkgtbl, dpkg_collected_package *cpkg,
void *data)
{
dpkg_packages *pkgs = data;
assert(pkgs->packages == pkgtbl);
printf("Trying %s (%s, %s)\n", cpkg->pkg->package, cpkg->pkg->version, pkgs->arch);
if (!checkinstallable2(pkgs, cpkg->pkg->package)) {
printf("Package: %s (%s, %s) is uninstallable\n",
cpkg->pkg->package, cpkg->pkg->version, pkgs->arch);
}
}
void print_memblock_summary(void);
int main(int argc, char **argv) {
dpkg_sources *src = NULL, *oldsrc = NULL;
dpkg_sources_note *srcsn;
dpkg_source *srcpkg;
dpkg_packages *pkgs[10];
int n_pkgs;
int i,j;
int reps;
if (argc < 3) {
printf("Usage: %s <reps> <arch>...\n", argv[0]);
exit(EXIT_FAILURE);
}
reps = atoi(argv[1]);
if (reps < 1) {
printf("reps must be >= 1\n");
exit(EXIT_FAILURE);
}
src = read_directory("cur", argc - 2, argv + 2);
oldsrc = read_directory("old", argc - 2, argv + 2);
srcsn = new_sources_note(argc - 2, argv + 2);
printf("FINISHED LOADING\n"); fflush(stdout); /* sleep(5); */
#if 0
iterate_sourcetbl(oldsrc->sources, checkoldsrc, src);
printf("FIRST\n");
iterate_sourcetbl(src->sources, checkuptodate, src);
printf("SECOND\n");
iterate_sourcetbl(src->sources, checkuptodate, src);
printf("END\n");
iterate_sourcetbl(src->sources, checknewsrc, oldsrc);
#endif
n_pkgs = 0;
for (i = argc - 1; i > 1; i--) {
pkgs[n_pkgs++] = get_architecture(oldsrc, argv[i]);
}
for (j = 0; j < reps; j++) {
printf("Round %d/%d starting...\n", j + 1, reps);
for (i = 0; i < n_pkgs; i++) {
iterate_packagetbl(pkgs[i]->packages, checkpkgs, pkgs[i]);
}
printf("Round %d ended.\n", j+1);
}
iterate_sourcetbl(src->sources, upgrade, srcsn);
iterate_sourcetbl(oldsrc->sources, upgrade, srcsn);
for (i = 0; i < n_pkgs; i++) {
free_packages(pkgs[i]);
}
srcpkg = lookup_sourcetbl(oldsrc->sources, "omirr");
if (srcpkg != NULL) {
printf("Adding old\n");
upgrade_source(srcsn, srcpkg);
}
srcpkg = lookup_sourcetbl(src->sources, "omirr");
if (srcpkg != NULL) {
printf("Adding cur\n");
upgrade_source(srcsn, srcpkg);
}
printf("FINISHED PROCESSING\n"); fflush(stdout); /* sleep(5); */
write_directory("out", oldsrc);
printf("FINISHED WRITING\n"); fflush(stdout); /* sleep(5); */
free_sources_note(srcsn);
free_sources(src);
free_sources(oldsrc);
DEBUG_ONLY( print_memblock_summary(); )
return 0;
}

@ -1,30 +0,0 @@
#include <apt-pkg/debversion.h>
extern "C" {
#include "dpkg.h"
int cmpversions(char *left, int op, char *right) {
int i = debVS.CmpVersion(left, right);
switch(op) {
case dr_LT: return i < 0;
case dr_LTEQ: return i <= 0;
case dr_EQ: return i == 0;
case dr_GTEQ: return i >= 0;
case dr_GT: return i > 0;
}
return 0;
}
}
#ifdef TESTBIN
int main(int argc, char **argv) {
if (argc != 3) { printf("Usage: %s <ver> <ver>\n", argv[0]); exit(1); }
printf("%d\n", versioncmp(argv[1], argv[2]));
return 0;
}
#endif

@ -1,966 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include "dpkg.h"
#include "memory.h"
// enlarge this if britney has issues parsing packages
// (e.g. very slow installability checks)
#define SIZEOFHASHMAP 16
/* #define DIAGNOSE 1 */
#define insert_packagenamelist(x,y) insert_l_packagenamelist(x,y,__LINE__)
static void free_dependency(dependency *dep);
static void free_collected_package(dpkg_collected_package *pkg);
static collpackagelist **get_matching_low(collpackagelist **addto,
dpkg_packages *pkgs, dependency *dep, int line);
static collpackagelist *get_matching(dpkg_packages *pkgs, deplist *depopts, int line);
static deplist *read_deplist(char **buf, char sep, char end);
static dependency *read_dependency(char **buf, char *end);
static void add_virtualpackage(virtualpkgtbl *vpkgs, char *package,
char *version, char *multiarch,
dpkg_collected_package *cpkg);
static void remove_virtualpackage(virtualpkgtbl *vpkgs, char *pkgname,
dpkg_collected_package *cpkg);
static char *read_packagename(char **buf, char *end);
static char *read_until_char(char **buf, char *end);
static int checkinstallable(dpkg_packages *pkgs, collpackagelist *instoneof);
// implemented in dpkg-lib.c
int cmpversions(char *left, int op, char *right);
#define block_malloc(s) block_malloc2(s, __LINE__)
static int dependency_counts[] = { 1, 1, 0, 0 };
#define SMB_SIZE (1<<22)
struct stringmemblock {
struct stringmemblock *next;
size_t last;
char mem[SMB_SIZE];
};
static struct stringmemblock *stringmemory = NULL;
static int stringmemorycount = 0;
static const unsigned long stringmemblocksizekib = (unsigned long) sizeof(struct stringmemblock) / 1024;
static char *my_strdup(char *foo) {
struct stringmemblock *which;
size_t len;
if (!foo) return NULL;
len = strlen(foo) + 1;
if (len > SMB_SIZE) return strdup(foo);
for (which = stringmemory; which; which = which->next) {
if (SMB_SIZE - which->last > len + 1) {
break;
}
}
if (!which) {
which = malloc(sizeof(struct stringmemblock));
if (!which) return NULL;
MDEBUG1_ONLY(fprintf(stderr,
"ALLOC: string memblock %d (%lu KiB, %lu KiB total)\n",
stringmemorycount, stringmemblocksizekib,
(stringmemorycount+1) * stringmemblocksizekib));
memset(which->mem, 0, SMB_SIZE);
which->last = 0;
which->next = stringmemory;
stringmemory = which;
stringmemorycount++;
}
strcpy(&which->mem[which->last], foo);
foo = &which->mem[which->last];
which->last += len;
return foo;
}
/* DIE **/
static void die(char *orig_msg) {
char *msg = my_strdup(orig_msg);
if (*msg && msg[strlen(msg)-1] == ':') {
msg[strlen(msg)-1] = '\0';
perror(msg);
} else {
printf("%s\n", msg);
}
abort();
}
/*************************************************************************
* Basic Package Operations
*/
static dpkg_collected_package *new_collected_package(dpkg_package *pkg) {
dpkg_collected_package *result;
result = block_malloc(sizeof(dpkg_collected_package));
if (result == NULL) die("new_collected_package alloc:");
result->pkg = pkg;
result->installed = 0;
result->conflicted = 0;
result->installable = UNKNOWN;
result->mayaffect = NULL;
return result;
}
static void free_collected_package(dpkg_collected_package *cpkg) {
if (cpkg == NULL) return;
cpkg->pkg = NULL;
free_packagenamelist(cpkg->mayaffect);
cpkg->mayaffect = NULL;
block_free(cpkg, sizeof(dpkg_collected_package));
}
LIST_IMPL(deplist, dependency*, free_dependency, block_malloc, block_free);
LIST_IMPL(deplistlist, deplist*, free_deplist, block_malloc, block_free);
LIST_IMPLX(packagenamelist, char*, KEEP(char*));
LIST_IMPL(ownedpackagenamelist, char*, KEEP(char*), block_malloc, block_free);
/* ownedpackagenamelist stores the packagename in the string store */
static int packagecmp(dpkg_package *l, dpkg_package *r) {
if (l->priority < r->priority) return -1;
if (l->priority > r->priority) return +1;
return strcmp(l->package, r->package);
}
/* container for existing pkgs */
LIST_IMPL(packagelist, dpkg_package *, KEEP(dpkg_package *), block_malloc, block_free);
LIST_IMPLX(collpackagelist, dpkg_collected_package *,
KEEP(dpkg_collected_package *))
#define insert_collpackagelist(x,y) insert_l_collpackagelist(x,y,__LINE__)
/*************************************************************************
* Operations on distributions (collections of packages)
*/
dpkg_packages *new_packages(char *arch) {
dpkg_packages *result;
result = block_malloc(sizeof(dpkg_packages));
if (result == NULL) die("new_packages alloc:");
result->arch = my_strdup(arch);
result->packages = new_packagetbl();
result->virtualpkgs = new_virtualpkgtbl();
return result;
}
void add_package(dpkg_packages *pkgs, dpkg_package *pkg)
{
ownedpackagenamelist *v;
dpkg_collected_package *cpkg;
if (lookup_packagetbl(pkgs->packages, pkg->package) != NULL)
return;
cpkg = new_collected_package(pkg);
add_packagetbl(pkgs->packages, cpkg->pkg->package, cpkg);
add_virtualpackage(pkgs->virtualpkgs, cpkg->pkg->package,
cpkg->pkg->version, cpkg->pkg->multiarch, cpkg);
for (v = cpkg->pkg->provides; v != NULL; v = v->next) {
add_virtualpackage(pkgs->virtualpkgs, v->value, NULL, NULL, cpkg);
}
}
void remove_package(dpkg_packages *pkgs, dpkg_collected_package *cpkg) {
ownedpackagenamelist *v;
packagenamelist *aff;
dpkg_collected_package *p;
for (aff = cpkg->mayaffect; aff != NULL; aff = aff->next) {
p = lookup_packagetbl(pkgs->packages, aff->value);
if (p == NULL) continue;
p->installable = UNKNOWN;
}
p = remove_packagetbl(pkgs->packages, cpkg->pkg->package);
if (p != cpkg) return;
remove_virtualpackage(pkgs->virtualpkgs, cpkg->pkg->package, cpkg);
for (v = cpkg->pkg->provides; v != NULL; v = v->next) {
remove_virtualpackage(pkgs->virtualpkgs, v->value, cpkg);
}
free_collected_package(cpkg);
}
void free_packages(dpkg_packages *pkgs) {
if (pkgs == NULL) return;
/* block_free(pkgs->arch); */
free_packagetbl(pkgs->packages);
free_virtualpkgtbl(pkgs->virtualpkgs);
block_free(pkgs, sizeof(dpkg_packages));
}
HASH_IMPL(packagetbl, char *, dpkg_collected_package *, SIZEOFHASHMAP, strhash, strcmp,
KEEP(char*),free_collected_package);
HASH_IMPL(virtualpkgtbl, char *, virtualpkg *, SIZEOFHASHMAP, strhash, strcmp,
KEEP(char*), free_virtualpkg);
/* dpkg_provision refers to memory allocated elsewhere */
LIST_IMPL(virtualpkg, dpkg_provision, KEEP(dpkg_provision), block_malloc, block_free);
static void remove_virtualpackage(virtualpkgtbl *vpkgs, char *pkgname,
dpkg_collected_package *cpkg)
{
virtualpkg *list;
virtualpkg **where;
list = lookup_virtualpkgtbl(vpkgs, pkgname);
assert(list != NULL);
where = &list;
while((*where)->value.pkg != cpkg) {
where = &(*where)->next;
assert(*where != NULL);
}
delete_virtualpkg(where);
if (list == NULL) {
remove_virtualpkgtbl(vpkgs, pkgname);
} else {
replace_virtualpkgtbl(vpkgs, pkgname, list);
}
}
static void add_virtualpackage(virtualpkgtbl *vpkgs, char *package,
char *version, char *multiarch,
dpkg_collected_package *cpkg)
{
dpkg_provision value;
virtualpkg *list, **addto;
int shouldreplace;
value.pkg = cpkg;
value.version = version;
value.multiarch = multiarch;
list = lookup_virtualpkgtbl(vpkgs, package);
shouldreplace = (list != NULL);
addto = &list;
while (*addto != NULL
&& packagecmp(cpkg->pkg, (*addto)->value.pkg->pkg) >= 0)
{
addto = &(*addto)->next;
}
insert_virtualpkg(addto, value);
if (shouldreplace) {
replace_virtualpkgtbl(vpkgs, package, list);
/* old list is included in new list, so we don't need to free */
} else {
add_virtualpkgtbl(vpkgs, package, list);
}
}
/*************************************************************************
* Parsing Helper Functions
*/
ownedpackagenamelist *read_packagenames(char *buf) {
ownedpackagenamelist *result = NULL;
ownedpackagenamelist **addto = &result;
DEBUG_ONLY( char *strend = buf + strlen(buf); )
char *sub;
while ((sub = my_strdup(read_packagename(&buf, ",")))) {
insert_ownedpackagenamelist(addto, sub);
addto = &(*addto)->next;
while(isspace(*buf)) buf++;
if (*buf == ',') {
buf++;
continue;
}
if (*buf == '\0') {
break;
}
die("read_packagenames no/bad seperator");
}
DEBUG_ONLY( assert(buf <= strend); )
return result;
}
static char *read_until_char(char **buf, char *end) {
static char *result = NULL;
char *start;
DEBUG_ONLY( char *strend = *buf + strlen(*buf); )
int n;
while(isspace(**buf)) (*buf)++;
start = *buf;
while (**buf && !isspace(**buf) && strchr(end, **buf) == NULL) {
(*buf)++;
}
n = *buf - start;
if (n == 0) return NULL;
result = realloc(result, n + 1);
if (result == NULL) die("read_until_char alloc:");
strncpy(result, start, n);
result[n] = '\0';
while(isspace(**buf)) (*buf)++;
DEBUG_ONLY( assert(*buf <= strend); )
return result;
}
static char *read_packagename(char **buf, char *end) {
return read_until_char(buf, end);
}
deplist *read_dep_and(char *buf) {
return read_deplist(&buf, ',', '\0');
}
static deplist *read_deplist(char **buf, char sep, char end) {
deplist *result = NULL;
deplist **addto = &result;
char separs[3] = { sep, end, '\0' };
DEBUG_ONLY( char *strend = *buf + strlen(*buf); )
dependency *sub;
while ((sub = read_dependency(buf, separs))) {
insert_deplist(addto, sub);
addto = &(*addto)->next;
while(isspace(**buf)) (*buf)++;
if (**buf == sep) {
(*buf)++;
continue;
}
if (**buf == '\0' || **buf == end) {
break;
}
die("read_deplist no/bad seperator");
}
DEBUG_ONLY( assert(*buf <= strend); )
return result;
}
deplistlist *read_dep_andor(char *buf) {
deplistlist *result = NULL;
deplistlist **addto = &result;
deplist *sub;
DEBUG_ONLY( char *strend = buf + strlen(buf); )
while ((sub = read_deplist(&buf, '|', ','))) {
insert_deplistlist(addto, sub);
addto = &(*addto)->next;
if (*buf == ',') buf++;
}
DEBUG_ONLY( assert(buf <= strend); )
return result;
}
static dependency *read_dependency(char **buf, char *end) {
dependency *dep;
char *name;
char newend[11];
DEBUG_ONLY( char *strend = *buf + strlen(*buf); )
assert(strlen(end) <= 8);
newend[0] = '('; newend[1] = ':'; strcpy(newend + 2, end);
name = my_strdup(read_until_char(buf, newend));
if (name == NULL) return NULL;
dep = block_malloc(sizeof(dependency));
if (dep == NULL) die("read_dependency alloc 1:");
dep->package = name;
if (**buf == ':') {
(*buf)++;
dep->archqual = my_strdup(read_until_char(buf, newend));
if (dep->archqual == NULL) return NULL;
} else
dep->archqual = NULL;
while(isspace(**buf)) (*buf)++;
if (**buf != '(') {
dep->op = dr_NOOP;
dep->version = NULL;
} else {
(*buf)++;
while(isspace(**buf)) (*buf)++;
/* << , <= , = , >= , >> */
if (**buf == '<') {
(*buf)++;
if (**buf == '<') {
dep->op = dr_LT;
(*buf)++;
} else if (**buf == '=') {
dep->op = dr_LTEQ;
(*buf)++;
} else {
/* The forms `<' and `>' were used to mean earlier/later or
* equal, rather than strictly earlier/later, so they should
* not appear in new packages (though `dpkg' still supports
* them).
*/
dep->op = dr_LTEQ;
}
} else if (**buf == '>') {
(*buf)++;
if (**buf == '>') {
dep->op = dr_GT;
(*buf)++;
} else if (**buf == '=') {
dep->op = dr_GTEQ;
(*buf)++;
} else {
dep->op = dr_GTEQ;
}
} else if (**buf == '=') {
dep->op = dr_EQ;
(*buf)++;
if (**buf == '>') {
dep->op = dr_GTEQ;
(*buf)++;
} else if (**buf == '<') {
dep->op = dr_LTEQ;
(*buf)++;
}
} else {
/* treat it as an implicit = :( */
dep->op = dr_EQ;
/* would prefer to: die("read_dependency unknown version op"); */
}
while (isspace(**buf)) (*buf)++;
newend[0] = ')'; strcpy(newend + 1, end);
dep->version = my_strdup(read_until_char(buf, newend));
while (isspace(**buf)) (*buf)++;
if (dep->version == NULL) die("read_dependency: no version");
if (**buf != ')') die("read_dependency: unterminated version");
(*buf)++;
}
DEBUG_ONLY( assert(*buf <= strend); )
return dep;
}
static void free_dependency(dependency *dep) {
if (dep == NULL) return;
/* block_free(dep->package); */
/* if (dep->version) block_free(dep->version); */
block_free(dep, sizeof(dependency));
}
/*************************************************************************
* Installability Checking
*/
static collpackagelist **get_matching_low(collpackagelist **addto,
dpkg_packages *pkgs, dependency *dep, int line)
{
virtualpkg *vpkg;
for (vpkg = lookup_virtualpkgtbl(pkgs->virtualpkgs, dep->package);
vpkg != NULL;
vpkg = vpkg->next)
{
int add;
add = 0;
if (dep->op == dr_NOOP) {
add = 1;
} else if (vpkg->value.version != NULL) {
if (cmpversions(vpkg->value.version, dep->op, dep->version)) {
add = 1;
}
}
if (dep->archqual != NULL) {
if (strcmp(dep->archqual, "any") == 0) {
if (vpkg->value.multiarch == NULL || strcmp(vpkg->value.multiarch, "allowed") != 0)
add = 0;
} else
add = 0;
}
if (add) {
insert_l_collpackagelist(addto, vpkg->value.pkg, line);
addto = &(*addto)->next;
}
}
return addto;
}
static collpackagelist *get_matching(dpkg_packages *pkgs, deplist *depopts, int line) {
collpackagelist *list = NULL;
collpackagelist **addto = &list;
for(; depopts != NULL; depopts = depopts->next) {
addto = get_matching_low(addto, pkgs, depopts->value, line);
}
return list;
}
typedef struct instonelist instonelist;
struct instonelist {
collpackagelist *curX;
collpackagelist *instoneX;
int expandedX;
struct instonelist *nextX, *prevX, *cutoffX;
};
#define I1CUR(i1) ((i1)->curX)
#define I1INSTONE(i1) ((i1)->instoneX)
#define I1CUTOFF(i1) ((i1)->cutoffX)
#define I1NEXT(i1) ((i1)->nextX) /* can be modified ! */
#define I1PREV(i1) ((i1)->prevX)
#define I1EXPANDED(i1) ((i1)->expandedX)
static instonelist *insert_instonelist(instonelist *where, collpackagelist *instone);
static void trim_instonelist_after(instonelist *first);
static void free_instonelist(instonelist *l);
static instonelist *insert_instonelist(instonelist *old, collpackagelist *instone)
{
instonelist *n = block_malloc(sizeof(instonelist));
if (n == NULL)
die("insert_instonelist alloc:");
n->curX = NULL;
n->instoneX = instone;
n->cutoffX = NULL;
n->nextX = (old ? old->nextX : NULL);
n->prevX = old;
n->expandedX = 0;
if (old) old->nextX = n;
if (n->nextX) n->nextX->prevX = n;
return n;
}
static void trim_instonelist_after(instonelist *first) {
if (!first->nextX) return;
first->nextX->prevX = NULL;
free_instonelist(first->nextX);
first->nextX = NULL;
}
static void free_instonelist(instonelist *l) {
instonelist *p, *k;
if (!l) return;
for (p = l; p->nextX; p = p->nextX);
do {
k = p;
p = k->prevX;
free_collpackagelist(k->instoneX);
block_free(k, sizeof(instonelist));
} while (k != l);
}
static int caninstall(dpkg_packages *pkgs, dpkg_collected_package *cpkg) {
collpackagelist *conflicts;
collpackagelist *conf;
int okay;
if (cpkg->installed > 0) return 1;
if (cpkg->conflicted > 0) return 0;
conflicts = get_matching(pkgs, cpkg->pkg->conflicts, __LINE__);
okay = 1;
for (conf = conflicts; conf != NULL; conf = conf->next) {
if (conf->value->installed > 0) {
okay = 0;
break;
}
}
free_collpackagelist(conflicts);
return okay;
}
static void install(dpkg_packages *pkgs, dpkg_collected_package *cpkg) {
if (cpkg->installed == 0) {
collpackagelist *conflicts = get_matching(pkgs, cpkg->pkg->conflicts, __LINE__);
collpackagelist *conf;
for (conf = conflicts; conf != NULL; conf = conf->next) {
if (conf->value == cpkg) continue;
assert(conf->value->installed == 0);
conf->value->conflicted++;
}
free_collpackagelist(conflicts);
}
assert(cpkg->conflicted == 0);
cpkg->installed++;
}
static void uninstall(dpkg_packages *pkgs, dpkg_collected_package *cpkg) {
assert(cpkg->installed > 0);
assert(cpkg->conflicted == 0);
cpkg->installed--;
if (cpkg->installed == 0) {
collpackagelist *conflicts = get_matching(pkgs, cpkg->pkg->conflicts, __LINE__);
collpackagelist *conf;
for (conf = conflicts; conf != NULL; conf = conf->next) {
if (conf->value == cpkg) continue;
assert(conf->value->installed == 0);
assert(conf->value->conflicted > 0);
conf->value->conflicted--;
}
free_collpackagelist(conflicts);
}
}
int checkinstallable2(dpkg_packages *pkgs, char *pkgname) {
dpkg_collected_package *cpkg = lookup_packagetbl(pkgs->packages, pkgname);
collpackagelist *cpl = NULL;
if (cpkg == NULL) return 0;
insert_collpackagelist(&cpl, cpkg);
/* cpl gets freed in checkinstallable :-/ */
return checkinstallable(pkgs, cpl);
}
static void debug_checkinstallable(FILE *out, instonelist *list,
instonelist *last, instonelist *pointer)
{
instonelist *l;
fprintf(out, "Status:");
/* codes: | = multiple options here
* @ = no options can satisfy this dep
* + = dependencies that can be expanded have been
* * = nothing selected yet
* > = where pointer points
* ^ = the cut point for where we are
*/
for (l = list; ; l = I1NEXT(l)) {
fprintf(out, " ");
if (l == pointer) fprintf(out, ">");
if (l == I1CUTOFF(pointer)) fprintf(out, "^");
if (I1INSTONE(l) == NULL) {
fprintf(out, "@");
} else {
if (I1INSTONE(l)->next != NULL) {
fprintf(out, "|");
}
if (I1EXPANDED(l)) {
fprintf(out, "+");
}
if (I1CUR(l) == NULL) {
fprintf(out, "*%s", I1INSTONE(l)->value->pkg->package);
} else {
fprintf(out, "%s", I1CUR(l)->value->pkg->package);
}
}
if (l == last) break;
}
fprintf(out, " ###\n");
fflush(out);
}
static int checkinstallable(dpkg_packages *pkgs, collpackagelist *instoneof) {
/* We use pkg->installed, pkg->conflicted to note how many
* times we've used this pkg to satisfy a dependency or installed
* a package that conflicts with it.
* Thus: pkg->installed == 0, or pkg->conflicted == 0
*
* We assume these are okay initially, aren't being played with
* concurrently elsewhere, and make sure they're still okay when
* we return.
*/
instonelist *list;
instonelist *last;
instonelist *pointer;
unsigned long counter = 10000000;
{
collpackagelist *cpkg;
for (cpkg = instoneof; cpkg; cpkg = cpkg->next) {
if (cpkg->value->installable == YES) {
free_collpackagelist(instoneof);
return 1;
}
}
}
list = insert_instonelist(NULL, instoneof);
last = list;
pointer = list;
while(--counter > 0 && pointer) {
deplistlist *dep;
dpkg_collected_package *instpkg; /* convenient alias */
int i;
#ifndef NDEBUG
{
instonelist *p;
for (p = list; p != pointer; p = I1NEXT(p)) {
assert(p != NULL);
assert(I1CUR(p) != NULL);
assert(I1CUR(p)->value != NULL);
assert(I1CUR(p)->value->installed > 0);
assert(I1CUR(p)->value->conflicted == 0);
}
if (I1NEXT(pointer) == NULL) {
assert(pointer == last);
} else {
for (p = I1NEXT(pointer); p; p = I1NEXT(p)) {
if (I1NEXT(p) == NULL) {
assert(p == last);
}
assert(I1CUR(p) == NULL);
}
}
}
#endif
#ifdef DIAGNOSE
debug_checkinstallable(stdout, list, last, pointer);
#endif
if (I1CUR(pointer) == NULL) {
I1CUR(pointer) = I1INSTONE(pointer);
/* try to choose an already installed package if there is one */
while (I1CUR(pointer) != NULL) {
if (I1CUR(pointer)->value->installed != 0) {
break;
}
I1CUR(pointer) = I1CUR(pointer)->next;
}
if (I1CUR(pointer) == NULL) {
I1CUR(pointer) = I1INSTONE(pointer);
}
assert(I1CUR(pointer) || !I1INSTONE(pointer));
I1CUTOFF(pointer) = last;
} else {
uninstall(pkgs, I1CUR(pointer)->value);
trim_instonelist_after(I1CUTOFF(pointer));
last = I1CUTOFF(pointer);
if (I1CUR(pointer)->value->installed > 0) {
/* this dependency isn't the issue -- even doing
* nothing to satisfy it (ie, using an already
* installed package) doesn't do any good. So give up.
*/
I1CUR(pointer) = NULL;
} else {
I1CUR(pointer) = I1CUR(pointer)->next;
}
}
while(I1CUR(pointer) && !caninstall(pkgs, I1CUR(pointer)->value)) {
I1CUR(pointer) = I1CUR(pointer)->next;
}
if (I1CUR(pointer) == NULL) {
if (I1PREV(pointer) == NULL) break;
pointer = I1PREV(pointer);
continue;
}
instpkg = I1CUR(pointer)->value;
install(pkgs, instpkg);
assert(instpkg->installed > 0);
if (instpkg->installed == 1) {
/* if it's been installed exactly once, then this must've been
* the first time it was touched, so we need to look at the
* dependencies. If it's the second or later, then we don't care
* about them.
*/
/* if any of the deps can't be satisfied, don't move on */
int bother = 1;
int expanded = I1EXPANDED(pointer);
for (i = 0; i < 4; i++) {
if (!dependency_counts[i]) continue;
for (dep = instpkg->pkg->depends[i];
dep != NULL; dep = dep->next)
{
collpackagelist *thisdep = get_matching(pkgs, dep->value, __LINE__);
if (thisdep == NULL) {
bother = 0;
} else if (thisdep != NULL && thisdep->next == NULL) {
collpackagelist *x;
/* if there's only one way of fulfilling this dep,
* do it "ASAP"
*/
/* optimisation: if thisdep == foo, but the parent
* was foo|bar, then we already know "foo" is not going
* to work in this combination, and we can skip it.
*
* This deals with cases like X deps: Y|bar, bar deps: Y
* where bar is a virtual package; cf xlibs
*/
for (x = I1INSTONE(pointer); x != I1CUR(pointer); x = x->next) {
if (x->value == thisdep->value) {
bother = 0;
break;
}
}
if (I1INSTONE(pointer)->next == NULL) {
/* the parent of this entry essentially depends
* on this too, so we'll get it out of the way
* ASAP, to reduce the degree of exponentiation
* in bad cases.
*
* _However_ we only want to do this _once_ for
* any particular node.
*/
if (expanded) {
/* thisdep isn't used! */
free_collpackagelist(thisdep);
} else {
insert_instonelist(pointer, thisdep);
I1EXPANDED(pointer) = 1;
}
} else {
insert_instonelist(I1CUTOFF(pointer), thisdep);
}
if (I1NEXT(last)) last = I1NEXT(last);
assert(!I1NEXT(last));
} else {
/* otherwise it's a multi possibility dep, so do it
* at the end
*/
last = insert_instonelist(last, thisdep);
}
}
}
if (!bother) {
/* stay where we are, and try the next possibility */
continue;
}
}
pointer = I1NEXT(pointer);
}
if (counter == 0) {
unsigned int package_count = 0;
fprintf(stderr, "AIEEE: counter overflow:");
assert(pointer != NULL);
if (I1CUR(pointer) == NULL || I1CUR(pointer)->value == NULL) {
/* we're not guaranteed that pointer will make sense here */
pointer = I1PREV(pointer);
}
for (; pointer != NULL; pointer = I1PREV(pointer)) {
if (I1CUR(pointer) == NULL) {
/* should only happen at pointer, so not here */
fprintf(stderr, " >> eep, no packages at pointer <<");
continue;
}
if (I1CUR(pointer)->value == NULL) {
/* should never happen */
fprintf(stderr, " >> eep, no package selected <<");
continue;
}
/* the full list is no as interesting as the "guilty" package,
* display the number of involved packages instead */
#if 0
fprintf(stderr, " %s%s",
(I1INSTONE(pointer)->next == NULL ? "" : "|"),
I1CUR(pointer)->value->pkg->package);
#endif
package_count++;
uninstall(pkgs, I1CUR(pointer)->value);
}
fprintf(stderr, " %u involved packages.\n", package_count);
free_instonelist(list);
/* let the caller know we hit a bad failure */
return -1;
}
if (pointer == NULL) {
dpkg_collected_package *cpkg = I1CUR(list)->value;
assert(cpkg->installable != YES);
cpkg->installable = YES;
for (pointer = last; pointer != NULL; pointer = I1PREV(pointer)) {
if (I1CUR(pointer)->value->installed == 1) {
packagenamelist **p = &I1CUR(pointer)->value->mayaffect;
#if 0
while ( *p && (*p)->value < cpkg->pkg->package ) {
p = &(*p)->next;
}
if (*p == NULL || (*p)->value > cpkg->pkg->package)
#endif
{
insert_packagenamelist(p, cpkg->pkg->package);
}
}
uninstall(pkgs, I1CUR(pointer)->value);
}
free_instonelist(list);
return 1;
} else {
assert(I1CUR(list) == NULL);
free_instonelist(list);
return 0;
}
}

@ -1,139 +0,0 @@
#ifndef DPKG_H
#define DPKG_H
#include "templates.h"
#include "memory.h"
#include <stdio.h>
/**************************************************************************
* Coping with an rfc822-esque field
*/
typedef struct dpkg_entry dpkg_entry;
struct dpkg_entry {
char *name;
char *value;
};
typedef struct dpkg_paragraph dpkg_paragraph;
struct dpkg_paragraph {
int n_entries;
int n_allocated;
dpkg_entry *entry;
};
/**************************************************************************
* Coping with a package (or many pkgs) as an abstract entity
*/
typedef enum {dr_NOOP,dr_LT,dr_LTEQ,dr_EQ,dr_GTEQ,dr_GT} dependency_relation;
extern char *dependency_relation_sym[];
typedef struct dependency dependency;
struct dependency {
char *package;
char *archqual;
dependency_relation op;
char *version;
};
LIST(deplist, dependency*);
LIST(deplistlist, deplist*);
LIST(packagenamelist, char*);
LIST(ownedpackagenamelist, char*);
typedef struct dpkg_package dpkg_package;
struct dpkg_package {
char *package;
char *version;
char *multiarch;
char *source;
char *source_ver;
int priority;
int arch_all;
deplistlist *depends[4];
deplist *conflicts;
ownedpackagenamelist *provides;
dpkg_paragraph *details;
};
LIST(packagelist, dpkg_package *);
LIST(ownedpackagelist, dpkg_package *);
/**************************************************************************
* Coping with a source package (and collections thereof) as an abstract
* entity, owning a bunch of binary packages
*/
/**************************************************************************
*/
typedef struct dpkg_collected_package dpkg_collected_package;
struct dpkg_collected_package {
dpkg_package *pkg;
int installed, conflicted;
enum { UNKNOWN, YES } installable;
packagenamelist *mayaffect;
/* on update, the installability_checked of each /mayaffect/ed package
* is cleared, and the mayaffect list is cleared.
*
* note that installable = NO couldn't be maintained over adding a package
* to testing. installable = YES can be, thanks to the mayaffect list
* (once a package is removed, everything it mayaffect must be set back
* to unknown, but everything else is okay)
*/
};
LIST(collpackagelist, dpkg_collected_package *);
/**************************************************************************
*/
typedef struct dpkg_provision dpkg_provision;
struct dpkg_provision {
char *version;
char *multiarch;
dpkg_collected_package *pkg;
};
LIST(virtualpkg, dpkg_provision);
HASH(virtualpkgtbl,char *,virtualpkg *);
HASH(packagetbl,char *,dpkg_collected_package *);
typedef struct dpkg_packages dpkg_packages;
struct dpkg_packages {
char *arch;
packagetbl *packages;
virtualpkgtbl *virtualpkgs;
};
// Used by britney-py.c
void add_package(dpkg_packages *pkgs, dpkg_package *pkg);
void remove_package(dpkg_packages *pkgs, dpkg_collected_package *pkg);
dpkg_packages *new_packages(char *arch);
void free_packages(dpkg_packages *pkgs);
deplistlist *read_dep_andor(char *buf);
deplist *read_dep_and(char *buf);
ownedpackagenamelist *read_packagenames(char *buf);
int checkinstallable2(dpkg_packages *pkgs, char *pkgname);
#endif

@ -1,27 +0,0 @@
#!/usr/bin/env python
import sys
import britney
# VERSION = 0
# SECTION = 1
# SOURCE = 2
# SOURCEVER = 3
# ARCHITECTURE = 4
# PREDEPENDS = 5
# DEPENDS = 6
# CONFLICTS = 7
# PROVIDES = 8
# RDEPENDS = 9
# RCONFLICTS = 10
packages = {'phpldapadmin': ['1.0', 'web', 'phpldapadmin', '1.0', 'all', '', 'apache2 (>= 2.0)', '', '', [], []],
'apache2': ['2.0', 'web', 'apache2', '2.0', 'i386', '', '', 'phpldapadmin (<= 1.0~)', '', [], []],
}
system = britney.buildSystem('i386', packages)
print system.is_installable('phpldapadmin'), system.packages
system.remove_binary('apache2')
print system.is_installable('phpldapadmin'), system.packages
system.add_binary('apache2', ['2.0', 'web', 'apache2', '2.0', 'i386', '', '', 'phpldapadmin (<= 1.0~)', '', [], []])
print system.is_installable('phpldapadmin'), system.packages

@ -1,188 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include "templates.h"
typedef unsigned long ul;
#define SIZE (sizeof(ul) * 8)
#define ROUND_DOWN(x) ((x) & ~(SIZE-1))
#define ROUND_UP(x) ROUND_DOWN((x) + (SIZE-1))
#define NEXT_UP(x) ROUND_DOWN((x) + SIZE)
#define NEXT_DOWN(x) ROUND_DOWN((x) - 1)
#define SETBIT(s,p) \
assert( (bits[(s)/SIZE] & (p)) == (setp ? 0 : (p)) ); \
if (setp) bits[(s)/SIZE] |= (p); \
else bits[(s)/SIZE] &= ~(p)
#define GETBIT(s) (bits[ROUND_DOWN(s)/SIZE] & (1ul << (NEXT_UP(s) - s - 1)))
size_t count_free_bits_back(ul *bits, size_t s) {
size_t cnt = 0;
ul w = ROUND_DOWN(s) / SIZE;
size_t add = s % SIZE;
ul off = (~0ul) << (SIZE - add);
ul H, d;
while ((bits[w] & off) == 0) {
cnt += add;
add = SIZE;
off = ~0ul;
if (w == 0)
return cnt;
w--;
}
H = add;
add = 0;
while ((d = (H - add) / 2) > 0) {
ul offM = (off >> d) & off;
if (bits[w] & offM) {
off = offM;
H = H - d;
} else {
add = H - d;
}
}
cnt += add;
return cnt;
}
size_t count_free_bits_after(ul *bits, size_t s, size_t end) {
size_t cnt = 0;
ul w = ROUND_DOWN(s) / SIZE;
size_t add = SIZE - s % SIZE;
ul off = (~0ul) >> (SIZE - add);
ul H, d;
end /= SIZE;
while ((bits[w] & off) == 0) {
cnt += add;
add = SIZE;
off = ~0ul;
w++;
if (w == end)
return cnt;
}
H = add;
add = 0;
while ((d = (H - add) / 2) > 0) {
ul offM = off << d;
if (bits[w] & offM) {
off = offM;
H = H - d;
} else {
add = H - d;
}
}
cnt += add;
return cnt;
}
void find_long_freebits(ul *bits, size_t s, ul *start, size_t *size) {
ul clen = 0;
ul bstart = 0, blen = 0;
ul i, k;
for (i = 0; i < s; i++) {
if (bits[i] == 0) {
clen++;
} else {
if (clen > blen) {
bstart = i - clen;
blen = clen;
}
clen = 0;
}
}
if (blen == 0) return;
bstart *= SIZE; blen *= SIZE;
k = count_free_bits_back(bits, bstart);
bstart -= k; blen += k;
blen += count_free_bits_after(bits, bstart + blen, s*SIZE);
*start = bstart; *size = blen;
}
void mark_bits(ul *bits, ul s, size_t size, int setp) {
ul e = s+size;
ul rds = ROUND_DOWN(s);
ul nus = rds + SIZE;
ul rue = ROUND_UP(e);
ul patl = (~0UL) >> (s % SIZE);
ul patr = (~0UL) << (rue - e);
assert(size > 0);
/* bits[s1..e1] get touched, but bits[s1], bits[e1] only partially
*
* if s1 == e1, then bits[s1] get touched from [s%SIZE, e%SIZE)
* else
* bits[s1] gets touched from [s%SIZE, SIZE)
* bits[s2..e1) get reset completely
* bits[e1] gets touched from [0, e%SIZE)
*/
if (nus >= e) {
/* ROUND_DOWN(s) <= s < e <= NEXT_UP(s) */
SETBIT(rds, patl & patr);
} else {
/* ROUND_DOWN(s) <= s < NEXT_UP(s) <= NEXT_DOWN(e) < e */
ul rde = ROUND_DOWN(e);
SETBIT(rds, patl);
SETBIT(rde, patr);
while (nus < rde) {
SETBIT(nus, ~0UL);
nus += SIZE;
}
}
}
void print_bits(ul *bits, ul s) {
ul i;
putchar(' ');
for (i = 0; i < s * SIZE; i++) {
putchar( GETBIT(i) ? '1' : '0' );
}
}
#ifdef TESTBIN
#define X 2
int main(void) {
ul memory[X];
ul l, r;
ul k = 5;
memset(memory, 0, sizeof(memory));
for (l = 0; l < X*SIZE; l += k) {
for (r = 1; l+(r*r) < X*SIZE; r++) {
printf("%lu %lu (%lu %lu", l, r*r,
(unsigned long) count_free_bits_back(memory, X*SIZE), (unsigned long) X*SIZE);
mark_bits(memory, l, r*r, 1);
printf("; %lu %lu %lu; %lu %lu %lu;): ",
(unsigned long) count_free_bits_back(memory, X*SIZE) + l + r*r,
(unsigned long) count_free_bits_after(memory, l + r*r, X*SIZE) + l + r*r,
(unsigned long) X*SIZE,
(unsigned long) count_free_bits_back(memory, l),
(unsigned long) count_free_bits_after(memory, 0, X*SIZE),
l);
print_bits(memory, X);
printf("\n");
mark_bits(memory, l, r*r, 0);
}
}
return 0;
}
#endif

@ -1,14 +0,0 @@
#ifndef FREELIST_H
#define FREELIST_H
#include <stdlib.h>
typedef unsigned long flb_t;
void mark_bits(flb_t *bits, flb_t s, size_t size, int setp);
size_t count_free_bits_back(flb_t *bits, size_t s);
size_t count_free_bits_after(flb_t *bits, size_t s, size_t end);
void find_long_freebits(flb_t *bits, flb_t s, flb_t *start, size_t *size);
#endif

@ -1,18 +0,0 @@
<a href="README">README</a><br>
<a href="Makefile">Makefile</a><br>
<a href="assert.c">assert.c</a><br>
<a href="britney-py.c">britney-py.c</a><br>
<a href="checklib.c">checklib.c</a><br>
<a href="dpkg.c">dpkg.c</a><br>
<a href="freelist.c">freelist.c</a><br>
<a href="memory.c">memory.c</a><br>
<a href="memory2.c">memory2.c</a><br>
<a href="memory3.c">memory3.c</a><br>
<a href="dpkg-lib.cpp">dpkg-lib.cpp</a><br>
<a href="dpkg.h">dpkg.h</a><br>
<a href="freelist.h">freelist.h</a><br>
<a href="memory.h">memory.h</a><br>
<a href="templates.h">templates.h</a><br>
<a href="check_out.py">check_out.py</a><br>
<a href="check_uptodate.py">check_uptodate.py</a><br>
<a href="update_out.py">update_out.py</a><br>

@ -1,47 +0,0 @@
#ifndef MEMORY_H
#define MEMORY_H
#if 1
void *block_malloc(size_t size);
void *block_malloc2(size_t size, int pool);
void block_free(void *vmem, size_t size);
#if defined(MDEBUG)
#define MDEBUG1
#endif
#define MDEBUG1_ONLY(x)
#define MDEBUG2_ONLY(x)
#define MDEBUG3_ONLY(x)
#ifdef MDEBUG3
#define MDEBUG1
#define MDEBUG2
#undef MDEBUG3_ONLY
#define MDEBUG3_ONLY(x) x
#endif
#ifdef MDEBUG2
#define MDEBUG1
#undef MDEBUG2_ONLY
#define MDEBUG2_ONLY(x) x
#endif
#ifdef MDEBUG1
#undef MDEBUG1_ONLY
#define MDEBUG1_ONLY(x) x
#endif
MDEBUG1_ONLY( void print_memblock_summary(void); )
#else
#define block_malloc(x) malloc(x)
#define block_free(x, s) free(x)
static void print_memblock_summary(void) {}
#endif
#endif /* MEMORY_H */

@ -1,209 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include "memory.h"
#include "templates.h"
#include "freelist.h"
typedef struct chunk chunk;
struct chunk {
chunk *next; /* only used when in free_lists */
};
#define GRAN (sizeof (struct chunk))
#define ALLOC_SIZE (1 << 20)
#define MAX_CHUNK_SIZE 256
#define NUM_BLOCK_TYPES (MAX_CHUNK_SIZE / GRAN)
#ifdef MDEBUG1
#define MAX_POOLS 100
#else
#define MAX_POOLS 1
#endif
#ifdef MDEBUG1
static void freesize(void *p, size_t s) { (void)s; free(p); }
static void die(char *blah) { perror(blah); abort(); }
LIST(alloclist, chunk *);
LIST_IMPL(alloclist, chunk *, KEEP(chunk *), malloc, freesize);
void print_memblock_summary2(int size);
#endif
struct chunkpool {
chunk *ch;
MDEBUG1_ONLY( int pool_id; )
MDEBUG1_ONLY( alloclist *all; )
};
static struct chunkpool free_lists[NUM_BLOCK_TYPES][MAX_POOLS];
#ifdef MDEBUG1
static int total[NUM_BLOCK_TYPES][MAX_POOLS];
static int used[NUM_BLOCK_TYPES][MAX_POOLS];
static int allocs[NUM_BLOCK_TYPES][MAX_POOLS];
static int total_mallocs = 0;
static int total_alloc = 0;
#endif
void *block_malloc(size_t size) {
return block_malloc2(size, -1);
}
void *block_malloc2(size_t size, int pool_id) {
chunk **fl = NULL;
void *result;
int granmult;
int pool = 0;
if (size > MAX_CHUNK_SIZE || size % GRAN != 0) {
MDEBUG1_ONLY( total_mallocs++; )
return malloc(size);
}
granmult = size / GRAN;
#ifdef MDEBUG1
for (pool = 0; pool + 1 < MAX_POOLS; pool++) {
if (free_lists[granmult - 1][pool].pool_id == 0) {
free_lists[granmult - 1][pool].pool_id = pool_id;
}
if (free_lists[granmult - 1][pool].pool_id == pool_id) {
break;
}
}
#endif
fl = &free_lists[granmult - 1][pool].ch;
if (*fl == NULL)
{
chunk *new_block = malloc(ALLOC_SIZE);
chunk *p;
MDEBUG1_ONLY( int old_size = total[granmult-1][pool]; )
if (!new_block) return NULL;
MDEBUG1_ONLY( insert_alloclist(&free_lists[granmult - 1][pool].all, new_block); )
for (p = new_block; (char*)(p + granmult) <= ((char*)new_block) + ALLOC_SIZE; p += granmult) {
/* each iteration adds a new chunk to the list */
MDEBUG1_ONLY( total[granmult-1][pool]++; )
*fl = p;
fl = &p->next;
}
*fl = NULL;
fl = &free_lists[granmult - 1][pool].ch;
MDEBUG1_ONLY( assert((total[granmult-1][pool]-old_size)*size <= ALLOC_SIZE); )
MDEBUG1_ONLY( assert(total[granmult-1][pool]*(int)size - old_size > ALLOC_SIZE - (int) size); )
#ifdef MDEBUG1
// print some info
MDEBUG2_ONLY(
fprintf(stderr, "ALLOC: for size %2ld (%d:line %d), %4ld B of %4ld B used, total alloced is %8ld KiB\n", (long int) size, pool, pool_id, (long int) used[granmult-1][pool] * size, (long int) total[granmult-1][pool] * size, (long int) total_alloc / 1024);
)
assert( used[granmult-1][pool] <= (signed long) total[granmult-1][pool] );
total_alloc += ALLOC_SIZE;
#endif
}
#ifdef MDEBUG1
{
static unsigned long cnt = 0, cnt2 = 0;
if (++cnt % (1L << 20) == 0) {
if (++cnt2 % 10 == 0) {
print_memblock_summary2(0);
} else {
print_memblock_summary();
}
}
}
#endif
MDEBUG1_ONLY( used[granmult-1][pool]++; )
MDEBUG1_ONLY( allocs[granmult-1][pool]++; )
result = *fl;
*fl = (*fl)->next;
*(int *)result = ~0;
return result;
}
#ifdef MDEBUG1
static int find_closest(void *vmem, size_t size, chunk **ch, int *p) {
int pool;
*ch = NULL;
for (pool = 0; pool < MAX_POOLS; pool++) {
alloclist *a;
if (!free_lists[size/GRAN - 1][pool].all) break;
for (a = free_lists[size/GRAN - 1][pool].all; a; a = a->next) {
if (*ch < a->value && a->value <= (chunk*)vmem) {
*ch = a->value;
*p = pool;
}
}
}
assert((char*)*ch <= (char*)vmem);
if ((char*)vmem - (char*)*ch < ALLOC_SIZE) {
return 1;
} else {
return 0;
}
}
#endif
void block_free(void *vmem, size_t size) {
int pool = 0;
if (size > MAX_CHUNK_SIZE || size % GRAN != 0) {
free(vmem);
return;
}
#if MDEBUG1
{ chunk *closest;
if (!find_closest(vmem, size, &closest, &pool)) {
fprintf(stderr, "AIEE: %p + %lx < %p\n", closest, (unsigned long) ALLOC_SIZE, vmem);
assert(0);
}
}
#endif
MDEBUG1_ONLY( used[size/GRAN-1][pool]--; )
{
chunk **fl, *x;
fl = &free_lists[size/GRAN - 1][pool].ch;
x = (chunk *) vmem;
x->next = *fl;
*fl = x;
}
}
#ifdef MDEBUG1
void print_memblock_summary(void) {
print_memblock_summary2(5*1024*1024);
}
void print_memblock_summary2(int size) {
unsigned int i, j;
fprintf(stderr, "MEMORY SUMMARY:\n");
for (i = 0; i < NUM_BLOCK_TYPES; i++) {
for (j = 0; j < MAX_POOLS; j++) {
if (total[i][j] * GRAN * (i+1) < size) continue;
if (free_lists[i][j].all != NULL) {
fprintf(stderr, " pool %dB/%d:%d; %d used %d allocated (%0.1f%% of %d MiB, %0.2f%% current)\n",
(i+1) * GRAN, j, free_lists[i][j].pool_id,
used[i][j], total[i][j],
(100.0 * used[i][j]) / total[i][j],
total[i][j] * GRAN * (i+1) / 1024 / 1024,
(100.0 * used[i][j]) / allocs[i][j]);
}
}
}
}
#endif

@ -1,277 +0,0 @@
#ifndef TEMPLATE_H
#define TEMPLATE_H
#include <stdio.h>
#include <string.h>
#include "memory.h"
#undef assert
/* gcc-3.0 sucks */
#if defined(DEBUG) && defined(NDEBUG)
#error "Can't debug and notdebug both"
#endif
#if !defined(DEBUG) && !defined(NDEBUG)
#define NDEBUG
#endif
#ifdef NDEBUG
# define DEBUG_ONLY( stmt )
# define assert(x) (void)0
#else
# define DEBUG_ONLY( stmt ) stmt
# define assert(x) ((x) ? 1 : _myassertbug(__LINE__, __FILE__, #x))
extern int _myassertbug(int line, char *file, char *err);
#endif
#ifdef __STRICT_ANSI__
#define inline __inline__
#endif
static inline unsigned long strhash(const char *x, unsigned char pow) {
unsigned long i = 0;
while (*x) {
i = (i * 39 + *x) % (1UL << pow);
x++;
}
return i;
}
#define KEEP(TYPE) ((void(*)(TYPE))NULL)
#define LIST(NAME,TYPE) \
typedef struct NAME NAME; \
struct NAME { TYPE value; struct NAME *next; }; \
void insert_##NAME(NAME **where, TYPE v); \
void insert_l_##NAME(NAME **where, TYPE v, int line); \
TYPE remove_##NAME(NAME **where); \
void delete_##NAME(NAME **where); \
void free_##NAME(NAME *l)
#define LIST_IMPL_2(NAME,TYPE,FREE,LFREE) \
TYPE remove_##NAME(NAME **where) { \
NAME *next; \
TYPE res; \
assert(*where != NULL); \
next = (*where)->next; \
res = (*where)->value; \
LFREE(*where, sizeof(NAME)); \
*where = next; \
return res; \
} \
void delete_##NAME(NAME **where) { \
NAME *next; \
assert(*where != NULL); \
next = (*where)->next; \
if (FREE != NULL) (FREE)((*where)->value); \
LFREE(*where, sizeof(NAME)); \
*where = next; \
} \
void free_##NAME(NAME *l) { \
NAME *n; \
while (l != NULL) { \
n = l->next; \
if (FREE != NULL) (FREE)(l->value); \
LFREE(l, sizeof(NAME)); \
l = n; \
} \
}
#define LIST_IMPL(NAME,TYPE,FREE,LMALLOC,LFREE) \
void insert_##NAME(NAME **where, TYPE v) { \
NAME *n = *where; \
*where = LMALLOC(sizeof(NAME)); \
if (*where == NULL) \
die("insert_" #NAME " malloc:"); \
(*where)->value = v; \
(*where)->next = n; \
} \
LIST_IMPL_2(NAME,TYPE,FREE,LFREE)
#define LIST_IMPLX(NAME,TYPE,FREE) \
void insert_l_##NAME(NAME **where, TYPE v, int line) { \
NAME *n = *where; \
*where = block_malloc2(sizeof(NAME), line); \
if (*where == NULL) \
die("insert_" #NAME " malloc:"); \
(*where)->value = v; \
(*where)->next = n; \
} \
LIST_IMPL_2(NAME,TYPE,FREE,block_free)
#define HASH(TYPE, KEY, VALUE) \
typedef struct TYPE TYPE; \
typedef struct TYPE##_iter TYPE##_iter; \
struct TYPE##_iter { \
unsigned long i; TYPE *h; KEY k; VALUE v; \
}; \
TYPE *new_##TYPE(void); \
void free_##TYPE(TYPE *h); \
TYPE##_iter first_##TYPE(TYPE *h); \
TYPE##_iter next_##TYPE(TYPE##_iter i); \
int done_##TYPE(TYPE##_iter i); \
\
void iterate_##TYPE(TYPE *h, void (*itf)(TYPE*,VALUE,void*), \
void *data); \
VALUE lookup_##TYPE(TYPE *h, KEY k); \
void add_##TYPE(TYPE *h, KEY k, VALUE v); \
VALUE replace_##TYPE(TYPE *h, KEY k, VALUE v); \
VALUE remove_##TYPE(TYPE *h, KEY k)
#define HASH_MAGIC (0x22DEAD22)
#define HASH_IMPL(TYPE, KEY, VALUE, POW2, HASH, CMP, FREEK, FREEV) \
struct TYPE { \
unsigned long magic; \
unsigned long size; \
unsigned long n_used; \
unsigned long n_collisions; \
struct { KEY key; VALUE value; } *hash; \
}; \
TYPE *new_##TYPE(void) { \
size_t i; \
TYPE *h = malloc(sizeof(TYPE)); \
if (h == NULL) die("new_" #TYPE " malloc:"); \
\
h->magic = HASH_MAGIC; \
h->size = (1 << POW2); \
h->n_used = 0; \
h->n_collisions = 0; \
h->hash = malloc(sizeof(*h->hash) * h->size ); \
if (h == NULL) die("new_" #TYPE " hash malloc:"); \
\
for (i = 0; i < h->size; i++) { \
h->hash[i].key = NULL; \
h->hash[i].value = NULL; \
} \
\
return h; \
} \
\
void free_##TYPE(TYPE *h) { \
size_t i; \
if (h == NULL) return; \
assert(h->magic == HASH_MAGIC); \
/* printf("Freeing: size: %lu used: %lu coll: %lu\n", */ \
/* h->size, h->n_used, h->n_collisions); */ \
h->magic = ~HASH_MAGIC; \
for (i = 0; i < h->size; i++) { \
if (FREEK && h->hash[i].key) \
(FREEK)(h->hash[i].key); \
if (FREEV && h->hash[i].value) \
(FREEV)(h->hash[i].value); \
} \
free(h->hash); \
free(h); \
} \
\
void iterate_##TYPE(TYPE *h, void (*itf)(TYPE*,VALUE,void*), \
void *data) \
{ \
TYPE##_iter x; \
for (x = first_##TYPE(h); \
!done_##TYPE(x); \
x = next_##TYPE(x)) \
{ \
itf(h, x.v, data); \
} \
} \
\
TYPE##_iter first_##TYPE(TYPE *h) { \
TYPE##_iter i; \
i.i = 0; \
i.h = h; \
return next_##TYPE(i); \
} \
\
TYPE##_iter next_##TYPE(TYPE##_iter i) { \
assert(i.h->magic == HASH_MAGIC); \
while(i.i < i.h->size) { \
if (i.h->hash[i.i].value != NULL) { \
i.k = i.h->hash[i.i].key; \
i.v = i.h->hash[i.i].value; \
i.i++; \
return i; \
} \
i.i++; \
} \
i.h = NULL; \
return i; \
} \
\
int done_##TYPE(TYPE##_iter i) { \
assert(i.h == NULL || i.h->magic == HASH_MAGIC); \
assert(i.h == NULL || (i.k != NULL && i.v != NULL)); \
assert(i.h == NULL || (0 < i.i && i.i <= i.h->size)); \
return i.h == NULL; \
} \
\
VALUE lookup_##TYPE(TYPE *h, KEY k) { \
int i = HASH(k, POW2); \
assert(h->magic == HASH_MAGIC); \
assert(h->n_used < h->size); /* ensure termination */ \
while(h->hash[i].key) { \
if ((CMP)(h->hash[i].key, k) == 0) { \
if (h->hash[i].value != NULL) { \
return h->hash[i].value; \
} \
} \
i = (i + 1) % (1 << POW2); \
} \
return NULL; \
} \
\
void add_##TYPE(TYPE *h, KEY k, VALUE v) { \
int i = HASH(k, POW2); \
assert(h->magic == HASH_MAGIC); \
assert(h->n_used < h->size); /* ensure termination */ \
assert(v != NULL); \
while(h->hash[i].value) { \
assert((CMP)(h->hash[i].key, k) != 0); \
i = (i + 1) % (1 << POW2); \
h->n_collisions++; \
} \
if (FREEK != NULL && h->hash[i].key) \
FREEK(h->hash[i].key); \
h->n_used++; \
h->hash[i].key = k; \
h->hash[i].value = v; \
} \
\
VALUE replace_##TYPE(TYPE *h, KEY k, VALUE v) { \
VALUE tmp; \
int i = HASH(k,POW2); \
assert(h->magic == HASH_MAGIC); \
assert(v != NULL); \
while(h->hash[i].key) { \
if ((CMP)(h->hash[i].key, k) == 0) break; \
i = (i + 1) % (1 << POW2); \
} \
assert(h->hash[i].value != NULL); \
tmp = h->hash[i].value; \
h->hash[i].key = k; \
h->hash[i].value = v; \
return tmp; \
} \
\
VALUE remove_##TYPE(TYPE *h, KEY k) { \
VALUE tmp; \
int i = HASH(k, POW2); \
assert(h->magic == HASH_MAGIC); \
while(h->hash[i].key) { \
if ((CMP)(h->hash[i].key, k) == 0) break; \
i = (i + 1) % (1 << POW2); \
} \
tmp = h->hash[i].value; \
h->hash[i].value = NULL; \
if (tmp != NULL) h->n_used--; \
return tmp; \
}
#endif

@ -1,13 +0,0 @@
remove_source(name) source exists
remove_sourcetbl
remove the binaries?? but how?
upgrade_source(dpkg_source) source may exist
remove_source(src->name)
add the new source to the table thing
foreach arch, binary: add binary to arch pkgs
upgrade_architecture(dpkg_source, arch) source exists, binary may
find source
foreach binary in <arch>: remove it
foreach binary in new <arch>: add it
(remove_architecture, upgrade_source_only; could also be used, theoretically)
Loading…
Cancel
Save