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