librazor/importer.c
author J. Ali Harlow <ali@juiblex.co.uk>
Wed Apr 22 15:09:17 2009 +0100 (2009-04-22)
changeset 359 c9c90315ea24
parent 309 a69289c9080c
child 369 f8c27fe9fe63
permissions -rw-r--r--
Add support for named roots so that we can understand MS-Windows paths
such as c:/windows. Without this, the user always has to ensure they
are on the correct drive before running razor.
     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 }