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