librazor/razor.c
author Kristian H?gsberg <krh@redhat.com>
Tue Jul 08 22:57:34 2008 -0400 (2008-07-08)
changeset 317 019a53b65271
parent 308 f4761f529b9e
child 318 829d6711b316
permissions -rw-r--r--
Convert main.c to use razor_root for most cases.

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