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