#include #include #include #include #include #include "dpkg.h" #include "memory.h" // enlarge this if britney has issues parsing packages // (e.g. very slow installability checks) #define SIZEOFHASHMAP 16 /* #define DIAGNOSE 1 */ #define insert_packagenamelist(x,y) insert_l_packagenamelist(x,y,__LINE__) static void free_dependency(dependency *dep); static void free_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); deplist *read_dep_and(char *buf); deplistlist *read_dep_andor(char *buf); 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); void add_package(dpkg_packages *pkgs, dpkg_package *pkg); 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; } 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); } } 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 *, SIZEOFHASHMAP, strhash, strcmp, KEEP(char*),free_collected_package); HASH_IMPL(virtualpkgtbl, char *, virtualpkg *, SIZEOFHASHMAP, strhash, strcmp, KEEP(char*), free_virtualpkg); /* dpkg_provision refers to memory allocated elsewhere */ LIST_IMPL(virtualpkg, dpkg_provision, KEEP(dpkg_provision), block_malloc, block_free); static void remove_virtualpackage(virtualpkgtbl *vpkgs, char *pkgname, dpkg_collected_package *cpkg) { virtualpkg *list; virtualpkg **where; list = lookup_virtualpkgtbl(vpkgs, pkgname); assert(list != NULL); where = &list; while((*where)->value.pkg != cpkg) { where = &(*where)->next; assert(*where != NULL); } delete_virtualpkg(where); if (list == NULL) { remove_virtualpkgtbl(vpkgs, pkgname); } else { replace_virtualpkgtbl(vpkgs, pkgname, list); } } static void add_virtualpackage(virtualpkgtbl *vpkgs, char *package, char *version, 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 */ ownedpackagenamelist *read_packagenames(char *buf) { ownedpackagenamelist *result = NULL; ownedpackagenamelist **addto = &result; DEBUG_ONLY( char *strend = buf + strlen(buf); ) char *sub; while ((sub = my_strdup(read_packagename(&buf, ",")))) { insert_ownedpackagenamelist(addto, sub); addto = &(*addto)->next; while(isspace(*buf)) buf++; if (*buf == ',') { buf++; continue; } if (*buf == '\0') { break; } die("read_packagenames no/bad seperator"); } DEBUG_ONLY( assert(buf <= strend); ) return result; } static char *read_until_char(char **buf, char *end) { static char *result = NULL; char *start; DEBUG_ONLY( char *strend = *buf + strlen(*buf); ) int n; while(isspace(**buf)) (*buf)++; start = *buf; while (**buf && !isspace(**buf) && strchr(end, **buf) == NULL) { (*buf)++; } n = *buf - start; if (n == 0) return NULL; result = realloc(result, n + 1); if (result == NULL) die("read_until_char alloc:"); strncpy(result, start, n); result[n] = '\0'; while(isspace(**buf)) (*buf)++; DEBUG_ONLY( assert(*buf <= strend); ) return result; } static char *read_packagename(char **buf, char *end) { return read_until_char(buf, end); } deplist *read_dep_and(char *buf) { return read_deplist(&buf, ',', '\0'); } static deplist *read_deplist(char **buf, char sep, char end) { deplist *result = NULL; deplist **addto = &result; char separs[3] = { sep, end, '\0' }; DEBUG_ONLY( char *strend = *buf + strlen(*buf); ) dependency *sub; while ((sub = read_dependency(buf, separs))) { insert_deplist(addto, sub); addto = &(*addto)->next; while(isspace(**buf)) (*buf)++; if (**buf == sep) { (*buf)++; continue; } if (**buf == '\0' || **buf == end) { break; } die("read_deplist no/bad seperator"); } DEBUG_ONLY( assert(*buf <= strend); ) return result; } deplistlist *read_dep_andor(char *buf) { deplistlist *result = NULL; deplistlist **addto = &result; deplist *sub; DEBUG_ONLY( char *strend = buf + strlen(buf); ) while ((sub = read_deplist(&buf, '|', ','))) { insert_deplistlist(addto, sub); addto = &(*addto)->next; if (*buf == ',') buf++; } DEBUG_ONLY( assert(buf <= strend); ) return result; } static dependency *read_dependency(char **buf, char *end) { dependency *dep; char *name; char newend[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 *, SIZEOFHASHMAP, 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 *, SIZEOFHASHMAP, 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]); } }