librazor/razor.c
author J. Ali Harlow <ali@juiblex.co.uk>
Tue Jan 06 14:06:00 2009 +0000 (2009-01-06)
changeset 320 53e1185e2366
parent 316 5ebed314390c
child 322 66c281524c98
permissions -rw-r--r--
Make rpmlib an optional dependency

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