librazor/importer.c
author J. Ali Harlow <ali@juiblex.co.uk>
Wed Apr 29 17:00:01 2009 +0100 (2009-04-29)
changeset 361 2523d03a840e
parent 309 a69289c9080c
child 369 f8c27fe9fe63
permissions -rw-r--r--
Add support for preloading lua modules. This is useful both when
providing lua bindings to applications based on librazor and when
producing static binaries using librazor (where otherwise the lua
POSIX library would need to be included as an additional dynamic
object).
     1 /*
     2  * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
     3  * Copyright (C) 2008  Red Hat, Inc
     4  *
     5  * This program is free software; you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License as published by
     7  * the Free Software Foundation; either version 2 of the License, or
     8  * (at your option) any later version.
     9  *
    10  * This program is distributed in the hope that it will be useful,
    11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  * GNU General Public License for more details.
    14  *
    15  * You should have received a copy of the GNU General Public License along
    16  * with this program; if not, write to the Free Software Foundation, Inc.,
    17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    18  */
    19 
    20 #define _GNU_SOURCE
    21 
    22 #include <string.h>
    23 #include "razor-internal.h"
    24 #include "razor.h"
    25 
    26 /**
    27  * razor_importer_create:
    28  *
    29  * Create a new %razor_importer.
    30  *
    31  * Returns: the new %razor_importer.
    32  **/
    33 RAZOR_EXPORT struct razor_importer *
    34 razor_importer_create(void)
    35 {
    36 	struct razor_importer *importer;
    37 
    38 	importer = zalloc(sizeof *importer);
    39 	importer->set = razor_set_create();
    40 	hashtable_init(&importer->table, &importer->set->string_pool);
    41 	hashtable_init(&importer->details_table,
    42 		       &importer->set->details_string_pool);
    43 	hashtable_init(&importer->file_table,
    44 		       &importer->set->file_string_pool);
    45 
    46 	return importer;
    47 }
    48 
    49 /**
    50  * razor_importer_destroy:
    51  * @importer: the %razor_importer
    52  *
    53  * Destroy an importer without creating a %razor_set.  Normally,
    54  * %razor_importer_finish will create a new %razor_set and destroy the
    55  * importer.  If the import must be aborted without creating the set,
    56  * just destroy the import using this function.
    57  **/
    58 RAZOR_EXPORT void
    59 razor_importer_destroy(struct razor_importer *importer)
    60 {
    61 	/* FIXME: write this */
    62 }
    63 
    64 
    65 /**
    66  * razor_importer_begin_package:
    67  * @importer: the %razor_importer
    68  * @name: the name of the new package
    69  * @version: the version of the new package
    70  * @arch: the architechture of the new package.
    71  *
    72  * Begin describing a new package to the importer.  This creates a new
    73  * package and sets the %name, %version and %arch.  Subsequent calls
    74  * to %razor_importer_add_details, %razor_importer_add_property and
    75  * %razor_importer_add_file further describe this package and
    76  * %razor_importer_finish_package marks the end of meta data for this
    77  * package.
    78  **/
    79 RAZOR_EXPORT void
    80 razor_importer_begin_package(struct razor_importer *importer,
    81 			     const char *name,
    82 			     const char *version,
    83 			     const char *arch)
    84 {
    85 	struct razor_package *p;
    86 
    87 	p = array_add(&importer->set->packages, sizeof *p);
    88 	p->name = hashtable_tokenize(&importer->table, name);
    89 	p->flags = 0;
    90 	p->version = hashtable_tokenize(&importer->table, version);
    91 	p->arch = hashtable_tokenize(&importer->table, arch);
    92 
    93 	importer->package = p;
    94 	array_init(&importer->properties);
    95 }
    96 
    97 /**
    98  * razor_importer_finish_package:
    99  * @importer: the %razor_importer
   100  *
   101  * Tells the importer that the current package is complete.
   102  **/
   103 RAZOR_EXPORT void
   104 razor_importer_finish_package(struct razor_importer *importer)
   105 {
   106 	list_set_array(&importer->package->properties,
   107 		       &importer->set->property_pool,
   108 		       &importer->properties,
   109 		       1);
   110 
   111 	array_release(&importer->properties);
   112 }
   113 
   114 /**
   115  * razor_importer_add_details:
   116  * @importer: the %razor_importer
   117  * @summary: the package summary
   118  * @description: the package description
   119  * @url: the package url
   120  * @license: the package license
   121  *
   122  * Provide additional information for the current package.
   123  **/
   124 RAZOR_EXPORT void
   125 razor_importer_add_details(struct razor_importer *importer,
   126 			   const char *summary,
   127 			   const char *description,
   128 			   const char *url,
   129 			   const char *license)
   130 {
   131 	importer->package->summary = hashtable_tokenize(&importer->details_table, summary);
   132 	importer->package->description = hashtable_tokenize(&importer->details_table, description);
   133 	importer->package->url = hashtable_tokenize(&importer->details_table, url);
   134 	importer->package->license = hashtable_tokenize(&importer->details_table, license);
   135 }
   136 
   137 /**
   138  * razor_importer_add_property:
   139  * @importer: the %razor_importer
   140  * @name: name of the property
   141  * @flags: property flags
   142  * @version: version of the property or %NULL
   143  *
   144  * Add a property for the current package.  The %flags parameter
   145  * determines the type of the property and optionally the relation to
   146  * the specified version and the availability constraint.  See
   147  * %razor_property_flags for further information about the flag
   148  * parameter.
   149  **/
   150 RAZOR_EXPORT void
   151 razor_importer_add_property(struct razor_importer *importer,
   152 			    const char *name,
   153 			    uint32_t flags,
   154 			    const char *version)
   155 {
   156 	struct razor_property *p;
   157 	uint32_t *r;
   158 
   159 	p = array_add(&importer->set->properties, sizeof *p);
   160 	p->name = hashtable_tokenize(&importer->table, name);
   161 	p->flags = flags;
   162 	p->version = hashtable_tokenize(&importer->table, version);
   163 	list_set_ptr(&p->packages, importer->package -
   164 		     (struct razor_package *) importer->set->packages.data);
   165 
   166 	r = array_add(&importer->properties, sizeof *r);
   167 	*r = p - (struct razor_property *) importer->set->properties.data;
   168 
   169 	if (((flags & RAZOR_PROPERTY_TYPE_MASK) == RAZOR_PROPERTY_REQUIRES) &&
   170 	    *name == '/') {
   171 		r = array_add(&importer->file_requires, sizeof *r);
   172 		*r = p->name;
   173 	}
   174 }
   175 
   176 /**
   177  * razor_importer_add_file:
   178  * @importer: the %razor_importer
   179  * @name: name of the file
   180  *
   181  * Add a file to the current package.
   182  **/
   183 RAZOR_EXPORT void
   184 razor_importer_add_file(struct razor_importer *importer, const char *name)
   185 {
   186 	struct import_entry *e;
   187 
   188 	e = array_add(&importer->files, sizeof *e);
   189 
   190 	e->package = importer->package -
   191 		(struct razor_package *) importer->set->packages.data;
   192 	e->name = strdup(name);
   193 }
   194 
   195 static int
   196 compare_packages(const void *p1, const void *p2, void *data)
   197 {
   198 	const struct razor_package *pkg1 = p1, *pkg2 = p2;
   199 	struct razor_set *set = data;
   200 	char *pool = set->string_pool.data;
   201 
   202 	/* FIXME: what if the flags are different? */
   203 	if (pkg1->name == pkg2->name)
   204 		return razor_versioncmp(&pool[pkg1->version], &pool[pkg2->version]);
   205 	else
   206 		return strcmp(&pool[pkg1->name], &pool[pkg2->name]);
   207 }
   208 
   209 static int
   210 compare_properties(const void *p1, const void *p2, void *data)
   211 {
   212 	const struct razor_property *prop1 = p1, *prop2 = p2;
   213 	struct razor_set *set = data;
   214 	char *pool = set->string_pool.data;
   215 
   216 	if (prop1->name != prop2->name)
   217 		return strcmp(&pool[prop1->name], &pool[prop2->name]);
   218 	else if (prop1->flags != prop2->flags)
   219 		return prop1->flags - prop2->flags;
   220 	else if (prop1->version != prop2->version)
   221 		return razor_versioncmp(&pool[prop1->version], &pool[prop2->version]);
   222 	else
   223 		return prop1->packages.list_ptr - prop2->packages.list_ptr;
   224 }
   225 
   226 static uint32_t *
   227 uniqueify_properties(struct razor_set *set)
   228 {
   229 	struct razor_property *rp, *up, *rp_end;
   230 	struct array *pkgs, *p;
   231 	struct list_head *r;
   232 	uint32_t *map, *rmap;
   233 	int i, count, unique;
   234 
   235 	count = set->properties.size / sizeof(struct razor_property);
   236 	map = razor_qsort_with_data(set->properties.data,
   237 				    count,
   238 				    sizeof(struct razor_property),
   239 				    compare_properties,
   240 				    set);
   241 
   242 	rp_end = set->properties.data + set->properties.size;
   243 	rmap = malloc(count * sizeof *map);
   244 	pkgs = zalloc(count * sizeof *pkgs);
   245 	for (rp = set->properties.data, up = rp, i = 0; rp < rp_end; rp++, i++) {
   246 		if (rp->name != up->name ||
   247 		    rp->flags != up->flags ||
   248 		    rp->version != up->version) {
   249 			up++;
   250 			up->name = rp->name;
   251 			up->flags = rp->flags;
   252 			up->version = rp->version;
   253 		}
   254 
   255 		unique = up - (struct razor_property *) set->properties.data;
   256 		rmap[map[i]] = unique;
   257 		r = array_add(&pkgs[unique], sizeof *r);
   258 		*r = rp->packages;
   259 	}
   260 	free(map);
   261 
   262 	if (up != rp)
   263 		up++;
   264 	set->properties.size = (void *) up - set->properties.data;
   265 	rp_end = up;
   266 	for (rp = set->properties.data, p = pkgs; rp < rp_end; rp++, p++) {
   267 		list_set_array(&rp->packages, &set->package_pool, p, 0);
   268 		array_release(p);
   269 	}
   270 
   271 	free(pkgs);
   272 
   273 	return rmap;
   274 }
   275 
   276 static int
   277 compare_filenames(const void *p1, const void *p2, void *data)
   278 {
   279 	const struct import_entry *e1 = p1;
   280 	const struct import_entry *e2 = p2;
   281 	const char *n1 = e1->name;
   282 	const char *n2 = e2->name;
   283 
   284 	/* Need to make sure that the contents of a directory
   285 	 * are sorted immediately after it. So "foo/bar" has to
   286 	 * sort before "foo.conf"
   287 	 *
   288 	 * FIXME: this is about 60% slower than strcmp
   289 	 */
   290 	while (*n1 && *n2) {
   291 		if (*n1 < *n2)
   292 			return *n2 == '/' ? 1 : -1;
   293 		else if (*n1 > *n2)
   294 			return *n1 == '/' ? -1 : 1;
   295 		n1++;
   296 		n2++;
   297 	}
   298 	if (*n1)
   299 		return 1;
   300 	else if (*n2)
   301 		return -1;
   302 	else
   303 		return 0;
   304 }
   305 
   306 static void
   307 count_entries(struct import_directory *d)
   308 {
   309 	struct import_directory *p, *end;
   310 
   311 	p = d->files.data;
   312 	end = d->files.data + d->files.size;
   313 	d->count = 0;
   314 	while (p < end) {
   315 		count_entries(p);
   316 		d->count += p->count + 1;
   317 		p++;
   318 	}
   319 }
   320 
   321 static void
   322 serialize_files(struct razor_set *set,
   323 		struct import_directory *d, struct array *array)
   324 {
   325 	struct import_directory *p, *end;
   326 	struct razor_entry *e = NULL;
   327 	uint32_t s;
   328 
   329 	p = d->files.data;
   330 	end = d->files.data + d->files.size;
   331 	s = array->size / sizeof *e + d->files.size / sizeof *p;
   332 	while (p < end) {
   333 		e = array_add(array, sizeof *e);
   334 		e->name = p->name;
   335 		e->flags = 0;
   336 		e->start = p->count > 0 ? s : 0;
   337 		s += p->count;
   338 
   339 		list_set_array(&e->packages, &set->package_pool, &p->packages, 0);
   340 		array_release(&p->packages);
   341 		p++;
   342 	}
   343 	if (e != NULL)
   344 		e->flags |= RAZOR_ENTRY_LAST;
   345 
   346 	p = d->files.data;
   347 	end = d->files.data + d->files.size;
   348 	while (p < end) {
   349 		serialize_files(set, p, array);
   350 		p++;
   351 	}
   352 }
   353 
   354 static void
   355 remap_property_package_links(struct array *properties, uint32_t *rmap)
   356 {
   357 	struct razor_property *p, *end;
   358 
   359 	end = properties->data + properties->size;
   360 	for (p = properties->data; p < end; p++)
   361 		list_remap_head(&p->packages, rmap);
   362 }
   363 
   364 static void
   365 build_file_tree(struct razor_importer *importer)
   366 {
   367 	int count, i, length;
   368 	struct import_entry *filenames;
   369 	char *f, *end;
   370 	uint32_t name, *r, s;
   371 	char rootname[256], dirname[256];
   372 	struct import_directory *d, *last_root;
   373 	struct array roots;
   374 	struct razor_entry *e;
   375 
   376 	count = importer->files.size / sizeof (struct import_entry);
   377 	filenames = importer->files.data;
   378 	razor_qsort_with_data(filenames,
   379 			      count,
   380 			      sizeof (struct import_entry),
   381 			      compare_filenames,
   382 			      NULL);
   383 
   384 	array_init(&roots);
   385 	last_root = NULL;
   386 
   387 	for (i = 0; i < count; i++) {
   388 		f = filenames[i].name;
   389 		d = NULL;
   390 		while (*f) {
   391 			end = strchr(f, '/');
   392 			if (end == NULL)
   393 				end = f + strlen(f);
   394 			length = end - f;
   395 			memcpy(dirname, f, length);
   396 			dirname[length] = '\0';
   397 			name = hashtable_tokenize(&importer->file_table,
   398 						  dirname);
   399 			if (!d) {
   400 				if (!last_root || last_root->name != name) {
   401 					d = array_add(&roots, sizeof *d);
   402 					d->name = name;
   403 					d->last = NULL;
   404 					array_init(&d->files);
   405 					array_init(&d->packages);
   406 					last_root = d;
   407 				}
   408 				d = last_root;
   409 			} else {
   410 				if (!d->last || d->last->name != name) {
   411 					d->last = array_add(&d->files,
   412 							    sizeof *d);
   413 					d->last->name = name;
   414 					d->last->last = NULL;
   415 					array_init(&d->last->files);
   416 					array_init(&d->last->packages);
   417 				}
   418 				d = d->last;
   419 			}
   420 			f = end + 1;
   421 			if (*end == '\0')
   422 				break;
   423 		}
   424 
   425 		r = array_add(&d->packages, sizeof *r);
   426 		*r = filenames[i].package;
   427 		free(filenames[i].name);
   428 	}
   429 
   430 	count = roots.size / sizeof (struct import_directory);
   431 	d = roots.data;
   432 	s = count;
   433 	for (i = 0; i < count; i++) {
   434 		count_entries(d);
   435 		if (i)
   436 			e = array_add(&importer->set->files, sizeof *e);
   437 		else
   438 			e = importer->set->files.data;
   439 		e->name = d->name;
   440 		e->flags = 0;
   441 		e->start = d->count > 0 ? s : 0;
   442 		s += d->count;
   443 		list_set_empty(&e->packages);
   444 		d++;
   445 	}
   446 	if (count)
   447 		e->flags |= RAZOR_ENTRY_LAST;
   448 
   449 	d = roots.data;
   450 	for (i = 0; i < count; i++) {
   451 		serialize_files(importer->set, d, &importer->set->files);
   452 		d++;
   453 	}
   454 
   455 	array_release(&importer->files);
   456 	array_release(&roots);
   457 }
   458 
   459 static void
   460 list_to_array(struct list *list, struct array *array)
   461 {
   462 	uint32_t *item;
   463 
   464 	while (list) {
   465 		 item = array_add(array, sizeof *item);
   466 		 *item = list->data;
   467 		 list = list_next(list);
   468 	}
   469 }
   470 
   471 static int
   472 compare_file_requires(const void *p1, const void *p2, void *data)
   473 {
   474 	uint32_t *f1 = (void *)p1, *f2 = (void *)p2;
   475 	const char *pool = data;
   476 
   477 	return strcmp(&pool[*f1], &pool[*f2]);
   478 }
   479 
   480 static void
   481 find_file_provides(struct razor_importer *importer)
   482 {
   483 	struct razor_property *prop;
   484 	struct razor_entry *top, *entry;
   485 	struct razor_package *packages;
   486 	struct array pkgprops;
   487 	struct list *pkg;
   488 	uint32_t *req, *req_start, *req_end;
   489 	uint32_t *map, *newprop;
   490 	char *pool;
   491 
   492 	pool = importer->set->string_pool.data;
   493 	packages = importer->set->packages.data;
   494 	top = importer->set->files.data;
   495 
   496 	req = req_start = importer->file_requires.data;
   497 	req_end = importer->file_requires.data + importer->file_requires.size;
   498 	map = razor_qsort_with_data(req, req_end - req, sizeof *req,
   499 				    compare_file_requires, pool);
   500 	free(map);
   501 
   502 	for (req = req_start; req < req_end; req++) {
   503 		if (req > req_start && req[0] == req[-1])
   504 			continue;
   505 		entry = razor_set_find_entry(importer->set, top, &pool[*req]);
   506 		if (!entry)
   507 			continue;
   508 
   509 		for (pkg = list_first(&entry->packages, &importer->set->package_pool); pkg; pkg = list_next(pkg)) {
   510 			prop = array_add(&importer->set->properties, sizeof *prop);
   511 			prop->name = *req;
   512 			prop->flags =
   513 				RAZOR_PROPERTY_PROVIDES | RAZOR_PROPERTY_EQUAL;
   514 			prop->version = hashtable_tokenize(&importer->table, "");
   515 			list_set_ptr(&prop->packages, pkg->data);
   516 
   517 			/* Update property list of pkg */
   518 			array_init(&pkgprops);
   519 			list_to_array(list_first(&packages[pkg->data].properties, &importer->set->property_pool), &pkgprops);
   520 			newprop = array_add(&pkgprops, sizeof *newprop);
   521 			*newprop = prop - (struct razor_property *)importer->set->properties.data;
   522 			list_set_array(&packages[pkg->data].properties, &importer->set->property_pool, &pkgprops, 1);
   523 			array_release(&pkgprops);
   524 		}
   525 	}
   526 
   527 	array_release(&importer->file_requires);
   528 }
   529 
   530 static void
   531 build_package_file_lists(struct razor_set *set, uint32_t *rmap)
   532 {
   533 	struct razor_package *p, *packages;
   534 	struct array *pkgs;
   535 	struct razor_entry *e, *end;
   536 	struct list *r;
   537 	uint32_t *q;
   538 	int i, count;
   539 
   540 	count = set->packages.size / sizeof *p;
   541 	pkgs = zalloc(count * sizeof *pkgs);
   542 
   543 	end = set->files.data + set->files.size;
   544 	for (e = set->files.data; e < end; e++) {
   545 		list_remap_head(&e->packages, rmap);
   546 		r = list_first(&e->packages, &set->package_pool);
   547 		while (r) {
   548 			q = array_add(&pkgs[r->data], sizeof *q);
   549 			*q = e - (struct razor_entry *) set->files.data;
   550 			r = list_next(r);
   551 		}
   552 	}
   553 
   554 	packages = set->packages.data;
   555 	for (i = 0; i < count; i++) {
   556 		list_set_array(&packages[i].files, &set->file_pool, &pkgs[i], 0);
   557 		array_release(&pkgs[i]);
   558 	}
   559 	free(pkgs);
   560 }
   561 
   562 /**
   563  * razor_importer_finish:
   564  * @importer: the %razor_importer
   565  *
   566  * Finish importing packages and create the package set.  This sorts
   567  * and indexes all the packages, properties and files in the importer
   568  * and creates a new %razor_set.  After creating the new package set,
   569  * the importer is destroyed.
   570  *
   571  * Returns: the new %razor_set
   572  **/
   573 RAZOR_EXPORT struct razor_set *
   574 razor_importer_finish(struct razor_importer *importer)
   575 {
   576 	struct razor_set *set;
   577 	uint32_t *map, *rmap;
   578 	int i, count;
   579 
   580 	build_file_tree(importer);
   581 	find_file_provides(importer);
   582 
   583 	map = uniqueify_properties(importer->set);
   584 	list_remap_pool(&importer->set->property_pool, map);
   585 	free(map);
   586 
   587 	count = importer->set->packages.size / sizeof(struct razor_package);
   588 	map = razor_qsort_with_data(importer->set->packages.data,
   589 				    count,
   590 				    sizeof(struct razor_package),
   591 				    compare_packages,
   592 				    importer->set);
   593 
   594 	rmap = malloc(count * sizeof *rmap);
   595 	for (i = 0; i < count; i++)
   596 		rmap[map[i]] = i;
   597 	free(map);
   598 
   599 	list_remap_pool(&importer->set->package_pool, rmap);
   600 	build_package_file_lists(importer->set, rmap);
   601 	remap_property_package_links(&importer->set->properties, rmap);
   602 	free(rmap);
   603 
   604 	set = importer->set;
   605 	hashtable_release(&importer->table);
   606 	hashtable_release(&importer->details_table);
   607 	hashtable_release(&importer->file_table);
   608 	free(importer);
   609 
   610 	return set;
   611 }