librazor/importer.c
author J. Ali Harlow <ali@juiblex.co.uk>
Thu Mar 12 20:52:12 2009 +0000 (2009-03-12)
changeset 356 d842feb64ecb
parent 269 03fc85294bc9
child 359 c9c90315ea24
permissions -rw-r--r--
Avoid use of legacy rpm library types
     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;
   371 	char dirname[256];
   372 	struct import_directory *d, root;
   373 	struct razor_entry *e;
   374 
   375 	count = importer->files.size / sizeof (struct import_entry);
   376 	razor_qsort_with_data(importer->files.data,
   377 			      count,
   378 			      sizeof (struct import_entry),
   379 			      compare_filenames,
   380 			      NULL);
   381 
   382 	root.name = hashtable_tokenize(&importer->file_table, "");
   383 	array_init(&root.files);
   384 	array_init(&root.packages);
   385 	root.last = NULL;
   386 
   387 	filenames = importer->files.data;
   388 	for (i = 0; i < count; i++) {
   389 		f = filenames[i].name;
   390 		if (*f != '/')
   391 			continue;
   392 		f++;
   393 
   394 		d = &root;
   395 		while (*f) {
   396 			end = strchr(f, '/');
   397 			if (end == NULL)
   398 				end = f + strlen(f);
   399 			length = end - f;
   400 			memcpy(dirname, f, length);
   401 			dirname[length] ='\0';
   402 			name = hashtable_tokenize(&importer->file_table,
   403 						  dirname);
   404 			if (d->last == NULL || d->last->name != name) {
   405 				d->last = array_add(&d->files, sizeof *d);
   406 				d->last->name = name;
   407 				d->last->last = NULL;
   408 				array_init(&d->last->files);
   409 				array_init(&d->last->packages);
   410 			}
   411 			d = d->last;
   412 			f = end + 1;
   413 			if (*end == '\0')
   414 				break;
   415 		}
   416 
   417 		r = array_add(&d->packages, sizeof *r);
   418 		*r = filenames[i].package;
   419 		free(filenames[i].name);
   420 	}
   421 
   422 	count_entries(&root);
   423 	e = importer->set->files.data;
   424 	e->name = root.name;
   425 	e->flags = RAZOR_ENTRY_LAST;
   426 	e->start = importer->files.size ? 1 : 0;
   427 	list_set_empty(&e->packages);
   428 
   429 	serialize_files(importer->set, &root, &importer->set->files);
   430 
   431 	array_release(&importer->files);
   432 }
   433 
   434 static void
   435 list_to_array(struct list *list, struct array *array)
   436 {
   437 	uint32_t *item;
   438 
   439 	while (list) {
   440 		 item = array_add(array, sizeof *item);
   441 		 *item = list->data;
   442 		 list = list_next(list);
   443 	}
   444 }
   445 
   446 static int
   447 compare_file_requires(const void *p1, const void *p2, void *data)
   448 {
   449 	uint32_t *f1 = (void *)p1, *f2 = (void *)p2;
   450 	const char *pool = data;
   451 
   452 	return strcmp(&pool[*f1], &pool[*f2]);
   453 }
   454 
   455 static void
   456 find_file_provides(struct razor_importer *importer)
   457 {
   458 	struct razor_property *prop;
   459 	struct razor_entry *top, *entry;
   460 	struct razor_package *packages;
   461 	struct array pkgprops;
   462 	struct list *pkg;
   463 	uint32_t *req, *req_start, *req_end;
   464 	uint32_t *map, *newprop;
   465 	char *pool;
   466 
   467 	pool = importer->set->string_pool.data;
   468 	packages = importer->set->packages.data;
   469 	top = importer->set->files.data;
   470 
   471 	req = req_start = importer->file_requires.data;
   472 	req_end = importer->file_requires.data + importer->file_requires.size;
   473 	map = razor_qsort_with_data(req, req_end - req, sizeof *req,
   474 				    compare_file_requires, pool);
   475 	free(map);
   476 
   477 	for (req = req_start; req < req_end; req++) {
   478 		if (req > req_start && req[0] == req[-1])
   479 			continue;
   480 		entry = razor_set_find_entry(importer->set, top, &pool[*req]);
   481 		if (!entry)
   482 			continue;
   483 
   484 		for (pkg = list_first(&entry->packages, &importer->set->package_pool); pkg; pkg = list_next(pkg)) {
   485 			prop = array_add(&importer->set->properties, sizeof *prop);
   486 			prop->name = *req;
   487 			prop->flags =
   488 				RAZOR_PROPERTY_PROVIDES | RAZOR_PROPERTY_EQUAL;
   489 			prop->version = hashtable_tokenize(&importer->table, "");
   490 			list_set_ptr(&prop->packages, pkg->data);
   491 
   492 			/* Update property list of pkg */
   493 			array_init(&pkgprops);
   494 			list_to_array(list_first(&packages[pkg->data].properties, &importer->set->property_pool), &pkgprops);
   495 			newprop = array_add(&pkgprops, sizeof *newprop);
   496 			*newprop = prop - (struct razor_property *)importer->set->properties.data;
   497 			list_set_array(&packages[pkg->data].properties, &importer->set->property_pool, &pkgprops, 1);
   498 			array_release(&pkgprops);
   499 		}
   500 	}
   501 
   502 	array_release(&importer->file_requires);
   503 }
   504 
   505 static void
   506 build_package_file_lists(struct razor_set *set, uint32_t *rmap)
   507 {
   508 	struct razor_package *p, *packages;
   509 	struct array *pkgs;
   510 	struct razor_entry *e, *end;
   511 	struct list *r;
   512 	uint32_t *q;
   513 	int i, count;
   514 
   515 	count = set->packages.size / sizeof *p;
   516 	pkgs = zalloc(count * sizeof *pkgs);
   517 
   518 	end = set->files.data + set->files.size;
   519 	for (e = set->files.data; e < end; e++) {
   520 		list_remap_head(&e->packages, rmap);
   521 		r = list_first(&e->packages, &set->package_pool);
   522 		while (r) {
   523 			q = array_add(&pkgs[r->data], sizeof *q);
   524 			*q = e - (struct razor_entry *) set->files.data;
   525 			r = list_next(r);
   526 		}
   527 	}
   528 
   529 	packages = set->packages.data;
   530 	for (i = 0; i < count; i++) {
   531 		list_set_array(&packages[i].files, &set->file_pool, &pkgs[i], 0);
   532 		array_release(&pkgs[i]);
   533 	}
   534 	free(pkgs);
   535 }
   536 
   537 /**
   538  * razor_importer_finish:
   539  * @importer: the %razor_importer
   540  *
   541  * Finish importing packages and create the package set.  This sorts
   542  * and indexes all the packages, properties and files in the importer
   543  * and creates a new %razor_set.  After creating the new package set,
   544  * the importer is destroyed.
   545  *
   546  * Returns: the new %razor_set
   547  **/
   548 RAZOR_EXPORT struct razor_set *
   549 razor_importer_finish(struct razor_importer *importer)
   550 {
   551 	struct razor_set *set;
   552 	uint32_t *map, *rmap;
   553 	int i, count;
   554 
   555 	build_file_tree(importer);
   556 	find_file_provides(importer);
   557 
   558 	map = uniqueify_properties(importer->set);
   559 	list_remap_pool(&importer->set->property_pool, map);
   560 	free(map);
   561 
   562 	count = importer->set->packages.size / sizeof(struct razor_package);
   563 	map = razor_qsort_with_data(importer->set->packages.data,
   564 				    count,
   565 				    sizeof(struct razor_package),
   566 				    compare_packages,
   567 				    importer->set);
   568 
   569 	rmap = malloc(count * sizeof *rmap);
   570 	for (i = 0; i < count; i++)
   571 		rmap[map[i]] = i;
   572 	free(map);
   573 
   574 	list_remap_pool(&importer->set->package_pool, rmap);
   575 	build_package_file_lists(importer->set, rmap);
   576 	remap_property_package_links(&importer->set->properties, rmap);
   577 	free(rmap);
   578 
   579 	set = importer->set;
   580 	hashtable_release(&importer->table);
   581 	hashtable_release(&importer->details_table);
   582 	hashtable_release(&importer->file_table);
   583 	free(importer);
   584 
   585 	return set;
   586 }