krh@248: /* krh@248: * Copyright (C) 2008 Kristian Høgsberg krh@248: * Copyright (C) 2008 Red Hat, Inc ali@369: * Copyright (C) 2009 J. Ali Harlow krh@248: * krh@248: * This program is free software; you can redistribute it and/or modify krh@248: * it under the terms of the GNU General Public License as published by krh@248: * the Free Software Foundation; either version 2 of the License, or krh@248: * (at your option) any later version. krh@248: * krh@248: * This program is distributed in the hope that it will be useful, krh@248: * but WITHOUT ANY WARRANTY; without even the implied warranty of krh@248: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the krh@248: * GNU General Public License for more details. krh@248: * krh@248: * You should have received a copy of the GNU General Public License along krh@248: * with this program; if not, write to the Free Software Foundation, Inc., krh@248: * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. krh@248: */ krh@248: krh@248: #define _GNU_SOURCE krh@248: ali@438: #include "config.h" krh@248: #include krh@248: #include "razor-internal.h" krh@248: #include "razor.h" krh@248: krh@309: /** krh@309: * razor_importer_create: krh@309: * krh@309: * Create a new %razor_importer. krh@309: * krh@309: * Returns: the new %razor_importer. krh@309: **/ krh@309: RAZOR_EXPORT struct razor_importer * krh@309: razor_importer_create(void) krh@309: { krh@309: struct razor_importer *importer; krh@309: krh@309: importer = zalloc(sizeof *importer); krh@309: importer->set = razor_set_create(); krh@309: hashtable_init(&importer->table, &importer->set->string_pool); krh@309: hashtable_init(&importer->details_table, krh@309: &importer->set->details_string_pool); krh@309: hashtable_init(&importer->file_table, krh@309: &importer->set->file_string_pool); krh@309: krh@309: return importer; krh@309: } krh@309: krh@309: /** krh@309: * razor_importer_destroy: krh@309: * @importer: the %razor_importer krh@309: * krh@309: * Destroy an importer without creating a %razor_set. Normally, krh@309: * %razor_importer_finish will create a new %razor_set and destroy the krh@309: * importer. If the import must be aborted without creating the set, krh@309: * just destroy the import using this function. krh@309: **/ krh@309: RAZOR_EXPORT void krh@309: razor_importer_destroy(struct razor_importer *importer) krh@309: { krh@309: /* FIXME: write this */ krh@309: } krh@309: krh@309: krh@309: /** krh@309: * razor_importer_begin_package: krh@309: * @importer: the %razor_importer krh@309: * @name: the name of the new package krh@309: * @version: the version of the new package krh@309: * @arch: the architechture of the new package. krh@309: * krh@309: * Begin describing a new package to the importer. This creates a new krh@309: * package and sets the %name, %version and %arch. Subsequent calls krh@309: * to %razor_importer_add_details, %razor_importer_add_property and krh@309: * %razor_importer_add_file further describe this package and krh@309: * %razor_importer_finish_package marks the end of meta data for this krh@309: * package. krh@309: **/ krh@269: RAZOR_EXPORT void krh@248: razor_importer_begin_package(struct razor_importer *importer, krh@248: const char *name, krh@248: const char *version, krh@248: const char *arch) krh@248: { ali@369: uint32_t empty; krh@248: struct razor_package *p; krh@248: krh@248: p = array_add(&importer->set->packages, sizeof *p); krh@248: p->name = hashtable_tokenize(&importer->table, name); krh@248: p->flags = 0; krh@248: p->version = hashtable_tokenize(&importer->table, version); krh@248: p->arch = hashtable_tokenize(&importer->table, arch); krh@248: krh@248: importer->package = p; krh@248: array_init(&importer->properties); ali@372: array_init(&importer->install_prefixes); ali@369: ali@369: empty = hashtable_tokenize(&importer->details_table, ""); ali@369: importer->package->preun.program = empty; ali@369: importer->package->preun.body = empty; ali@369: importer->package->postun.program = empty; ali@369: importer->package->postun.body = empty; krh@248: } krh@248: krh@309: /** krh@309: * razor_importer_finish_package: krh@309: * @importer: the %razor_importer krh@309: * krh@309: * Tells the importer that the current package is complete. krh@309: **/ krh@269: RAZOR_EXPORT void krh@248: razor_importer_finish_package(struct razor_importer *importer) krh@248: { krh@248: list_set_array(&importer->package->properties, krh@248: &importer->set->property_pool, krh@248: &importer->properties, krh@248: 1); krh@248: ali@372: list_set_array(&importer->package->install_prefixes, ali@372: &importer->set->prefix_pool, ali@372: &importer->install_prefixes, ali@372: 0); ali@372: krh@248: array_release(&importer->properties); ali@372: array_release(&importer->install_prefixes); krh@248: } krh@248: krh@309: /** krh@309: * razor_importer_add_details: krh@309: * @importer: the %razor_importer krh@309: * @summary: the package summary krh@309: * @description: the package description krh@309: * @url: the package url krh@309: * @license: the package license krh@309: * krh@309: * Provide additional information for the current package. krh@309: **/ krh@269: RAZOR_EXPORT void jbowes@258: razor_importer_add_details(struct razor_importer *importer, jbowes@258: const char *summary, jbowes@258: const char *description, jbowes@258: const char *url, jbowes@258: const char *license) jbowes@258: { jbowes@258: importer->package->summary = hashtable_tokenize(&importer->details_table, summary); jbowes@258: importer->package->description = hashtable_tokenize(&importer->details_table, description); jbowes@258: importer->package->url = hashtable_tokenize(&importer->details_table, url); jbowes@258: importer->package->license = hashtable_tokenize(&importer->details_table, license); jbowes@258: } jbowes@258: krh@309: /** ali@369: * razor_importer_add_script: ali@369: * @importer: the %razor_importer ali@369: * @script: either %RAZOR_PROPERTY_PREUN or %RAZOR_PROPERTY_POSTUN ali@369: * @program: the program to run the script ali@369: * @body: the body of the script ali@369: * ali@369: * Provide a script to use when uninstalling the current package. ali@369: **/ ali@369: RAZOR_EXPORT void ali@369: razor_importer_add_script(struct razor_importer *importer, ali@369: enum razor_property_flags script, ali@369: const char *program, ali@369: const char *body) ali@369: { ali@369: switch (script) { ali@369: case RAZOR_PROPERTY_PREUN: ali@369: importer->package->preun.program = ali@369: hashtable_tokenize(&importer->table, program); ali@369: importer->package->preun.body = ali@369: hashtable_tokenize(&importer->table, body); ali@369: break; ali@369: case RAZOR_PROPERTY_POSTUN: ali@369: importer->package->postun.program = ali@369: hashtable_tokenize(&importer->table, program); ali@369: importer->package->postun.body = ali@369: hashtable_tokenize(&importer->table, body); ali@369: break; ali@369: default: ali@369: break; ali@369: } ali@369: } ali@369: ali@369: /** ali@372: * razor_importer_add_install_prefixes: ali@372: * @importer: the %razor_importer ali@372: * @install_prefix: the relocated prefix ali@372: * ali@372: * Adds a relocated prefix for the current package. ali@372: **/ ali@372: RAZOR_EXPORT void ali@372: razor_importer_add_install_prefix(struct razor_importer *importer, ali@372: const char *install_prefix) ali@372: { ali@372: uint32_t *r; ali@372: ali@372: r = array_add(&importer->install_prefixes, sizeof *r); ali@372: *r = hashtable_tokenize(&importer->table, install_prefix); ali@372: } ali@372: ali@372: /** krh@309: * razor_importer_add_property: krh@309: * @importer: the %razor_importer krh@309: * @name: name of the property krh@309: * @flags: property flags krh@309: * @version: version of the property or %NULL krh@309: * krh@309: * Add a property for the current package. The %flags parameter krh@309: * determines the type of the property and optionally the relation to krh@309: * the specified version and the availability constraint. See krh@309: * %razor_property_flags for further information about the flag krh@309: * parameter. krh@309: **/ krh@269: RAZOR_EXPORT void krh@248: razor_importer_add_property(struct razor_importer *importer, krh@248: const char *name, krh@248: uint32_t flags, krh@248: const char *version) krh@248: { krh@248: struct razor_property *p; krh@248: uint32_t *r; krh@248: krh@248: p = array_add(&importer->set->properties, sizeof *p); krh@248: p->name = hashtable_tokenize(&importer->table, name); krh@248: p->flags = flags; krh@248: p->version = hashtable_tokenize(&importer->table, version); krh@248: list_set_ptr(&p->packages, importer->package - krh@248: (struct razor_package *) importer->set->packages.data); krh@248: krh@248: r = array_add(&importer->properties, sizeof *r); krh@248: *r = p - (struct razor_property *) importer->set->properties.data; krh@248: krh@248: if (((flags & RAZOR_PROPERTY_TYPE_MASK) == RAZOR_PROPERTY_REQUIRES) && krh@248: *name == '/') { krh@248: r = array_add(&importer->file_requires, sizeof *r); krh@248: *r = p->name; krh@248: } krh@248: } krh@248: krh@309: /** krh@309: * razor_importer_add_file: krh@309: * @importer: the %razor_importer krh@309: * @name: name of the file krh@309: * krh@309: * Add a file to the current package. krh@309: **/ krh@269: RAZOR_EXPORT void krh@248: razor_importer_add_file(struct razor_importer *importer, const char *name) krh@248: { krh@248: struct import_entry *e; krh@248: krh@248: e = array_add(&importer->files, sizeof *e); krh@248: krh@248: e->package = importer->package - krh@248: (struct razor_package *) importer->set->packages.data; krh@248: e->name = strdup(name); krh@248: } krh@248: krh@248: static int krh@248: compare_packages(const void *p1, const void *p2, void *data) krh@248: { krh@248: const struct razor_package *pkg1 = p1, *pkg2 = p2; krh@248: struct razor_set *set = data; krh@248: char *pool = set->string_pool.data; krh@248: krh@248: /* FIXME: what if the flags are different? */ krh@248: if (pkg1->name == pkg2->name) krh@248: return razor_versioncmp(&pool[pkg1->version], &pool[pkg2->version]); krh@248: else krh@248: return strcmp(&pool[pkg1->name], &pool[pkg2->name]); krh@248: } krh@248: krh@248: static int krh@248: compare_properties(const void *p1, const void *p2, void *data) krh@248: { krh@248: const struct razor_property *prop1 = p1, *prop2 = p2; krh@248: struct razor_set *set = data; krh@248: char *pool = set->string_pool.data; krh@248: krh@248: if (prop1->name != prop2->name) krh@248: return strcmp(&pool[prop1->name], &pool[prop2->name]); krh@248: else if (prop1->flags != prop2->flags) krh@248: return prop1->flags - prop2->flags; krh@257: else if (prop1->version != prop2->version) krh@257: return razor_versioncmp(&pool[prop1->version], &pool[prop2->version]); krh@248: else krh@257: return prop1->packages.list_ptr - prop2->packages.list_ptr; krh@248: } krh@248: krh@248: static uint32_t * krh@248: uniqueify_properties(struct razor_set *set) krh@248: { krh@248: struct razor_property *rp, *up, *rp_end; krh@248: struct array *pkgs, *p; krh@248: struct list_head *r; krh@248: uint32_t *map, *rmap; krh@248: int i, count, unique; krh@248: krh@248: count = set->properties.size / sizeof(struct razor_property); krh@248: map = razor_qsort_with_data(set->properties.data, krh@248: count, krh@248: sizeof(struct razor_property), krh@248: compare_properties, krh@248: set); krh@248: krh@248: rp_end = set->properties.data + set->properties.size; krh@248: rmap = malloc(count * sizeof *map); krh@248: pkgs = zalloc(count * sizeof *pkgs); krh@248: for (rp = set->properties.data, up = rp, i = 0; rp < rp_end; rp++, i++) { krh@248: if (rp->name != up->name || krh@248: rp->flags != up->flags || krh@248: rp->version != up->version) { krh@248: up++; krh@248: up->name = rp->name; krh@248: up->flags = rp->flags; krh@248: up->version = rp->version; krh@248: } krh@248: krh@248: unique = up - (struct razor_property *) set->properties.data; krh@248: rmap[map[i]] = unique; krh@248: r = array_add(&pkgs[unique], sizeof *r); krh@248: *r = rp->packages; krh@248: } krh@248: free(map); krh@248: krh@248: if (up != rp) krh@248: up++; krh@248: set->properties.size = (void *) up - set->properties.data; krh@248: rp_end = up; krh@248: for (rp = set->properties.data, p = pkgs; rp < rp_end; rp++, p++) { krh@248: list_set_array(&rp->packages, &set->package_pool, p, 0); krh@248: array_release(p); krh@248: } krh@248: krh@248: free(pkgs); krh@248: krh@248: return rmap; krh@248: } krh@248: krh@248: static int krh@248: compare_filenames(const void *p1, const void *p2, void *data) krh@248: { krh@248: const struct import_entry *e1 = p1; krh@248: const struct import_entry *e2 = p2; krh@248: const char *n1 = e1->name; krh@248: const char *n2 = e2->name; krh@248: krh@248: /* Need to make sure that the contents of a directory krh@248: * are sorted immediately after it. So "foo/bar" has to krh@248: * sort before "foo.conf" krh@248: * krh@248: * FIXME: this is about 60% slower than strcmp krh@248: */ krh@248: while (*n1 && *n2) { krh@248: if (*n1 < *n2) krh@248: return *n2 == '/' ? 1 : -1; krh@248: else if (*n1 > *n2) krh@248: return *n1 == '/' ? -1 : 1; krh@248: n1++; krh@248: n2++; krh@248: } krh@248: if (*n1) krh@248: return 1; krh@248: else if (*n2) krh@248: return -1; krh@248: else krh@248: return 0; krh@248: } krh@248: krh@248: static void krh@248: count_entries(struct import_directory *d) krh@248: { krh@248: struct import_directory *p, *end; krh@248: krh@248: p = d->files.data; krh@248: end = d->files.data + d->files.size; krh@248: d->count = 0; krh@248: while (p < end) { krh@248: count_entries(p); krh@248: d->count += p->count + 1; krh@248: p++; krh@248: } krh@248: } krh@248: krh@248: static void krh@248: serialize_files(struct razor_set *set, krh@248: struct import_directory *d, struct array *array) krh@248: { krh@248: struct import_directory *p, *end; krh@248: struct razor_entry *e = NULL; krh@248: uint32_t s; krh@248: krh@248: p = d->files.data; krh@248: end = d->files.data + d->files.size; krh@248: s = array->size / sizeof *e + d->files.size / sizeof *p; krh@248: while (p < end) { krh@248: e = array_add(array, sizeof *e); krh@248: e->name = p->name; krh@248: e->flags = 0; krh@248: e->start = p->count > 0 ? s : 0; krh@248: s += p->count; krh@248: krh@248: list_set_array(&e->packages, &set->package_pool, &p->packages, 0); krh@248: array_release(&p->packages); krh@248: p++; krh@248: } krh@248: if (e != NULL) krh@248: e->flags |= RAZOR_ENTRY_LAST; krh@248: krh@248: p = d->files.data; krh@248: end = d->files.data + d->files.size; krh@248: while (p < end) { krh@248: serialize_files(set, p, array); krh@248: p++; krh@248: } ali@458: ali@458: array_release(&d->files); krh@248: } krh@248: krh@248: static void krh@248: remap_property_package_links(struct array *properties, uint32_t *rmap) krh@248: { krh@248: struct razor_property *p, *end; krh@248: krh@248: end = properties->data + properties->size; krh@248: for (p = properties->data; p < end; p++) krh@248: list_remap_head(&p->packages, rmap); krh@248: } krh@248: krh@248: static void krh@248: build_file_tree(struct razor_importer *importer) krh@248: { krh@248: int count, i, length; krh@248: struct import_entry *filenames; krh@248: char *f, *end; ali@359: uint32_t name, *r, s; ali@458: uint32_t *map; ali@442: char dirname[256]; ali@359: struct import_directory *d, *last_root; ali@359: struct array roots; krh@248: struct razor_entry *e; krh@248: krh@248: count = importer->files.size / sizeof (struct import_entry); ali@359: filenames = importer->files.data; ali@458: map = razor_qsort_with_data(filenames, ali@458: count, ali@458: sizeof (struct import_entry), ali@458: compare_filenames, ali@458: NULL); ali@458: free(map); krh@248: ali@359: array_init(&roots); ali@359: last_root = NULL; krh@248: krh@248: for (i = 0; i < count; i++) { krh@248: f = filenames[i].name; ali@359: d = NULL; krh@248: while (*f) { krh@248: end = strchr(f, '/'); krh@248: if (end == NULL) krh@248: end = f + strlen(f); krh@248: length = end - f; krh@248: memcpy(dirname, f, length); ali@359: dirname[length] = '\0'; jbowes@264: name = hashtable_tokenize(&importer->file_table, jbowes@264: dirname); ali@359: if (!d) { ali@359: if (!last_root || last_root->name != name) { ali@359: d = array_add(&roots, sizeof *d); ali@359: d->name = name; ali@359: d->last = NULL; ali@359: array_init(&d->files); ali@359: array_init(&d->packages); ali@359: last_root = d; ali@359: } ali@359: d = last_root; ali@359: } else { ali@359: if (!d->last || d->last->name != name) { ali@359: d->last = array_add(&d->files, ali@359: sizeof *d); ali@359: d->last->name = name; ali@359: d->last->last = NULL; ali@359: array_init(&d->last->files); ali@359: array_init(&d->last->packages); ali@359: } ali@359: d = d->last; krh@248: } krh@248: f = end + 1; krh@248: if (*end == '\0') krh@248: break; krh@248: } krh@248: krh@248: r = array_add(&d->packages, sizeof *r); krh@248: *r = filenames[i].package; krh@248: free(filenames[i].name); krh@248: } krh@248: ali@359: count = roots.size / sizeof (struct import_directory); ali@359: d = roots.data; ali@359: s = count; ali@359: for (i = 0; i < count; i++) { ali@359: count_entries(d); ali@359: if (i) ali@359: e = array_add(&importer->set->files, sizeof *e); ali@359: else ali@359: e = importer->set->files.data; ali@359: e->name = d->name; ali@359: e->flags = 0; ali@359: e->start = d->count > 0 ? s : 0; ali@359: s += d->count; ali@359: list_set_empty(&e->packages); ali@359: d++; ali@359: } ali@359: if (count) ali@359: e->flags |= RAZOR_ENTRY_LAST; krh@248: ali@359: d = roots.data; ali@359: for (i = 0; i < count; i++) { ali@359: serialize_files(importer->set, d, &importer->set->files); ali@359: d++; ali@359: } krh@248: krh@248: array_release(&importer->files); ali@359: array_release(&roots); krh@248: } krh@248: krh@248: static void krh@248: list_to_array(struct list *list, struct array *array) krh@248: { krh@248: uint32_t *item; krh@248: krh@248: while (list) { krh@248: item = array_add(array, sizeof *item); krh@248: *item = list->data; krh@248: list = list_next(list); krh@248: } krh@248: } krh@248: krh@248: static int krh@248: compare_file_requires(const void *p1, const void *p2, void *data) krh@248: { krh@248: uint32_t *f1 = (void *)p1, *f2 = (void *)p2; krh@248: const char *pool = data; krh@248: krh@248: return strcmp(&pool[*f1], &pool[*f2]); krh@248: } krh@248: krh@248: static void krh@248: find_file_provides(struct razor_importer *importer) krh@248: { krh@248: struct razor_property *prop; krh@248: struct razor_entry *top, *entry; krh@248: struct razor_package *packages; krh@248: struct array pkgprops; krh@248: struct list *pkg; krh@248: uint32_t *req, *req_start, *req_end; krh@248: uint32_t *map, *newprop; krh@248: char *pool; krh@248: krh@248: pool = importer->set->string_pool.data; krh@248: packages = importer->set->packages.data; krh@248: top = importer->set->files.data; krh@248: krh@248: req = req_start = importer->file_requires.data; krh@248: req_end = importer->file_requires.data + importer->file_requires.size; krh@248: map = razor_qsort_with_data(req, req_end - req, sizeof *req, krh@248: compare_file_requires, pool); krh@248: free(map); krh@248: krh@248: for (req = req_start; req < req_end; req++) { krh@248: if (req > req_start && req[0] == req[-1]) krh@248: continue; krh@248: entry = razor_set_find_entry(importer->set, top, &pool[*req]); krh@248: if (!entry) krh@248: continue; krh@248: krh@248: for (pkg = list_first(&entry->packages, &importer->set->package_pool); pkg; pkg = list_next(pkg)) { krh@248: prop = array_add(&importer->set->properties, sizeof *prop); krh@248: prop->name = *req; krh@248: prop->flags = krh@248: RAZOR_PROPERTY_PROVIDES | RAZOR_PROPERTY_EQUAL; krh@248: prop->version = hashtable_tokenize(&importer->table, ""); krh@248: list_set_ptr(&prop->packages, pkg->data); krh@248: krh@248: /* Update property list of pkg */ krh@248: array_init(&pkgprops); krh@248: list_to_array(list_first(&packages[pkg->data].properties, &importer->set->property_pool), &pkgprops); krh@248: newprop = array_add(&pkgprops, sizeof *newprop); krh@248: *newprop = prop - (struct razor_property *)importer->set->properties.data; krh@248: list_set_array(&packages[pkg->data].properties, &importer->set->property_pool, &pkgprops, 1); krh@248: array_release(&pkgprops); krh@248: } krh@248: } krh@248: krh@248: array_release(&importer->file_requires); krh@248: } krh@248: krh@248: static void krh@248: build_package_file_lists(struct razor_set *set, uint32_t *rmap) krh@248: { krh@248: struct razor_package *p, *packages; krh@248: struct array *pkgs; krh@248: struct razor_entry *e, *end; krh@248: struct list *r; krh@248: uint32_t *q; krh@248: int i, count; krh@248: krh@248: count = set->packages.size / sizeof *p; krh@248: pkgs = zalloc(count * sizeof *pkgs); krh@248: krh@248: end = set->files.data + set->files.size; krh@248: for (e = set->files.data; e < end; e++) { krh@248: list_remap_head(&e->packages, rmap); krh@248: r = list_first(&e->packages, &set->package_pool); krh@248: while (r) { krh@248: q = array_add(&pkgs[r->data], sizeof *q); krh@248: *q = e - (struct razor_entry *) set->files.data; krh@248: r = list_next(r); krh@248: } krh@248: } krh@248: krh@248: packages = set->packages.data; krh@248: for (i = 0; i < count; i++) { krh@248: list_set_array(&packages[i].files, &set->file_pool, &pkgs[i], 0); krh@248: array_release(&pkgs[i]); krh@248: } krh@248: free(pkgs); krh@248: } krh@248: krh@309: /** krh@309: * razor_importer_finish: krh@309: * @importer: the %razor_importer krh@309: * krh@309: * Finish importing packages and create the package set. This sorts krh@309: * and indexes all the packages, properties and files in the importer krh@309: * and creates a new %razor_set. After creating the new package set, krh@309: * the importer is destroyed. krh@309: * krh@309: * Returns: the new %razor_set krh@309: **/ krh@269: RAZOR_EXPORT struct razor_set * krh@248: razor_importer_finish(struct razor_importer *importer) krh@248: { krh@248: struct razor_set *set; krh@248: uint32_t *map, *rmap; krh@248: int i, count; krh@248: krh@248: build_file_tree(importer); krh@248: find_file_provides(importer); krh@248: krh@248: map = uniqueify_properties(importer->set); krh@248: list_remap_pool(&importer->set->property_pool, map); krh@248: free(map); krh@248: krh@248: count = importer->set->packages.size / sizeof(struct razor_package); krh@248: map = razor_qsort_with_data(importer->set->packages.data, krh@248: count, krh@248: sizeof(struct razor_package), krh@248: compare_packages, krh@248: importer->set); krh@248: krh@248: rmap = malloc(count * sizeof *rmap); krh@248: for (i = 0; i < count; i++) krh@248: rmap[map[i]] = i; krh@248: free(map); krh@248: krh@248: list_remap_pool(&importer->set->package_pool, rmap); krh@248: build_package_file_lists(importer->set, rmap); krh@248: remap_property_package_links(&importer->set->properties, rmap); krh@248: free(rmap); krh@248: krh@248: set = importer->set; krh@248: hashtable_release(&importer->table); jbowes@264: hashtable_release(&importer->details_table); jbowes@264: hashtable_release(&importer->file_table); krh@248: free(importer); krh@248: krh@248: return set; krh@248: }