librazor/razor.c
author J. Ali Harlow <ali@juiblex.co.uk>
Thu Jul 02 11:31:45 2009 +0100 (2009-07-02)
changeset 371 d7eea3164151
parent 367 e45f50e940b6
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 "config.h"
    24 
    25 #include <stdlib.h>
    26 #include <stddef.h>
    27 #include <stdint.h>
    28 #include <stdio.h>
    29 #include <stdarg.h>
    30 #include <string.h>
    31 #include <sys/types.h>
    32 #include <sys/stat.h>
    33 #include <unistd.h>
    34 #include <fcntl.h>
    35 #include <errno.h>
    36 #include <ctype.h>
    37 #include <fnmatch.h>
    38 #include <limits.h>
    39 #include <assert.h>
    40 
    41 #include "razor-internal.h"
    42 #include "razor.h"
    43 
    44 #ifndef O_BINARY
    45 #define O_BINARY	0
    46 #endif
    47 
    48 void *
    49 zalloc(size_t size)
    50 {
    51 	void *p;
    52 
    53 	p = malloc(size);
    54 	memset(p, 0, size);
    55 
    56 	return p;
    57 }
    58 
    59 struct razor_set_section_index {
    60 	const char *name;
    61 	uint32_t offset;
    62 };
    63 
    64 struct razor_set_section_index razor_sections[] = {
    65 	{ RAZOR_STRING_POOL,	offsetof(struct razor_set, string_pool) },
    66 	{ RAZOR_PACKAGES,	offsetof(struct razor_set, packages) },
    67 	{ RAZOR_PROPERTIES,	offsetof(struct razor_set, properties) },
    68 	{ RAZOR_PACKAGE_POOL,	offsetof(struct razor_set, package_pool) },
    69 	{ RAZOR_PROPERTY_POOL,	offsetof(struct razor_set, property_pool) },
    70 };
    71 
    72 struct razor_set_section_index razor_files_sections[] = {
    73 	{ RAZOR_FILES,			offsetof(struct razor_set, files) },
    74 	{ RAZOR_FILE_POOL,		offsetof(struct razor_set, file_pool) },
    75 	{ RAZOR_FILE_STRING_POOL,	offsetof(struct razor_set, file_string_pool) },
    76 };
    77 
    78 struct razor_set_section_index razor_details_sections[] = {
    79 	{ RAZOR_DETAILS_STRING_POOL,	offsetof(struct razor_set, details_string_pool) },
    80 };
    81 
    82 RAZOR_EXPORT struct razor_set *
    83 razor_set_create_without_root(void)
    84 {
    85 	struct razor_set *set;
    86 	char *empty;
    87 
    88 	set = zalloc(sizeof *set);
    89 
    90 	empty = array_add(&set->string_pool, 1);
    91 	*empty = '\0';
    92 
    93 	return set;
    94 }
    95 
    96 RAZOR_EXPORT struct razor_set *
    97 razor_set_create(void)
    98 {
    99 	struct razor_set *set;
   100 	struct razor_entry *e;
   101 
   102 	set = razor_set_create_without_root();
   103 
   104 	e = array_add(&set->files, sizeof *e);
   105 	e->name = 0;
   106 	e->flags = RAZOR_ENTRY_LAST;
   107 	e->start = 0;
   108 	list_set_empty(&e->packages);
   109 
   110 	return set;
   111 }
   112 
   113 static int
   114 razor_set_bind_sections(struct razor_set *set,
   115 			struct razor_set_header **header,
   116 			size_t *header_size,
   117 			struct razor_set_section_index section_index[],
   118 			int section_index_size,
   119 			const char *filename)
   120 {
   121 	struct razor_set_section *s, *sections;
   122 	struct array *array;
   123 	const char *pool;
   124 	int i;
   125 
   126 	*header = razor_file_get_contents(filename, header_size);
   127 	if (!*header)
   128 		return -1;
   129 
   130 	sections = (void *) *header + sizeof **header;
   131 	pool = (void *) sections + (*header)->num_sections * sizeof *sections;
   132 
   133 	for (i = 0; i < (*header)->num_sections; i++) {
   134 		int j;
   135 		s = sections + i;
   136 		for (j = 0; j < section_index_size; j++)
   137 			if (!strcmp(section_index[j].name,
   138 				    &pool[s->name]))
   139 				break;
   140 		if (j == section_index_size)
   141 			continue;
   142 		array = (void *) set + section_index[j].offset;
   143 		array->data = (void *) *header + s->offset;
   144 		array->size = s->size;
   145 		array->alloc = s->size;
   146 	}
   147 
   148 	return 0;
   149 }
   150 
   151 RAZOR_EXPORT struct razor_set *
   152 razor_set_open(const char *filename)
   153 {
   154 	struct razor_set *set;
   155 
   156 	set = zalloc(sizeof *set);
   157 	if (razor_set_bind_sections(set, &set->header, &set->header_size,
   158 				    razor_sections, ARRAY_SIZE(razor_sections),
   159 				    filename)){
   160 		free(set);
   161 		return NULL;
   162 	}
   163 	return set;
   164 }
   165 
   166 RAZOR_EXPORT int
   167 razor_set_open_details(struct razor_set *set, const char *filename)
   168 {
   169 	return razor_set_bind_sections(set, &set->details_header,
   170 				       &set->details_header_size,
   171 				       razor_details_sections,
   172 				       ARRAY_SIZE(razor_details_sections),
   173 				       filename);
   174 }
   175 
   176 RAZOR_EXPORT int
   177 razor_set_open_files(struct razor_set *set, const char *filename)
   178 {
   179 	return razor_set_bind_sections(set, &set->files_header,
   180 				       &set->files_header_size,
   181 				       razor_files_sections,
   182 				       ARRAY_SIZE(razor_files_sections),
   183 				       filename);
   184 }
   185 
   186 RAZOR_EXPORT void
   187 razor_set_destroy(struct razor_set *set)
   188 {
   189 	struct array *a;
   190 	int i;
   191 
   192 	assert (set != NULL);
   193 
   194 	if (set->header) {
   195 		razor_file_free_contents(set->header, set->header_size);
   196 	} else {
   197 		for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
   198 			a = (void *) set + razor_sections[i].offset;
   199 			free(a->data);
   200 		}
   201 	}
   202 
   203 	if (set->details_header) {
   204 		razor_file_free_contents(set->details_header,
   205 			set->details_header_size);
   206 	} else {
   207 		for (i = 0; i < ARRAY_SIZE(razor_details_sections); i++) {
   208 			a = (void *) set + razor_details_sections[i].offset;
   209 			free(a->data);
   210 		}
   211 	}
   212 
   213 	if (set->files_header) {
   214 		razor_file_free_contents(set->files_header,
   215 			set->files_header_size);
   216 	} else {
   217 		for (i = 0; i < ARRAY_SIZE(razor_files_sections); i++) {
   218 			a = (void *) set + razor_files_sections[i].offset;
   219 			free(a->data);
   220 		}
   221 	}
   222 
   223 	free(set);
   224 }
   225 
   226 static int
   227 razor_set_write_sections_to_fd(struct razor_set *set, int fd,
   228 			       struct razor_set_section_index *sections,
   229 			       size_t array_size)
   230 {
   231 	struct razor_set_header header;
   232 	struct razor_set_section *out_sections =
   233 		malloc(array_size * sizeof *out_sections);
   234 	struct hashtable table;
   235 	struct array *a, pool;
   236 	uint32_t offset;
   237 	int i;
   238 
   239 	header.magic = RAZOR_MAGIC;
   240 	header.version = RAZOR_VERSION;
   241 	header.num_sections = array_size;
   242 	offset = sizeof header + array_size * sizeof *out_sections;
   243 
   244 	array_init(&pool);
   245 	hashtable_init(&table, &pool);
   246 
   247 	for (i = 0; i < array_size; i++)
   248 		out_sections[i].name =
   249 			hashtable_tokenize(&table, sections[i].name);
   250 
   251 	offset += pool.size;
   252 
   253 	for (i = 0; i < array_size; i++) {
   254 		a = (void *) set + sections[i].offset;
   255 		out_sections[i].offset = offset;
   256 		out_sections[i].size = a->size;
   257 		offset += a->size;
   258 	}
   259 
   260 	razor_write(fd, &header, sizeof header);
   261 	razor_write(fd, out_sections, array_size * sizeof *out_sections);
   262 	razor_write(fd, pool.data, pool.size);
   263 
   264 	for (i = 0; i < array_size; i++) {
   265 		a = (void *) set + sections[i].offset;
   266 		razor_write(fd, a->data, a->size);
   267 	}
   268 
   269 	free(out_sections);
   270 
   271 	return 0;
   272 }
   273 
   274 RAZOR_EXPORT int
   275 razor_set_write_to_fd(struct razor_set *set, int fd,
   276 		      enum razor_repo_file_type type)
   277 {
   278 	switch (type) {
   279 	case RAZOR_REPO_FILE_MAIN:
   280 		return razor_set_write_sections_to_fd(set, fd,
   281 						      razor_sections,
   282 						      ARRAY_SIZE(razor_sections));
   283 
   284 	case RAZOR_REPO_FILE_DETAILS:
   285 		return razor_set_write_sections_to_fd(set, fd,
   286 						      razor_details_sections,
   287 						      ARRAY_SIZE(razor_details_sections));
   288 	case RAZOR_REPO_FILE_FILES:
   289 		return razor_set_write_sections_to_fd(set, fd,
   290 						      razor_files_sections,
   291 						      ARRAY_SIZE(razor_files_sections));
   292 	default:
   293 		return -1;
   294 	}
   295 }
   296 
   297 RAZOR_EXPORT int
   298 razor_set_write(struct razor_set *set, const char *filename,
   299 		enum razor_repo_file_type type)
   300 {
   301 	int fd, status;
   302 
   303 	fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY, 0666);
   304 	if (fd < 0)
   305 		return -1;
   306 
   307 	status = razor_set_write_to_fd(set, fd, type);
   308 	if (status) {
   309 	    close(fd);
   310 	    return status;
   311 	}
   312 
   313 	return close(fd);
   314 }
   315 
   316 RAZOR_EXPORT void
   317 razor_build_evr(char *evr_buf, int size, const char *epoch,
   318 		const char *version, const char *release)
   319 {
   320 	int len;
   321 
   322 	if (!version || !*version) {
   323 		*evr_buf = '\0';
   324 		return;
   325 	}
   326 
   327 	if (epoch && *epoch && strcmp(epoch, "0") != 0) {
   328 		len = snprintf(evr_buf, size, "%s:", epoch);
   329 		evr_buf += len;
   330 		size -= len;
   331 	}
   332 	len = snprintf(evr_buf, size, "%s", version);
   333 	evr_buf += len;
   334 	size -= len;
   335 	if (release && *release)
   336 		snprintf(evr_buf, size, "-%s", release);
   337 }
   338 
   339 RAZOR_EXPORT int
   340 razor_versioncmp(const char *s1, const char *s2)
   341 {
   342 	const char *p1, *p2;
   343 	long n1, n2;
   344 	int res;
   345 
   346 	assert (s1 != NULL);
   347 	assert (s2 != NULL);
   348 
   349 	n1 = strtol(s1, (char **) &p1, 10);
   350 	n2 = strtol(s2, (char **) &p2, 10);
   351 
   352 	/* Epoch; if one but not the other has an epoch set, default
   353 	 * the epoch-less version to 0. */
   354 	res = (*p1 == ':') - (*p2 == ':');
   355 	if (res < 0) {
   356 		n1 = 0;
   357 		p1 = s1;
   358 		p2++;
   359 	} else if (res > 0) {
   360 		p1++;
   361 		n2 = 0;
   362 		p2 = s2;
   363 	}
   364 
   365 	if (n1 != n2)
   366 		return n1 - n2;
   367 	while (*p1 && *p2) {
   368 		if (*p1 != *p2)
   369 			return *p1 - *p2;
   370 		p1++;
   371 		p2++;
   372 		if (isdigit(*p1) && isdigit(*p2))
   373 			return razor_versioncmp(p1, p2);
   374 	}
   375 
   376 	return *p1 - *p2;
   377 }
   378 
   379 static const char *
   380 razor_package_get_details_type(struct razor_set *set,
   381 			       struct razor_package *package,
   382 			       enum razor_detail_type type)
   383 {
   384 	const char *pool;
   385 
   386 	switch (type) {
   387 	case RAZOR_DETAIL_NAME:
   388 		pool = set->string_pool.data;
   389 		return &pool[package->name];
   390 
   391 	case RAZOR_DETAIL_VERSION:
   392 		pool = set->string_pool.data;
   393 		return &pool[package->version];
   394 
   395 	case RAZOR_DETAIL_ARCH:
   396 		pool = set->string_pool.data;
   397 		return &pool[package->arch];
   398 
   399 	case RAZOR_DETAIL_SUMMARY:
   400 		pool = set->details_string_pool.data;
   401 		return &pool[package->summary];
   402 
   403 	case RAZOR_DETAIL_DESCRIPTION:
   404 		pool = set->details_string_pool.data;
   405 		return &pool[package->description];
   406 
   407 	case RAZOR_DETAIL_URL:
   408 		pool = set->details_string_pool.data;
   409 		return &pool[package->url];
   410 
   411 	case RAZOR_DETAIL_LICENSE:
   412 		pool = set->details_string_pool.data;
   413 		return &pool[package->license];
   414 
   415 	case RAZOR_DETAIL_PREUNPROG:
   416 		pool = set->string_pool.data;
   417 		return &pool[package->preun.program];
   418 
   419 	case RAZOR_DETAIL_PREUN:
   420 		pool = set->string_pool.data;
   421 		return &pool[package->preun.body];
   422 
   423 	case RAZOR_DETAIL_POSTUNPROG:
   424 		pool = set->string_pool.data;
   425 		return &pool[package->postun.program];
   426 
   427 	case RAZOR_DETAIL_POSTUN:
   428 		pool = set->string_pool.data;
   429 		return &pool[package->postun.body];
   430 
   431 	default:
   432 		fprintf(stderr, "type %u not found\n", type);
   433 		return NULL;
   434 	}
   435 }
   436 
   437 /**
   438  * razor_package_get_details_varg:
   439  * @set: a %razor_set
   440  * @package: a %razor_package
   441  * @args: a va_list of arguments to set
   442  **/
   443 void
   444 razor_package_get_details_varg(struct razor_set *set,
   445 			       struct razor_package *package,
   446 			       va_list args)
   447 {
   448 	int i;
   449 	enum razor_detail_type type;
   450 	const char **data;
   451 
   452 	for (i = 0;; i += 2) {
   453 		type = va_arg(args, enum razor_detail_type);
   454 		if (type == RAZOR_DETAIL_LAST)
   455 			break;
   456 		data = va_arg(args, const char **);
   457 		*data = razor_package_get_details_type(set, package, type);
   458 	}
   459 
   460 }
   461 
   462 /**
   463  * razor_package_get_details:
   464  * @set: a %razor_set
   465  * @package: a %razor_package
   466  *
   467  * Gets details about a package using a varg interface
   468  * The vararg must be terminated with %RAZOR_DETAIL_LAST.
   469  *
   470  * Example: razor_package_get_details (set, package,
   471  *				       RAZOR_DETAIL_URL, &url,
   472  *				       RAZOR_DETAIL_LAST);
   473  **/
   474 RAZOR_EXPORT void
   475 razor_package_get_details(struct razor_set *set, struct razor_package *package, ...)
   476 {
   477 	va_list args;
   478 
   479 	assert (set != NULL);
   480 	assert (package != NULL);
   481 
   482 	va_start(args, NULL);
   483 	razor_package_get_details_varg (set, package, args);
   484 	va_end (args);
   485 }
   486 
   487 /**
   488  * razor_package_remove:
   489  * @set: a %razor_set
   490  * @package: a %razor_package
   491  * @root: the root into which the package is currently installed
   492  *
   493  * Removes an installed package.
   494  **/
   495 RAZOR_EXPORT int
   496 razor_package_remove(struct razor_set *set, struct razor_package *package,
   497 		     const char *root)
   498 {
   499 	struct razor_file_iterator *fi;
   500 	struct razor_package_iterator *pi;
   501 	struct razor_package *p;
   502 	char buffer[PATH_MAX];
   503 	const char *name, *program, *script;
   504 	int retval = 0, count;
   505 
   506 	razor_package_get_details(set, package,
   507 				  RAZOR_DETAIL_PREUNPROG, &program,
   508 				  RAZOR_DETAIL_PREUN, &script,
   509 				  RAZOR_DETAIL_LAST);
   510 
   511 	if (razor_run_script(root, RAZOR_PROPERTY_PREUN, program, script))
   512 		return -1;
   513 
   514 	fi = razor_file_iterator_create(set, package);
   515 
   516 	while (!retval && razor_file_iterator_next(fi, &name)) {
   517 		pi = razor_package_iterator_create_for_file(set, name);
   518 		count = 0;
   519 		while (razor_package_iterator_next(pi, &p, RAZOR_DETAIL_LAST))
   520 			count++;
   521 		razor_package_iterator_destroy(pi);
   522 		if (count <= 1) {
   523 			snprintf(buffer, sizeof buffer, "%s%s", root, name);
   524 			retval = remove(buffer);
   525 		}
   526 	}
   527 
   528 	razor_file_iterator_destroy(fi);
   529 
   530 	if (retval)
   531 		return retval;
   532 
   533 	razor_package_get_details(set, package,
   534 				  RAZOR_DETAIL_POSTUNPROG, &program,
   535 				  RAZOR_DETAIL_POSTUN, &script,
   536 				  RAZOR_DETAIL_LAST);
   537 
   538 	return razor_run_script(root, RAZOR_PROPERTY_POSTUN, program, script);
   539 }
   540 
   541 RAZOR_EXPORT const char *
   542 razor_property_relation_to_string(struct razor_property *p)
   543 {
   544 	assert (p != NULL);
   545 
   546 	switch (p->flags & RAZOR_PROPERTY_RELATION_MASK) {
   547 	case RAZOR_PROPERTY_LESS:
   548 		return "<";
   549 
   550 	case RAZOR_PROPERTY_LESS | RAZOR_PROPERTY_EQUAL:
   551 		return "<=";
   552 
   553 	case RAZOR_PROPERTY_EQUAL:
   554 		return "=";
   555 
   556 	case RAZOR_PROPERTY_GREATER | RAZOR_PROPERTY_EQUAL:
   557 		return ">=";
   558 
   559 	case RAZOR_PROPERTY_GREATER:
   560 		return ">";
   561 
   562 	default:
   563 		return "?";
   564 	}
   565 }
   566 
   567 RAZOR_EXPORT const char *
   568 razor_property_type_to_string(struct razor_property *p)
   569 {
   570 	assert (p != NULL);
   571 
   572 	switch (p->flags & RAZOR_PROPERTY_TYPE_MASK) {
   573 	case RAZOR_PROPERTY_REQUIRES:
   574 		return "requires";
   575 	case RAZOR_PROPERTY_PROVIDES:
   576 		return "provides";
   577 	case RAZOR_PROPERTY_CONFLICTS:
   578 		return "conflicts";
   579 	case RAZOR_PROPERTY_OBSOLETES:
   580 		return "obsoletes";
   581 	default:
   582 		return NULL;
   583 	}
   584 }
   585 
   586 RAZOR_EXPORT struct razor_entry *
   587 razor_set_find_entry(struct razor_set *set,
   588 		     struct razor_entry *dir, const char *pattern)
   589 {
   590 	struct razor_entry *e, *subdir;
   591 	const char *n, *pool = set->file_string_pool.data;
   592 	int len;
   593 
   594 	assert (set != NULL);
   595 	assert (dir != NULL);
   596 	assert (pattern != NULL);
   597 
   598 	e = dir;
   599 	do {
   600 		n = pool + e->name;
   601 		if (strcmp(pattern, n) == 0)
   602 			return e;
   603 		len = strlen(n);
   604 		if (e->start != 0 && strncmp(pattern, n, len) == 0 &&
   605 		    pattern[len] == '/') {
   606 			subdir = (struct razor_entry *) set->files.data +
   607 				 e->start;
   608 			return razor_set_find_entry(set, subdir,
   609 						    pattern + len + 1);
   610 		}
   611 	} while (!((e++)->flags & RAZOR_ENTRY_LAST));
   612 
   613 	return NULL;
   614 }
   615 
   616 static void
   617 list_dir(struct razor_set *set, struct razor_entry *dir,
   618 	 char *prefix, const char *pattern)
   619 {
   620 	struct razor_entry *e, *subdir;
   621 	const char *n, *pool = set->file_string_pool.data;
   622 
   623 	e = dir;
   624 	do {
   625 		n = pool + e->name;
   626 		if (pattern && pattern[0] && fnmatch(pattern, n, 0) != 0)
   627 			continue;
   628 		printf("%s/%s\n", prefix, n);
   629 		if (e->start) {
   630 			char *sub = prefix + strlen (prefix);
   631 			*sub = '/';
   632 			strcpy (sub + 1, n);
   633 			subdir = (struct razor_entry *) set->files.data +
   634 				 e->start;
   635 			list_dir(set, subdir, prefix, pattern);
   636 			*sub = '\0';
   637 		}
   638 	} while (!((e++)->flags & RAZOR_ENTRY_LAST));
   639 }
   640 
   641 RAZOR_EXPORT void
   642 razor_set_list_files(struct razor_set *set, const char *pattern)
   643 {
   644 	struct razor_entry *root, *e;
   645 	char buffer[512], *p, *base;
   646 
   647 	assert (set != NULL);
   648 
   649 	root = (struct razor_entry *) set->files.data;
   650 
   651 	if (pattern == NULL) {
   652 		p = set->file_string_pool.data;
   653 		e = root;
   654 		do {
   655 			if (e->start) {
   656 				strcpy(buffer, p + e->name);
   657 				list_dir(set, root + e->start, buffer, NULL);
   658 			}
   659 		} while (!((e++)->flags & RAZOR_ENTRY_LAST));
   660 		return;
   661 	}
   662 
   663 	strcpy(buffer, pattern);
   664 	e = razor_set_find_entry(set, root, buffer);
   665 	if (e && e->start) {
   666 		base = NULL;
   667 	} else {
   668 		p = strrchr(buffer, '/');
   669 		if (p) {
   670 			*p = '\0';
   671 			base = p + 1;
   672 		} else {
   673 			base = NULL;
   674 		}
   675 	}
   676 	e = razor_set_find_entry(set, root, buffer);
   677 	if (e && e->start)
   678 		list_dir(set, root + e->start, buffer, base);
   679 }
   680 
   681 RAZOR_EXPORT void
   682 razor_set_list_package_files(struct razor_set *set,
   683 			     struct razor_package *package)
   684 {
   685 	struct razor_file_iterator *fi;
   686 	const char *name;
   687 
   688 	assert (set != NULL);
   689 	assert (package != NULL);
   690 
   691 	fi = razor_file_iterator_create(set, package);
   692 
   693 	while (razor_file_iterator_next(fi, &name))
   694 		printf("%s\n", name);
   695 
   696 	razor_file_iterator_destroy(fi);
   697 }
   698 
   699 RAZOR_EXPORT void
   700 razor_set_diff(struct razor_set *set, struct razor_set *upstream,
   701 	       razor_diff_callback_t callback, void *data)
   702 {
   703  	struct razor_package_iterator *pi1, *pi2;
   704  	struct razor_package *p1, *p2;
   705 	const char *name1, *name2, *version1, *version2, *arch1, *arch2;
   706 	int res;
   707 
   708 	assert (set != NULL);
   709 	assert (upstream != NULL);
   710 
   711 	pi1 = razor_package_iterator_create(set);
   712 	pi2 = razor_package_iterator_create(upstream);
   713 
   714 	razor_package_iterator_next(pi1, &p1,
   715 				    RAZOR_DETAIL_NAME, &name1,
   716 				    RAZOR_DETAIL_VERSION, &version1,
   717 				    RAZOR_DETAIL_ARCH, &arch1,
   718 				    RAZOR_DETAIL_LAST);
   719 	razor_package_iterator_next(pi2, &p2,
   720 				    RAZOR_DETAIL_NAME, &name2,
   721 				    RAZOR_DETAIL_VERSION, &version2,
   722 				    RAZOR_DETAIL_ARCH, &arch2,
   723 				    RAZOR_DETAIL_LAST);
   724 
   725 	while (p1 || p2) {
   726 		if (p1 && p2) {
   727 			res = strcmp(name1, name2);
   728 			if (res == 0)
   729 				res = razor_versioncmp(version1, version2);
   730 		} else {
   731 			res = 0;
   732 		}
   733 
   734 		if (p2 == NULL || res < 0)
   735 			callback(RAZOR_DIFF_ACTION_REMOVE,
   736 				 p1, name1, version1, arch1, data);
   737 		else if (p1 == NULL || res > 0)
   738 			callback(RAZOR_DIFF_ACTION_ADD,
   739 				 p2, name2, version2, arch2, data);
   740 
   741 		if (p1 != NULL && res <= 0)
   742 			razor_package_iterator_next(pi1, &p1,
   743 						    RAZOR_DETAIL_NAME, &name1,
   744 						    RAZOR_DETAIL_VERSION, &version1,
   745 						    RAZOR_DETAIL_ARCH, &arch1,
   746 						    RAZOR_DETAIL_LAST);
   747 		if (p2 != NULL && res >= 0)
   748 			razor_package_iterator_next(pi2, &p2,
   749 						    RAZOR_DETAIL_NAME, &name2,
   750 						    RAZOR_DETAIL_VERSION, &version2,
   751 						    RAZOR_DETAIL_ARCH, &arch2,
   752 						    RAZOR_DETAIL_LAST);
   753 	}
   754 
   755 	razor_package_iterator_destroy(pi1);
   756 	razor_package_iterator_destroy(pi2);
   757 }
   758 
   759 struct install_action {
   760 	enum razor_install_action action;
   761 	struct razor_package *package;
   762 };
   763 
   764 struct razor_install_iterator {
   765 	struct razor_set *set;
   766 	struct razor_set *next;
   767 	struct array actions;
   768 	struct deque *order;
   769 };
   770 
   771 static void
   772 add_action(enum razor_diff_action action,
   773 	   struct razor_package *package,
   774 	   const char *name,
   775 	   const char *version,
   776 	   const char *arch,
   777 	   void *data)
   778 {
   779 	struct razor_install_iterator *ii = data;
   780 	struct install_action *a;
   781 
   782 	a = array_add(&ii->actions, sizeof *a);
   783 	a->package = package;
   784 
   785 	switch (action) {
   786 	case RAZOR_DIFF_ACTION_ADD:
   787 		a->action = RAZOR_INSTALL_ACTION_ADD;
   788 		break;
   789 	case RAZOR_DIFF_ACTION_REMOVE:
   790 		a->action = RAZOR_INSTALL_ACTION_REMOVE;
   791 		break;
   792 	}
   793 }
   794 
   795 RAZOR_EXPORT struct razor_install_iterator *
   796 razor_set_create_install_iterator(struct razor_set *set,
   797 				  struct razor_set *next)
   798 {
   799 	struct razor_install_iterator *ii;
   800 	struct razor_property *prop;
   801 	/* A graph of the actions to be perfomed where
   802 	 * A->B means action A should follow action B.
   803 	 */
   804 	struct graph follows;
   805 	struct install_action *actions, *ai, *aj;
   806 	int i, j, count, vertex_added;
   807 	struct list *link;
   808 	struct razor_set *rs;
   809 
   810 	assert (set != NULL);
   811 	assert (next != NULL);
   812 
   813 	ii = zalloc(sizeof *ii);
   814 	ii->set = set;
   815 	ii->next = next;
   816 	
   817 	razor_set_diff(set, next, add_action, ii);
   818 
   819 	actions = ii->actions.data;
   820 	count = ii->actions.size / sizeof (struct install_action);
   821 
   822 	graph_init(&follows);
   823 
   824 	for(i = 0; i < count; i++) {
   825 		ai = actions + i;
   826 		rs = ai->action == RAZOR_INSTALL_ACTION_ADD ? next : set;
   827 		vertex_added = 0;
   828 		link = list_first(&ai->package->properties, &rs->property_pool);
   829 		for(; link; link = list_next(link)) {
   830 			prop = rs->properties.data;
   831 			prop += link->data;
   832 			switch(prop->flags & RAZOR_PROPERTY_TYPE_MASK) {
   833 			case RAZOR_PROPERTY_REQUIRES:
   834 			case RAZOR_PROPERTY_CONFLICTS:
   835 				for(j = 0; j < count; j++) {
   836 					if (j == i)
   837 						continue;
   838 					aj = actions + j;
   839 					if (aj->package->name == prop->name) {
   840 						if (ai->action ==
   841 						    RAZOR_INSTALL_ACTION_ADD)
   842 							graph_add_edge(&follows,
   843 								       i, j);
   844 						else
   845 							graph_add_edge(&follows,
   846 								       j, i);
   847 						vertex_added++;
   848 					}
   849 				}
   850 				break;
   851 			}
   852 		}
   853 		if (ai->action == RAZOR_INSTALL_ACTION_ADD) {
   854 			for(j = 0; j < count; j++) {
   855 				if (j == i)
   856 					continue;
   857 				aj = actions + j;
   858 				if (aj->package == ai->package &&
   859 				    aj->action == RAZOR_INSTALL_ACTION_REMOVE) {
   860 					graph_add_edge(&follows, i, j);
   861 					vertex_added++;
   862 				}
   863 			}
   864 		}
   865 		if (!vertex_added)
   866 			graph_add_edge(&follows, i, i);
   867 	}
   868 
   869 	ii->order = graph_sort(&follows);
   870 	graph_release(&follows);
   871 
   872 	return ii;
   873 }
   874 
   875 RAZOR_EXPORT int
   876 razor_install_iterator_next(struct razor_install_iterator *ii,
   877 			    struct razor_set **set,
   878 			    struct razor_package **package,
   879 			    enum razor_install_action *action,
   880 			    int *count)
   881 {
   882 	struct install_action *a;
   883 	if (deque_empty(ii->order))
   884 		return 0;
   885 
   886 	a = (struct install_action *)ii->actions.data + deque_pop(ii->order);
   887 	switch (a->action) {
   888 	case RAZOR_INSTALL_ACTION_ADD:
   889 		*set = ii->next;
   890 		break;
   891 	case RAZOR_INSTALL_ACTION_REMOVE:
   892 		*set = ii->set;
   893 		break;
   894 	}
   895 
   896 	*package = a->package;
   897 	*action = a->action;
   898 	*count = 0;
   899 
   900 	return 1;
   901 }
   902 
   903 RAZOR_EXPORT void
   904 razor_install_iterator_destroy(struct razor_install_iterator *ii)
   905 {
   906 	array_release(&ii->actions);
   907 	deque_free(ii->order);
   908 	free(ii);
   909 }