diff -r cae6308aa5b1 -r 29d5002bd17f razor.c --- a/razor.c Sun Jun 15 18:16:20 2008 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3244 +0,0 @@ -/* - * Copyright (C) 2008 Kristian Høgsberg - * Copyright (C) 2008 Red Hat, Inc - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "razor.h" -#include "razor-internal.h" -#include "types.h" - -struct razor_set_section { - uint32_t type; - uint32_t offset; - uint32_t size; -}; - -struct razor_set_header { - uint32_t magic; - uint32_t version; - struct razor_set_section sections[0]; -}; - -#define RAZOR_MAGIC 0x7a7a7a7a -#define RAZOR_DETAILS_MAGIC 0x7a7a7a7b -#define RAZOR_FILES_MAGIC 0x7a7a7a7c -#define RAZOR_VERSION 1 - -#define RAZOR_STRING_POOL 0 -#define RAZOR_PACKAGES 1 -#define RAZOR_PROPERTIES 2 -#define RAZOR_PACKAGE_POOL 3 -#define RAZOR_PROPERTY_POOL 4 - -#define RAZOR_DETAILS_STRING_POOL 0 - -#define RAZOR_FILES 0 -#define RAZOR_FILE_POOL 1 -#define RAZOR_FILE_STRING_POOL 2 - -struct razor_package { - uint name : 24; - uint flags : 8; - uint32_t version; - uint32_t arch; - uint32_t summary; - uint32_t description; - uint32_t url; - uint32_t license; - struct list_head properties; - struct list_head files; -}; - -struct razor_property { - uint name : 24; - uint flags : 6; - enum razor_property_type type : 2; - enum razor_version_relation relation : 32; - uint32_t version; - struct list_head packages; -}; - -struct razor_entry { - uint name : 24; - uint flags : 8; - uint32_t start; - struct list_head packages; -}; - -#define RAZOR_ENTRY_LAST 0x80 - -struct razor_set { - struct array string_pool; - struct array packages; - struct array properties; - struct array files; - struct array package_pool; - struct array property_pool; - struct array file_pool; - struct array file_string_pool; - struct array details_string_pool; - struct razor_set_header *header; - struct razor_set_header *details_header; - struct razor_set_header *files_header; -}; - -struct import_entry { - uint32_t package; - char *name; -}; - -struct import_directory { - uint32_t name, count; - struct array files; - struct array packages; - struct import_directory *last; -}; - -struct razor_importer { - struct razor_set *set; - struct hashtable table; - struct hashtable file_table; - struct hashtable details_table; - struct razor_package *package; - struct array properties; - struct array files; - struct array file_requires; -}; - -static void * -zalloc(size_t size) -{ - void *p; - - p = malloc(size); - memset(p, 0, size); - - return p; -} - -struct razor_set_section razor_sections[] = { - { RAZOR_STRING_POOL, offsetof(struct razor_set, string_pool) }, - { RAZOR_PACKAGES, offsetof(struct razor_set, packages) }, - { RAZOR_PROPERTIES, offsetof(struct razor_set, properties) }, - { RAZOR_PACKAGE_POOL, offsetof(struct razor_set, package_pool) }, - { RAZOR_PROPERTY_POOL, offsetof(struct razor_set, property_pool) }, -}; - -struct razor_set_section razor_files_sections[] = { - { RAZOR_FILES, offsetof(struct razor_set, files) }, - { RAZOR_FILE_POOL, offsetof(struct razor_set, file_pool) }, - { RAZOR_FILE_STRING_POOL, offsetof(struct razor_set, file_string_pool) }, -}; - -struct razor_set_section razor_details_sections[] = { - { RAZOR_DETAILS_STRING_POOL, offsetof(struct razor_set, details_string_pool) }, -}; - -struct razor_set * -razor_set_create(void) -{ - struct razor_set *set; - struct razor_entry *e; - char *empty; - - set = zalloc(sizeof *set); - - e = array_add(&set->files, sizeof *e); - empty = array_add(&set->string_pool, 1); - *empty = '\0'; - empty = array_add(&set->file_string_pool, 1); - *empty = '\0'; - empty = array_add(&set->details_string_pool, 1); - *empty = '\0'; - e->name = 0; - e->flags = RAZOR_ENTRY_LAST; - e->start = 0; - list_set_empty(&e->packages); - - return set; -} - -struct razor_set * -razor_set_open(const char *filename) -{ - struct razor_set *set; - struct razor_set_section *s; - struct stat stat; - struct array *array; - int fd; - - set = zalloc(sizeof *set); - fd = open(filename, O_RDONLY); - if (fstat(fd, &stat) < 0) - return NULL; - set->header = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - if (set->header == MAP_FAILED) { - free(set); - return NULL; - } - - for (s = set->header->sections; ~s->type; s++) { - if (s->type >= ARRAY_SIZE(razor_sections)) - continue; - if (s->type != razor_sections[s->type].type) - continue; - array = (void *) set + razor_sections[s->type].offset; - array->data = (void *) set->header + s->offset; - array->size = s->size; - array->alloc = s->size; - } - close(fd); - - return set; -} - -void -razor_set_destroy(struct razor_set *set) -{ - unsigned int size; - struct array *a; - int i; - - if (set->header) { - for (i = 0; set->header->sections[i].type; i++) - ; - size = set->header->sections[i].type; - munmap(set->header, size); - } else { - for (i = 0; i < ARRAY_SIZE(razor_sections); i++) { - a = (void *) set + razor_sections[i].offset; - free(a->data); - } - } - - if (set->details_header) { - for (i = 0; set->details_header->sections[i].type; i++) - ; - size = set->details_header->sections[i].type; - munmap(set->details_header, size); - } else { - for (i = 0; i < ARRAY_SIZE(razor_details_sections); i++) { - a = (void *) set + razor_details_sections[i].offset; - free(a->data); - } - } - - if (set->files_header) { - for (i = 0; set->files_header->sections[i].type; i++) - ; - size = set->files_header->sections[i].type; - munmap(set->files_header, size); - } else { - for (i = 0; i < ARRAY_SIZE(razor_files_sections); i++) { - a = (void *) set + razor_files_sections[i].offset; - free(a->data); - } - } - - free(set); -} - -void -razor_set_open_details(struct razor_set *set, const char *filename) -{ - struct razor_set_section *s; - struct stat stat; - struct array *array; - int fd; - - fd = open(filename, O_RDONLY); - if (fstat(fd, &stat) < 0) - return; - set->details_header = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - if (set->details_header == MAP_FAILED) - return; - - for (s = set->details_header->sections; ~s->type; s++) { - if (s->type >= ARRAY_SIZE(razor_details_sections)) - continue; - if (s->type != razor_details_sections[s->type].type) - continue; - array = (void *) set + razor_details_sections[s->type].offset; - array->data = (void *) set->details_header + s->offset; - array->size = s->size; - array->alloc = s->size; - } - close(fd); -} - -void -razor_set_open_files(struct razor_set *set, const char *filename) -{ - struct razor_set_section *s; - struct stat stat; - struct array *array; - int fd; - - fd = open(filename, O_RDONLY); - if (fstat(fd, &stat) < 0) - return; - set->files_header = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - if (set->files_header == MAP_FAILED) - return; - - for (s = set->files_header->sections; ~s->type; s++) { - if (s->type >= ARRAY_SIZE(razor_files_sections)) - continue; - if (s->type != razor_files_sections[s->type].type) - continue; - array = (void *) set + razor_files_sections[s->type].offset; - array->data = (void *) set->files_header + s->offset; - array->size = s->size; - array->alloc = s->size; - } - close(fd); -} - -static int -razor_set_write_sections_to_fd(struct razor_set *set, int fd, int magic, - struct razor_set_section *sections, - size_t array_size) -{ - char data[4096]; - struct razor_set_header *header = (struct razor_set_header *) data; - struct array *a; - uint32_t offset; - int i; - - memset(data, 0, sizeof data); - header->magic = magic; - header->version = RAZOR_VERSION; - offset = sizeof data; - - for (i = 0; i < array_size; i++) { - if (sections[i].type != i) - continue; - a = (void *) set + sections[i].offset; - header->sections[i].type = i; - header->sections[i].offset = offset; - header->sections[i].size = a->size; - offset += ALIGN(a->size, 4096); - } - - header->sections[i].type = ~0; - header->sections[i].offset = 0; - header->sections[i].size = 0; - - razor_write(fd, data, sizeof data); - memset(data, 0, sizeof data); - for (i = 0; i < array_size; i++) { - if (sections[i].type != i) - continue; - a = (void *) set + sections[i].offset; - razor_write(fd, a->data, a->size); - razor_write(fd, data, ALIGN(a->size, 4096) - a->size); - } - - return 0; -} - -int -razor_set_write_to_fd(struct razor_set *set, int fd, - enum razor_repo_file_type type) -{ - switch (type) { - case RAZOR_REPO_FILE_MAIN: - return razor_set_write_sections_to_fd(set, fd, RAZOR_MAGIC, - razor_sections, - ARRAY_SIZE(razor_sections)); - - case RAZOR_REPO_FILE_DETAILS: - return razor_set_write_sections_to_fd(set, fd, RAZOR_DETAILS_MAGIC, - razor_details_sections, - ARRAY_SIZE(razor_details_sections)); - case RAZOR_REPO_FILE_FILES: - return razor_set_write_sections_to_fd(set, fd, RAZOR_FILES_MAGIC, - razor_files_sections, - ARRAY_SIZE(razor_files_sections)); - default: - return -1; - } -} - -int -razor_set_write(struct razor_set *set, const char *filename, - enum razor_repo_file_type type) -{ - int fd, status; - - fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666); - if (fd < 0) - return -1; - - status = razor_set_write_to_fd(set, fd, type); - if (status) { - close(fd); - return status; - } - - return close(fd); -} - -void -razor_build_evr(char *evr_buf, int size, const char *epoch, - const char *version, const char *release) -{ - int len; - - if (!version || !*version) { - *evr_buf = '\0'; - return; - } - - if (epoch && *epoch && strcmp(epoch, "0") != 0) { - len = snprintf(evr_buf, size, "%s:", epoch); - evr_buf += len; - size -= len; - } - len = snprintf(evr_buf, size, "%s", version); - evr_buf += len; - size -= len; - if (release && *release) - snprintf(evr_buf, size, "-%s", release); -} - -void -razor_importer_begin_package(struct razor_importer *importer, - const char *name, - const char *version, - const char *arch) -{ - struct razor_package *p; - - p = array_add(&importer->set->packages, sizeof *p); - p->name = hashtable_tokenize(&importer->table, name); - p->flags = 0; - p->version = hashtable_tokenize(&importer->table, version); - p->arch = hashtable_tokenize(&importer->table, arch); - - importer->package = p; - array_init(&importer->properties); -} - -void -razor_importer_finish_package(struct razor_importer *importer) -{ - list_set_array(&importer->package->properties, - &importer->set->property_pool, - &importer->properties, - 1); - - array_release(&importer->properties); -} - -void -razor_importer_add_details(struct razor_importer *importer, - const char *summary, - const char *description, - const char *url, - const char *license) -{ - importer->package->summary = hashtable_tokenize(&importer->details_table, summary); - importer->package->description = hashtable_tokenize(&importer->details_table, description); - importer->package->url = hashtable_tokenize(&importer->details_table, url); - importer->package->license = hashtable_tokenize(&importer->details_table, license); -} - -void -razor_importer_add_property(struct razor_importer *importer, - const char *name, - enum razor_version_relation relation, - const char *version, - enum razor_property_type type) -{ - struct razor_property *p; - uint32_t *r; - - p = array_add(&importer->set->properties, sizeof *p); - p->name = hashtable_tokenize(&importer->table, name); - p->flags = 0; - p->type = type; - p->relation = relation; - p->version = hashtable_tokenize(&importer->table, version); - list_set_ptr(&p->packages, importer->package - - (struct razor_package *) importer->set->packages.data); - - r = array_add(&importer->properties, sizeof *r); - *r = p - (struct razor_property *) importer->set->properties.data; - - if (type == RAZOR_PROPERTY_REQUIRES && *name == '/') { - r = array_add(&importer->file_requires, sizeof *r); - *r = p->name; - } -} - -void -razor_importer_add_file(struct razor_importer *importer, const char *name) -{ - struct import_entry *e; - - e = array_add(&importer->files, sizeof *e); - - e->package = importer->package - - (struct razor_package *) importer->set->packages.data; - e->name = strdup(name); -} - -struct razor_importer * -razor_importer_new(void) -{ - struct razor_importer *importer; - - importer = zalloc(sizeof *importer); - importer->set = razor_set_create(); - hashtable_init(&importer->table, &importer->set->string_pool); - hashtable_init(&importer->file_table, &importer->set->file_string_pool); - hashtable_init(&importer->details_table, &importer->set->details_string_pool); - - return importer; -} - -/* Destroy an importer without creating the set. */ -void -razor_importer_destroy(struct razor_importer *importer) -{ - /* FIXME: write this */ -} - -static int -versioncmp(const char *s1, const char *s2) -{ - const char *p1, *p2; - long n1, n2; - int res; - - n1 = strtol(s1, (char **) &p1, 10); - n2 = strtol(s2, (char **) &p2, 10); - - /* Epoch; if one but not the other has an epoch set, default - * the epoch-less version to 0. */ - res = (*p1 == ':') - (*p2 == ':'); - if (res < 0) { - n1 = 0; - p1 = s1; - p2++; - } else if (res > 0) { - p1++; - n2 = 0; - p2 = s2; - } - - if (n1 != n2) - return n1 - n2; - while (*p1 && *p2) { - if (*p1 != *p2) - return *p1 - *p2; - p1++; - p2++; - if (isdigit(*p1) && isdigit(*p2)) - return versioncmp(p1, p2); - } - - return *p1 - *p2; -} - -static int -compare_packages(const void *p1, const void *p2, void *data) -{ - const struct razor_package *pkg1 = p1, *pkg2 = p2; - struct razor_set *set = data; - char *pool = set->string_pool.data; - - /* FIXME: what if the flags are different? */ - if (pkg1->name == pkg2->name) - return versioncmp(&pool[pkg1->version], &pool[pkg2->version]); - else - return strcmp(&pool[pkg1->name], &pool[pkg2->name]); -} - -static int -compare_properties(const void *p1, const void *p2, void *data) -{ - const struct razor_property *prop1 = p1, *prop2 = p2; - struct razor_set *set = data; - char *pool = set->string_pool.data; - - if (prop1->name != prop2->name) - return strcmp(&pool[prop1->name], &pool[prop2->name]); - else if (prop1->type != prop2->type) - return prop1->type - prop2->type; - else if (prop1->relation != prop2->relation) - return prop1->relation - prop2->relation; - else - return versioncmp(&pool[prop1->version], &pool[prop2->version]); -} - -static uint32_t * -uniqueify_properties(struct razor_set *set) -{ - struct razor_property *rp, *up, *rp_end; - struct array *pkgs, *p; - struct list_head *r; - uint32_t *map, *rmap; - int i, count, unique; - - count = set->properties.size / sizeof(struct razor_property); - map = razor_qsort_with_data(set->properties.data, - count, - sizeof(struct razor_property), - compare_properties, - set); - - rp_end = set->properties.data + set->properties.size; - rmap = malloc(count * sizeof *map); - pkgs = zalloc(count * sizeof *pkgs); - for (rp = set->properties.data, up = rp, i = 0; rp < rp_end; rp++, i++) { - if (rp->name != up->name || rp->type != up->type || - rp->relation != up->relation || rp->version != up->version) { - up++; - up->name = rp->name; - up->flags = 0; - up->type = rp->type; - up->relation = rp->relation; - up->version = rp->version; - } - - unique = up - (struct razor_property *) set->properties.data; - rmap[map[i]] = unique; - r = array_add(&pkgs[unique], sizeof *r); - *r = rp->packages; - } - free(map); - - if (up != rp) - up++; - set->properties.size = (void *) up - set->properties.data; - rp_end = up; - for (rp = set->properties.data, p = pkgs; rp < rp_end; rp++, p++) { - list_set_array(&rp->packages, &set->package_pool, p, 0); - array_release(p); - } - - free(pkgs); - - return rmap; -} - -static int -compare_filenames(const void *p1, const void *p2, void *data) -{ - const struct import_entry *e1 = p1; - const struct import_entry *e2 = p2; - const char *n1 = e1->name; - const char *n2 = e2->name; - - /* Need to make sure that the contents of a directory - * are sorted immediately after it. So "foo/bar" has to - * sort before "foo.conf" - * - * FIXME: this is about 60% slower than strcmp - */ - while (*n1 && *n2) { - if (*n1 < *n2) - return *n2 == '/' ? 1 : -1; - else if (*n1 > *n2) - return *n1 == '/' ? -1 : 1; - n1++; - n2++; - } - if (*n1) - return 1; - else if (*n2) - return -1; - else - return 0; -} - -static void -count_entries(struct import_directory *d) -{ - struct import_directory *p, *end; - - p = d->files.data; - end = d->files.data + d->files.size; - d->count = 0; - while (p < end) { - count_entries(p); - d->count += p->count + 1; - p++; - } -} - -static void -serialize_files(struct razor_set *set, - struct import_directory *d, struct array *array) -{ - struct import_directory *p, *end; - struct razor_entry *e = NULL; - uint32_t s; - - p = d->files.data; - end = d->files.data + d->files.size; - s = array->size / sizeof *e + d->files.size / sizeof *p; - while (p < end) { - e = array_add(array, sizeof *e); - e->name = p->name; - e->flags = 0; - e->start = p->count > 0 ? s : 0; - s += p->count; - - list_set_array(&e->packages, &set->package_pool, &p->packages, 0); - array_release(&p->packages); - p++; - } - if (e != NULL) - e->flags |= RAZOR_ENTRY_LAST; - - p = d->files.data; - end = d->files.data + d->files.size; - while (p < end) { - serialize_files(set, p, array); - p++; - } -} - -static void -remap_property_package_links(struct array *properties, uint32_t *rmap) -{ - struct razor_property *p, *end; - - end = properties->data + properties->size; - for (p = properties->data; p < end; p++) - list_remap_head(&p->packages, rmap); -} - -static void -build_file_tree(struct razor_importer *importer) -{ - int count, i, length; - struct import_entry *filenames; - char *f, *end; - uint32_t name, *r; - char dirname[256]; - struct import_directory *d, root; - struct razor_entry *e; - - count = importer->files.size / sizeof (struct import_entry); - razor_qsort_with_data(importer->files.data, - count, - sizeof (struct import_entry), - compare_filenames, - NULL); - - root.name = hashtable_tokenize(&importer->file_table, ""); - array_init(&root.files); - array_init(&root.packages); - root.last = NULL; - - filenames = importer->files.data; - for (i = 0; i < count; i++) { - f = filenames[i].name; - if (*f != '/') - continue; - f++; - - d = &root; - while (*f) { - end = strchr(f, '/'); - if (end == NULL) - end = f + strlen(f); - length = end - f; - memcpy(dirname, f, length); - dirname[length] ='\0'; - name = hashtable_tokenize(&importer->file_table, dirname); - if (d->last == NULL || d->last->name != name) { - d->last = array_add(&d->files, sizeof *d); - d->last->name = name; - d->last->last = NULL; - array_init(&d->last->files); - array_init(&d->last->packages); - } - d = d->last; - f = end + 1; - if (*end == '\0') - break; - } - - r = array_add(&d->packages, sizeof *r); - *r = filenames[i].package; - free(filenames[i].name); - } - - count_entries(&root); - e = importer->set->files.data; - e->name = root.name; - e->flags = RAZOR_ENTRY_LAST; - e->start = importer->files.size ? 1 : 0; - list_set_empty(&e->packages); - - serialize_files(importer->set, &root, &importer->set->files); - - array_release(&importer->files); -} - -static struct razor_entry * -find_entry(struct razor_set *set, struct razor_entry *dir, const char *pattern); - -static void -list_to_array(struct list *list, struct array *array) -{ - uint32_t *item; - - while (list) { - item = array_add(array, sizeof *item); - *item = list->data; - list = list_next(list); - } -} - -static int -compare_file_requires(const void *p1, const void *p2, void *data) -{ - uint32_t *f1 = (void *)p1, *f2 = (void *)p2; - const char *pool = data; - - return strcmp(&pool[*f1], &pool[*f2]); -} - -static void -find_file_provides(struct razor_importer *importer) -{ - struct razor_property *prop; - struct razor_entry *top, *entry; - struct razor_package *packages; - struct array pkgprops; - struct list *pkg; - uint32_t *req, *req_start, *req_end; - uint32_t *map, *newprop; - char *pool; - - pool = importer->set->string_pool.data; - packages = importer->set->packages.data; - top = importer->set->files.data; - - req = req_start = importer->file_requires.data; - req_end = importer->file_requires.data + importer->file_requires.size; - map = razor_qsort_with_data(req, req_end - req, sizeof *req, - compare_file_requires, pool); - free(map); - - for (req = req_start; req < req_end; req++) { - if (req > req_start && req[0] == req[-1]) - continue; - entry = find_entry(importer->set, top, &pool[*req]); - if (!entry) - continue; - - for (pkg = list_first(&entry->packages, &importer->set->package_pool); pkg; pkg = list_next(pkg)) { - prop = array_add(&importer->set->properties, sizeof *prop); - prop->name = *req; - prop->type = RAZOR_PROPERTY_PROVIDES; - prop->relation = RAZOR_VERSION_EQUAL; - prop->version = hashtable_tokenize(&importer->table, ""); - list_set_ptr(&prop->packages, pkg->data); - - /* Update property list of pkg */ - array_init(&pkgprops); - list_to_array(list_first(&packages[pkg->data].properties, &importer->set->property_pool), &pkgprops); - newprop = array_add(&pkgprops, sizeof *newprop); - *newprop = prop - (struct razor_property *)importer->set->properties.data; - list_set_array(&packages[pkg->data].properties, &importer->set->property_pool, &pkgprops, 1); - array_release(&pkgprops); - } - } - - array_release(&importer->file_requires); -} - -static void -build_package_file_lists(struct razor_set *set, uint32_t *rmap) -{ - struct razor_package *p, *packages; - struct array *pkgs; - struct razor_entry *e, *end; - struct list *r; - uint32_t *q; - int i, count; - - count = set->packages.size / sizeof *p; - pkgs = zalloc(count * sizeof *pkgs); - - end = set->files.data + set->files.size; - for (e = set->files.data; e < end; e++) { - list_remap_head(&e->packages, rmap); - r = list_first(&e->packages, &set->package_pool); - while (r) { - q = array_add(&pkgs[r->data], sizeof *q); - *q = e - (struct razor_entry *) set->files.data; - r = list_next(r); - } - } - - packages = set->packages.data; - for (i = 0; i < count; i++) { - list_set_array(&packages[i].files, &set->file_pool, &pkgs[i], 0); - array_release(&pkgs[i]); - } - free(pkgs); -} - -struct razor_set * -razor_importer_finish(struct razor_importer *importer) -{ - struct razor_set *set; - uint32_t *map, *rmap; - int i, count; - - build_file_tree(importer); - find_file_provides(importer); - - map = uniqueify_properties(importer->set); - list_remap_pool(&importer->set->property_pool, map); - free(map); - - count = importer->set->packages.size / sizeof(struct razor_package); - map = razor_qsort_with_data(importer->set->packages.data, - count, - sizeof(struct razor_package), - compare_packages, - importer->set); - - rmap = malloc(count * sizeof *rmap); - for (i = 0; i < count; i++) - rmap[map[i]] = i; - free(map); - - list_remap_pool(&importer->set->package_pool, rmap); - build_package_file_lists(importer->set, rmap); - remap_property_package_links(&importer->set->properties, rmap); - free(rmap); - - set = importer->set; - hashtable_release(&importer->table); - hashtable_release(&importer->file_table); - hashtable_release(&importer->details_table); - free(importer); - - return set; -} - -struct razor_package_iterator { - struct razor_set *set; - struct razor_package *package, *end; - struct list *index; - int free_index; -}; - -static struct razor_package_iterator * -razor_package_iterator_create_with_index(struct razor_set *set, - struct list *index) -{ - struct razor_package_iterator *pi; - - pi = zalloc(sizeof *pi); - pi->set = set; - pi->index = index; - - return pi; -} - -struct razor_package_iterator * -razor_package_iterator_create(struct razor_set *set) -{ - struct razor_package_iterator *pi; - - pi = zalloc(sizeof *pi); - pi->set = set; - pi->end = set->packages.data + set->packages.size; - pi->package = set->packages.data; - - return pi; -} - -struct razor_package_iterator * -razor_package_iterator_create_for_property(struct razor_set *set, - struct razor_property *property) -{ - struct list *index; - - index = list_first(&property->packages, &set->package_pool); - return razor_package_iterator_create_with_index(set, index); -} - -int -razor_package_iterator_next(struct razor_package_iterator *pi, - struct razor_package **package, - const char **name, - const char **version, - const char **arch) -{ - char *pool; - int valid; - struct razor_package *p, *packages; - - if (pi->package) { - p = pi->package++; - valid = p < pi->end; - } else if (pi->index) { - packages = pi->set->packages.data; - p = &packages[pi->index->data]; - pi->index = list_next(pi->index); - valid = 1; - } else - valid = 0; - - if (valid) { - pool = pi->set->string_pool.data; - *package = p; - *name = &pool[p->name]; - *version = &pool[p->version]; - *arch = &pool[p->arch]; - } else { - *package = NULL; - } - - return valid; -} - -void -razor_package_iterator_destroy(struct razor_package_iterator *pi) -{ - if (pi->free_index) - free(pi->index); - - free(pi); -} - -struct razor_package * -razor_set_get_package(struct razor_set *set, const char *package) -{ - struct razor_package_iterator *pi; - struct razor_package *p; - const char *name, *version, *arch; - - pi = razor_package_iterator_create(set); - while (razor_package_iterator_next(pi, &p, &name, &version, &arch)) { - if (strcmp(package, name) == 0) - break; - } - razor_package_iterator_destroy(pi); - - return p; -} - -void -razor_package_get_details(struct razor_set *set, struct razor_package *package, - const char **summary, const char **description, - const char **url, const char **license) -{ - const char *pool = set->details_string_pool.data; - - *summary = &pool[package->summary]; - *description = &pool[package->description]; - *url = &pool[package->url]; - *license = &pool[package->license]; -} - -struct razor_property_iterator { - struct razor_set *set; - struct razor_property *property, *end; - struct list *index; -}; - -struct razor_property_iterator * -razor_property_iterator_create(struct razor_set *set, - struct razor_package *package) -{ - struct razor_property_iterator *pi; - - pi = zalloc(sizeof *pi); - pi->set = set; - - if (package) { - pi->index = list_first(&package->properties, - &set->property_pool); - } else { - pi->property = set->properties.data; - pi->end = set->properties.data + set->properties.size; - } - - return pi; -} - -int -razor_property_iterator_next(struct razor_property_iterator *pi, - struct razor_property **property, - const char **name, - enum razor_version_relation *relation, - const char **version, - enum razor_property_type *type) -{ - char *pool; - int valid; - struct razor_property *p, *properties; - - if (pi->property) { - p = pi->property++; - valid = p < pi->end; - } else if (pi->index) { - properties = pi->set->properties.data; - p = &properties[pi->index->data]; - pi->index = list_next(pi->index); - valid = 1; - } else - valid = 0; - - if (valid) { - pool = pi->set->string_pool.data; - *property = p; - *name = &pool[p->name]; - *relation = p->relation; - *version = &pool[p->version]; - *type = p->type; - } else { - *property = NULL; - } - - return valid; -} - -void -razor_property_iterator_destroy(struct razor_property_iterator *pi) -{ - free(pi); -} - -static struct razor_entry * -find_entry(struct razor_set *set, struct razor_entry *dir, const char *pattern) -{ - struct razor_entry *e; - const char *n, *pool = set->file_string_pool.data; - int len; - - e = (struct razor_entry *) set->files.data + dir->start; - do { - n = pool + e->name; - if (strcmp(pattern + 1, n) == 0) - return e; - len = strlen(n); - if (e->start != 0 && strncmp(pattern + 1, n, len) == 0 && - pattern[len + 1] == '/') { - return find_entry(set, e, pattern + len + 1); - } - } while (!((e++)->flags & RAZOR_ENTRY_LAST)); - - return NULL; -} - -static void -list_dir(struct razor_set *set, struct razor_entry *dir, - char *prefix, const char *pattern) -{ - struct razor_entry *e; - const char *n, *pool = set->file_string_pool.data; - - e = (struct razor_entry *) set->files.data + dir->start; - do { - n = pool + e->name; - if (pattern && pattern[0] && fnmatch(pattern, n, 0) != 0) - continue; - printf("%s/%s\n", prefix, n); - if (e->start) { - char *sub = prefix + strlen (prefix); - *sub = '/'; - strcpy (sub + 1, n); - list_dir(set, e, prefix, pattern); - *sub = '\0'; - } - } while (!((e++)->flags & RAZOR_ENTRY_LAST)); -} - -void -razor_set_list_files(struct razor_set *set, const char *pattern) -{ - struct razor_entry *e; - char buffer[512], *p, *base; - - if (pattern == NULL || !strcmp (pattern, "/")) { - buffer[0] = '\0'; - list_dir(set, set->files.data, buffer, NULL); - return; - } - - strcpy(buffer, pattern); - e = find_entry(set, set->files.data, buffer); - if (e && e->start > 0) { - base = NULL; - } else { - p = strrchr(buffer, '/'); - if (p) { - *p = '\0'; - base = p + 1; - } else { - base = NULL; - } - } - e = find_entry(set, set->files.data, buffer); - if (e->start != 0) - list_dir(set, e, buffer, base); -} - -struct razor_package_iterator * -razor_package_iterator_create_for_file(struct razor_set *set, - const char *filename) -{ - struct razor_entry *entry; - struct list *index; - - entry = find_entry(set, set->files.data, filename); - if (entry == NULL) - return NULL; - - index = list_first(&entry->packages, &set->package_pool); - return razor_package_iterator_create_with_index(set, index); -} - -static struct list * -list_package_files(struct razor_set *set, struct list *r, - struct razor_entry *dir, uint32_t end, - char *prefix) -{ - struct razor_entry *e, *f, *entries; - uint32_t next, file; - char *pool; - int len; - - entries = (struct razor_entry *) set->files.data; - pool = set->file_string_pool.data; - - e = entries + dir->start; - do { - if (entries + r->data == e) { - printf("%s/%s\n", prefix, pool + e->name); - r = list_next(r); - if (!r) - return NULL; - if (r->data >= end) - return r; - } - } while (!((e++)->flags & RAZOR_ENTRY_LAST)); - - e = entries + dir->start; - do { - if (e->start == 0) - continue; - - if (e->flags & RAZOR_ENTRY_LAST) - next = end; - else { - f = e + 1; - while (f->start == 0 && !(f->flags & RAZOR_ENTRY_LAST)) - f++; - if (f->start == 0) - next = end; - else - next = f->start; - } - - file = r->data; - if (e->start <= file && file < next) { - len = strlen(prefix); - prefix[len] = '/'; - strcpy(prefix + len + 1, pool + e->name); - r = list_package_files(set, r, e, next, prefix); - prefix[len] = '\0'; - } - } while (!((e++)->flags & RAZOR_ENTRY_LAST) && r != NULL); - - return r; -} - -void -razor_set_list_package_files(struct razor_set *set, const char *name) -{ - struct razor_package *package; - struct list *r; - uint32_t end; - char buffer[512]; - - package = razor_set_get_package(set, name); - - r = list_first(&package->files, &set->file_pool); - end = set->files.size / sizeof (struct razor_entry); - buffer[0] = '\0'; - list_package_files(set, r, set->files.data, end, buffer); -} - -static void -razor_set_validate(struct razor_set *set, struct array *unsatisfied) -{ - struct razor_property *r, *p, *end; - uint32_t *u; - char *pool; - - end = set->properties.data + set->properties.size; - pool = set->string_pool.data; - - for (r = set->properties.data, p = r; r < end; r++) { - if (r->type != RAZOR_PROPERTY_REQUIRES) - continue; - - p = r; - while (p < end && p->name == r->name && - p->type == r->type) - p++; - - /* If there is more than one version of a provides, - * seek to the end for the highest version. */ - /* FIXME: This doesn't work if we have a series of - * requires a = 1, provides a = 1, requires a = 2, - * provides a = 2, as the kernel and kernel-devel - * does.*/ - while (p + 1 < end && p->name == (p + 1)->name && - p->type == (p + 1)->type) - p++; - - /* FIXME: We need to track property flags (<, <=, = - * etc) to properly determine if a requires is - * satisfied. The current code doesn't track that the - * requires a = 1 isn't satisfied by a = 2 provides. */ - - if (p == end || - p->type != RAZOR_PROPERTY_PROVIDES || - r->name != p->name || - versioncmp(&pool[r->version], &pool[p->version]) > 0) { - /* FIXME: We ignore file requires for now. */ - if (pool[r->name] == '/') - continue; - u = array_add(unsatisfied, sizeof *u); - *u = r - (struct razor_property *) set->properties.data; - } - } -} - -void -razor_set_list_unsatisfied(struct razor_set *set) -{ - struct array unsatisfied; - struct razor_property *properties, *r; - uint32_t *u, *end; - char *pool; - - array_init(&unsatisfied); - razor_set_validate(set, &unsatisfied); - - end = unsatisfied.data + unsatisfied.size; - properties = set->properties.data; - pool = set->string_pool.data; - - for (u = unsatisfied.data; u < end; u++) { - r = properties + *u; - if (pool[r->version] == '\0') - printf("%s not satisfied\n", - &pool[r->name]); - else - printf("%s-%s not satisfied\n", - &pool[r->name], - &pool[r->version]); - } - - array_release(&unsatisfied); -} - -#define UPSTREAM_SOURCE 0x80 - -struct source { - struct razor_set *set; - uint32_t *property_map; - uint32_t *file_map; -}; - -struct razor_merger { - struct razor_set *set; - struct hashtable table; - struct hashtable file_table; - struct hashtable details_table; - struct source source1; - struct source source2; -}; - -static struct razor_merger * -razor_merger_create(struct razor_set *set1, struct razor_set *set2) -{ - struct razor_merger *merger; - int count; - size_t size; - - merger = zalloc(sizeof *merger); - merger->set = razor_set_create(); - hashtable_init(&merger->table, &merger->set->string_pool); - hashtable_init(&merger->file_table, &merger->set->file_string_pool); - hashtable_init(&merger->details_table, &merger->set->details_string_pool); - - merger->source1.set = set1; - count = set1->properties.size / sizeof (struct razor_property); - size = count * sizeof merger->source1.property_map[0]; - merger->source1.property_map = zalloc(size); - count = set1->files.size / sizeof (struct razor_entry); - size = count * sizeof merger->source1.file_map[0]; - merger->source1.file_map = zalloc(size); - - merger->source2.set = set2; - count = set2->properties.size / sizeof (struct razor_property); - size = count * sizeof merger->source2.property_map[0]; - merger->source2.property_map = zalloc(size); - count = set2->files.size / sizeof (struct razor_entry); - size = count * sizeof merger->source2.file_map[0]; - merger->source2.file_map = zalloc(size); - - return merger; -} - -static void -add_package(struct razor_merger *merger, - struct razor_package *package, struct source *source, - uint32_t flags) -{ - char *pool; - struct list *r; - struct razor_package *p; - - pool = source->set->string_pool.data; - p = array_add(&merger->set->packages, sizeof *p); - p->name = hashtable_tokenize(&merger->table, &pool[package->name]); - p->flags = flags; - p->version = hashtable_tokenize(&merger->table, - &pool[package->version]); - p->arch = hashtable_tokenize(&merger->table, - &pool[package->arch]); - - p->properties = package->properties; - r = list_first(&package->properties, &source->set->property_pool); - while (r) { - source->property_map[r->data] = 1; - r = list_next(r); - } - - p->files = package->files; - r = list_first(&package->files, &source->set->file_pool); - while (r) { - source->file_map[r->data] = 1; - r = list_next(r); - } -} - -static uint32_t -add_property(struct razor_merger *merger, - const char *name, enum razor_version_relation relation, - const char *version, int type) -{ - struct razor_property *p; - - p = array_add(&merger->set->properties, sizeof *p); - p->name = hashtable_tokenize(&merger->table, name); - p->flags = 0; - p->type = type; - p->relation = relation; - p->version = hashtable_tokenize(&merger->table, version); - - return p - (struct razor_property *) merger->set->properties.data; -} - -static void -merge_properties(struct razor_merger *merger) -{ - struct razor_property *p1, *p2; - struct razor_set *set1, *set2; - uint32_t *map1, *map2; - int i, j, cmp, count1, count2; - char *pool1, *pool2; - - set1 = merger->source1.set; - set2 = merger->source2.set; - map1 = merger->source1.property_map; - map2 = merger->source2.property_map; - - i = 0; - j = 0; - pool1 = set1->string_pool.data; - pool2 = set2->string_pool.data; - - count1 = set1->properties.size / sizeof *p1; - count2 = set2->properties.size / sizeof *p2; - while (i < count1 || j < count2) { - if (i < count1 && map1[i] == 0) { - i++; - continue; - } - if (j < count2 && map2[j] == 0) { - j++; - continue; - } - p1 = (struct razor_property *) set1->properties.data + i; - p2 = (struct razor_property *) set2->properties.data + j; - if (i < count1 && j < count2) - cmp = strcmp(&pool1[p1->name], &pool2[p2->name]); - else if (i < count1) - cmp = -1; - else - cmp = 1; - if (cmp == 0) - cmp = p1->type - p2->type; - if (cmp == 0) - cmp = p1->relation - p2->relation; - if (cmp == 0) - cmp = versioncmp(&pool1[p1->version], - &pool2[p2->version]); - if (cmp < 0) { - map1[i++] = add_property(merger, - &pool1[p1->name], - p1->relation, - &pool1[p1->version], - p1->type); - } else if (cmp > 0) { - map2[j++] = add_property(merger, - &pool2[p2->name], - p2->relation, - &pool2[p2->version], - p2->type); - } else { - map1[i++] = map2[j++] = add_property(merger, - &pool1[p1->name], - p1->relation, - &pool1[p1->version], - p1->type); - } - } -} - -static void -emit_properties(struct list_head *properties, struct array *source_pool, - uint32_t *map, struct array *pool) -{ - uint32_t r; - struct list *p, *q; - - r = pool->size / sizeof *q; - p = list_first(properties, source_pool); - while (p) { - q = array_add(pool, sizeof *q); - q->data = map[p->data]; - q->flags = p->flags; - p = list_next(p); - } - - list_set_ptr(properties, r); -} - -static uint32_t -add_file(struct razor_merger *merger, const char *name) -{ - struct razor_entry *e; - - e = array_add(&merger->set->files, sizeof *e); - e->name = hashtable_tokenize(&merger->file_table, name); - e->flags = 0; - e->start = 0; - - return e - (struct razor_entry *)merger->set->files.data; -} - -/* FIXME. Blah */ -static int -fix_file_map(uint32_t *map, - struct razor_entry *files, - struct razor_entry *top) -{ - uint32_t e; - int found_file = 0; - - e = top->start; - do { - if (files[e].start) - fix_file_map(map, files, &files[e]); - if (map[e]) - found_file = 1; - } while (!(files[e++].flags & RAZOR_ENTRY_LAST)); - - if (found_file) - map[top - files] = 1; - return found_file; -} - -struct merge_directory { - uint32_t merged, dir1, dir2; -}; - -static void -merge_one_directory(struct razor_merger *merger, struct merge_directory *md) -{ - struct razor_entry *root1, *root2, *mroot, *e1, *e2; - struct razor_set *set1, *set2; - struct array merge_stack; - struct merge_directory *child_md, *end_md; - uint32_t *map1, *map2, start, last; - int cmp; - char *pool1, *pool2; - - set1 = merger->source1.set; - set2 = merger->source2.set; - map1 = merger->source1.file_map; - map2 = merger->source2.file_map; - pool1 = set1->string_pool.data; - pool2 = set2->string_pool.data; - root1 = (struct razor_entry *) set1->files.data; - root2 = (struct razor_entry *) set2->files.data; - - array_init(&merge_stack); - - start = merger->set->files.size / sizeof (struct razor_entry); - last = 0; - e1 = md->dir1 ? root1 + md->dir1 : NULL; - e2 = md->dir2 ? root2 + md->dir2 : NULL; - while (e1 || e2) { - if (!e2 && !map1[e1 - root1]) { - if ((e1++)->flags & RAZOR_ENTRY_LAST) - e1 = NULL; - continue; - } - if (!e1 && !map2[e2 - root2]) { - if ((e2++)->flags & RAZOR_ENTRY_LAST) - e2 = NULL; - continue; - } - if (e1 && !map1[e1 - root1] && - e2 && !map1[e2 - root2]) { - if ((e1++)->flags & RAZOR_ENTRY_LAST) - e1 = NULL; - if ((e2++)->flags & RAZOR_ENTRY_LAST) - e2 = NULL; - continue; - } - - if (!e1) - cmp = 1; - else if (!e2) - cmp = -1; - else { - cmp = strcmp (&pool1[e1->name], - &pool2[e2->name]); - } - - if (cmp < 0) { - if (map1[e1 - root1]) { - map1[e1 - root1] = last = - add_file(merger, &pool1[e1->name]); - if (e1->start) { - child_md = array_add(&merge_stack, sizeof (struct merge_directory)); - child_md->merged = last; - child_md->dir1 = e1->start; - child_md->dir2 = 0; - } - } - if ((e1++)->flags & RAZOR_ENTRY_LAST) - e1 = NULL; - } else if (cmp > 0) { - if (map2[e2 - root2]) { - map2[e2 - root2] = last = - add_file(merger, &pool2[e2->name]); - if (e2->start) { - child_md = array_add(&merge_stack, sizeof (struct merge_directory)); - child_md->merged = last; - child_md->dir1 = 0; - child_md->dir2 = e2->start; - } - } - if ((e2++)->flags & RAZOR_ENTRY_LAST) - e2 = NULL; - } else { - map1[e1 - root1] = map2[e2- root2] = last = - add_file(merger, &pool1[e1->name]); - if (e1->start || e2->start) { - child_md = array_add(&merge_stack, sizeof (struct merge_directory)); - child_md->merged = last; - child_md->dir1 = e1->start; - child_md->dir2 = e2->start; - } - if ((e1++)->flags & RAZOR_ENTRY_LAST) - e1 = NULL; - if ((e2++)->flags & RAZOR_ENTRY_LAST) - e2 = NULL; - } - } - - mroot = (struct razor_entry *)merger->set->files.data; - if (last) { - mroot[last].flags = RAZOR_ENTRY_LAST; - mroot[md->merged].start = start; - } else - mroot[md->merged].start = 0; - - end_md = merge_stack.data + merge_stack.size; - for (child_md = merge_stack.data; child_md < end_md; child_md++) - merge_one_directory(merger, child_md); - array_release(&merge_stack); -} - -static void -merge_files(struct razor_merger *merger) -{ - struct razor_entry *root; - struct merge_directory md; - uint32_t *map1, *map2; - - map1 = merger->source1.file_map; - map2 = merger->source2.file_map; - - md.merged = 0; - - if (merger->source1.set->files.size) { - root = (struct razor_entry *) merger->source1.set->files.data; - if (root->start) - fix_file_map(map1, root, root); - md.dir1 = root->start; - } else - md.dir1 = 0; - - if (merger->source2.set->files.size) { - root = (struct razor_entry *) merger->source2.set->files.data; - if (root->start) - fix_file_map(map2, root, root); - md.dir2 = root->start; - } else - md.dir2 = 0; - - merge_one_directory(merger, &md); -} - -static void -emit_files(struct list_head *files, struct array *source_pool, - uint32_t *map, struct array *pool) -{ - uint32_t r; - struct list *p, *q; - - r = pool->size / sizeof *q; - p = list_first(files, source_pool); - while (p) { - q = array_add(pool, sizeof *q); - q->data = map[p->data]; - q->flags = p->flags; - p = list_next(p); - } - - list_set_ptr(files, r); -} - -/* Rebuild property->packages maps. We can't just remap these, as a - * property may have lost or gained a number of packages. Allocate an - * array per property and loop through the packages and add them to - * the arrays for their properties. */ -static void -rebuild_property_package_lists(struct razor_set *set) -{ - struct array *pkgs, *a; - struct razor_package *pkg, *pkg_end; - struct razor_property *prop, *prop_end; - struct list *r; - uint32_t *q; - int count; - - count = set->properties.size / sizeof (struct razor_property); - pkgs = zalloc(count * sizeof *pkgs); - pkg_end = set->packages.data + set->packages.size; - - for (pkg = set->packages.data; pkg < pkg_end; pkg++) { - r = list_first(&pkg->properties, &set->property_pool); - while (r) { - q = array_add(&pkgs[r->data], sizeof *q); - *q = pkg - (struct razor_package *) set->packages.data; - r = list_next(r); - } - } - - prop_end = set->properties.data + set->properties.size; - a = pkgs; - for (prop = set->properties.data; prop < prop_end; prop++, a++) { - list_set_array(&prop->packages, &set->package_pool, a, 0); - array_release(a); - } - free(pkgs); -} - -static void -rebuild_file_package_lists(struct razor_set *set) -{ - struct array *pkgs, *a; - struct razor_package *pkg, *pkg_end; - struct razor_entry *entry, *entry_end; - struct list *r; - uint32_t *q; - int count; - - count = set->files.size / sizeof (struct razor_entry); - pkgs = zalloc(count * sizeof *pkgs); - pkg_end = set->packages.data + set->packages.size; - - for (pkg = set->packages.data; pkg < pkg_end; pkg++) { - r = list_first(&pkg->files, &set->file_pool); - while (r) { - q = array_add(&pkgs[r->data], sizeof *q); - *q = pkg - (struct razor_package *) set->packages.data; - r = list_next(r); - } - } - - entry_end = set->files.data + set->files.size; - a = pkgs; - for (entry = set->files.data; entry < entry_end; entry++, a++) { - list_set_array(&entry->packages, &set->package_pool, a, 0); - array_release(a); - } - free(pkgs); -} - -static struct razor_set * -razor_merger_finish(struct razor_merger *merger) -{ - struct razor_set *result; - struct razor_package *p, *pend; - - /* As we built the package list, we filled out a bitvector of - * the properties that are referenced by the packages in the - * new set. Now we do a parallel loop through the properties - * and emit those marked in the bit vector to the new set. In - * the process, we update the bit vector to actually map from - * indices in the old property list to indices in the new - * property list for both sets. */ - - merge_properties(merger); - merge_files(merger); - - /* Now we loop through the packages again and emit the - * property lists, remapped to point to the new properties. */ - - pend = merger->set->packages.data + merger->set->packages.size; - for (p = merger->set->packages.data; p < pend; p++) { - struct source *src; - - if (p->flags & UPSTREAM_SOURCE) - src = &merger->source2; - else - src = &merger->source1; - - emit_properties(&p->properties, - &src->set->property_pool, - src->property_map, - &merger->set->property_pool); - emit_files(&p->files, - &src->set->file_pool, - src->file_map, - &merger->set->file_pool); - p->flags &= ~UPSTREAM_SOURCE; - } - - rebuild_property_package_lists(merger->set); - rebuild_file_package_lists(merger->set); - - result = merger->set; - hashtable_release(&merger->table); - hashtable_release(&merger->file_table); - hashtable_release(&merger->details_table); - free(merger); - - return result; -} - -/* The diff order matters. We should sort the packages so that a - * REMOVE of a package comes before the INSTALL, and so that all - * requires for a package have been installed before the package. - **/ - -void -razor_set_diff(struct razor_set *set, struct razor_set *upstream, - razor_package_callback_t callback, void *data) -{ - struct razor_package_iterator *pi1, *pi2; - struct razor_package *p1, *p2; - const char *name1, *name2, *version1, *version2, *arch1, *arch2; - int res; - - pi1 = razor_package_iterator_create(set); - pi2 = razor_package_iterator_create(upstream); - - razor_package_iterator_next(pi1, &p1, &name1, &version1, &arch1); - razor_package_iterator_next(pi2, &p2, &name2, &version2, &arch2); - - while (p1 || p2) { - if (p1 && p2) { - res = strcmp(name1, name2); - if (res == 0) - res = versioncmp(version1, version2); - } else { - res = 0; - } - - if (p2 == NULL || res < 0) - callback(name1, version1, NULL, arch1, data); - else if (p1 == NULL || res > 0) - callback(name2, NULL, version2, arch2, data); - - if (p1 != NULL && res <= 0) - razor_package_iterator_next(pi1, &p1, - &name1, &version1, &arch1); - if (p2 != NULL && res >= 0) - razor_package_iterator_next(pi2, &p2, - &name2, &version2, &arch2); - } - - razor_package_iterator_destroy(pi1); - razor_package_iterator_destroy(pi2); -} - -struct razor_transaction; -struct razor_transaction_package; -struct razor_transaction_resolver; - -struct razor_transaction { - int package_count, errors; - struct razor_set *system, *upstream; - - struct bitarray syspkgs, uppkgs; - struct array packages; -}; - -struct razor_transaction_package { - const char *name, *old_version, *new_version; - struct razor_package *old_package, *new_package; - enum razor_transaction_package_state state; - - /* dep_package is the name of the package that resulted in - * this entry being created (or NULL if the user requested the - * install/remove), with the other dep_ fields providing - * additional information. - * - * For INSTALL, if dep_type is REQUIRES, then dep_package - * required something that this package provides. If dep_type - * is CONFLICTS, then dep_package is a package that conflicted - * with an older version of this package, forcing an upgrade. - * - * For REMOVE, if dep_type is REQUIRES, then dep_package is a - * package that is being removed. If dep_type is OBSOLETES, - * then dep_package is a package that obsoletes this one. - * - * For OLD_CONFLICT or NEW_CONFLICT, dep_package is an - * existing package that conflicts with this one. The - * conflicting property comes from the already-installed - * package for OLD_CONFLICT, or the to-be-installed package - * for NEW_CONFLICT. - * - * For UNSATISFIABLE, the dep_ fields are as for an INSTALL, - * but the name field will be NULL. - */ - const char *dep_package; - enum razor_property_type dep_type; - const char *dep_property; - enum razor_version_relation dep_relation; - const char *dep_version; -}; - -static int -package_in_set(void *package, struct razor_set *set) -{ - return package >= set->packages.data && - package < set->packages.data + set->packages.size; -} - -static int -property_in_set(void *property, struct razor_set *set) -{ - return property >= set->properties.data && - property < set->properties.data + set->properties.size; -} - -static struct razor_package * -property_provider_package(struct razor_transaction *trans, - struct razor_property *prop, - int installed) -{ - struct razor_set *set; - struct bitarray *pkgbits; - struct razor_package *pkgs; - struct list *p; - - if (installed && prop->type != RAZOR_PROPERTY_PROVIDES) - return NULL; - else if (!installed && - prop->type != RAZOR_PROPERTY_PROVIDES && - prop->type != RAZOR_PROPERTY_OBSOLETES) - return NULL; - - if (property_in_set(prop, trans->system)) { - set = trans->system; - pkgbits = &trans->syspkgs; - } else { - set = trans->upstream; - pkgbits = &trans->uppkgs; - } - pkgs = set->packages.data; - - for (p = list_first(&prop->packages, &set->package_pool); p; p = list_next(p)) { - if (bitarray_get(pkgbits, p->data) != installed) - continue; - if (prop->type == RAZOR_PROPERTY_OBSOLETES || - pkgs[p->data].name == prop->name) - return &pkgs[p->data]; - } - return NULL; -} - -static int -compare_transaction_packages(const void *one, const void *two) -{ - struct razor_transaction_package **tp1 = (void *)one; - struct razor_transaction_package **tp2 = (void *)two; - - if (!(*tp1)->name) - return 1; - else if (!(*tp2)->name) - return -1; - else - return strcmp((*tp1)->name, (*tp2)->name); -} - -/* FIXME: merge this into the other property loop in razor_transaction_satisfy */ -static void -resolve_new_packages(struct razor_transaction *trans, - int start, int end) -{ - struct razor_property *sp, *up, *sp_end, *up_end; - struct razor_package *spkg, *spkgs, *upkg, *upkgs; - struct razor_transaction_package **packages; - const char *spool, *upool; - int i; - - sp_end = trans->system->properties.data + trans->system->properties.size; - spool = trans->system->string_pool.data; - spkgs = trans->system->packages.data; - up_end = trans->upstream->properties.data + trans->upstream->properties.size; - upool = trans->upstream->string_pool.data; - upkgs = trans->upstream->packages.data; - - /* FIXME, check if sorting the packages directly (rather than - * sorting pointers-to-packages) still results in confusing - * descriptions. - */ - packages = calloc(end - start, sizeof *packages); - for (i = start; i < end; i++) - packages[i - start] = ((struct razor_transaction_package *)trans->packages.data) + i; - qsort(packages, end - start, sizeof *packages, - compare_transaction_packages); - - sp = trans->system->properties.data; - up = trans->upstream->properties.data; - for (i = 0; i < end - start; i++) { - if (!packages[i]->name || - packages[i]->state >= RAZOR_PACKAGE_FIRST_ERROR_STATE) - continue; - - spkg = NULL; - while (sp < sp_end && - strcmp(&spool[sp->name], packages[i]->name) < 0) - sp++; - while (sp < sp_end && - strcmp(&spool[sp->name], packages[i]->name) == 0 && - !(spkg = property_provider_package(trans, sp, 1))) - sp++; - - upkg = NULL; - while (up < up_end && - strcmp(&upool[up->name], packages[i]->name) < 0) - up++; - while (up < up_end && - strcmp(&upool[up->name], packages[i]->name) == 0 && - !(upkg = property_provider_package(trans, up, 0))) - up++; - - if (packages[i]->state == RAZOR_PACKAGE_REMOVE || - packages[i]->state == RAZOR_PACKAGE_OBSOLETED) { - if (spkg) { - packages[i]->old_package = spkg; - packages[i]->name = &spool[spkg->name]; - packages[i]->old_version = &spool[spkg->version]; - bitarray_set(&trans->syspkgs, spkg - spkgs, 0); - } - if (!packages[i]->old_package) { - packages[i]->name = strdup(packages[i]->name); - packages[i]->state |= RAZOR_PACKAGE_UNAVAILABLE_FLAG; - trans->errors++; - } - } else { - if (upkg) { - packages[i]->new_package = upkg; - packages[i]->name = &upool[upkg->name]; - packages[i]->new_version = &upool[upkg->version]; - - if (up->name != upkg->name) { - packages[i]->dep_package = &upool[upkg->name]; - packages[i]->dep_type = up->type; - packages[i]->dep_property = &upool[up->name]; - packages[i]->dep_relation = up->relation; - packages[i]->dep_version = &upool[up->version]; - } - - if (spkg) { - packages[i]->old_package = spkg; - packages[i]->old_version = &spool[spkg->version]; - if (versioncmp(&spool[spkg->version], &upool[up->version]) >= 0) { - packages[i]->state = RAZOR_PACKAGE_UP_TO_DATE; - trans->errors++; - continue; - } - bitarray_set(&trans->syspkgs, spkg - spkgs, 0); - } - bitarray_set(&trans->uppkgs, upkg - upkgs, 1); - } - if (!packages[i]->new_package) { - packages[i]->name = strdup(packages[i]->name); - packages[i]->state |= RAZOR_PACKAGE_UNAVAILABLE_FLAG; - trans->errors++; - } - } - } -} - -static int -provider_satisfies_requirement(struct razor_property *provider, - const char *provider_strings, - struct razor_property *requirement, - const char *requirement_strings) -{ - int cmp, len; - const char *provided = &provider_strings[provider->version]; - const char *required = &requirement_strings[requirement->version]; - - if (!*required) - return 1; - if (!*provided) { - if (requirement->relation >= RAZOR_VERSION_EQUAL) - return 1; - else - return 0; - } - - cmp = versioncmp(provided, required); - - switch (requirement->relation) { - case RAZOR_VERSION_LESS: - return cmp < 0; - - case RAZOR_VERSION_LESS_OR_EQUAL: - if (cmp <= 0) - return 1; - /* fall through: FIXME, make sure this is correct */ - - case RAZOR_VERSION_EQUAL: - if (cmp == 0) - return 1; - - /* "foo == 1.1" is satisfied by "foo 1.1-2" */ - len = strlen(required); - if (!strncmp(required, provided, len) && provided[len] == '-') - return 1; - return 0; - - case RAZOR_VERSION_GREATER_OR_EQUAL: - return cmp >= 0; - - case RAZOR_VERSION_GREATER: - return cmp > 0; - } - - /* shouldn't happen */ - return 0; -} - -static struct razor_package * -find_package_for_file(struct razor_set *set, struct bitarray *pkgbits, - const char *filename, int installed) -{ - struct razor_package *pkgs = set->packages.data; - struct razor_entry *entry; - struct list *p; - - if (filename[0] != '/') - return 0; - - entry = find_entry(set, set->files.data, filename); - if (!entry) - return 0; - - for (p = list_first(&entry->packages, &set->package_pool); p; p = list_next(p)) { - if (bitarray_get(pkgbits, p->data) == installed) - return &pkgs[p->data]; - } - return NULL; -} - -static struct razor_package * -find_installed_package_for_file(struct razor_transaction *trans, - const char *filename) -{ - struct razor_package *pkg; - - pkg = find_package_for_file(trans->system, &trans->syspkgs, - filename, 1); - if (!pkg) - pkg = find_package_for_file(trans->upstream, &trans->uppkgs, - filename, 1); - return pkg; -} - -static struct razor_package * -find_uninstalled_package_for_file(struct razor_transaction *trans, - const char *filename) -{ - struct razor_package *pkg; - - pkg = find_package_for_file(trans->upstream, &trans->uppkgs, - filename, 0); - if (!pkg) - pkg = find_package_for_file(trans->system, &trans->syspkgs, - filename, 0); - return pkg; -} - -static struct razor_property * -skip_to_matching_property(struct razor_transaction *trans, - struct razor_property *match, - struct razor_property *prop) -{ - struct razor_set *mset, *pset; - const char *ppool, *mpool; - struct razor_property *prop_end; - - if (property_in_set(match, trans->system)) - mset = trans->system; - else - mset = trans->upstream; - - if (property_in_set(prop, trans->system)) - pset = trans->system; - else if (property_in_set(prop, trans->upstream)) - pset = trans->upstream; - else - return prop; - - prop_end = pset->properties.data + pset->properties.size; - ppool = pset->string_pool.data; - mpool = mset->string_pool.data; - - while (prop < prop_end && - strcmp(&ppool[prop->name], &mpool[match->name]) < 0) - prop++; - return prop; -} - -static struct razor_package * -find_package_matching(struct razor_transaction *trans, int installed, - struct razor_property *prop, - struct razor_property *req, - struct razor_set *req_set) -{ - struct razor_set *set; - struct bitarray *pkgbits; - struct razor_package *pkgs; - struct razor_property *props, *prop_end; - enum razor_property_type match_type; - const char *pool; - const char *rpool; - int match_name = (req->type == RAZOR_PROPERTY_OBSOLETES); - int match; - - if (property_in_set(prop, trans->system)) { - set = trans->system; - pkgbits = &trans->syspkgs; - } else if (property_in_set(prop, trans->upstream)) { - set = trans->upstream; - pkgbits = &trans->uppkgs; - } else - return NULL; - - if (!req_set) { - if (property_in_set(req, trans->system)) - req_set = trans->system; - else - req_set = trans->upstream; - } - rpool = req_set->string_pool.data; - - if (req->type == RAZOR_PROPERTY_PROVIDES) - match_type = RAZOR_PROPERTY_CONFLICTS; - else - match_type = RAZOR_PROPERTY_PROVIDES; - - pkgs = set->packages.data; - props = set->properties.data; - prop_end = set->properties.data + set->properties.size; - pool = set->string_pool.data; - - /* Find first matching property */ - while (prop < prop_end && - strcmp(&pool[prop->name], &rpool[req->name]) < 0) - prop++; - if (prop == prop_end || - strcmp(&pool[prop->name], &rpool[req->name]) > 0) - return NULL; - - if (prop->type < match_type) { - while (prop < prop_end && prop->type != match_type) - prop++; - } else { - while (prop >= props && prop->type != match_type) - prop--; - while (prop > props + 1 && (prop - 1)->name == prop->name && - (prop - 1)->type == match_type) - prop--; - } - - /* Scan matching properties */ - while (prop < prop_end && prop->type == match_type && - strcmp(&pool[prop->name], &rpool[req->name]) == 0) { - if (match_type == RAZOR_PROPERTY_PROVIDES) - match = provider_satisfies_requirement(prop, pool, req, rpool); - else - match = provider_satisfies_requirement(req, rpool, prop, pool); - if (match) { - struct list *pkg; - - for (pkg = list_first(&prop->packages, &set->package_pool); pkg; pkg = list_next(pkg)) { - if (bitarray_get(pkgbits, pkg->data) != installed) - continue; - if (!match_name || - strcmp(&pool[pkgs[pkg->data].name], - &rpool[req->name]) == 0) - return &pkgs[pkg->data]; - } - } - prop++; - } - - return NULL; -} - -static struct razor_package * -find_installed_package_for_property(struct razor_transaction *trans, - struct razor_property *sys_start, - struct razor_property *up_start, - struct razor_property *req) -{ - struct razor_package *pkg; - - pkg = find_package_matching(trans, 1, sys_start, req, NULL); - if (!pkg) - pkg = find_package_matching(trans, 1, up_start, req, NULL); - return pkg; -} - -static struct razor_package * -find_uninstalled_package_for_property(struct razor_transaction *trans, - struct razor_property *sys_start, - struct razor_property *up_start, - struct razor_property *req) -{ - struct razor_package *pkg; - - pkg = find_package_matching(trans, 0, up_start, req, NULL); - if (!pkg) - pkg = find_package_matching(trans, 0, sys_start, req, NULL); - return pkg; -} - -static struct razor_transaction_package * -find_transaction_package(struct razor_transaction *trans, const char *name) -{ - struct razor_transaction_package *packages; - int count, i; - - packages = trans->packages.data; - count = trans->packages.size / sizeof *packages; - for (i = 0; i < count; i++) { - if (packages[i].name && !strcmp(packages[i].name, name)) - return &packages[i]; - } - return NULL; -} - -/* FIXME? */ -static int -prop_is_being_installed(struct razor_transaction *trans, - struct razor_property *prop) -{ - struct list *pkg; - - for (pkg = list_first(&prop->packages, &trans->upstream->package_pool); pkg; pkg = list_next(pkg)) { - if (bitarray_get(&trans->uppkgs, pkg->data)) - return 1; - } - return 0; -} - -static int -prop_is_being_removed(struct razor_transaction *trans, - struct razor_property *prop) -{ - struct list *pkg; - - for (pkg = list_first(&prop->packages, &trans->system->package_pool); pkg; pkg = list_next(pkg)) { - if (bitarray_get(&trans->syspkgs, pkg->data)) - return 0; - } - return 1; -} - -static int -prop_is_being_updated(struct razor_transaction *trans, - struct razor_property *prop) -{ - struct razor_package *packages = trans->system->packages.data; - const char *pool = trans->system->string_pool.data; - struct razor_transaction_package *tp; - struct list *pkg; - - /* Assumes prop_is_being_removed returns true */ - - for (pkg = list_first(&prop->packages, &trans->system->package_pool); pkg; pkg = list_next(pkg)) { - tp = find_transaction_package(trans, &pool[packages[pkg->data].name]); - if (tp && tp->state == RAZOR_PACKAGE_REMOVE) - return 0; - } - return 1; -} - -static void -add_transaction_package(struct razor_transaction *trans, - struct razor_package *new_package, - struct razor_package *old_package, - enum razor_transaction_package_state state, - const char *req_package, - struct razor_property *req_prop) -{ - struct razor_set *new_package_set, *old_package_set, *req_set; - struct bitarray *reqpkgbits; - struct razor_transaction_package *tp, *already; - const char *pool; - struct razor_package *pkgs; - struct list *pkg; - int contradiction = 0; - - if (package_in_set(new_package, trans->system)) - new_package_set = trans->system; - else - new_package_set = trans->upstream; - if (package_in_set(old_package, trans->system)) - old_package_set = trans->system; - else - old_package_set = trans->upstream; - if (property_in_set(req_prop, trans->system)) { - req_set = trans->system; - reqpkgbits = &trans->syspkgs; - } else { - req_set = trans->upstream; - reqpkgbits = &trans->uppkgs; - } - - if (new_package) { - pool = new_package_set->string_pool.data; - already = find_transaction_package(trans, &pool[new_package->name]); - if (already) { - if (already->new_package == new_package) { - /* Already taken care of */ - return; - } else if (new_package_set == trans->upstream && - already->state == RAZOR_PACKAGE_FORCED_UPDATE) { - already->new_package = new_package; - return; - } else if (new_package_set == trans->upstream) { - return; - } - - /* Oops. We lose */ - if (state != RAZOR_PACKAGE_CONTRADICTION) - contradiction = 1; - } - } else if (old_package) { - pool = old_package_set->string_pool.data; - already = find_transaction_package(trans, &pool[old_package->name]); - if (already) { - if (already->old_package == old_package) { - /* Already taken care of */ - return; - } else if (old_package_set == trans->system) { - already->old_package = old_package; - return; - } - - /* Oops. We lose */ - if (state != RAZOR_PACKAGE_CONTRADICTION) - contradiction = 1; - } - } else - state = RAZOR_PACKAGE_UNSATISFIABLE; - - tp = array_add(&trans->packages, sizeof *tp); - memset(tp, 0, sizeof *tp); - - if (new_package) { - pool = new_package_set->string_pool.data; - tp->new_package = new_package; - tp->name = &pool[new_package->name]; - tp->new_version = &pool[new_package->version]; - - pkgs = new_package_set->packages.data; - } - if (old_package) { - pool = old_package_set->string_pool.data; - tp->old_package = old_package; - tp->name = &pool[old_package->name]; - tp->old_version = &pool[old_package->version]; - - pkgs = old_package_set->packages.data; - } - - tp->state = state; - if (state != RAZOR_PACKAGE_INSTALL && - state != RAZOR_PACKAGE_FORCED_UPDATE && - state != RAZOR_PACKAGE_REMOVE && - state != RAZOR_PACKAGE_OBSOLETED) - trans->errors++; - - if (contradiction) { - /* Do this now, after adding tp, so that it ends up - * after both the INSTALL and the REMOVE in the array. - */ - add_transaction_package(trans, new_package, old_package, - RAZOR_PACKAGE_CONTRADICTION, - NULL, NULL); - } - - if (req_package) - tp->dep_package = req_package; - if (!req_prop) - return; - - pool = req_set->string_pool.data; - pkgs = req_set->packages.data; - if (!req_package) { - for (pkg = list_first(&req_prop->packages, &req_set->package_pool); pkg; pkg = list_next(pkg)) { - if (bitarray_get(reqpkgbits, pkg->data)) - break; - } - if (pkg) - tp->dep_package = &pool[pkgs[pkg->data].name]; - } - - tp->dep_type = req_prop->type; - tp->dep_property = &pool[req_prop->name]; - tp->dep_relation = req_prop->relation; - tp->dep_version = &pool[req_prop->version]; -} - -static void -razor_transaction_satisfy(struct razor_transaction *trans) -{ - struct razor_package *spkgs, *upkgs, *pkg; - struct razor_property *sp, *sprops, *sprop_end; - struct razor_property *up, *uprops, *uprop_end; - struct razor_property *sr, *ur, *first_up; - const char *spool, *upool, *removed_package; - struct list *reqpkg; - - spkgs = trans->system->packages.data; - sprops = trans->system->properties.data; - sprop_end = trans->system->properties.data + trans->system->properties.size; - spool = trans->system->string_pool.data; - upkgs = trans->upstream->packages.data; - uprops = trans->upstream->properties.data; - uprop_end = trans->upstream->properties.data + trans->upstream->properties.size; - upool = trans->upstream->string_pool.data; - - sp = sprops; - for (up = uprops; up < uprop_end; up++) { - /* Skip 'up' ahead to a property of a package which is - * to-be-installed. - */ - while (up < uprop_end && - !prop_is_being_installed(trans, up)) - up++; - if (up == uprop_end) - break; - sp = skip_to_matching_property(trans, up, sp); - - switch (up->type) { - case RAZOR_PROPERTY_REQUIRES: - if (!strncmp(&upool[up->name], "rpmlib(", 7)) - break; - - if (find_installed_package_for_property(trans, sp, up, up) || - find_installed_package_for_file(trans, &upool[up->name])) { - /* Requires something that is either installed - * or to-be-installed. - */ - break; - } - - /* See if we can install a new upstream provider */ - pkg = find_uninstalled_package_for_property(trans, sp, up, up); - if (!pkg) - pkg = find_uninstalled_package_for_file(trans, &upool[up->name]); - add_transaction_package(trans, pkg, NULL, - RAZOR_PACKAGE_INSTALL, - NULL, up); - break; - - case RAZOR_PROPERTY_PROVIDES: - /* find_installed_package_for_property works backwards - * here, finding a *conflicting* installed package. - */ - pkg = find_installed_package_for_property(trans, sp, up, up); - if (!pkg) - break; - - if (package_in_set(pkg, trans->system)) { - /* pkg CONFLICTS with what 'up' PROVIDES. Try - * finding an upgrade - */ - add_transaction_package(trans, NULL, pkg, - RAZOR_PACKAGE_FORCED_UPDATE, - &upool[up->name], sp); - } else { - add_transaction_package(trans, NULL, pkg, - RAZOR_PACKAGE_CONTRADICTION, - NULL, up); - } - break; - - case RAZOR_PROPERTY_CONFLICTS: - pkg = find_installed_package_for_property(trans, sp, up, up); - if (!pkg) - break; - - if (package_in_set(pkg, trans->system)) { - /* Conflicts with something already installed. - * Try to upgrade out. - */ - add_transaction_package(trans, NULL, pkg, - RAZOR_PACKAGE_FORCED_UPDATE, - NULL, up); - } else { - add_transaction_package(trans, pkg, NULL, - RAZOR_PACKAGE_CONTRADICTION, - NULL, up); - } - break; - - case RAZOR_PROPERTY_OBSOLETES: - pkg = find_installed_package_for_property(trans, sp, up, up); - if (pkg) { - /* If pkg is to-be-installed, this - * will add a CONTRADICTION error as well. - */ - add_transaction_package(trans, NULL, pkg, - RAZOR_PACKAGE_OBSOLETED, - NULL, up); - } - break; - - default: - /* can't happen */ - break; - } - } - - up = uprops; - for (sp = sprops; sp < sprop_end; sp++) { - /* Skip 'sp' ahead to a PROVIDES of a package which is - * to-be-removed. - */ - while (sp < sprop_end && - (sp->type != RAZOR_PROPERTY_PROVIDES || - !prop_is_being_removed(trans, sp))) - sp++; - if (sp == sprop_end) - break; - - removed_package = &spool[spkgs[list_first(&sp->packages, &trans->system->package_pool)->data].name]; - - /* Skip 'up' to match */ - up = skip_to_matching_property(trans, sp, up); - ur = first_up = up; - - /* If the package is just being upgraded, we may - * already be installing an identical PROVIDES, so - * check for that. - */ - while (up < uprop_end && - strcmp(&spool[sp->name], &upool[up->name]) == 0 && - (up->type != RAZOR_PROPERTY_PROVIDES || - sp->relation != up->relation || - strcmp(&spool[sp->name], &upool[up->name]) != 0)) - up++; - if (up < uprop_end && - up->type == RAZOR_PROPERTY_PROVIDES && - strcmp(&spool[sp->name], &upool[up->name]) == 0 && - sp->relation == up->relation && - strcmp(&spool[sp->version], &upool[up->version]) == 0 && - prop_is_being_installed(trans, up)) { - up = first_up; - continue; - } - up = first_up; - - /* For all still-installed packages that require - * sp->name, see if they are satisfied by any other - * still-installed or to-be-installed property. If - * not, either remove or attempt to update the - * package, depending on why the required property has - * disappeared - */ - sr = sp; - while (sr > sprops + 1 && (sr - 1)->name == sr->name) - sr--; - for (; sr->type == RAZOR_PROPERTY_REQUIRES; sr++) { - if (prop_is_being_removed(trans, sr)) - continue; - if (find_installed_package_for_property(trans, sp, up, sr)) - continue; - - for (reqpkg = list_first(&sr->packages, &trans->system->package_pool); reqpkg; reqpkg = list_next(reqpkg)) { - if (!bitarray_get(&trans->syspkgs, reqpkg->data)) - continue; - pkg = &spkgs[reqpkg->data]; - if (prop_is_being_updated(trans, sp)) { - add_transaction_package(trans, NULL, pkg, - RAZOR_PACKAGE_FORCED_UPDATE, - removed_package, NULL); - } else { - add_transaction_package(trans, NULL, pkg, - RAZOR_PACKAGE_REMOVE, - removed_package, sr); - } - } - } - } -} - -void -razor_transaction_install_package(struct razor_transaction *transaction, - struct razor_package *package) -{ - add_transaction_package(transaction, package, NULL, - RAZOR_PACKAGE_INSTALL, NULL, NULL); -} - -void -razor_transaction_remove_package(struct razor_transaction *transaction, - struct razor_package *package) -{ - add_transaction_package(transaction, NULL, package, - RAZOR_PACKAGE_REMOVE, NULL, NULL); -} - -void -razor_transaction_update_all(struct razor_transaction *trans) -{ - struct razor_package *sp, *spkgs, *send, *up, *upkgs, *uend; - const char *spool, *upool; - - spkgs = trans->system->packages.data; - send = trans->system->packages.data + trans->system->packages.size; - spool = trans->system->string_pool.data; - up = upkgs = trans->upstream->packages.data; - uend = trans->upstream->packages.data + trans->upstream->packages.size; - upool = trans->upstream->string_pool.data; - - for (sp = spkgs; sp < send; sp++) { - while (up < uend && strcmp(&spool[sp->name], &upool[up->name]) > 0) - up++; - if (strcmp(&spool[sp->name], &upool[up->name]) == 0 && - versioncmp(&spool[sp->version], &upool[up->version]) < 0) { - add_transaction_package(trans, up, sp, - RAZOR_PACKAGE_INSTALL, - NULL, NULL); - } - } -} - -struct razor_transaction * -razor_transaction_create(struct razor_set *system, struct razor_set *upstream) -{ - struct razor_transaction *trans; - int count; - - trans = zalloc(sizeof *trans); - - trans->system = system; - trans->upstream = upstream ? upstream : razor_set_create(); - array_init(&trans->packages); - count = trans->system->packages.size / sizeof (struct razor_package); - bitarray_init(&trans->syspkgs, count, 1); - count = trans->upstream->packages.size / sizeof (struct razor_package); - bitarray_init(&trans->uppkgs, count, 0); - - return trans; -} - -static void -resolve_transaction(struct razor_transaction *trans) -{ - int start, end; - - if (trans->package_count > 0) - /* Already did this, return. */ - return; - - start = 0; - end = trans->packages.size / sizeof (struct razor_transaction_package); - - while (start != end) { - resolve_new_packages(trans, start, end); - if (trans->errors) - break; - - razor_transaction_satisfy(trans); - - start = end; - end = trans->packages.size / sizeof (struct razor_transaction_package); - } - - trans->package_count = end; -} - -const char * const razor_version_relations[] = { - /* same order as enum razor_version_relation */ - "<", "<=", "=", ">=", ">" -}; - -const char * const razor_property_types[] = { - /* same order as enum razor_property_type */ - "requires", "provides", "conflicts with", "obsoletes" -}; - -static void -print_requirement(struct razor_transaction_package *p) -{ - if (p->dep_type == RAZOR_PROPERTY_CONFLICTS && - !strcmp(p->dep_package, p->name)) { - printf(" because %s %s conflicts with %s", - p->name, p->old_version, p->dep_property); - if (*p->dep_version) { - printf(" %s %s", - razor_version_relations[p->dep_relation], - p->dep_version); - } - } else { - if (strcmp(p->name, p->dep_package) != 0) - printf(" for %s", p->dep_package); - if (*p->dep_version) { - printf(", which %s %s %s %s", - razor_property_types[p->dep_type], - p->dep_property, - razor_version_relations[p->dep_relation], - p->dep_version); - } else if (strcmp(p->dep_property, p->name) != 0) { - printf(", which %s %s", - razor_property_types[p->dep_type], - p->dep_property); - } - } -} - -int -razor_transaction_resolve(struct razor_transaction *trans) -{ - struct razor_transaction_package *p, *pend, *tps; - int errors_only = 0; - - resolve_transaction(trans); - - tps = trans->packages.data; - pend = trans->packages.data + trans->packages.size; - for (p = trans->packages.data; p < pend; p++) { - switch (p->state) { - case RAZOR_PACKAGE_INSTALL: - if (errors_only) - break; - - printf("Installing %s %s", p->name, p->new_version); - if (p->dep_package) - print_requirement(p); - printf("\n"); - break; - - case RAZOR_PACKAGE_FORCED_UPDATE: - if (errors_only) - break; - - printf("Updating %s to %s due to update of %s\n", - p->name, p->new_version, p->dep_package); - break; - - case RAZOR_PACKAGE_REMOVE: - if (errors_only) - break; - printf("Removing %s %s", p->name, p->old_version); - if (p->dep_package) { - printf(" which required %s", - p->dep_package); - if (strcmp(p->dep_property, p->dep_package) != 0) - printf(" for %s", p->dep_property); - } - printf("\n"); - break; - - case RAZOR_PACKAGE_OBSOLETED: - if (errors_only) - break; - printf("Removing %s %s", p->name, p->old_version); - if (p->dep_package) { - printf(" which is obsoleted by %s", - p->dep_package); - } - printf("\n"); - break; - - case RAZOR_PACKAGE_INSTALL_UNAVAILABLE: - printf("Error: can't find %s", p->name); - if (p->dep_package) { - printf(" (which is required"); - print_requirement(p); - printf(")"); - } - printf("\n"); - errors_only = 1; - break; - - case RAZOR_PACKAGE_UPDATE_UNAVAILABLE: - printf("Error: can't find an updated version of %s (which must be updated due to update of %s)\n", - p->name, p->dep_package); - errors_only = 1; - break; - - case RAZOR_PACKAGE_REMOVE_NOT_INSTALLED: - printf("Error: can't remove %s: not installed\n", p->name); - errors_only = 1; - break; - - case RAZOR_PACKAGE_UP_TO_DATE: - printf("Error: can't update %s", p->name); - if (p->dep_package) - printf(" (which must be updated due to update of %s)", p->dep_package); - printf(": %s is most recent version\n", p->old_version); - errors_only = 1; - break; - - case RAZOR_PACKAGE_CONTRADICTION: - printf("Error: package %s is marked for both installation and removal\n", p->name); - errors_only = 1; - break; - - case RAZOR_PACKAGE_OLD_CONFLICT: - printf("Error: can't install %s, because installed package %s conflicts with ", - p->name, p->dep_package); - if (*p->dep_version) { - printf("%s %s %s", - p->dep_property, - razor_version_relations[p->dep_relation], - p->dep_version); - } else - printf("it"); - printf("\n"); - - errors_only = 1; - break; - - case RAZOR_PACKAGE_NEW_CONFLICT: - printf("Error: can't install %s, because it conflicts with %s", - p->name, p->dep_package); - if (*p->dep_version) { - printf(" %s %s", - razor_version_relations[p->dep_relation], - p->dep_version); - } - printf("\n"); - - errors_only = 1; - break; - - case RAZOR_PACKAGE_UNSATISFIABLE: - printf("Error: can't find package for %s", p->dep_property); - if (*p->dep_version) { - printf(" %s %s", - razor_version_relations[p->dep_relation], - p->dep_version); - } - printf(" which is required by %s\n", - p->dep_package); - errors_only = 1; - break; - - default: - /* Shouldn't actually happen */ - break; - } - } - - return trans->errors; -} - -int -razor_transaction_unsatisfied_property(struct razor_transaction *trans, - const char *name, - enum razor_version_relation rel, - const char *version) -{ - struct razor_transaction_package *p, *end; - - end = trans->packages.data + trans->packages.size; - for (p = trans->packages.data; p < end; p++) { - if (p->state != RAZOR_PACKAGE_UNSATISFIABLE) - continue; - if (strcmp(name, p->dep_property) != 0 || - rel != p->dep_relation || - strcmp(version, p->dep_version) != 0) - continue; - - return 1; - } - - return 0; -} - -struct razor_set * -razor_transaction_finish(struct razor_transaction *trans) -{ - struct array install_packages, remove_packages; - struct razor_merger *merger; - struct razor_package *pkg, *i, *iend, *r, *rend, *s, *send; - struct razor_set *set; - struct source *source1, *source2; - char *spool, *ipool, *rpool; - uint32_t *map; - struct razor_transaction_package *p, *end; - int cmp; - - /* FIXME */ - if (trans->errors) - return NULL; - - /* Sort the transaction packages into two arrays */ - array_init(&install_packages); - array_init(&remove_packages); - - end = trans->packages.data + trans->packages.size; - for (p = trans->packages.data; p < end; p++) { - if (p->new_package) { - pkg = array_add(&install_packages, sizeof *pkg); - *pkg = *p->new_package; - } else { - pkg = array_add(&remove_packages, sizeof *pkg); - *pkg = *p->old_package; - } - } - map = razor_qsort_with_data(install_packages.data, - install_packages.size / sizeof *pkg, - sizeof *pkg, - compare_packages, - trans->upstream); - free(map); - map = razor_qsort_with_data(remove_packages.data, - remove_packages.size / sizeof *pkg, - sizeof *pkg, - compare_packages, - trans->system); - free(map); - - merger = razor_merger_create(trans->system, trans->upstream); - - source1 = &merger->source1; - source2 = &merger->source2; - - i = install_packages.data; - iend = install_packages.data + install_packages.size; - ipool = trans->upstream->string_pool.data; - - r = remove_packages.data; - rend = remove_packages.data + remove_packages.size; - rpool = trans->system->string_pool.data; - - s = trans->system->packages.data; - send = trans->system->packages.data + trans->system->packages.size; - spool = trans->system->string_pool.data; - - while (s < send || i < iend) { - /* Check if s is being removed */ - if (s < send && r < rend && - s->name == r->name && s->version && r->version) { - s++; - r++; - continue; - } - - if (s < send && i < iend) - cmp = strcmp(&spool[s->name], &ipool[i->name]); - else if (s < send) - cmp = -1; - else - cmp = 1; - if (cmp < 0) { - add_package(merger, s, source1, 0); - s++; - } else if (cmp == 0) { - add_package(merger, i, source2, UPSTREAM_SOURCE); - s++; - i++; - } else { - add_package(merger, i, source2, UPSTREAM_SOURCE); - i++; - } - } - - array_release(&install_packages); - array_release(&remove_packages); - - set = razor_merger_finish(merger); - razor_transaction_destroy(trans); - - return set; -} - -void -razor_transaction_destroy(struct razor_transaction *trans) -{ - struct razor_transaction_package *p, *end; - - end = trans->packages.data + trans->packages.size; - for (p = trans->packages.data; p < end; p++) { - if (!p->dep_package && - (p->state == RAZOR_PACKAGE_INSTALL_UNAVAILABLE || - p->state == RAZOR_PACKAGE_REMOVE_NOT_INSTALLED)) - free((char *)p->name); - } - - array_release(&trans->packages); - bitarray_release(&trans->syspkgs); - bitarray_release(&trans->uppkgs); - free(trans); - - /* FIXME: free upstream if it was created as an empty set */ -} - -struct razor_package_query { - struct razor_set *set; - char *vector; - int count; -}; - -struct razor_package_query * -razor_package_query_create(struct razor_set *set) -{ - struct razor_package_query *pq; - int count; - - pq = zalloc(sizeof *pq); - pq->set = set; - count = set->packages.size / sizeof(struct razor_package); - pq->vector = zalloc(count * sizeof(char)); - - return pq; -} - -void -razor_package_query_add_package(struct razor_package_query *pq, - struct razor_package *p) -{ - struct razor_package *packages; - - packages = pq->set->packages.data; - pq->count += pq->vector[p - packages] ^ 1; - pq->vector[p - packages] = 1; -} - -void -razor_package_query_add_iterator(struct razor_package_query *pq, - struct razor_package_iterator *pi) -{ - struct razor_package *packages, *p; - const char *name, *version, *arch; - - packages = pq->set->packages.data; - while (razor_package_iterator_next(pi, &p, &name, &version, &arch)) { - pq->count += pq->vector[p - packages] ^ 1; - pq->vector[p - packages] = 1; - } -} - -struct razor_package_iterator * -razor_package_query_finish(struct razor_package_query *pq) -{ - struct razor_package_iterator *pi; - struct razor_set *set; - struct list *index; - int i, j, count; - - set = pq->set; - count = set->packages.size / sizeof(struct razor_package); - index = zalloc(pq->count * sizeof *index); - - for (i = 0, j = 0; i < count; i++) { - if (!pq->vector[i]) - continue; - - index[j].data = i; - if (j == pq->count - 1) - index[j].flags = 0x80; - j++; - } - - free(pq); - - pi = razor_package_iterator_create_with_index(set, index); - pi->free_index = 1; - - return pi; -}