diff --git a/lib/Makefile b/lib/Makefile new file mode 100644 index 0000000..8e2fdac --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,32 @@ + +CC = gcc +CXX = g++ +CFLAGS = -Wall -W -O2 -DNDEBUG -DMDEBUG0 -g -p + +all : checklib aptvercmp freelist britneymodule.so # 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 diff --git a/lib/Makefile.dep b/lib/Makefile.dep new file mode 100644 index 0000000..2e2da0d --- /dev/null +++ b/lib/Makefile.dep @@ -0,0 +1,10 @@ +assert.o: assert.c +britney-py.o: britney-py.c dpkg.h templates.h memory.h +checklib.o: checklib.c dpkg.h templates.h memory.h +dpkg.o: dpkg.c dpkg.h templates.h memory.h +freelist.o: freelist.c templates.h memory.h +memory2.o: memory2.c +memory3.o: memory3.c memory.h templates.h freelist.h +memory.o: memory.c memory.h templates.h freelist.h +dpkg-lib.o: dpkg-lib.cpp dpkg.h templates.h memory.h +Makefile.dep : Makefile assert.c britney-py.c checklib.c dpkg.c freelist.c memory2.c memory3.c memory.c dpkg.h freelist.h memory.h templates.h diff --git a/lib/README b/lib/README new file mode 100644 index 0000000..b46100c --- /dev/null +++ b/lib/README @@ -0,0 +1,32 @@ + +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 diff --git a/lib/assert.c b/lib/assert.c new file mode 100644 index 0000000..43cb306 --- /dev/null +++ b/lib/assert.c @@ -0,0 +1,11 @@ +#include +#include + +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; +} + diff --git a/lib/britney-py.c b/lib/britney-py.c new file mode 100644 index 0000000..aa0690f --- /dev/null +++ b/lib/britney-py.c @@ -0,0 +1,871 @@ +#include + +#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 PyObject *dpkgpackages_new(dpkg_packages *pkgs, + dpkgpackages_freeme freeme, PyObject *ref) +{ + dpkgpackages *res; + + res = PyObject_NEW(dpkgpackages, &Packages_Type); + if (res == NULL) return NULL; + + res->pkgs = pkgs; + res->ref = ref; Py_INCREF(res->ref); + res->freeme = freeme; + + return (PyObject *) res; +} + +static void dpkgpackages_dealloc(dpkgpackages *self) { + if (self->freeme == FREE) free_packages(self->pkgs); + Py_XDECREF(self->ref); + self->pkgs = NULL; + self->ref = NULL; + PyMem_DEL(self); +} + + +static dpkg_collected_package *dpkgpackages_lookuppkg(dpkgpackages *self, + char *pkgname) +{ + dpkg_collected_package *cpkg = NULL; + cpkg = lookup_packagetbl(self->pkgs->packages, pkgname); + if (!cpkg) { + PyErr_SetString(PyExc_ValueError, "Not a valid package"); + } + return cpkg; +} + +static PyObject *dpkgpackages_ispresent(dpkgpackages *self, PyObject *args) { + dpkg_collected_package *cpkg; + char *pkgname; + if (!PyArg_ParseTuple(args, "s", &pkgname)) return NULL; + cpkg = lookup_packagetbl(self->pkgs->packages, pkgname); + return cpkg ? Py_BuildValue("i", 1) : Py_BuildValue("i", 0); +} + +static PyObject *dpkgpackages_getversion(dpkgpackages *self, PyObject *args) { + dpkg_collected_package *cpkg; + char *pkgname; + if (!PyArg_ParseTuple(args, "s", &pkgname)) return NULL; + cpkg = lookup_packagetbl(self->pkgs->packages, pkgname); + if (cpkg) return Py_BuildValue("s", cpkg->pkg->version); + else return Py_BuildValue(""); +} +static PyObject *dpkgpackages_getsource(dpkgpackages *self, PyObject *args) { + dpkg_collected_package *cpkg; + char *pkgname; + if (!PyArg_ParseTuple(args, "s", &pkgname)) return NULL; + cpkg = lookup_packagetbl(self->pkgs->packages, pkgname); + if (cpkg) return Py_BuildValue("s", cpkg->pkg->source); + else return Py_BuildValue(""); +} +static PyObject *dpkgpackages_getsourcever(dpkgpackages *self, PyObject *args) { + dpkg_collected_package *cpkg; + char *pkgname; + if (!PyArg_ParseTuple(args, "s", &pkgname)) return NULL; + cpkg = lookup_packagetbl(self->pkgs->packages, pkgname); + if (cpkg) return Py_BuildValue("s", cpkg->pkg->source_ver); + else return Py_BuildValue(""); +} +static PyObject *dpkgpackages_isarchall(dpkgpackages *self, PyObject *args) { + dpkg_collected_package *cpkg; + char *pkgname; + if (!PyArg_ParseTuple(args, "s", &pkgname)) return NULL; + cpkg = lookup_packagetbl(self->pkgs->packages, pkgname); + if (cpkg) return Py_BuildValue("i", cpkg->pkg->arch_all); + else return Py_BuildValue(""); +} +static PyObject *dpkgpackages_isntarchall(dpkgpackages *self, PyObject *args) { + dpkg_collected_package *cpkg; + char *pkgname; + if (!PyArg_ParseTuple(args, "s", &pkgname)) return NULL; + cpkg = lookup_packagetbl(self->pkgs->packages, pkgname); + if (cpkg) return Py_BuildValue("i", !cpkg->pkg->arch_all); + else return Py_BuildValue(""); +} +static PyObject *dpkgpackages_getfield(dpkgpackages *self, PyObject *args) { + char *field; + char *pkgname; + int i; + dpkg_collected_package *cpkg; + dpkg_paragraph *para; + if (!PyArg_ParseTuple(args, "ss", &pkgname, &field)) return NULL; + cpkg = dpkgpackages_lookuppkg(self, pkgname); + if (!cpkg) return NULL; + para = cpkg->pkg->details; + for (i = 0; i < para->n_entries; i++) { + if (strcasecmp(para->entry[i].name, field) == 0) { + return Py_BuildValue("s", para->entry[i].value); + } + } + return Py_BuildValue(""); +} +static PyObject *dpkgpackages_isinstallable(dpkgpackages *self, PyObject *args) +{ + char *pkgname; + if (!PyArg_ParseTuple(args, "s", &pkgname)) return NULL; + if (checkinstallable2(self->pkgs, pkgname)) { + return Py_BuildValue("i", 1); + } else { + return Py_BuildValue(""); + } +} +static PyObject *dpkgpackages_isuninstallable(dpkgpackages *self, + PyObject *args) +{ + char *pkgname; + if (!PyArg_ParseTuple(args, "s", &pkgname)) return NULL; + if (!checkinstallable2(self->pkgs, pkgname)) { + return Py_BuildValue("i", 1); + } else { + return Py_BuildValue(""); + } +} +static PyObject *dpkgpackages_unsatdeps(dpkgpackages *self, PyObject *args) { + /* arguments are: + * testingpkgs[arch].unsatisfiable_deps(unstablepkgs[arch], "netbase", "Depends") + * exciting, huh? + */ + + dpkgpackages *pkgpkgs; + char *pkgname, *fieldname; + dpkg_collected_package *cpkg; + int fieldidx; + int buflen = 100; + char *buf = malloc(buflen); + const char *fields[] = { "Pre-Depends", "Depends", "Recommends", + "Suggests", NULL }; + satisfieddeplist *unsatdeps, *dl; + PyObject *res = Py_BuildValue("[]"); + + if (!PyArg_ParseTuple(args, "O!ss", &Packages_Type, &pkgpkgs, &pkgname, &fieldname)) return NULL; + + cpkg = lookup_packagetbl(pkgpkgs->pkgs->packages, pkgname); + if (!cpkg) { + PyErr_SetString(PyExc_ValueError, "Not a valid package"); + return NULL; + } + + for (fieldidx = 0; fields[fieldidx]; fieldidx++) { + if (strcmp(fields[fieldidx], fieldname) == 0) break; + } + if (!fields[fieldidx]) { + PyErr_SetString(PyExc_ValueError, "Not a valid dependency field"); + return NULL; + } + + unsatdeps = checkunsatisfiabledeps(self->pkgs, + cpkg->pkg->depends[fieldidx]); + for (dl = unsatdeps; dl != NULL; dl = dl->next) { + int len; + packagelist *it; + PyObject *pkglist; + deplist *depl; + dependency *dep; + + len = 0; + buf[0] = '\0'; + for (depl = dl->value->depl; depl; depl = depl->next) { + dep = depl->value; + len += strlen(dep->package) + 4; + /* 4 = strlen(" | ") + 1 */ + if (dep->op != dr_NOOP) { + len += strlen(dep->version) + 6; + /* 6 = strlen(" (>= )") */ + } + if (len >= buflen) { + char *newbuf; + newbuf = realloc(buf, len + 100); + if (newbuf == NULL) { + free_satisfieddeplist(unsatdeps); + free(buf); + Py_DECREF(res); + PyErr_SetFromErrno(PyExc_MemoryError); + return NULL; + } + buf = newbuf; + buflen = len + 100; + } + if (buf[0] != '\0') strcat(buf, " | "); + strcat(buf, dep->package); + if (dep->op != dr_NOOP) { + sprintf(buf + strlen(buf), " (%s %s)", + dependency_relation_sym[dep->op], + dep->version); + } + } + + MAKE_PY_LIST(pkglist, it = dl->value->pkgs, it, it = it->next, + ("s", it->value->package) + ); + + { + PyObject *depel = Py_BuildValue("(sN)", buf, pkglist); + PyList_Append(res, depel); + Py_DECREF(depel); + } + } + + free_satisfieddeplist(unsatdeps); + free(buf); + + return res; +} + +static PyObject *dpkgpackages_getattr(dpkgpackages *self, char *name) { + static struct PyMethodDef dpkgsources_methods[] = { + { "is_present", (binaryfunc) dpkgpackages_ispresent, + METH_VARARGS, NULL }, + { "get_version", (binaryfunc) dpkgpackages_getversion, + METH_VARARGS, NULL }, + { "get_source", (binaryfunc) dpkgpackages_getsource, + METH_VARARGS, NULL }, + { "get_sourcever", (binaryfunc) dpkgpackages_getsourcever, + METH_VARARGS, NULL }, + { "is_arch_all", (binaryfunc) dpkgpackages_isarchall, + METH_VARARGS, NULL }, + { "isnt_arch_all", (binaryfunc) dpkgpackages_isntarchall, + METH_VARARGS, NULL }, + { "get_field", (binaryfunc) dpkgpackages_getfield, + METH_VARARGS, NULL }, + { "is_installable", (binaryfunc) dpkgpackages_isinstallable, + METH_VARARGS, NULL }, + { "is_uninstallable", (binaryfunc)dpkgpackages_isuninstallable, + METH_VARARGS, NULL }, + { "unsatisfiable_deps", (binaryfunc) dpkgpackages_unsatdeps, + 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.Sources -- dpkg_sources wrapper + *****************************************/ + +typedef struct { + PyObject_HEAD + dpkg_sources *srcs; +} dpkgsources; + +staticforward PyTypeObject Sources_Type; + +static PyObject *dpkgsources_new(PyObject *self, PyObject *args) { + dpkgsources *res = NULL; + char *dir; + PyObject *arches; + char **archesStr = NULL; + int i, count; + + (void)self; /* unused */ + + if (!PyArg_ParseTuple(args, "sO!", &dir, &PyList_Type, &arches)) { + goto end; + } + + count = PyList_Size(arches); + if (count <= 0) { + PyErr_SetString(PyExc_TypeError, "No architectures specified"); + goto end; + } + + archesStr = malloc(sizeof(char *) * count); + if (!archesStr) { + PyErr_SetFromErrno(PyExc_MemoryError); + goto end; + } + + for (i = 0; i < count; i++) { + PyObject *arch = PyList_GetItem(arches, i); + if (!PyString_Check(arch)) { + goto end; + } + archesStr[i] = PyString_AsString(arch); + } + + res = PyObject_NEW(dpkgsources, &Sources_Type); + if (res == NULL) goto end; + + res->srcs = read_directory(dir, count, archesStr); + if (!res->srcs) { + Py_DECREF(res); + res = NULL; + goto end; + } + +end: + if (archesStr) free(archesStr); + return (PyObject *) res; +} + +static void dpkgsources_dealloc(dpkgsources *self) { + free_sources(self->srcs); + self->srcs = NULL; + PyMem_DEL(self); +} + +static PyObject *dpkgsources_packages(dpkgsources *self, PyObject *args) +{ + char *arch; + dpkg_packages *pkgs; + if (!PyArg_ParseTuple(args, "s", &arch)) return NULL; + pkgs = get_architecture(self->srcs, arch); + return dpkgpackages_new(pkgs, FREE, (PyObject *) self); +} + +static PyObject *dpkgsources_isfake(dpkgsources *self, PyObject *args) { + char *srcname; + dpkg_source *src; + + if (!PyArg_ParseTuple(args, "s", &srcname)) return NULL; + src = lookup_sourcetbl(self->srcs->sources, srcname); + if (src) return Py_BuildValue("i", src->fake); + else return Py_BuildValue(""); +} + +static PyObject *dpkgsources_getversion(dpkgsources *self, PyObject *args) { + char *srcname; + dpkg_source *src; + + if (!PyArg_ParseTuple(args, "s", &srcname)) return NULL; + src = lookup_sourcetbl(self->srcs->sources, srcname); + if (src) return Py_BuildValue("s", src->version); + else return Py_BuildValue(""); +} + +static PyObject *dpkgsources_getfield(dpkgsources *self, PyObject *args) { + char *srcname, *field; + dpkg_source *src; + int i; + dpkg_paragraph *para; + + if (!PyArg_ParseTuple(args, "ss", &srcname, &field)) return NULL; + src = lookup_sourcetbl(self->srcs->sources, srcname); + if (!src) { + PyErr_SetString(PyExc_ValueError, "Not a valid source package"); + return NULL; + } + para = src->details; + if (para) { + for (i = 0; i < para->n_entries; i++) { + if (strcasecmp(para->entry[i].name, field) == 0) { + return Py_BuildValue("s", para->entry[i].value); + } + } + } + return Py_BuildValue(""); +} + +static PyObject *dpkgsources_ispresent(dpkgsources *self, PyObject *args) { + char *srcname; + if (!PyArg_ParseTuple(args, "s", &srcname)) return NULL; + if (lookup_sourcetbl(self->srcs->sources, srcname)) { + return Py_BuildValue("i", 1); + } else { + return Py_BuildValue("i", 0); + } +} + +static PyObject *dpkgsources_binaries(dpkgsources *self, PyObject *args) { + char *srcname, *arch; + int archnum; + dpkg_source *src; + PyObject *res; + ownedpackagelist *p; + + if (!PyArg_ParseTuple(args, "ss", &srcname, &arch)) return NULL; + + for (archnum = 0; archnum < self->srcs->n_arches; archnum++) { + if (strcmp(arch, self->srcs->archname[archnum]) == 0) break; + } + if (archnum == self->srcs->n_arches) { + PyErr_SetString(PyExc_ValueError, "Not a valid architecture"); + return NULL; + } + + src = lookup_sourcetbl(self->srcs->sources, srcname); + if (src == NULL) { + PyErr_SetString(PyExc_ValueError, "Not a valid source package"); + return NULL; + } + + MAKE_PY_LIST(res, p = src->packages[archnum], p, p = p->next, + ("s", p->value->package) + ); + return res; +} + +static PyObject *dpkgsources_getattr(dpkgsources *self, char *name) { + static struct PyMethodDef dpkgsources_methods[] = { + { "Packages", (binaryfunc) dpkgsources_packages, + METH_VARARGS, NULL }, + { "is_fake", (binaryfunc) dpkgsources_isfake, + METH_VARARGS, NULL }, + { "get_version", (binaryfunc) dpkgsources_getversion, + METH_VARARGS, NULL }, + { "get_field", (binaryfunc) dpkgsources_getfield, + METH_VARARGS, NULL }, + { "is_present", (binaryfunc) dpkgsources_ispresent, + METH_VARARGS, NULL }, + { "binaries", (binaryfunc) dpkgsources_binaries, + METH_VARARGS, NULL }, + { NULL, NULL, 0, NULL } + }; + + if (strcmp(name, "arches") == 0) { + PyObject *arches; + int i; + MAKE_PY_LIST(arches, i = 0, i < self->srcs->n_arches, i++, + ("s", self->srcs->archname[i]) + ); + return arches; + } else if (strcmp(name, "sources") == 0) { + PyObject *sources; + sourcetbl_iter it; + MAKE_PY_LIST(sources, + it = first_sourcetbl(self->srcs->sources), + !done_sourcetbl(it), it = next_sourcetbl(it), + ("s", it.k) + ); + return sources; + } + + return Py_FindMethod(dpkgsources_methods, (PyObject *)self, name); +} + +static PyTypeObject Sources_Type = { + PyObject_HEAD_INIT(&PyType_Type) + + 0, /* ob_size (0) */ + "Sources", /* type name */ + sizeof(dpkgsources), /* basicsize */ + 0, /* itemsize (0) */ + + (destructor) dpkgsources_dealloc, + (printfunc) 0, + (getattrfunc) dpkgsources_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.SourcesNote -- dpkg_sourcesnote wrapper + *************************************************/ + +typedef struct { + PyObject_HEAD + dpkg_sources_note *srcsn; + PyObject *refs; /* list of referenced dpkgsources */ +} dpkgsrcsn; + +staticforward PyTypeObject SourcesNote_Type; + +static PyObject *dpkgsrcsn_new(PyObject *self, PyObject *args) { + dpkgsrcsn *res = NULL; + PyObject *arches; + char **archesStr = NULL; + int i, count; + + (void)self; /* unused */ + + if (!PyArg_ParseTuple(args, "O!", &PyList_Type, &arches)) { + goto end; + } + + count = PyList_Size(arches); + if (count <= 0) { + PyErr_SetString(PyExc_TypeError, "No architectures specified"); + goto end; + } + + archesStr = malloc(sizeof(char *) * count); + if (!archesStr) { + PyErr_SetFromErrno(PyExc_MemoryError); + goto end; + } + + for (i = 0; i < count; i++) { + PyObject *arch = PyList_GetItem(arches, i); + if (!PyString_Check(arch)) { + goto end; + } + archesStr[i] = PyString_AsString(arch); + } + + res = PyObject_NEW(dpkgsrcsn, &SourcesNote_Type); + if (res == NULL) goto end; + + res->refs = PyList_New(0); + res->srcsn = new_sources_note(count, archesStr); + if (!res->refs || !res->srcsn) { + Py_DECREF(res); + res = NULL; + goto end; + } + +end: + if (archesStr) free(archesStr); + return (PyObject *) res; +} + +static void dpkgsrcsn_dealloc(dpkgsrcsn *self) { + if (self->srcsn) free_sources_note(self->srcsn); + self->srcsn = NULL; + Py_XDECREF(self->refs); + self->refs = NULL; + + PyMem_DEL(self); +} + +static PyObject *dpkgsrcsn_removesource(dpkgsrcsn *self, PyObject *args) { + char *name; + if (!PyArg_ParseTuple(args, "s", &name)) return NULL; + remove_source(self->srcsn, name); + return Py_BuildValue(""); +} +static PyObject *dpkgsrcsn_upgradesource(dpkgsrcsn *self, PyObject *args) { + char *name; + dpkgsources *srcs; + dpkg_source *src; + if (!PyArg_ParseTuple(args, "O!s", &Sources_Type, &srcs, &name)) + return NULL; + src = lookup_sourcetbl(srcs->srcs->sources, name); + if (!src) { + PyErr_SetString(PyExc_ValueError, "Source does not exist"); + return NULL; + } + if (!PySequence_In(self->refs, (PyObject *)srcs)) + PyList_Append(self->refs, (PyObject *)srcs); + upgrade_source(self->srcsn, src); + return Py_BuildValue(""); +} +static PyObject *dpkgsrcsn_upgradearch(dpkgsrcsn *self, PyObject *args) { + char *name, *arch; + dpkgsources *srcs; + dpkg_source *src; + if (!PyArg_ParseTuple(args, "O!ss", &Sources_Type, &srcs, &name, &arch)) + return NULL; + src = lookup_sourcetbl(srcs->srcs->sources, name); + if (!src) { + PyErr_SetString(PyExc_ValueError, "Source does not exist"); + return NULL; + } + if (!PySequence_In(self->refs, (PyObject *)srcs)) + PyList_Append(self->refs, (PyObject *)srcs); + upgrade_arch(self->srcsn, src, arch); + return Py_BuildValue(""); +} + +static PyObject *dpkgsrcsn_undochange(dpkgsrcsn *self, PyObject *args) { + if (!PyArg_ParseTuple(args, "")) return NULL; + undo_change(self->srcsn); + return Py_BuildValue(""); +} + +static PyObject *dpkgsrcsn_commitchanges(dpkgsrcsn *self, PyObject *args) { + if (!PyArg_ParseTuple(args, "")) return NULL; + commit_changes(self->srcsn); + return Py_BuildValue(""); +} + +static PyObject *dpkgsrcsn_writenotes(dpkgsrcsn *self, PyObject *args) { + char *dir; + if (!PyArg_ParseTuple(args, "s", &dir)) return NULL; + write_notes(dir, self->srcsn); + return Py_BuildValue(""); +} + +static PyObject *dpkgsrcsn_packages(dpkgsrcsn *self, PyObject *args) { + char *arch; + int archnum; + if (!PyArg_ParseTuple(args, "s", &arch)) return NULL; + for (archnum = 0; archnum < self->srcsn->n_arches; archnum++) { + if (strcmp(arch, self->srcsn->archname[archnum]) == 0) break; + } + if (archnum == self->srcsn->n_arches) { + PyErr_SetString(PyExc_ValueError, "Not a valid architecture"); + return NULL; + } + return dpkgpackages_new(self->srcsn->pkgs[archnum], DONTFREE, + (PyObject *) self); +} + +static PyObject *dpkgsrcsn_getversion(dpkgsrcsn *self, PyObject *args) { + char *srcname; + dpkg_source_note *srcn; + + if (!PyArg_ParseTuple(args, "s", &srcname)) return NULL; + srcn = lookup_sourcenotetbl(self->srcsn->sources, srcname); + if (srcn) return Py_BuildValue("s", srcn->source->version); + else return Py_BuildValue(""); +} +static PyObject *dpkgsrcsn_getfield(dpkgsrcsn *self, PyObject *args) { + char *srcname, *field; + dpkg_source_note *srcn; + int i; + dpkg_paragraph *para; + + if (!PyArg_ParseTuple(args, "ss", &srcname, &field)) return NULL; + srcn = lookup_sourcenotetbl(self->srcsn->sources, srcname); + if (!srcn) { + PyErr_SetString(PyExc_ValueError, "Not a valid source package"); + return NULL; + } + para = srcn->source->details; + if (para) { + for (i = 0; i < para->n_entries; i++) { + if (strcasecmp(para->entry[i].name, field) == 0) { + return Py_BuildValue("s", para->entry[i].value); + } + } + } + return Py_BuildValue(""); +} +static PyObject *dpkgsrcsn_ispresent(dpkgsrcsn *self, PyObject *args) { + char *srcname; + if (!PyArg_ParseTuple(args, "s", &srcname)) return NULL; + if (lookup_sourcenotetbl(self->srcsn->sources, srcname)) { + return Py_BuildValue("i", 1); + } else { + return Py_BuildValue("i", 0); + } +} + +static PyObject *dpkgsrcsn_isfake(dpkgsrcsn *self, PyObject *args) { + char *srcname; + dpkg_source_note *srcn; + + if (!PyArg_ParseTuple(args, "s", &srcname)) return NULL; + srcn = lookup_sourcenotetbl(self->srcsn->sources, srcname); + if (srcn) return Py_BuildValue("i", srcn->source->fake); + else return Py_BuildValue(""); +} + +static PyObject *dpkgsrcsn_binaries(dpkgsrcsn *self, PyObject *args) { + char *srcname, *arch; + int archnum; + dpkg_source_note *srcn; + PyObject *res; + packagelist *p; + + if (!PyArg_ParseTuple(args, "ss", &srcname, &arch)) return NULL; + + for (archnum = 0; archnum < self->srcsn->n_arches; archnum++) { + if (strcmp(arch, self->srcsn->archname[archnum]) == 0) break; + } + if (archnum == self->srcsn->n_arches) { + PyErr_SetString(PyExc_ValueError, "Not a valid architecture"); + return NULL; + } + + srcn = lookup_sourcenotetbl(self->srcsn->sources, srcname); + if (srcn == NULL) { + PyErr_SetString(PyExc_ValueError, "Not a valid source package"); + return NULL; + } + + MAKE_PY_LIST(res, p = srcn->binaries[archnum], p, p = p->next, + ("s", p->value->package) + ); + return res; +} + +static PyObject *dpkgsrcsn_getattr(dpkgsrcsn *self, char *name) { + static struct PyMethodDef dpkgsrcsn_methods[] = { + { "remove_source", (binaryfunc) dpkgsrcsn_removesource, + METH_VARARGS, NULL }, + { "upgrade_source", (binaryfunc) dpkgsrcsn_upgradesource, + METH_VARARGS, NULL }, + { "upgrade_arch", (binaryfunc) dpkgsrcsn_upgradearch, + METH_VARARGS, NULL }, + + { "undo_change", (binaryfunc) dpkgsrcsn_undochange, + METH_VARARGS, NULL }, + { "commit_changes", (binaryfunc) dpkgsrcsn_commitchanges, + METH_VARARGS, NULL }, + + { "write_notes", (binaryfunc) dpkgsrcsn_writenotes, + METH_VARARGS, NULL }, + + { "Packages", (binaryfunc) dpkgsrcsn_packages, + METH_VARARGS, NULL }, + + { "get_version", (binaryfunc) dpkgsrcsn_getversion, + METH_VARARGS, NULL }, + { "get_field", (binaryfunc) dpkgsrcsn_getfield, + METH_VARARGS, NULL }, + { "is_present", (binaryfunc) dpkgsrcsn_ispresent, + METH_VARARGS, NULL }, + { "is_fake", (binaryfunc) dpkgsrcsn_isfake, + METH_VARARGS, NULL }, + { "binaries", (binaryfunc) dpkgsrcsn_binaries, + METH_VARARGS, NULL }, + { NULL, NULL, 0, NULL } + }; + + if (strcmp(name, "arches") == 0) { + PyObject *arches; + int i; + MAKE_PY_LIST(arches, i = 0, i < self->srcsn->n_arches, i++, + ("s", self->srcsn->archname[i]) + ); + return arches; + } else if (strcmp(name, "sources") == 0) { + PyObject *sources; + sourcenotetbl_iter it; + MAKE_PY_LIST(sources, + it = first_sourcenotetbl(self->srcsn->sources), + !done_sourcenotetbl(it), + it = next_sourcenotetbl(it), + ("s", it.k) + ); + return sources; + } else if (strcmp(name, "can_undo") == 0) { + if (can_undo(self->srcsn)) { + return Py_BuildValue("i", 1); + } else { + return Py_BuildValue(""); + } + } + + return Py_FindMethod(dpkgsrcsn_methods, (PyObject *)self, name); +} + +static PyTypeObject SourcesNote_Type = { + PyObject_HEAD_INIT(&PyType_Type) + + 0, /* ob_size (0) */ + "SourcesNote", /* type name */ + sizeof(dpkgsrcsn), /* basicsize */ + 0, /* itemsize (0) */ + + (destructor) dpkgsrcsn_dealloc, + (printfunc) 0, + (getattrfunc) dpkgsrcsn_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.versioncmp() -- apt version compare function + ******************************************************/ + +static PyObject *apt_versioncmp(PyObject *self, PyObject *args) { + char *l, *r; + int res; + + (void)self; /* unused */ + + if (!PyArg_ParseTuple(args, "ss", &l, &r)) { + return NULL; + } + + res = versioncmp(l,r); + return Py_BuildValue("i", res); +} + +/************************************************************************** + * module initialisation + ***********************/ + +static PyMethodDef britneymethods[] = { + { "Sources", dpkgsources_new, METH_VARARGS, NULL }, + { "SourcesNote", dpkgsrcsn_new, METH_VARARGS, NULL }, + + { "versioncmp", apt_versioncmp, METH_VARARGS, NULL }, + + { NULL, NULL, 0, NULL } +}; + +void initbritney(void) { + Py_InitModule("britney", britneymethods); +} + diff --git a/lib/checklib.c b/lib/checklib.c new file mode 100644 index 0000000..42d13df --- /dev/null +++ b/lib/checklib.c @@ -0,0 +1,185 @@ +#include +#include + +#include + +#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 ...\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; +} diff --git a/lib/dpkg-lib.cpp b/lib/dpkg-lib.cpp new file mode 100644 index 0000000..aa02427 --- /dev/null +++ b/lib/dpkg-lib.cpp @@ -0,0 +1,34 @@ + +#include + +extern "C" { + +#include "dpkg.h" + +int versioncmp(char *left, char *right) { + return debVS.CmpVersion(left, right); +} + +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 \n", argv[0]); exit(1); } + + printf("%d\n", versioncmp(argv[1], argv[2])); + return 0; +} +#endif diff --git a/lib/dpkg.c b/lib/dpkg.c new file mode 100644 index 0000000..396e34d --- /dev/null +++ b/lib/dpkg.c @@ -0,0 +1,1968 @@ +#include +#include +#include +#include +#include + +#include "dpkg.h" +#include "memory.h" + +/* #define DIAGNOSE 1 */ + +#define insert_packagenamelist(x,y) insert_l_packagenamelist(x,y,__LINE__) + +static void free_dependency(dependency *dep); +static void free_package(dpkg_package *pkg); +static void free_collected_package(dpkg_collected_package *pkg); +static dpkg_paragraph *read_paragraph( FILE *f ); +static dpkg_package *read_package( FILE *f ); +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_dep_and(char *buf); +static deplistlist *read_dep_andor(char *buf); +static ownedpackagenamelist *read_packagenames(char *buf); +static dpkg_sources *read_sources_file(char *filename, int n_arches); +static dpkg_source *new_source(dpkg_sources *owner); +static dpkg_source *read_source(FILE *f, dpkg_sources *owner); +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, 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 void add_package(dpkg_packages *pkgs, dpkg_package *pkg); +static void remove_package(dpkg_packages *pkgs, dpkg_collected_package *pkg); +static dpkg_source_note *copy_source_note(dpkg_source_note *srcn); + +#if 0 +static inline void *bm(size_t s, int n) { void *res = block_malloc(s); fprintf(stderr, "ALLOCED: %d %p %lu\n", n, res, s); return res; } +static inline void bf(void *p, size_t s, int n) { block_free(p,s); fprintf(stderr, "FREED: %d %p %lu\n", n, p, s); } + +#define block_malloc(s) bm(s,__LINE__) +#define block_free(p,s) bf(p,s,__LINE__) +#endif + +#define block_malloc(s) block_malloc2(s, __LINE__) + +static char *priorities[] = { + "required", + "important", + "standard", + "optional", + "extra", + NULL +}; + +static char *dependency_title[] = { + "Pre-Depends", "Depends", "Recommends", "Suggests", NULL +}; + +static int dependency_counts[] = { 1, 1, 0, 0 }; + +char *dependency_relation_sym[] = {"*", "<<", "<=", "=", ">=", ">>"}; + +#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; + +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; +} + +char *my_rep_strdup(char *foo) { + static char *repeats[1000] = {0}; + int i; + + for (i = 0; i < 1000; i++) { + if (repeats[i] == NULL) { + DEBUG_ONLY(fprintf(stderr, "REPEAT NR %d\n", i+1); ) + return repeats[i] = my_strdup(foo); + } + if (strcmp(repeats[i], foo) == 0) return repeats[i]; + } + + return my_strdup(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(); +} + +/************************************************************************* + * Dpkg Control/Packages/etc Operations + */ + +static dpkg_paragraph *read_paragraph( FILE *f ) { + dpkg_paragraph *result; + static int line_size = 0; + static char *line = NULL; + char *pch; + + dpkg_entry *c_entry = NULL; + char *c_value = NULL; + + if (line == NULL) { + line_size = 10; + line = malloc(line_size); + if (line == NULL) die("read_paragraph alloc 0:"); + } + + result = block_malloc( sizeof(dpkg_paragraph) ); + if (result == NULL) die("read_paragraph alloc 1:"); + + result->n_entries = 0; + result->entry = NULL; + result->n_allocated = 0; + + while(fgets(line, line_size, f)) { + while (!feof(f) && *line && line[strlen(line)-1] != '\n') { + line = realloc(line, line_size * 2); + if (!line) die("read_paragraph realloc:"); + fgets(line + strlen(line), line_size, f); + line_size *= 2; + } + + if (line[0] == '\n') break; + + if (isspace(line[0])) { + if (c_value == NULL) + die("read_paragraph early spaces"); + + if (c_entry == NULL) { + /* no need to bother */ + } else { + /* extend the line */ + c_value = realloc(c_value, strlen(c_value) + strlen(line) + 1); + if (c_value == NULL) + die("read_paragraph realloc c_value:"); + + strcat(c_value, line); + } + } else { + if (c_entry) { + c_entry->value = my_strdup(c_value); + c_value[0] = '\0'; + free(c_value); + c_value = NULL; + } else if (c_value) { + free(c_value); + c_value = NULL; + } + pch = strchr(line, ':'); + if (pch == NULL) { + fprintf(stderr, "the line was: \"%s\"\n", line); + die("read_paragraph: no colon"); + } + + *pch = '\0'; + while(isspace(*++pch)); + + if (strcmp(line, "Description") == 0) { + c_value = strdup(pch); + c_entry = NULL; + } else { + assert(result->n_entries <= result->n_allocated); + if (result->n_entries >= result->n_allocated) { + result->n_allocated += 10; + result->entry = realloc( result->entry, + sizeof(dpkg_entry) + * result->n_allocated); + if (result->entry == NULL) + die("read_paragraph realloc entry:"); + } + + c_entry = &result->entry[result->n_entries++]; + c_entry->name = my_rep_strdup(line); + c_value = strdup(pch); + } + } + } + + if (c_entry) { + c_entry->value = my_strdup(c_value); + c_value[0] = '\0'; + free(c_value); + c_value = NULL; + } + + if (result->n_entries == 0) { + if (result->entry) free(result->entry); + block_free(result, sizeof(dpkg_paragraph)); + return NULL; + } else { + result->entry = realloc(result->entry, + sizeof(result->entry[0]) * result->n_entries); + result->n_allocated = result->n_entries; + } + + return result; +} + +static void write_paragraph(FILE *f, dpkg_paragraph *p) { + int i; + + for (i = 0; i < p->n_entries; i++) { + fprintf(f, "%s: %s", p->entry[i].name, p->entry[i].value); + } + fprintf(f, "\n"); +} + + +static void free_paragraph(dpkg_paragraph *p) { + int i; + + if (p == NULL) return; + + for (i = 0; i < p->n_entries; i++) { + /* block_free(p->entry[i].name); */ + /* block_free(p->entry[i].value); */ + } + free(p->entry); + block_free(p, sizeof(dpkg_paragraph)); +} + +/************************************************************************* + * Basic Package Operations + */ + +static dpkg_package *new_package(void) { + dpkg_package *result; + + result = block_malloc(sizeof(dpkg_package)); + if (result == NULL) die("new_package alloc:"); + + result->package = NULL; + result->version = NULL; + + result->priority = 0; + result->arch_all = 0; + + result->source = NULL; + result->source_ver = NULL; + + result->depends[0] = NULL; + result->depends[1] = NULL; + result->depends[2] = NULL; + result->depends[3] = NULL; + + result->conflicts = NULL; + + result->provides = NULL; + result->details = NULL; + + return result; +} + +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)); +} + +static void free_package(dpkg_package *pkg) { + int i; + if (pkg == NULL) return; + + /* block_free(pkg->package); + * block_free(pkg->version); + * block_free(pkg->source); + * block_free(pkg->source_ver); */ + + for (i = 0; i < 4; i++) + free_deplistlist(pkg->depends[i]); + + free_deplist(pkg->conflicts); + free_ownedpackagenamelist(pkg->provides); + + free_paragraph(pkg->details); + + block_free(pkg, sizeof(dpkg_package)); +} + +static dpkg_package *read_package( FILE *f ) { + dpkg_package *result; + dpkg_paragraph *para; + dpkg_entry *e; + int i; + + para = read_paragraph(f); + if (para == NULL) return NULL; + + result = new_package(); + result->details = para; + + for (e = ¶->entry[0]; e < ¶->entry[para->n_entries]; e++) { + if (strcasecmp("Package", e->name) == 0) { + result->package = my_strdup(e->value); + if (result->package == NULL) + die("read_package my_strdup:"); + result->package[strlen(result->package)-1] = '\0'; + } + + if (strcasecmp("Version", e->name) == 0) { + result->version = my_strdup(e->value); + if (result->version == NULL) + die("read_package my_strdup:"); + result->version[strlen(result->version)-1] = '\0'; + } + + if (strcasecmp("Priority", e->name) == 0) { + int i; + for (i = 0; priorities[i] != NULL; i++) { + if (strcasecmp(priorities[i], e->value)) + break; + } + result->priority = i; + if (priorities[i] == NULL) { + die("read_package: unknown priority"); + } + } + + if (strcasecmp("Architecture", e->name) == 0) { + if (strncasecmp(e->value, "all", 3) == 0) { + if (!e->value[3] || isspace(e->value[3])) { + result->arch_all = 1; + } + } + } + + for (i = 0; dependency_title[i] != NULL; i++) { + if (strcasecmp(dependency_title[i], e->name) == 0) { + result->depends[i] = read_dep_andor(e->value); + } + } + + if (strcasecmp("Conflicts", e->name) == 0) + result->conflicts = read_dep_and(e->value); + + if (strcasecmp("Provides", e->name) == 0) + result->provides = read_packagenames(e->value); + + if (strcasecmp("source", e->name) == 0) { + char *pch = e->value; + + assert(result->source == NULL); + assert(result->source_ver == NULL); + + result->source = my_strdup(read_packagename(&pch, "(")); + if (result->source == NULL) + die("read_package: bad source header"); + + while(isspace(*pch)) pch++; + if (*pch == '(') { + pch++; + result->source_ver = my_strdup(read_until_char(&pch, ")")); + if (result->source_ver == NULL) + die("read_package: bad source version"); + while(isspace(*pch)) pch++; + if (*pch != ')') + die("read_package: unterminated ver"); + } + } + } + + if (result->source == NULL) { + assert(result->source_ver == NULL); + result->source = my_strdup(result->package); + } + if (result->source_ver == NULL) { + result->source_ver = my_strdup(result->version); + } + + return result; +} + +static void freesize(void *p, size_t s) { (void)s; free(p); } + +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 owned pkgs */ +LIST_IMPL(ownedpackagelist, dpkg_package *, free_package, + block_malloc, block_free); + +/* 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; +} + +static 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); + for (v = cpkg->pkg->provides; v != NULL; v = v->next) { + add_virtualpackage(pkgs->virtualpkgs, v->value, NULL, cpkg); + } +} + +static 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); +} + +dpkg_packages *get_architecture(dpkg_sources *srcs, char *arch) { + int i, arch_index; + dpkg_packages *result; + sourcetbl_iter srci; + ownedpackagelist *p; + + arch_index = -1; + for (i = 0; i < srcs->n_arches; i++) { + if (strcmp(srcs->archname[i], arch) == 0) { + arch_index = i; + break; + } + } + if (arch_index == -1) die("get_architecture: unknown arch"); + + result = new_packages(arch); + + for (srci = first_sourcetbl(srcs->sources); + !done_sourcetbl(srci); + srci = next_sourcetbl(srci)) + { + for (p = srci.v->packages[arch_index]; p != NULL; p = p->next) { + add_package(result, p->value); + } + } + + return result; +} + +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 *, 15, strhash, strcmp, + KEEP(char*),free_collected_package); +HASH_IMPL(virtualpkgtbl, char *, virtualpkg *, 15, 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, dpkg_collected_package *cpkg) +{ + dpkg_provision value; + virtualpkg *list, **addto; + int shouldreplace; + + value.pkg = cpkg; + value.version = version; + + 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 + */ + +static 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); +} + +static 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; +} + +static 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[10]; + DEBUG_ONLY( char *strend = *buf + strlen(*buf); ) + + assert(strlen(end) <= 8); + newend[0] = '('; strcpy(newend + 1, 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; + + 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] = ')'; + 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 (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); + } +} + +satisfieddep *new_satisfieddep(void) { + satisfieddep *sd = block_malloc(sizeof(satisfieddep)); + if (!sd) die("new_satisfieddep alloc:"); + return sd; +} + +void free_satisfieddep(satisfieddep *sd) { + if (!sd) return; + free_packagelist(sd->pkgs); + block_free(sd, sizeof(satisfieddep)); +} + +LIST_IMPL(satisfieddeplist, satisfieddep *, free_satisfieddep, block_malloc, block_free); + +packagelist *collpkglist2pkglist(collpackagelist *l) { + packagelist *r = NULL; + packagelist **addto = &r; + + for (; l != NULL; l = l->next) { + insert_packagelist(addto, l->value->pkg); + addto = &(*addto)->next; + } + + return r; +} + +satisfieddeplist *checkunsatisfiabledeps(dpkg_packages *pkgs, + deplistlist *deps) { + satisfieddeplist *unsatisfiable = NULL; + satisfieddeplist **addto = &unsatisfiable; + satisfieddep *sd; + collpackagelist *deppkgs; + + for (; deps != NULL; deps = deps->next) { + /* deplist *dep; */ + /* for (dep = deps->value; dep != NULL; dep = dep->next) { */ + sd = new_satisfieddep(); + /* sd->dep = dep->value; */ + sd->depl = deps->value; + + deppkgs = NULL; + /* get_matching_low(&deppkgs, pkgs, dep->value); */ + deppkgs = get_matching(pkgs, deps->value, __LINE__); + sd->pkgs = collpkglist2pkglist(deppkgs); + free_collpackagelist(deppkgs); + + insert_satisfieddeplist(addto, sd); + addto = &(*addto)->next; + /* } */ + } + + return unsatisfiable; +} + +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); +} + +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) { + 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; + } + fprintf(stderr, " %s%s", + (I1INSTONE(pointer)->next == NULL ? "" : "|"), + I1CUR(pointer)->value->pkg->package); + uninstall(pkgs, I1CUR(pointer)->value); + } + fprintf(stderr, ".\n"); + free_instonelist(list); + return 0; + } + + 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; + } +} + +/******************/ + +HASH_IMPL(sourcetbl, char *, dpkg_source *, 14, strhash, strcmp, + KEEP(char*), free_source); + +static dpkg_sources *read_sources_file(char *filename, int n_arches) { + FILE *f; + dpkg_sources *result; + dpkg_source *src; + int i; + + f = fopen(filename, "r"); + if (f == NULL && errno != ENOENT) { + die("read_sources_file: couldn't open file:"); + } + + result = block_malloc(sizeof(dpkg_sources)); + if (result == NULL) die("read_sources_file alloc 1:"); + + result->n_arches = n_arches; + result->archname = block_malloc(sizeof(char*) * n_arches); + if (result->archname == NULL) die("read_sources_file alloc 2:"); + for (i = 0; i < n_arches; i++) result->archname[i] = NULL; + result->unclaimedpackages = block_malloc(sizeof(ownedpackagelist*) + * n_arches); + if (result->unclaimedpackages == NULL) die("read_sources_file alloc 3:"); + for (i = 0; i < n_arches; i++) result->unclaimedpackages[i] = NULL; + + result->sources = new_sourcetbl(); + + if (f != NULL) { + while ((src = read_source(f, result))) { + dpkg_source *old = lookup_sourcetbl(result->sources, src->package); + if (old == NULL) { + add_sourcetbl(result->sources, src->package, src); + } else { + if (versioncmp(old->version, src->version) || 1) { + int i; + old = replace_sourcetbl(result->sources, src->package, src); + for (i = 0; i < old->owner->n_arches; i++) { + assert(old->packages[i] == NULL); + } + free_source(old); + } else { + int i; + for (i = 0; i < src->owner->n_arches; i++) { + assert(src->packages[i] == NULL); + } + free_source(src); + } + } + } + fclose(f); + } + + return result; +} + +void free_sources(dpkg_sources *s) { + int i; + if (s == NULL) return; + free_sourcetbl(s->sources); + for (i = 0; i < s->n_arches; i++) { + /* block_free(s->archname[i]); */ + free_ownedpackagelist(s->unclaimedpackages[i]); + } + block_free(s->archname, s->n_arches * sizeof(char*)); + block_free(s->unclaimedpackages, s->n_arches * sizeof(ownedpackagelist*)); + block_free(s, sizeof(dpkg_sources)); +} + +static dpkg_source *new_source(dpkg_sources *owner) { + dpkg_source *result; + int i; + + result = block_malloc(sizeof(dpkg_source)); + if (result == NULL) die("new_source alloc 1:"); + + result->package = NULL; + result->version = NULL; + result->details = NULL; + result->fake = 0; + + result->owner = owner; + result->packages = block_malloc(sizeof(packagelist*) * owner->n_arches); + if (result->packages == NULL) die("new_source alloc 2:"); + for (i = 0; i < owner->n_arches; i++) { + result->packages[i] = NULL; + } + + return result; +} + +static dpkg_source *read_source(FILE *f, dpkg_sources *owner) { + dpkg_source *result; + dpkg_paragraph *para; + dpkg_entry *e; + + para = read_paragraph(f); + if (para == NULL) return NULL; + + result = new_source(owner); + result->details = para; + + for (e = ¶->entry[0]; e < ¶->entry[para->n_entries]; e++) { + if (strcmp("Package", e->name) == 0) { + result->package = my_strdup(e->value); + if (result->package == NULL) + die("read_source strdup:"); + result->package[strlen(result->package)-1] = '\0'; + } + + if (strcmp("Version", e->name) == 0) { + result->version = my_strdup(e->value); + if (result->version == NULL) + die("read_source strdup:"); + result->version[strlen(result->version)-1] = '\0'; + } + } + + return result; +} + +void free_source(dpkg_source *s) { + int i; + if (s == NULL) return; + assert(s->owner != NULL); /* shouldn't have allocated it */ + /* block_free(s->package); */ + /* block_free(s->version); */ + free_paragraph(s->details); + for (i = 0; i < s->owner->n_arches; i++) { + free_ownedpackagelist(s->packages[i]); + } + block_free(s->packages, s->owner->n_arches * sizeof(ownedpackagelist*)); + block_free(s, sizeof(dpkg_source)); +} + +/******************************/ + +dpkg_sources *read_directory(char *dir, int n_arches, char *archname[]) { + char buf[1000]; + dpkg_sources *srcs; + int i; + + snprintf(buf, 1000, "%s/Sources", dir); + srcs = read_sources_file(buf, n_arches); + + for (i = 0; i < n_arches; i++) { + FILE *f; + dpkg_package *pkg; + + srcs->archname[i] = my_strdup(archname[i]); + + snprintf(buf, 1000, "%s/Packages_%s", dir, archname[i]); + f = fopen(buf, "r"); + if (f == NULL && errno != ENOENT) die("load_dirctory fopen:"); + if (f != NULL) { + while ((pkg = read_package(f))) { + dpkg_source *src = lookup_sourcetbl(srcs->sources, pkg->source); + if (src == NULL) { + src = new_source(srcs); + src->fake = 1; + src->package = my_strdup(pkg->source); + src->version = my_strdup(pkg->source_ver); + add_sourcetbl(srcs->sources, src->package, src); + } + insert_ownedpackagelist(&src->packages[i], pkg); + } + fclose(f); + } + } + + return srcs; +} + +void write_directory(char *dir, dpkg_sources *srcs) { + FILE *src; + FILE *archfile[100]; + char buf[1000]; + int i; + sourcetbl_iter srciter; + + snprintf(buf, 1000, "%s/Sources", dir); + src = fopen(buf, "w"); + if (!src) die("write_directory: Couldn't open Sources file for output"); + + for (i = 0; i < srcs->n_arches; i++) { + snprintf(buf, 1000, "%s/Packages_%s", dir, srcs->archname[i]); + archfile[i] = fopen(buf, "w"); + } + + for (srciter = first_sourcetbl(srcs->sources); + !done_sourcetbl(srciter); + srciter = next_sourcetbl(srciter)) + { + ownedpackagelist *p; + int i; + + if (!srciter.v->fake) + write_paragraph(src, srciter.v->details); + + for (i = 0; i < srcs->n_arches; i++) { + for (p = srciter.v->packages[i]; p != NULL; p = p->next) { + write_paragraph(archfile[i], p->value->details); + } + } + } + + fclose(src); + for (i = 0; i < srcs->n_arches; i++) { + fclose(archfile[i]); + } +} + +/*********************/ + +HASH_IMPL(sourcenotetbl, char *, dpkg_source_note *, 14, strhash, strcmp, + KEEP(char*), free_source_note); + +dpkg_source_note *new_source_note(dpkg_source *src, int n_arches) { + dpkg_source_note *result = block_malloc(sizeof(dpkg_source_note)); + int i; + + if (result == NULL) die("new_source_note alloc 1:"); + result->source = src; + result->n_arches = n_arches; + result->binaries = block_malloc(n_arches * sizeof(packagelist*)); + if (result->binaries == NULL) die("new_source_note alloc 2:"); + for (i = 0; i < n_arches; i++) { + result->binaries[i] = NULL; + } + return result; +} + +void free_source_note(dpkg_source_note *srcn) { + int i; + + if (srcn == NULL) return; + + if (srcn->binaries != NULL) { + for (i = 0; i < srcn->n_arches; i++) { + free_packagelist(srcn->binaries[i]); + } + block_free(srcn->binaries, sizeof(packagelist*) * srcn->n_arches); + } + block_free(srcn, sizeof(dpkg_source_note)); +} + +#ifdef DEBUG +static int is_sources_note(dpkg_sources_note *srcsn) { + int i; + + assert(srcsn != NULL); + assert(srcsn->magic == 0xa1eebabe); + assert(srcsn->pkgs != NULL); + for (i = 0; i < srcsn->n_arches; i++) { + assert(srcsn->pkgs[i] != NULL && srcsn->archname[i] != NULL); + assert(strcmp(srcsn->archname[i], srcsn->pkgs[i]->arch) == 0); + } + + return 1; +} +#endif + +dpkg_sources_note *new_sources_note(int n_arches, char **archname) { + dpkg_sources_note *result = block_malloc(sizeof(dpkg_sources_note)); + int i; + + if (result == NULL) die("new_sources_note alloc 1:"); + result->magic = 0xA1EEBABE; + result->sources = new_sourcenotetbl(); + result->pkgs = block_malloc(n_arches * sizeof(dpkg_packages*)); + if (result->pkgs == NULL) die("new_sources_note alloc 2:"); + result->archname = block_malloc(n_arches * sizeof(char*)); + if (result->archname == NULL) die("new_sources_note alloc 3:"); + + result->n_arches = n_arches; + for (i = 0; i < n_arches; i++) { + result->archname[i] = my_strdup(archname[i]); + result->pkgs[i] = new_packages(result->archname[i]); + } + result->undo = NULL; + return result; +} + +void free_sources_note(dpkg_sources_note *srcsn) { + int i; + if (srcsn == NULL) return; + assert(is_sources_note(srcsn)); + srcsn->magic = 0xBABEA1EE; + free_sourcenotetbl(srcsn->sources); + for (i = 0; i < srcsn->n_arches; i++) { + free_packages(srcsn->pkgs[i]); + /* block_free(srcsn->archname[i]); */ + } + block_free(srcsn->pkgs, sizeof(dpkg_packages*) * srcsn->n_arches); + block_free(srcsn->archname, sizeof(char*) * srcsn->n_arches); + free_source_note_listlist(srcsn->undo); + block_free(srcsn, sizeof(dpkg_sources_note)); +} + +static void new_op(dpkg_sources_note *srcsn) { + assert(is_sources_note(srcsn)); + insert_source_note_listlist(&srcsn->undo, NULL); +} +static void save_source_note(dpkg_sources_note *srcsn, dpkg_source_note *srcn) { + source_note_list **where; + assert(is_sources_note(srcsn)); + assert(srcsn->undo != NULL); + + for (where = &srcsn->undo->value; + *where != NULL; + where = &(*where)->next) + { + if ((*where)->value->source == srcn->source) + return; /* already saved */ + } + + insert_source_note_list(where, copy_source_note(srcn)); +} +static void save_empty_source_note(dpkg_sources_note *srcsn, dpkg_source *src) { + dpkg_source_note *srcn; + source_note_list **where; + + for (where = &srcsn->undo->value; + *where != NULL; + where = &(*where)->next) + { + if ((*where)->value->source == src) + return; /* already saved */ + } + + srcn = block_malloc(sizeof(dpkg_source_note)); + if (srcn == NULL) die("save_empty_source_note alloc:"); + assert(is_sources_note(srcsn)); + + srcn->source = src; + srcn->n_arches = 0; + srcn->binaries = NULL; + + insert_source_note_list(where, srcn); +} + +typedef enum { DO_ARCHALL = 0, SKIP_ARCHALL = 1 } do_this; +static void remove_binaries_by_arch(dpkg_sources_note *srcsn, + dpkg_source_note *srcn, int archnum, + do_this arch_all) +{ + packagelist *p; + packagelist *leftovers = NULL, **addto = &leftovers; + assert(is_sources_note(srcsn)); + + assert(arch_all == SKIP_ARCHALL || NULL == lookup_sourcenotetbl(srcsn->sources,srcn->source->package)); + /* if we're removing the entire binary, we should already have + * removed the source. if we're removing just the binaries on this + * arch (not arch:all) then we may be keeping the source + * + * really a logical XOR, I think. we don't rely on this assertion + * here + */ + + for (p = srcn->binaries[archnum]; p != NULL; p = p->next) { + dpkg_collected_package *cpkg; + if (arch_all == SKIP_ARCHALL && p->value->arch_all) { + insert_packagelist(addto, p->value); + addto = &(*addto)->next; + continue; + } + cpkg = lookup_packagetbl(srcsn->pkgs[archnum]->packages, + p->value->package); + remove_package(srcsn->pkgs[archnum], cpkg); + } + free_packagelist(srcn->binaries[archnum]); + srcn->binaries[archnum] = leftovers; +} + +typedef enum { NOTUNDOABLE = 0, UNDOABLE = 1 } undoable; +static void add_binaries_by_arch(dpkg_sources_note *srcsn, + dpkg_source_note *srcn, dpkg_source *src, + int archnum, undoable undoop, do_this arch_all) +{ + ownedpackagelist *p; + const char *archname = srcsn->archname[archnum]; + int origarchnum = -1; + int i; + + assert(is_sources_note(srcsn)); + assert(srcn == lookup_sourcenotetbl(srcsn->sources,srcn->source->package)); + for (i = 0; i < src->owner->n_arches; i++) { + if (strcmp(archname, src->owner->archname[i]) == 0) { + origarchnum = i; + break; + } + } + if (origarchnum == -1) return; /* nothing to add, no biggie */ + + for (p = src->packages[origarchnum]; p != NULL; p = p->next) { + dpkg_collected_package *cpkg; + + if (arch_all == SKIP_ARCHALL && p->value->arch_all) continue; + + if ((cpkg = lookup_packagetbl(srcsn->pkgs[archnum]->packages, + p->value->package))) + { + dpkg_source_note *srcnB; + packagelist **p; + + if (!undoop) { + printf("conflict w/o undo: binary %s, owned by %s, replaced by %s\n", cpkg->pkg->package, cpkg->pkg->source, src->package); + fflush(stdout); + } + srcnB = lookup_sourcenotetbl(srcsn->sources, cpkg->pkg->source); + assert(srcnB != NULL); + + for (p = &srcnB->binaries[archnum]; *p != NULL; p = &(*p)->next) { + if ((*p)->value == cpkg->pkg) break; + } + assert(*p != NULL); /* binary should be from source */ + + assert(undoop); + save_source_note(srcsn, srcnB); + remove_package(srcsn->pkgs[archnum], cpkg); + remove_packagelist(p); + } + + add_package(srcsn->pkgs[archnum], p->value); + insert_packagelist(&srcn->binaries[archnum], p->value); + } +} + +void upgrade_source(dpkg_sources_note *srcsn, dpkg_source *src) { + dpkg_source_note *srcn; + int i; + + new_op(srcsn); + + assert(is_sources_note(srcsn)); + /* first, find the old source, if it exists */ + srcn = remove_sourcenotetbl(srcsn->sources, src->package); + if (srcn != NULL) { + save_source_note(srcsn, srcn); + for (i = 0; i < srcn->n_arches; i++) { + remove_binaries_by_arch(srcsn, srcn, i, DO_ARCHALL); + } + free_source_note(srcn); + } else { + save_empty_source_note(srcsn, src); + } + + /* then add the new one */ + srcn = new_source_note(src, srcsn->n_arches); + add_sourcenotetbl(srcsn->sources, src->package, srcn); + for (i = 0; i < srcsn->n_arches; i++) { + add_binaries_by_arch(srcsn, srcn, src, i, UNDOABLE, DO_ARCHALL); + } + assert(is_sources_note(srcsn)); +} + +void upgrade_arch(dpkg_sources_note *srcsn, dpkg_source *src, char *arch) { + dpkg_source_note *srcn; + int archnum = -1; + int i; + + assert(is_sources_note(srcsn)); + /* first, find the old source */ + srcn = lookup_sourcenotetbl(srcsn->sources, src->package); + + assert(srcn != NULL); + new_op(srcsn); + save_source_note(srcsn, srcn); + + /* then lookup the archnum */ + for (i = 0; i < srcsn->n_arches; i++) { + if (strcmp(arch, srcsn->archname[i]) == 0) { + archnum = i; + break; + } + } + if (archnum == -1) die("upgrade_arch: unknown arch"); + + /* then remove the old stuff and add the new */ + remove_binaries_by_arch(srcsn, srcn, archnum, SKIP_ARCHALL); + add_binaries_by_arch(srcsn, srcn, src, archnum, UNDOABLE, SKIP_ARCHALL); + assert(is_sources_note(srcsn)); +} + +void remove_source(dpkg_sources_note *srcsn, char *name) { + dpkg_source_note *srcn; + int i; + + assert(is_sources_note(srcsn)); + srcn = remove_sourcenotetbl(srcsn->sources, name); + assert(srcn != NULL); + + new_op(srcsn); + save_source_note(srcsn, srcn); + for (i = 0; i < srcn->n_arches; i++) { + remove_binaries_by_arch(srcsn, srcn, i, DO_ARCHALL); + } + free_source_note(srcn); + assert(is_sources_note(srcsn)); +} + +int can_undo(dpkg_sources_note *srcsn) { + assert(is_sources_note(srcsn)); + return srcsn->undo != NULL; +} + +void undo_change(dpkg_sources_note *srcsn) { + dpkg_source_note *srcnO, *srcnC; /* old, current */ + source_note_list *srcnl; + int i; + + assert(is_sources_note(srcsn)); + assert(can_undo(srcsn)); + + srcnl = remove_source_note_listlist(&srcsn->undo); + + while(srcnl) { + srcnO = remove_source_note_list(&srcnl); + assert(srcnO != NULL); /* can_undo() implies this is true... */ + + srcnC = remove_sourcenotetbl(srcsn->sources, srcnO->source->package); + if (srcnC != NULL) { + for (i = 0; i < srcnC->n_arches; i++) { + remove_binaries_by_arch(srcsn, srcnC, i, DO_ARCHALL); + } + free_source_note(srcnC); + assert(!lookup_sourcenotetbl(srcsn->sources, + srcnO->source->package)); + } + + if (srcnO->binaries == NULL) { + /* no original source */ + assert(srcnC != NULL); /* some sort of no-op? freaky. */ + free_source_note(srcnO); + } else { + packagelist *p; + /* original source */ + add_sourcenotetbl(srcsn->sources, srcnO->source->package, srcnO); + for (i = 0; i < srcsn->n_arches; i++) { + for (p = srcnO->binaries[i]; p != NULL; p = p->next) { + add_package(srcsn->pkgs[i], p->value); + } + } + } + } +} + +LIST_IMPL(source_note_list, dpkg_source_note *, free_source_note, + block_malloc, block_free); +LIST_IMPL(source_note_listlist, source_note_list *, free_source_note_list, + block_malloc, block_free); + +void commit_changes(dpkg_sources_note *srcsn) { + assert(is_sources_note(srcsn)); + free_source_note_listlist(srcsn->undo); + srcsn->undo = NULL; +} + +dpkg_source_note *copy_source_note(dpkg_source_note *srcn) { + dpkg_source_note *srcn2; + packagelist *src, **dest; + int i; + + assert(srcn->binaries != NULL); + + srcn2 = block_malloc(sizeof(dpkg_source_note)); + if (srcn2 == NULL) die("copy_source_note alloc:"); + + srcn2->source = srcn->source; + srcn2->n_arches = srcn->n_arches; + srcn2->binaries = block_malloc(sizeof(packagenamelist*) * srcn2->n_arches); + if (srcn2->binaries == NULL) die("copy_source_note alloc:"); + + for (i = 0; i < srcn2->n_arches; i++) { + dest = &(srcn2->binaries[i]); + *dest = NULL; + for (src = srcn->binaries[i]; src; src = src->next) { + insert_packagelist(dest, src->value); + dest = &((*dest)->next); + } + } + + return srcn2; +} + +void write_notes(char *dir, dpkg_sources_note *srcsn) { + FILE *src; + FILE *archfile[100]; + char buf[1000]; + int i; + sourcenotetbl_iter srciter; + + assert(is_sources_note(srcsn)); + snprintf(buf, 1000, "%s/Sources", dir); + src = fopen(buf, "w"); + for (i = 0; i < srcsn->n_arches; i++) { + snprintf(buf, 1000, "%s/Packages_%s", dir, srcsn->archname[i]); + archfile[i] = fopen(buf, "w"); + } + + for (srciter = first_sourcenotetbl(srcsn->sources); + !done_sourcenotetbl(srciter); + srciter = next_sourcenotetbl(srciter)) + { + packagelist *p; + int i; + + if (!srciter.v->source->fake) + write_paragraph(src, srciter.v->source->details); + + for (i = 0; i < srcsn->n_arches; i++) { + for (p = srciter.v->binaries[i]; p != NULL; p = p->next) { + write_paragraph(archfile[i], p->value->details); + } + } + } + + fclose(src); + for (i = 0; i < srcsn->n_arches; i++) { + fclose(archfile[i]); + } +} + diff --git a/lib/dpkg.h b/lib/dpkg.h new file mode 100644 index 0000000..b07d7bd --- /dev/null +++ b/lib/dpkg.h @@ -0,0 +1,207 @@ +#ifndef DPKG_H +#define DPKG_H + +#include "templates.h" +#include "memory.h" + +#include + +/************************************************************************** + * 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; + 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 *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 *); + +typedef struct satisfieddep satisfieddep; + +struct satisfieddep { + /* dependency *dep; */ + deplist *depl; + packagelist *pkgs; +}; + +LIST(satisfieddeplist, satisfieddep *); + +/************************************************************************** + * Coping with a source package (and collections thereof) as an abstract + * entity, owning a bunch of binary packages + */ + +typedef struct dpkg_source dpkg_source; +struct dpkg_source { + char *package; + char *version; + + int fake; + + struct dpkg_sources *owner; + ownedpackagelist **packages; /* one for each architecture */ + + dpkg_paragraph *details; +}; + +HASH(sourcetbl,char *,dpkg_source *); + +typedef struct dpkg_sources dpkg_sources; +struct dpkg_sources { + int n_arches; + char **archname; + sourcetbl *sources; + ownedpackagelist **unclaimedpackages; /* one for each arch */ +}; + +/************************************************************************** + */ + +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; + 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; +}; + +typedef struct dpkg_source_note dpkg_source_note; +struct dpkg_source_note { + dpkg_source *source; /* unowned */ + int n_arches; + packagelist **binaries; /* one for each arch */ +}; +HASH(sourcenotetbl, char *, dpkg_source_note *); + +LIST(source_note_list, dpkg_source_note *); + /* contains a copy of the previous source_note */ +LIST(source_note_listlist, source_note_list *); + /* contains a copy of all the source_notes modified by the last op */ + +typedef struct dpkg_sources_note dpkg_sources_note; +struct dpkg_sources_note { + unsigned long magic; + sourcenotetbl *sources; + int n_arches; + dpkg_packages **pkgs; + char **archname; + + source_note_listlist *undo; +}; + +void free_packages(dpkg_packages *pkgs); +void free_sources(dpkg_sources *s); + +dpkg_packages *get_architecture(dpkg_sources *srcs, char *arch); + +/* parsing things */ +int checkinstallable(dpkg_packages *pkgs, collpackagelist *instoneof); +int checkinstallable2(dpkg_packages *pkgs, char *pkgname); +satisfieddeplist *checkunsatisfiabledeps(dpkg_packages *pkgs, + deplistlist *deps); + +dpkg_sources *read_directory(char *dir, int n_arches, char *archname[]); +void write_directory(char *dir, dpkg_sources *srcs); + +void free_source(dpkg_source *s); + +/* adding and deleting and stuff */ +dpkg_sources_note *new_sources_note(int n_arches, char **archname); +void remove_source(dpkg_sources_note *srcsn, char *name); +void upgrade_source(dpkg_sources_note *srcsn, dpkg_source *src); +void upgrade_arch(dpkg_sources_note *srcsn, dpkg_source *src, char *arch); +void write_notes(char *dir, dpkg_sources_note *srcsn); +void free_sources_note(dpkg_sources_note *srcsn); +void free_source_note(dpkg_source_note *srcn); +void undo_change(dpkg_sources_note *srcsn); +int can_undo(dpkg_sources_note *srcsn); +void commit_changes(dpkg_sources_note *srcsn); + +int versioncmp(char *left, char *right); +int cmpversions(char *left, int op, char *right); + +#endif diff --git a/lib/freelist.c b/lib/freelist.c new file mode 100644 index 0000000..81440d6 --- /dev/null +++ b/lib/freelist.c @@ -0,0 +1,188 @@ +#include +#include +#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 diff --git a/lib/freelist.h b/lib/freelist.h new file mode 100644 index 0000000..5c0170a --- /dev/null +++ b/lib/freelist.h @@ -0,0 +1,14 @@ + +#ifndef FREELIST_H +#define FREELIST_H + +#include + +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 diff --git a/lib/index.html b/lib/index.html new file mode 100644 index 0000000..589afd5 --- /dev/null +++ b/lib/index.html @@ -0,0 +1,18 @@ +README
+Makefile
+assert.c
+britney-py.c
+checklib.c
+dpkg.c
+freelist.c
+memory.c
+memory2.c
+memory3.c
+dpkg-lib.cpp
+dpkg.h
+freelist.h
+memory.h
+templates.h
+check_out.py
+check_uptodate.py
+update_out.py
diff --git a/lib/memory.c b/lib/memory.c new file mode 100644 index 0000000..d63b768 --- /dev/null +++ b/lib/memory.c @@ -0,0 +1,389 @@ +#include +#include + +#include "memory.h" +#include "templates.h" +#include "freelist.h" + +/**** THEORY + * + +So, we have blocks with a freelist + + XXX............XXXXXXX..XXXXX.....XXXXXX...... + +Within a block, we work with segments. A segment is... + + ^..........| + +Every now and then we make sure we've got a decent sized segment. + +We have multiple blocks. They're kept ordered by the size of their +current segment. + + **********************************************/ + +#define ALIGN 4 + +#define FLBT_BITS (sizeof(flb_t)*8) +#define MEMBLOCKSIZE (1 << 22) +#define ALIGNEDSIZE(s) (((s) + ALIGN - 1) / ALIGN * ALIGN) + +struct memblock { + struct memblock *next; + + size_t n_bytes; /* index of free char */ + size_t size; /* size of block after char */ + + unsigned n_used_chunks; /* number of unfreed blocks */ + size_t n_used_bytes; /* number of bytes actually used */ + size_t n_productive_bytes; /* number of bytes used usefully */ + + flb_t free[MEMBLOCKSIZE/ALIGN/FLBT_BITS + 1]; + unsigned char mem[MEMBLOCKSIZE]; +}; +typedef struct memblock memblock; + +static memblock *base = NULL; + +#ifdef MDEBUG1 +static int valid_memblock_mdebug1(struct memblock *mb) { + size_t cnt, i; + static int rarity = 0; + + assert(mb->n_bytes + mb->size <= sizeof(mb->mem)); + + if (mb->n_used_chunks == 0) assert(mb->n_bytes == 0); + assert(((unsigned long)mb->mem + mb->n_bytes) % ALIGN == 0); + + assert(mb->n_productive_bytes <= mb->n_used_bytes); + assert(mb->n_used_bytes + mb->size <= sizeof(mb->mem)); + +#define TWO(k) (1ul << (k)) +#define CYCL(k) (~0ul / (1 + TWO(TWO(k)))) + + rarity++; rarity %= 25000; + if (rarity != 0) { + cnt = mb->n_used_bytes; + } else { + cnt = 0; + for (i = 0; i < sizeof(mb->mem)/ALIGN/FLBT_BITS+1; i++) { + unsigned long x = mb->free[i]; + size_t s; + x = (x & CYCL(0)) + ((x >> TWO(0)) & CYCL(0)); + x = (x & CYCL(1)) + ((x >> TWO(1)) & CYCL(1)); + for (s = 2; (2u << s) <= FLBT_BITS; s++) { + x += x >> TWO(s); + x &= CYCL(s); + } + cnt += x * ALIGN; + } + } +#undef TWO +#undef CYCL + + assert(cnt == mb->n_used_bytes); + + return 1; +} +#endif + +#if MDEBUG3 +static int valid_memblock_mdebug3(struct memblock *mb) { + size_t offset, step, used; + unsigned chunk = 0; + + offset = 0; + used = 0; + if ((unsigned long)mb->mem % ALIGN != 0) + offset = ALIGN - ((unsigned long)mb->mem % ALIGN); + + while(offset < mb->n_bytes) { + step = *(size_t*)(mb->mem + offset); + assert(step % ALIGN == 0 || step % ALIGN == 1); + if (step % ALIGN == 1) step--; /* freed */ + else used += step; + assert(step > 0); + offset += step; + chunk++; + } + + assert(used == mb->n_used_bytes); + + return 1; +} +#endif + +inline static int valid_memblock(struct memblock *mb) { + (void)mb; + + MDEBUG1_ONLY( if (!valid_memblock_mdebug1(mb)) return 0; ) + MDEBUG3_ONLY( if (!valid_memblock_mdebug3(mb)) return 0; ) + + return 1; +} + +void print_memblock_summary(void) { + struct memblock *mb; + unsigned long tused = 0, talloc = 0, tprod = 0, tavail = 0, nb = 0; + + for (mb = base; mb != NULL; mb = mb->next) { + assert(valid_memblock(mb)); + + MDEBUG3_ONLY( + fprintf(stderr, "%p: [%d,%lu/%lu,%p,%p]\n", mb, + mb->n_used_chunks, (unsigned long)mb->n_used_bytes, + (unsigned long)mb->n_bytes, mb->next, mb->mem); + ) + + if (mb != base && mb->size * 50 < sizeof(mb->mem) - mb->n_used_bytes) { + flb_t k; size_t s; + k = mb->n_bytes / ALIGN; + s = mb->size / ALIGN; + find_long_freebits(mb->free,MEMBLOCKSIZE/ALIGN/FLBT_BITS+1,&k,&s); + k *= ALIGN; s *= ALIGN; + fprintf(stderr, "%p %lu: Wasted block " + "[%d chunks, %lu free bytes, %lu avail bytes, %2.2f%%], suggested [%ld,%ld] -> [%ld,%ld]\n", + mb->mem, nb, mb->n_used_chunks, + (unsigned long) sizeof(mb->mem) - mb->n_used_bytes, + (unsigned long) mb->size, + (float) 100.0 * mb->size / (sizeof(mb->mem) - mb->n_used_bytes), + (unsigned long) mb->n_bytes, (unsigned long) mb->size, + (unsigned long) k, (unsigned long) s); + if (s > mb->size * 4 || s * 25 > sizeof(mb->mem) - mb->n_used_bytes) { + mb->n_bytes = k; + mb->size = s; + } + } + nb++; + tprod += mb->n_productive_bytes; + tused += mb->n_used_bytes; + tavail += mb->size; + talloc += sizeof(memblock); + } + fprintf(stderr, "TOTAL: %lu %lu KiB alloc" + "(%lu/%lu available, %2.2f%%) (%lu KiB used, %2.2f%%) (%lu KiB useful, %2.2f%%)\n", + nb, talloc / 1024, + (unsigned long) (base ? base->size / 1024 : 0), + tavail / 1024, (talloc > 0 ? 100.0*tavail/talloc : 0.0), + tused / 1024, (talloc > 0 ? 100.0*tused/talloc : 0.0), + tprod / 1024, (talloc > 0 ? 100.0*tprod/talloc : 0.0)); +} + +MDEBUG1_ONLY(static int first_malloc = 0;) + +#ifdef MDEBUG3 +static void print_memblock_stats(void) { + struct memblock *mb; + size_t offset; + + for (mb = base; mb != NULL; mb = mb->next) { + assert(valid_memblock(mb)); + + printf("%p: [%d,%lu/%lu/%lu,%p,%p:\n", mb, + mb->n_used_chunks, (unsigned long)mb->n_productive_bytes, + (unsigned long)mb->n_used_bytes, (unsigned long)mb->n_bytes, + mb->next, mb->mem); + + offset = 0; + if ((unsigned long)mb->mem % ALIGN != 0) + offset = ALIGN - ((unsigned long)mb->mem % ALIGN); + while(offset < mb->n_bytes) { + size_t step = *(size_t*)(mb->mem + offset); + if (step % ALIGN == 1) { + step--; + printf(" (%d)", (int) step); + } else { + printf(" %d", (int) step); + } + offset += step; + } + printf("\n"); + } + printf("\n"); + return; +} +#endif + +void *block_malloc(size_t size) { + memblock *where = base; + void *result; + size_t realsize = size; + + MDEBUG3_ONLY( if (first_malloc) print_memblock_stats(); ) + MDEBUG3_ONLY( first_malloc = 0; ) + + (void)assert(ALIGN >= sizeof(size_t)); /* ALIGN is set too small! */ + + MDEBUG2_ONLY(size += ALIGN;) + /* for the size, so the caller can be checked */ + + size = ALIGNEDSIZE(size); + + assert(size > 0 && size < sizeof(where->mem)); + assert(!where || ((unsigned long)where->mem + where->n_bytes) % ALIGN == 0); + + if ( !where || where->size < size ) { + MDEBUG1_ONLY(print_memblock_summary();) + where = malloc(sizeof(memblock)); + if (where == NULL) { + int i; + fprintf(stderr, "block_malloc: failed trying to allocate memblock\n"); + i = 0; where = base; while(where) {i++; where = where->next;} + fprintf(stderr, "(had allocated %d blocks, each %lu bytes)\n", i, + (unsigned long)sizeof(memblock)); + return NULL; + } + + where->n_used_chunks = 0; + memset(where->free, 0, sizeof(where->free)); + where->n_bytes = 0; + where->size = sizeof(where->mem); + + assert( (unsigned long)where->mem % ALIGN == 0); + /* XXX: should be able to cope with this :( */ + + where->n_used_bytes = where->n_bytes; + where->n_productive_bytes = 0; + (where)->next = base; + base = where; + + MDEBUG2_ONLY(memset(where->mem, 0xDD, sizeof(where->mem));) + } + + result = where->mem + where->n_bytes; + + assert( (unsigned long)where->mem % ALIGN == where->n_bytes % ALIGN ); + assert( size % ALIGN == 0 ); + mark_bits(where->free, + (unsigned long)((unsigned char*)result - where->mem) / ALIGN, + size / ALIGN, 1); + + where->n_bytes += size; + where->size -= size; + where->n_used_bytes += size; + where->n_productive_bytes += realsize; + where->n_used_chunks++; + + MDEBUG2_ONLY( memset(result, 0xEE, size); ) + + MDEBUG2_ONLY( *(size_t *)result = realsize; ) + MDEBUG2_ONLY( result += ALIGN; ) + + assert(((unsigned long)where->mem + where->n_bytes) % ALIGN == 0); + + assert(valid_memblock(where)); + + return result; +} + +static memblock **find_memblock(unsigned char *mem) { + memblock **where; + + for (where = &base; *where != NULL; where = &(*where)->next) { + memblock *mb = *where; + assert(valid_memblock(mb)); + if (&mb->mem[0] <= mem && (size_t)(mem - mb->mem) < sizeof(mb->mem)) { + return where; + } + } + return NULL; +} + +static void free_in_memblock(memblock *mb, unsigned char *mem, size_t size) { + MDEBUG2_ONLY(size_t *stmem = ((size_t*)mem) - 1;) + + assert(mb && mem && size > 0); + + mb->n_used_chunks--; + + mb->n_used_bytes -= ALIGNEDSIZE(size); + mark_bits(mb->free, (unsigned long)(mem - mb->mem) / ALIGN, + ALIGNEDSIZE(size) / ALIGN, 0); + +#ifdef MDEBUG2 + mark_bits(mb->free, (unsigned long)(mem - mb->mem) / ALIGN - 1, 1, 0); + mb->n_used_bytes -= ALIGN; +#endif + + if ((size_t)(mem - mb->mem) + ALIGNEDSIZE(size) == mb->n_bytes) { + size_t k = count_free_bits_back(mb->free, mb->n_bytes / ALIGN) * ALIGN; + mb->n_bytes -= k; + mb->size += k; + } + if ((size_t)(mem - mb->mem) == mb->n_bytes + mb->size) { + mb->size += count_free_bits_after(mb->free, + (mb->n_bytes + mb->size) / ALIGN, + sizeof(mb->mem) / ALIGN) * ALIGN; + } + + mb->n_productive_bytes -= size; + + if (mb->n_used_chunks == 0) { + assert(mb->n_productive_bytes == 0); + assert(mb->n_used_bytes == 0); + + mb->n_bytes = 0; + mb->size = sizeof(mb->mem); + mb->n_used_bytes = 0; + mb->n_productive_bytes = 0; + } + + MDEBUG2_ONLY( memset(mem, 0xAA, size); ) + +#ifdef MDEBUG2 + assert((unsigned char*)stmem >= mb->mem && (unsigned char*)stmem < mb->mem + sizeof(mb->mem)); + assert(*stmem % ALIGN == 0); + assert(*stmem == size); +#endif + + assert(valid_memblock(mb)); +} + +void block_free(void *vmem, size_t size) { + memblock **where; + MDEBUG1_ONLY(static int free_count = 0;) + + if (vmem == NULL) return; + + MDEBUG1_ONLY(first_malloc = 1;) + + where = find_memblock(vmem); + assert(where); + free_in_memblock(*where, vmem, size); + if ((*where)->n_used_chunks == 0 && *where != base) { + memblock *mb = *where; + MDEBUG1_ONLY( print_memblock_summary(); ) + *where = (*where)->next; + free(mb); + MDEBUG1_ONLY( fprintf(stderr, "Freed memblock\n"); ) + } + MDEBUG1_ONLY( free_count++; free_count %= 10000; ) + MDEBUG1_ONLY( if (!free_count) print_memblock_summary(); ) +} + +void *block_realloc(void *vmem, size_t oldsize, size_t newsize) { + void *vnewmem; + + if (vmem == NULL && newsize == 0) abort(); + if (vmem == NULL) return block_malloc(newsize); + if (newsize == 0) { + block_free(vmem, oldsize); + return NULL; + } + + vnewmem = block_malloc(newsize); + if (vnewmem) { + memcpy(vnewmem, vmem, (oldsize < newsize ? oldsize : newsize)); + block_free(vmem, oldsize); + } + return vnewmem; +} + +char *block_strdup(char *from) { + char *result; + if (!from) return NULL; + result = block_malloc(strlen(from) + 1); + strcpy(result, from); + return result; +} diff --git a/lib/memory.h b/lib/memory.h new file mode 100644 index 0000000..9e035a2 --- /dev/null +++ b/lib/memory.h @@ -0,0 +1,47 @@ +#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 */ diff --git a/lib/memory2.c b/lib/memory2.c new file mode 100644 index 0000000..60a429f --- /dev/null +++ b/lib/memory2.c @@ -0,0 +1,20 @@ +#include +#include +#include + +#define MIN(x,y) ((x) < (y) ? (x) : (y)) + +#define M 16 + +void *block_malloc(size_t size) { return malloc(size); } +void block_free(void *vmem) { free(vmem); } +void *block_realloc(void *vmem, size_t newsize) { return realloc(vmem, newsize); } +char *block_strdup(char *from) { + char *d; + if (!from) return 0; + d = block_malloc(strlen(from+1)); + if (d) strcpy(d, from); + return d; +} + +void print_memblock_summary(void) { } diff --git a/lib/memory3.c b/lib/memory3.c new file mode 100644 index 0000000..b3097fb --- /dev/null +++ b/lib/memory3.c @@ -0,0 +1,209 @@ +#include +#include + +#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 diff --git a/lib/templates.h b/lib/templates.h new file mode 100644 index 0000000..c0536a7 --- /dev/null +++ b/lib/templates.h @@ -0,0 +1,277 @@ +#ifndef TEMPLATE_H +#define TEMPLATE_H + +#include +#include +#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 + + diff --git a/lib/thoughts b/lib/thoughts new file mode 100644 index 0000000..411e7cb --- /dev/null +++ b/lib/thoughts @@ -0,0 +1,13 @@ +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 : remove it + foreach binary in new : add it + +(remove_architecture, upgrade_source_only; could also be used, theoretically) \ No newline at end of file