razor.c
author Kristian H?gsberg <krh@redhat.com>
Thu Sep 20 14:53:03 2007 -0400 (2007-09-20)
changeset 38 88f3ec190a3f
parent 37 094daff9a8dc
child 39 5fe9c9286cd0
permissions -rw-r--r--
Implement updating all packages.
     1 #define _GNU_SOURCE
     2 
     3 #include <stdlib.h>
     4 #include <stddef.h>
     5 #include <stdio.h>
     6 #include <string.h>
     7 #include <sys/types.h>
     8 #include <sys/stat.h>
     9 #include <sys/mman.h>
    10 #include <unistd.h>
    11 #include <fcntl.h>
    12 #include <errno.h>
    13 #include <ctype.h>
    14 
    15 #include "razor.h"
    16 
    17 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
    18 
    19 struct array {
    20 	void *data;
    21 	int size, alloc;
    22 };
    23 
    24 struct razor_set_section {
    25 	unsigned int type;
    26 	unsigned int offset;
    27 	unsigned int size;
    28 };
    29 
    30 struct razor_set_header {
    31 	unsigned int magic;
    32 	unsigned int version;
    33 	struct razor_set_section sections[0];
    34 };
    35 
    36 #define RAZOR_MAGIC 0x7a7a7a7a
    37 #define RAZOR_VERSION 1
    38 
    39 #define RAZOR_PACKAGES 0
    40 #define RAZOR_REQUIRES 1
    41 #define RAZOR_PROVIDES 2
    42 #define RAZOR_STRING_POOL 3
    43 #define RAZOR_PROPERTY_POOL 4
    44 
    45 struct razor_package {
    46 	unsigned long name;
    47 	unsigned long version;
    48 	unsigned long requires;
    49 	unsigned long provides;
    50 };
    51 
    52 struct razor_property {
    53 	unsigned long name;
    54 	unsigned long version;
    55 	unsigned long packages;
    56 };
    57 
    58 struct razor_set {
    59 	struct array string_pool;
    60 	struct array property_pool;
    61  	struct array packages;
    62  	struct array requires;
    63  	struct array provides;
    64 	struct razor_set_header *header;
    65 };
    66 
    67 struct import_property_context {
    68 	struct array *all;
    69 	struct array package;
    70 };
    71 
    72 struct razor_importer {
    73 	struct razor_set *set;
    74 	struct array buckets;
    75 	struct import_property_context requires;
    76 	struct import_property_context provides;
    77 	struct razor_package *package;
    78 	unsigned long *requires_map;
    79 	unsigned long *provides_map;
    80 };
    81 
    82 static void
    83 array_init(struct array *array)
    84 {
    85 	memset(array, 0, sizeof *array);
    86 }
    87 
    88 static void
    89 array_release(struct array *array)
    90 {
    91 	free(array->data);
    92 }
    93 
    94 static void *
    95 array_add(struct array *array, int size)
    96 {
    97 	int alloc;
    98 	void *data, *p;
    99 
   100 	if (array->alloc > 0)
   101 		alloc = array->alloc;
   102 	else
   103 		alloc = 16;
   104 
   105 	while (alloc < array->size + size)
   106 		alloc *= 2;
   107 
   108 	if (array->alloc < alloc) {
   109 		data = realloc(array->data, alloc);
   110 		if (data == NULL)
   111 			return 0;
   112 		array->data = data;
   113 		array->alloc = alloc;
   114 	}
   115 
   116 	p = array->data + array->size;
   117 	array->size += size;
   118 
   119 	return p;
   120 }
   121 
   122 static int
   123 write_to_fd(int fd, void *p, size_t size)
   124 {
   125 	int rest, len;
   126 
   127 	rest = size;
   128 	while (rest > 0) {
   129 		len = write(fd, p, rest);
   130 		if (len < 0)
   131 			return -1;
   132 		rest -= len;
   133 	}
   134 
   135 	return 0;
   136 }
   137 
   138 static void *
   139 zalloc(size_t size)
   140 {
   141 	void *p;
   142 
   143 	p = malloc(size);
   144 	memset(p, 0, size);
   145 
   146 	return p;
   147 }
   148 
   149 struct razor_set_section razor_sections[] = {
   150 	{ RAZOR_PACKAGES,	offsetof(struct razor_set, packages) },
   151 	{ RAZOR_REQUIRES,	offsetof(struct razor_set, requires) },
   152 	{ RAZOR_PROVIDES,	offsetof(struct razor_set, provides) },
   153 	{ RAZOR_STRING_POOL,	offsetof(struct razor_set, string_pool) },
   154 	{ RAZOR_PROPERTY_POOL,	offsetof(struct razor_set, property_pool) },
   155 };
   156 
   157 struct razor_set *
   158 razor_set_create(void)
   159 {
   160 	return zalloc(sizeof(struct razor_set));
   161 }
   162 
   163 struct razor_set *
   164 razor_set_open(const char *filename)
   165 {
   166 	struct razor_set *set;
   167 	struct razor_set_section *s;
   168 	struct stat stat;
   169 	struct array *array;
   170 	int fd;
   171 
   172 	set = zalloc(sizeof *set);
   173 	fd = open(filename, O_RDONLY);
   174 	if (fstat(fd, &stat) < 0)
   175 		return NULL;
   176 	set->header = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
   177 	if (set->header == MAP_FAILED) {
   178 		free(set);
   179 		return NULL;
   180 	}
   181 
   182 	for (s = set->header->sections; ~s->type; s++) {
   183 		if (s->type >= ARRAY_SIZE(razor_sections))
   184 			continue;
   185 		if (s->type != razor_sections[s->type].type)
   186 			continue;
   187 		array = (void *) set + razor_sections[s->type].offset;
   188 		array->data = (void *) set->header + s->offset;
   189 		array->size = s->size;
   190 		array->alloc = s->size;
   191 	}
   192 	close(fd);
   193 
   194 	return set;
   195 }
   196 
   197 void
   198 razor_set_destroy(struct razor_set *set)
   199 {
   200 	unsigned int size;
   201 	struct array *a;
   202 	int i;
   203 
   204 	if (set->header) {
   205 		for (i = 0; set->header->sections[i].type; i++)
   206 			;
   207 		size = set->header->sections[i].type;
   208 		munmap(set->header, size);
   209 	} else {
   210 		for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
   211 			a = (void *) set + razor_sections[i].offset;
   212 			free(a->data);
   213 		}
   214 	}
   215 
   216 	free(set);
   217 }
   218 
   219 static int
   220 razor_set_write(struct razor_set *set, const char *filename)
   221 {
   222 	char data[4096];
   223 	struct razor_set_header *header = (struct razor_set_header *) data;
   224 	struct array *a;
   225 	unsigned long offset;
   226 	int i, fd;
   227 
   228 	memset(data, 0, sizeof data);
   229 	header->magic = RAZOR_MAGIC;
   230 	header->version = RAZOR_VERSION;
   231 	offset = sizeof data;
   232 
   233 	for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
   234 		if (razor_sections[i].type != i)
   235 			continue;
   236 		a = (void *) set + razor_sections[i].offset;
   237 		header->sections[i].type = i;
   238 		header->sections[i].offset = offset;
   239 		header->sections[i].size = a->size;
   240 		offset += (a->size + 4095) & ~4095;
   241 	}
   242 
   243 	header->sections[i].type = ~0;
   244 	header->sections[i].offset = 0;
   245 	header->sections[i].size = 0;
   246 
   247 	fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666);
   248 	if (fd < 0)
   249 		return -1;
   250 
   251 	write_to_fd(fd, data, sizeof data);
   252 	for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
   253 		if (razor_sections[i].type != i)
   254 			continue;
   255 		a = (void *) set + razor_sections[i].offset;
   256 		write_to_fd(fd, a->data, (a->size + 4095) & ~4095);
   257 	}
   258 
   259 	close(fd);
   260 
   261 	return 0;
   262 }
   263 
   264 static unsigned int
   265 hash_string(const char *key)
   266 {
   267 	const char *p;
   268 	unsigned int hash = 0;
   269 
   270 	for (p = key; *p; p++)
   271 		hash = (hash * 617) ^ *p;
   272 
   273 	return hash;
   274 }
   275 
   276 static unsigned long
   277 razor_importer_lookup(struct razor_importer *importer, const char *key)
   278 {
   279 	unsigned int mask, start, i;
   280 	unsigned long *b;
   281 	char *pool;
   282 
   283 	pool = importer->set->string_pool.data;
   284 	mask = importer->buckets.alloc - 1;
   285 	start = hash_string(key) * sizeof(unsigned long);
   286 
   287 	for (i = 0; i < importer->buckets.alloc; i += sizeof *b) {
   288 		b = importer->buckets.data + ((start + i) & mask);
   289 
   290 		if (*b == 0)
   291 			return 0;
   292 
   293 		if (strcmp(key, &pool[*b]) == 0)
   294 			return *b;
   295 	}
   296 
   297 	return 0;
   298 }
   299 
   300 static unsigned long
   301 add_to_string_pool(struct razor_set *set, const char *key)
   302 {
   303 	int len;
   304 	char *p;
   305 
   306 	len = strlen(key) + 1;
   307 	p = array_add(&set->string_pool, len);
   308 	memcpy(p, key, len);
   309 
   310 	return p - (char *) set->string_pool.data;
   311 }
   312 
   313 static unsigned long
   314 add_to_property_pool(struct razor_set *set, struct array *properties)
   315 {
   316 	unsigned long  *p;
   317 
   318 	p = array_add(properties, sizeof *p);
   319 	*p = ~0ul;
   320 	p = array_add(&set->property_pool, properties->size);
   321 	memcpy(p, properties->data, properties->size);
   322 
   323 	return p - (unsigned long *) set->property_pool.data;
   324 }
   325 
   326 static void
   327 do_insert(struct razor_importer *importer, unsigned long value)
   328 {
   329 	unsigned int mask, start, i;
   330 	unsigned long *b;
   331 	const char *key;
   332 
   333 	key = (char *) importer->set->string_pool.data + value;
   334 	mask = importer->buckets.alloc - 1;
   335 	start = hash_string(key) * sizeof(unsigned long);
   336 
   337 	for (i = 0; i < importer->buckets.alloc; i += sizeof *b) {
   338 		b = importer->buckets.data + ((start + i) & mask);
   339 		if (*b == 0) {
   340 			*b = value;
   341 			break;
   342 		}
   343 	}
   344 }
   345 
   346 static unsigned long
   347 razor_importer_insert(struct razor_importer *importer, const char *key)
   348 {
   349 	unsigned long value, *buckets, *b, *end;
   350 	int alloc;
   351 
   352 	alloc = importer->buckets.alloc;
   353 	array_add(&importer->buckets, 4 * sizeof *buckets);
   354 	if (alloc != importer->buckets.alloc) {
   355 		end = importer->buckets.data + alloc;
   356 		memset(end, 0, importer->buckets.alloc - alloc);
   357 		for (b = importer->buckets.data; b < end; b++) {
   358 			value = *b;
   359 			if (value != 0) {
   360 				*b = 0;
   361 				do_insert(importer, value);
   362 			}
   363 		}
   364 	}
   365 
   366 	value = add_to_string_pool(importer->set, key);
   367 	do_insert (importer, value);
   368 
   369 	return value;
   370 }
   371 
   372 static unsigned long
   373 razor_importer_tokenize(struct razor_importer *importer, const char *string)
   374 {
   375 	unsigned long token;
   376 
   377 	if (string == NULL)
   378 		return razor_importer_tokenize(importer, "");
   379 
   380 	token = razor_importer_lookup(importer, string);
   381 	if (token != 0)
   382 		return token;
   383 
   384 	return razor_importer_insert(importer, string);
   385 }
   386 
   387 void
   388 razor_importer_begin_package(struct razor_importer *importer,
   389 			     const char *name, const char *version)
   390 {
   391 	struct razor_package *p;
   392 
   393 	p = array_add(&importer->set->packages, sizeof *p);
   394 	p->name = razor_importer_tokenize(importer, name);
   395 	p->version = razor_importer_tokenize(importer, version);
   396 
   397 	importer->package = p;
   398 	array_init(&importer->requires.package);
   399 	array_init(&importer->provides.package);
   400 }
   401 
   402 void
   403 razor_importer_finish_package(struct razor_importer *importer)
   404 {
   405 	struct razor_package *p;
   406 
   407 	p = importer->package;
   408 	p->requires = add_to_property_pool(importer->set,
   409 					   &importer->requires.package);
   410 	p->provides = add_to_property_pool(importer->set,
   411 					   &importer->provides.package);
   412 
   413 	array_release(&importer->requires.package);
   414 	array_release(&importer->provides.package);
   415 }
   416 
   417 static void
   418 razor_importer_add_property(struct razor_importer *importer,
   419 			    struct import_property_context *pctx,
   420 			    const char *name, const char *version)
   421 {
   422 	struct razor_property *p;
   423 	unsigned long *r;
   424 
   425 	p = array_add(pctx->all, sizeof *p);
   426 	p->name = razor_importer_tokenize(importer, name);
   427 	p->version = razor_importer_tokenize(importer, version);
   428 	p->packages = importer->package -
   429 		(struct razor_package *) importer->set->packages.data;
   430 
   431 	r = array_add(&pctx->package, sizeof *r);
   432 	*r = p - (struct razor_property *) pctx->all->data;
   433 }
   434 
   435 void
   436 razor_importer_add_requires(struct razor_importer *importer,
   437 			    const char *name, const char *version)
   438 {
   439 	razor_importer_add_property(importer,
   440 				    &importer->requires, name, version);
   441 }
   442 
   443 void
   444 razor_importer_add_provides(struct razor_importer *importer,
   445 			    const char *name, const char *version)
   446 {
   447 	razor_importer_add_property(importer,
   448 				    &importer->provides, name, version);
   449 }
   450 
   451 struct razor_importer *
   452 razor_importer_new(void)
   453 {
   454 	struct razor_importer *importer;
   455 
   456 	importer = zalloc(sizeof *importer);
   457 	importer->set = razor_set_create();
   458 	importer->requires.all = &importer->set->requires;
   459 	importer->provides.all = &importer->set->provides;
   460 
   461 	return importer;
   462 }
   463 
   464 typedef int (*compare_with_data_func_t)(const void *p1,
   465 					const void *p,
   466 					void *data);
   467 
   468 struct qsort_context {
   469 	size_t size;
   470 	compare_with_data_func_t compare;
   471 	void *data;
   472 };
   473 
   474 static void
   475 qsort_swap(void *p1, void *p2, size_t size)
   476 {
   477 	char buffer[size];
   478 
   479 	memcpy(buffer, p1, size);
   480 	memcpy(p1, p2, size);
   481 	memcpy(p2, buffer, size);
   482 }
   483 
   484 static void
   485 __qsort_with_data(void *base, size_t nelem, unsigned long *map,
   486 		  struct qsort_context *ctx)
   487 {
   488 	void *p, *start, *end, *pivot;
   489 	unsigned long *mp, *mstart, *mend, tmp;
   490 	int left, right, result;
   491 	size_t size = ctx->size;
   492 
   493 	p = base;
   494 	start = base;
   495 	end = base + nelem * size;
   496 	mp = map;
   497 	mstart = map;
   498 	mend = map + nelem;
   499 	pivot = base + (random() % nelem) * size;
   500 
   501 	while (p < end) {
   502 		result = ctx->compare(p, pivot, ctx->data);
   503 		if (result < 0) {
   504 			qsort_swap(p, start, size);
   505 			tmp = *mp;
   506 			*mp = *mstart;
   507 			*mstart = tmp;
   508 			if (start == pivot)
   509 				pivot = p;
   510 			start += size;
   511 			mstart++;
   512 			p += size;
   513 			mp++;
   514 		} else if (result == 0) {
   515 			p += size;
   516 			mp++;
   517 		} else {
   518  			end -= size;
   519 			mend--;
   520 			qsort_swap(p, end, size);
   521 			tmp = *mp;
   522 			*mp = *mend;
   523 			*mend = tmp;
   524 			if (end == pivot)
   525 				pivot = p;
   526 		}
   527 	}
   528 
   529 	left = (start - base) / size;
   530 	right = (base + nelem * size - end) / size;
   531 	if (left > 1)
   532 		__qsort_with_data(base, left, map, ctx);
   533 	if (right > 1)
   534 		__qsort_with_data(end, right, mend, ctx);
   535 }
   536 
   537 unsigned long *
   538 qsort_with_data(void *base, size_t nelem, size_t size,
   539 		compare_with_data_func_t compare, void *data)
   540 {
   541 	struct qsort_context ctx;
   542 	unsigned long *map;
   543 	int i;
   544 
   545 	ctx.size = size;
   546 	ctx.compare = compare;
   547 	ctx.data = data;
   548 
   549 	map = malloc(nelem * sizeof (unsigned long));
   550 	for (i = 0; i < nelem; i++)
   551 		map[i] = i;
   552 
   553 	__qsort_with_data(base, nelem, map, &ctx);
   554 
   555 	return map;
   556 }
   557 
   558 static int
   559 versioncmp(const char *s1, const char *s2)
   560 {
   561 	const char *p1, *p2;
   562 	long n1, n2;
   563 	int res;
   564 
   565 	n1 = strtol(s1, (char **) &p1, 0);
   566 	n2 = strtol(s2, (char **) &p2, 0);
   567 
   568 	/* Epoch; if one but not the other has an epoch set, default
   569 	 * the epoch-less version to 0. */
   570 	res = (*p1 == ':') - (*p2 == ':');
   571 	if (res < 0) {
   572 		n1 = 0;
   573 		p1 = s1;
   574 		p2++;
   575 	} else if (res > 0) {
   576 		p1++;
   577 		n2 = 0;
   578 		p2 = s2;
   579 	}
   580 
   581 	if (n1 != n2)
   582 		return n1 - n2;
   583 	while (*p1 && *p2) {
   584 		if (*p1 != *p2)
   585 			return *p1 - *p2;
   586 		p1++;
   587 		p2++;
   588 		if (isdigit(*p1) && isdigit(*p2))
   589 			return versioncmp(p1, p2);
   590 	}
   591 
   592 	return *p1 - *p2;
   593 }
   594 
   595 
   596 static int
   597 compare_packages(const void *p1, const void *p2, void *data)
   598 {
   599 	const struct razor_package *pkg1 = p1, *pkg2 = p2;
   600 	struct razor_set *set = data;
   601 	char *pool = set->string_pool.data;
   602 
   603 	if (pkg1->name == pkg2->name)
   604 		return versioncmp(&pool[pkg1->version], &pool[pkg2->version]);
   605 	else
   606 		return strcmp(&pool[pkg1->name], &pool[pkg2->name]);
   607 }
   608 
   609 static int
   610 compare_properties(const void *p1, const void *p2, void *data)
   611 {
   612 	const struct razor_property *prop1 = p1, *prop2 = p2;
   613 	struct razor_set *set = data;
   614 	char *pool = set->string_pool.data;
   615 
   616 	if (prop1->name == prop2->name)
   617 		return versioncmp(&pool[prop1->version], &pool[prop2->version]);
   618 	else
   619 		return strcmp(&pool[prop1->name], &pool[prop2->name]);
   620 }
   621 
   622 static unsigned long *
   623 uniqueify_properties(struct razor_set *set, struct array *properties)
   624 {
   625 	struct razor_property *rp, *up, *rp_end;
   626 	struct array *pkgs, *p;
   627 	unsigned long *map, *rmap, *r;
   628 	int i, count, unique;
   629 
   630 	count = properties->size / sizeof(struct razor_property);
   631 	map = qsort_with_data(properties->data,
   632 			      count,
   633 			      sizeof(struct razor_property),
   634 			      compare_properties,
   635 			      set);
   636 
   637 	rp_end = properties->data + properties->size;
   638 	rmap = malloc(count * sizeof *map);
   639 	pkgs = zalloc(count * sizeof *pkgs);
   640 	for (rp = properties->data, up = rp, i = 0; rp < rp_end; rp++, i++) {
   641 		if (rp->name != up->name || rp->version != up->version) {
   642 			up++;
   643 			up->name = rp->name;
   644 			up->version = rp->version;
   645 		}
   646 
   647 		unique = up - (struct razor_property *) properties->data;
   648 		rmap[map[i]] = unique;
   649 		r = array_add(&pkgs[unique], sizeof *r);
   650 		*r = rp->packages;
   651 	}
   652 	free(map);
   653 
   654 	up++;
   655 	properties->size = (void *) up - properties->data;
   656 	rp_end = up;
   657 	for (rp = properties->data, p = pkgs; rp < rp_end; rp++, p++) {
   658 		rp->packages = add_to_property_pool(set, p);
   659 		array_release(p);
   660 	}
   661 
   662 	free(pkgs);
   663 
   664 	return rmap;
   665 }
   666 
   667 static void
   668 remap_package_links(struct razor_importer *importer)
   669 {
   670 	struct razor_package *p, *end;
   671 	unsigned long *pool, *r;
   672 
   673 	pool = importer->set->property_pool.data;
   674 	end = importer->set->packages.data + importer->set->packages.size;
   675 	for (p = importer->set->packages.data; p < end; p++) {
   676 		for (r = &pool[p->requires]; ~*r; r++)
   677 			*r = importer->requires_map[*r];
   678 		for (r = &pool[p->provides]; ~*r; r++)
   679 			*r = importer->provides_map[*r];
   680 	}
   681 }
   682 
   683 static void
   684 remap_property_links(struct razor_importer *importer, unsigned long *map)
   685 {
   686 	struct razor_property *p, *end;
   687 	struct razor_package *rp;
   688 	unsigned long *pool, *r, *rmap;
   689 	int i, count;
   690 
   691 	pool = importer->set->property_pool.data;
   692 	count = importer->set->packages.size / sizeof(struct razor_package);
   693 	rmap = malloc(count * sizeof *map);
   694 	rp = importer->set->packages.data;
   695 	for (i = 0; i < count; i++)
   696 		rmap[map[i]] = i;
   697 
   698 	/* FIXME: This will break if we implement package list sharing
   699 	 * for all properties, since we'll remap those lists more than
   700 	 * once. We should just have a separate pool for property
   701 	 * lists and a separate pool for package lists and remap it as
   702 	 * a flat pool.  Right now, as property lists and package
   703 	 * lists are mixed, we can't do that. */
   704 
   705 	end = importer->set->requires.data + importer->set->requires.size;
   706 	for (p = importer->set->requires.data; p < end; p++)
   707 		for (r = &pool[p->packages]; ~*r; r++)
   708 			*r = rmap[*r];
   709 
   710 	end = importer->set->provides.data + importer->set->provides.size;
   711 	for (p = importer->set->provides.data; p < end; p++)
   712 		for (r = &pool[p->packages]; ~*r; r++)
   713 			*r = rmap[*r];
   714 
   715 	free(rmap);
   716 }
   717 
   718 struct razor_set *
   719 razor_importer_finish(struct razor_importer *importer)
   720 {
   721 	struct razor_set *set;
   722 	unsigned long *map;
   723 	int count;
   724 
   725 	importer->requires_map = uniqueify_properties(importer->set,
   726 						      importer->requires.all);
   727 	importer->provides_map = uniqueify_properties(importer->set,
   728 						      importer->provides.all);
   729 	remap_package_links(importer);
   730 	free(importer->requires_map);
   731 	free(importer->provides_map);
   732 
   733 	count = importer->set->packages.size / sizeof(struct razor_package);
   734 	map = qsort_with_data(importer->set->packages.data,
   735 			      count,
   736 			      sizeof(struct razor_package),
   737 			      compare_packages,
   738 			      importer->set);
   739 	remap_property_links(importer, map);
   740 	free(map);
   741 
   742 	set = importer->set;
   743 	array_release(&importer->buckets);
   744 	free(importer);
   745 
   746 	return set;
   747 }
   748 
   749 void
   750 razor_set_list(struct razor_set *set)
   751 {
   752 	struct razor_package *p, *end;
   753 	char *pool;
   754 
   755 	pool = set->string_pool.data;
   756 	end = set->packages.data + set->packages.size;
   757 	for (p = set->packages.data; p < end; p++)
   758 		printf("%s %s\n", &pool[p->name], &pool[p->version]);
   759 }
   760 
   761 struct razor_set *bsearch_set;
   762 
   763 static int
   764 compare_package_name(const void *key, const void *data)
   765 {
   766 	const struct razor_package *p = data;
   767 	char *pool;
   768 
   769 	pool = bsearch_set->string_pool.data;
   770 
   771 	return strcmp(key, &pool[p->name]);
   772 }
   773 
   774 struct razor_package *
   775 razor_set_get_package(struct razor_set *set, const char *package)
   776 {
   777 	bsearch_set = set;
   778 	return bsearch(package, set->packages.data,
   779 		       set->packages.size / sizeof(struct razor_package),
   780 		       sizeof(struct razor_package), compare_package_name);
   781 }
   782 
   783 static int
   784 compare_property_name(const void *key, const void *data)
   785 {
   786 	const struct razor_property *p = data;
   787 	char *pool;
   788 
   789 	pool = bsearch_set->string_pool.data;
   790 
   791 	return strcmp(key, &pool[p->name]);
   792 }
   793 
   794 struct razor_property *
   795 razor_set_get_property(struct razor_set *set,
   796 		       struct array *properties,
   797 		       const char *property)
   798 {
   799 	struct razor_property *p, *start;
   800 
   801 	bsearch_set = set;
   802 	p = bsearch(property, properties->data,
   803 		    properties->size / sizeof(struct razor_property),
   804 		    sizeof(struct razor_property), compare_property_name);
   805 
   806 	start = properties->data;
   807 	while (p > start && (p - 1)->name == p->name)
   808 		p--;
   809 
   810 	return p;
   811 }
   812 
   813 static void
   814 razor_set_list_all_properties(struct razor_set *set, struct array *properties)
   815 {
   816 	struct razor_property *p, *end;
   817 	char *pool;
   818 
   819 	pool = set->string_pool.data;
   820 	end = properties->data + properties->size;
   821 	for (p = properties->data; p < end; p++)
   822 		printf("%s %s\n", &pool[p->name], &pool[p->version]);
   823 }
   824 
   825 void
   826 razor_set_list_requires(struct razor_set *set, const char *name)
   827 {
   828 	struct razor_property *p, *requires;
   829 	struct razor_package *package;
   830 	unsigned long *r;
   831 	char *pool;
   832 
   833 	if (name) {
   834 		package = razor_set_get_package(set, name);
   835 		r = (unsigned long *) set->property_pool.data +
   836 			package->requires;
   837 		requires = set->requires.data;
   838 		pool = set->string_pool.data;
   839 		while (~*r) {
   840 			p = &requires[*r++];
   841 			printf("%s %s\n", &pool[p->name], &pool[p->version]);
   842 		}
   843 	} else
   844 		razor_set_list_all_properties(set, &set->requires);
   845 }
   846 
   847 void
   848 razor_set_list_provides(struct razor_set *set, const char *name)
   849 {
   850 	struct razor_property *p, *provides;
   851 	struct razor_package *package;
   852 	unsigned long *r;
   853 	char *pool;
   854 
   855 	if (name) {
   856 		package = razor_set_get_package(set, name);
   857 		r = (unsigned long *) set->property_pool.data +
   858 			package->provides;
   859 		provides = set->provides.data;
   860 		pool = set->string_pool.data;
   861 		while (~*r) {
   862 			p = &provides[*r++];
   863 			printf("%s %s\n", &pool[p->name], &pool[p->version]);
   864 		}
   865 	} else 
   866 		razor_set_list_all_properties(set, &set->provides);
   867 }
   868 
   869 void
   870 razor_set_list_property_packages(struct razor_set *set,
   871 				 struct array *properties,
   872 				 const char *name,
   873 				 const char *version)
   874 {
   875 	struct razor_property *property, *end;
   876 	struct razor_package *p, *packages;
   877 	unsigned long *r;
   878 	char *pool;
   879 
   880 	if (name == NULL)
   881 		return;
   882 
   883 	property = razor_set_get_property(set, properties, name);
   884 	packages = set->packages.data;
   885 	pool = set->string_pool.data;
   886 	end = properties->data + properties->size;
   887 	while (property < end && strcmp(name, &pool[property->name]) == 0) {
   888 		if (version && versioncmp(version, &pool[property->version]) != 0)
   889 			goto next;
   890 		r = (unsigned long *)
   891 			set->property_pool.data + property->packages;
   892 		while (~*r) {
   893 			p = &packages[*r++];
   894 			printf("%s %s\n",
   895 			       &pool[p->name], &pool[p->version]);
   896 		}
   897 	next:
   898 		property++;
   899 	}
   900 }
   901 
   902 void
   903 razor_set_validate(struct razor_set *set, struct array *unsatisfied)
   904 {
   905 	struct razor_property *r, *p, *rend, *pend;
   906 	unsigned long *u;
   907 	char *pool;
   908 
   909 	p = set->provides.data;
   910 	rend = set->requires.data + set->requires.size;
   911 	pend = set->provides.data + set->provides.size;
   912 	pool = set->string_pool.data;
   913 	
   914 	for (r = set->requires.data; r < rend; r++) {
   915 		while (p < pend && strcmp(&pool[r->name], &pool[p->name]) > 0)
   916 			p++;
   917 
   918 		/* If there is more than one version of a provides,
   919 		 * seek to the end for the highest version. */
   920 		while (p + 1 < pend && p->name == (p + 1)->name)
   921 			p++;
   922 
   923 		/* FIXME: We need to track property flags (<, <=, =
   924 		 * etc) to properly determine if a requires is
   925 		 * satisfied.  The current code doesn't track that the
   926 		 * requires a = 1 isn't satisfied by a = 2 provides. */
   927 
   928 		if (p == pend || strcmp(&pool[r->name], &pool[p->name]) != 0 ||
   929 		    versioncmp(&pool[r->version], &pool[p->version]) > 0) {
   930 			/* FIXME: We ignore file requires for now. */
   931 			if (pool[r->name] == '/')
   932 				continue;
   933 			u = array_add(unsatisfied, sizeof *u);
   934 			*u = r - (struct razor_property *) set->requires.data;
   935 		}
   936 	}
   937 }
   938 
   939 void
   940 razor_set_list_unsatisfied(struct razor_set *set)
   941 {
   942 	struct array unsatisfied;
   943 	struct razor_property *requires, *r;
   944 	unsigned long *u, *end;
   945 	char *pool;
   946 
   947 	array_init(&unsatisfied);
   948 	razor_set_validate(set, &unsatisfied);
   949 
   950 	end = unsatisfied.data + unsatisfied.size;
   951 	requires = set->requires.data;
   952 	pool = set->string_pool.data;
   953 
   954 	for (u = unsatisfied.data; u < end; u++) {
   955 		r = requires + *u;
   956 		printf("%s %s not satisfied\n",
   957 		       &pool[r->name], &pool[r->version]);
   958 	}
   959 
   960 	array_release(&unsatisfied);
   961 }
   962 
   963 static void
   964 add_package(struct razor_importer *importer,
   965 	    struct razor_package *package, struct razor_set *set)
   966 {
   967 	char *pool;
   968 	unsigned long *r;
   969 	struct razor_property *p, *properties;
   970 
   971 	pool = set->string_pool.data;
   972 	razor_importer_begin_package(importer,
   973 				     &pool[package->name],
   974 				     &pool[package->version]);
   975 
   976 	r = (unsigned long *) set->property_pool.data + package->requires;
   977 	properties = set->requires.data;
   978 	while (~*r) {
   979 		p = &properties[*r++];
   980 		razor_importer_add_requires(importer,
   981 					    &pool[p->name], &pool[p->version]);
   982 	}
   983 
   984 	r = (unsigned long *) set->property_pool.data + package->provides;
   985 	properties = set->provides.data;
   986 	while (~*r) {
   987 		p = &properties[*r++];
   988 		razor_importer_add_provides(importer,
   989 					    &pool[p->name], &pool[p->version]);
   990 	}
   991 
   992 	razor_importer_finish_package(importer);
   993 }
   994 
   995 /* Add packages from 'upstream' to 'set'.  The packages to add are
   996  * specified by the 'packages' array, which is a sorted list of
   997  * package indexes.  Returns a newly allocated package set.  Does not
   998  * enforce validity of the resulting package set. */
   999 
  1000 /* FIXME: We can do this in a linear sweep instead of using an
  1001  * importer and the sorting that incurs: build the new package list
  1002  * sorted, build up a map from package index in old set to package
  1003  * index in new set for both sets. ~0 means 'not in new set'.  build
  1004  * new string pool as we go, probably just re-use that part of the
  1005  * importer.  as we build the package list, fill out a bitvector of
  1006  * the properties that are referenced by the pacakges in the new
  1007  * set. then do a parallel loop through the properties and emit them
  1008  * to the new set and build a map from indices in the old set to
  1009  * indices in the new set. then loop through the packages again and
  1010  * emit the property lists. */
  1011 
  1012 struct razor_set *
  1013 razor_set_add(struct razor_set *set, struct razor_set *upstream,
  1014 	      struct array *packages)
  1015 {
  1016 	struct razor_importer *importer;
  1017 	struct razor_package *upstream_packages, *p, *s, *send;
  1018 	char *spool, *upool;
  1019 	unsigned long *u, *uend;
  1020 	int cmp;
  1021 
  1022 	importer = razor_importer_new();
  1023 	upstream_packages = upstream->packages.data;
  1024 	u = packages->data;
  1025 	uend = packages->data + packages->size;
  1026 	upool = upstream->string_pool.data;
  1027 	s = set->packages.data;
  1028 	send = set->packages.data + set->packages.size;
  1029 	spool = set->string_pool.data;
  1030 
  1031 	while (s < send) {
  1032 		p = upstream_packages + *u;
  1033 		if (u < uend)
  1034 			cmp = strcmp(&spool[s->name], &upool[p->name]);
  1035 		if (u >= uend || cmp < 0) {
  1036 			add_package(importer, s, set);
  1037 			s++;
  1038 		} else if (cmp == 0) {
  1039 			add_package(importer, p, upstream);
  1040 			s++;
  1041 			u++;
  1042 		} else {
  1043 			add_package(importer, p, upstream);
  1044 			u++;
  1045 		}
  1046 	}
  1047 
  1048 	return razor_importer_finish(importer);
  1049 }
  1050 
  1051 void
  1052 razor_set_satisfy(struct razor_set *set, struct array *unsatisfied,
  1053 		  struct razor_set *upstream, struct array *list)
  1054 {
  1055 	struct razor_property *requires, *r;
  1056 	struct razor_property *p, *pend;
  1057 	unsigned long *u, *end, *pkg, *property_pool;
  1058 	char *pool, *upool;
  1059 
  1060 	end = unsatisfied->data + unsatisfied->size;
  1061 	requires = set->requires.data;
  1062 	pool = set->string_pool.data;
  1063 
  1064 	p = upstream->provides.data;
  1065 	pend = upstream->provides.data + upstream->provides.size;
  1066 	upool = upstream->string_pool.data;
  1067 	property_pool = upstream->property_pool.data;
  1068 
  1069 	for (u = unsatisfied->data; u < end; u++) {
  1070 		r = requires + *u;
  1071 
  1072 		while (p < pend && strcmp(&pool[r->name], &upool[p->name]) > 0)
  1073 			p++;
  1074 		/* If there is more than one version of a provides,
  1075 		 * seek to the end for the highest version. */
  1076 		while (p + 1 < pend && p->name == (p + 1)->name)
  1077 			p++;
  1078 
  1079 		if (p == pend ||
  1080 		    strcmp(&pool[r->name], &upool[p->name]) != 0 ||
  1081 		    versioncmp(&pool[r->version], &upool[p->version]) > 0) {
  1082 			/* Do we need to track unsatisfiable requires
  1083 			 * as we go, or should we just do a
  1084 			 * razor_set_validate() at the end? */
  1085 		} else {
  1086 			pkg = array_add(list, sizeof *pkg);
  1087 			/* We just pull in the first package that provides */
  1088 			*pkg = property_pool[p->packages];
  1089 		}
  1090 	}	
  1091 }
  1092 
  1093 static void
  1094 find_packages(struct razor_set *set,
  1095 	      int count, const char **packages, struct array *list)
  1096 {
  1097 	struct razor_package *p;
  1098 	unsigned long *r;
  1099 	int i;
  1100 
  1101 	/* FIXME: Sort the packages. */
  1102 	for (i = 0; i < count; i++) {
  1103 		p = razor_set_get_package(set, packages[i]);
  1104 		r = array_add(list, sizeof *r);
  1105 		*r = p - (struct razor_package *) set->packages.data;
  1106 	}
  1107 }
  1108 
  1109 static void
  1110 find_all_packages(struct razor_set *set,
  1111 		  struct razor_set *upstream, struct array *list)
  1112 {
  1113 	struct razor_package *p, *u, *pend, *uend;
  1114 	unsigned long *r;
  1115 	char *pool, *upool;
  1116 
  1117 	pend = set->packages.data + set->packages.size;
  1118 	pool = set->string_pool.data;
  1119 	u = upstream->packages.data;
  1120 	uend = upstream->packages.data + upstream->packages.size;
  1121 	upool = upstream->string_pool.data;
  1122 
  1123 	for (p = set->packages.data; p < pend; p++) {
  1124 		while (u < uend && strcmp(&pool[p->name], &upool[u->name]) > 0)
  1125 			u++;
  1126 		if (strcmp(&pool[p->name], &upool[u->name]) == 0) {
  1127 			r = array_add(list, sizeof *r);
  1128 			*r = u - (struct razor_package *) upstream->packages.data;
  1129 		}
  1130 	}
  1131 }
  1132 
  1133 struct razor_set *
  1134 razor_set_update(struct razor_set *set, struct razor_set *upstream,
  1135 		 int count, const char **packages)
  1136 {
  1137 	struct razor_set *new;
  1138 	struct razor_package *p, *upackages;
  1139 	struct array list, unsatisfied;
  1140 	char *pool;
  1141 	unsigned long *u, *end;
  1142 	int total = 0;
  1143 
  1144 	array_init(&list);
  1145 	if (count > 0)
  1146 		find_packages(upstream, count, packages, &list);
  1147 	else
  1148 		find_all_packages(set, upstream, &list);
  1149 
  1150 	end = list.data + list.size;
  1151 	upackages = upstream->packages.data;
  1152 	pool = upstream->string_pool.data;
  1153 	for (u = list.data; u < end; u++) {
  1154 		p = upackages + *u;
  1155 		printf("package %s-%s set to be updated\n",
  1156 		       &pool[p->name], &pool[p->version]);
  1157 	}
  1158 	total += list.size / sizeof *u;
  1159 
  1160 	while (list.size > 0) {
  1161 		printf(" -- satisfying new requires\n");
  1162 
  1163 		new = razor_set_add(set, upstream, &list);
  1164 		array_release(&list);
  1165 		razor_set_destroy(set);
  1166 		set = new;
  1167 
  1168 		array_init(&unsatisfied);
  1169 		razor_set_validate(new, &unsatisfied);
  1170 		array_init(&list);
  1171 		razor_set_satisfy(new, &unsatisfied, upstream, &list);
  1172 		array_release(&unsatisfied);
  1173 
  1174 		end = list.data + list.size;
  1175 		upackages = upstream->packages.data;
  1176 		pool = upstream->string_pool.data;
  1177 		for (u = list.data; u < end; u++) {
  1178 			p = upackages + *u;
  1179 			printf("package %s-%s set to be updated\n",
  1180 			       &pool[p->name], &pool[p->version]);
  1181 		}
  1182 		total += list.size / sizeof *u;
  1183 	}
  1184 
  1185 	array_release(&list);
  1186 
  1187 	printf("total of %d packages set to be updated\n", total);
  1188 
  1189 	return set;
  1190 }
  1191 
  1192 void
  1193 razor_set_info(struct razor_set *set)
  1194 {
  1195 	unsigned int offset, size;
  1196 	int i;
  1197 
  1198 	for (i = 0; i < set->header->sections[i].type; i++) {
  1199 		offset = set->header->sections[i].offset;
  1200 		size = set->header->sections[i].size;
  1201 
  1202 		switch (set->header->sections[i].type) {
  1203 		case RAZOR_PACKAGES:
  1204 			printf("package section:\t%dkb\n", size / 1024);
  1205 			break;
  1206 		case RAZOR_REQUIRES:
  1207 			printf("requires section:\t%dkb\n", size / 1024);
  1208 			break;
  1209 		case RAZOR_PROVIDES:
  1210 			printf("provides section:\t%dkb\n", size / 1024);
  1211 			break;
  1212 		case RAZOR_STRING_POOL:
  1213 			printf("string pool:\t\t%dkb\n", size / 1024);
  1214 			break;
  1215 		case RAZOR_PROPERTY_POOL:
  1216 			printf("properties section:\t%dkb\n", size / 1024);
  1217 			break;
  1218 		}
  1219 	}
  1220 }
  1221 
  1222 static int
  1223 usage(void)
  1224 {
  1225 	printf("usage: razor [ import FILES | lookup <key> | "
  1226 	       "list | list-requires | list-provides | eat-yum | info ]\n");
  1227 	exit(1);
  1228 }
  1229 
  1230 static const char *repo_filename = "system.repo";
  1231 static const char rawhide_repo_filename[] = "rawhide.repo";
  1232 
  1233 int
  1234 main(int argc, const char *argv[])
  1235 {
  1236 	struct razor_set *set, *upstream;
  1237 	struct stat statbuf;
  1238 	char *repo;
  1239 
  1240 	repo = getenv("RAZOR_REPO");
  1241 	if (repo != NULL)
  1242 		repo_filename = repo;
  1243 
  1244 	if (argc < 2) {
  1245 		usage();
  1246 	} else if (strcmp(argv[1], "import") == 0) {
  1247 		if (stat("set", &statbuf) && mkdir("set", 0777)) {
  1248 			fprintf(stderr, "could not create directory 'set'\n");
  1249 			exit(-1);
  1250 		}
  1251 			
  1252 		set = razor_import_rzr_files(argc - 2, argv + 2);
  1253 
  1254 		printf("pool size: %d\n", set->string_pool.size);
  1255 		printf("pool allocation: %d\n", set->string_pool.alloc);
  1256 		printf("packages: %d\n",
  1257 		       set->packages.size / sizeof(struct razor_package));
  1258 		printf("requires: %d\n",
  1259 		       set->requires.size / sizeof(struct razor_property));
  1260 		printf("provides: %d\n",
  1261 		       set->provides.size / sizeof(struct razor_property));
  1262 
  1263 		razor_set_write(set, repo_filename);
  1264 
  1265 		razor_set_destroy(set);
  1266 	} else if (strcmp(argv[1], "list") == 0) {
  1267 		set = razor_set_open(repo_filename);
  1268 		razor_set_list(set);
  1269 		razor_set_destroy(set);
  1270 	} else if (strcmp(argv[1], "list-requires") == 0) {
  1271 		set = razor_set_open(repo_filename);
  1272 		razor_set_list_requires(set, argv[2]);
  1273 		razor_set_destroy(set);
  1274 	} else if (strcmp(argv[1], "list-provides") == 0) {
  1275 		set = razor_set_open(repo_filename);
  1276 		razor_set_list_provides(set, argv[2]);
  1277 		razor_set_destroy(set);
  1278 	} else if (strcmp(argv[1], "what-requires") == 0) {
  1279 		set = razor_set_open(repo_filename);
  1280 		razor_set_list_property_packages(set, &set->requires,
  1281 						 argv[2], argv[3]);
  1282 		razor_set_destroy(set);
  1283 	} else if (strcmp(argv[1], "what-provides") == 0) {
  1284 		set = razor_set_open(repo_filename);
  1285 		razor_set_list_property_packages(set, &set->provides,
  1286 						 argv[2], argv[3]);
  1287 		razor_set_destroy(set);
  1288 	} else if (strcmp(argv[1], "info") == 0) {
  1289 		set = razor_set_open(repo_filename);
  1290 		razor_set_info(set);
  1291 		razor_set_destroy(set);
  1292 	} else if (strcmp(argv[1], "eat-yum") == 0) {
  1293 		set = razor_set_create_from_yum_filelist(STDIN_FILENO);
  1294 		if (set == NULL)
  1295 			return 1;
  1296 		razor_set_write(set, rawhide_repo_filename);
  1297 		razor_set_destroy(set);
  1298 		printf("wrote %s\n", rawhide_repo_filename);
  1299 	} else if (strcmp(argv[1], "import-rpmdb") == 0) {
  1300 		set = razor_set_create_from_rpmdb();
  1301 		if (set == NULL)
  1302 			return 1;
  1303 		razor_set_write(set, repo_filename);
  1304 		razor_set_destroy(set);
  1305 		printf("wrote %s\n", repo_filename);
  1306 	} else if (strcmp(argv[1], "validate") == 0) {
  1307 		set = razor_set_open(repo_filename);
  1308 		if (set == NULL)
  1309 			return 1;
  1310 		razor_set_list_unsatisfied(set);
  1311 		razor_set_destroy(set);
  1312 	} else if (strcmp(argv[1], "update") == 0) {
  1313 		set = razor_set_open(repo_filename);
  1314 		upstream = razor_set_open(rawhide_repo_filename);
  1315 		if (set == NULL || upstream == NULL)
  1316 			return 1;
  1317 		set = razor_set_update(set, upstream, argc - 2, argv + 2);
  1318 		razor_set_write(set, "system-updated.repo");
  1319 		razor_set_destroy(set);
  1320 		razor_set_destroy(upstream);
  1321 		printf("wrote system-updated.repo\n");
  1322 	} else {
  1323 		usage();
  1324 	}
  1325 
  1326 	return 0;
  1327 }