librazor/razor.c
author James Bowes <jbowes@redhat.com>
Wed Jul 09 10:11:13 2008 -0400 (2008-07-09)
changeset 318 829d6711b316
parent 316 5ebed314390c
child 322 66c281524c98
permissions -rw-r--r--
Use strings to identify section types in the on-disk repo format.

Previously, a given razor file type had a fixed number of sections in a
fixed order, identified by an integer type. Now, sections are identified
by a named string (stored in a string pool after the section lists).

This will allow for razor files to contain arbitrary sections.

For bonus points, also drop the 4k section alignment and change the
magic byte string to "RZDB".

committer: Kristian H?gsberg <krh@redhat.com>
     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 }