diff options
Diffstat (limited to 'librazor/importer.c')
-rw-r--r-- | librazor/importer.c | 488 |
1 files changed, 488 insertions, 0 deletions
diff --git a/librazor/importer.c b/librazor/importer.c new file mode 100644 index 0000000..10d87c2 --- /dev/null +++ b/librazor/importer.c @@ -0,0 +1,488 @@ +/* + * Copyright (C) 2008 Kristian Høgsberg <krh@redhat.com> + * 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 <string.h> +#include "razor-internal.h" +#include "razor.h" + +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_property(struct razor_importer *importer, + const char *name, + uint32_t flags, + const char *version) +{ + struct razor_property *p; + uint32_t *r; + + p = array_add(&importer->set->properties, sizeof *p); + p->name = hashtable_tokenize(&importer->table, name); + p->flags = flags; + 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 (((flags & RAZOR_PROPERTY_TYPE_MASK) == 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); + + return importer; +} + +/* Destroy an importer without creating the set. */ +void +razor_importer_destroy(struct razor_importer *importer) +{ + /* FIXME: write this */ +} + +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 razor_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->flags != prop2->flags) + return prop1->flags - prop2->flags; + else + return razor_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->flags != up->flags || + rp->version != up->version) { + up++; + up->name = rp->name; + up->flags = rp->flags; + 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->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->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 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 = razor_set_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->flags = + RAZOR_PROPERTY_PROVIDES | RAZOR_PROPERTY_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); + free(importer); + + return set; +} |