razor.c
author Kristian H?gsberg <krh@redhat.com>
Wed Sep 19 23:24:57 2007 -0400 (2007-09-19)
changeset 36 266dba01582d
parent 35 42be648b39d8
child 37 094daff9a8dc
permissions -rw-r--r--
Add comment about how to implement package addition faster.
     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 		/* If there is more than one version of a provides,
   918 		 * seek to the end for the highest version. */
   919 		while (p + 1 < pend && p->name == (p + 1)->name)
   920 			p++;
   921 		if (p == pend || strcmp(&pool[r->name], &pool[p->name]) != 0 ||
   922 		    versioncmp(&pool[r->version], &pool[p->version]) > 0) {
   923 			/* FIXME: We ignore file requires for now. */
   924 			if (pool[r->name] == '/')
   925 				continue;
   926 			u = array_add(unsatisfied, sizeof *u);
   927 			*u = r - (struct razor_property *) set->requires.data;
   928 		}
   929 	}
   930 }
   931 
   932 void
   933 razor_set_list_unsatisfied(struct razor_set *set)
   934 {
   935 	struct array unsatisfied;
   936 	struct razor_property *requires, *r;
   937 	unsigned long *u, *end;
   938 	char *pool;
   939 
   940 	array_init(&unsatisfied);
   941 	razor_set_validate(set, &unsatisfied);
   942 
   943 	end = unsatisfied.data + unsatisfied.size;
   944 	requires = set->requires.data;
   945 	pool = set->string_pool.data;
   946 
   947 	for (u = unsatisfied.data; u < end; u++) {
   948 		r = requires + *u;
   949 		printf("%s %s not satisfied\n",
   950 		       &pool[r->name], &pool[r->version]);
   951 	}
   952 
   953 	array_release(&unsatisfied);
   954 }
   955 
   956 static void
   957 add_package(struct razor_importer *importer,
   958 	    struct razor_package *package, struct razor_set *set)
   959 {
   960 	char *pool;
   961 	unsigned long *r;
   962 	struct razor_property *p, *properties;
   963 
   964 	pool = set->string_pool.data;
   965 	razor_importer_begin_package(importer,
   966 				     &pool[package->name],
   967 				     &pool[package->version]);
   968 
   969 	r = (unsigned long *) set->property_pool.data + package->requires;
   970 	properties = set->requires.data;
   971 	while (~*r) {
   972 		p = &properties[*r++];
   973 		razor_importer_add_requires(importer,
   974 					    &pool[p->name], &pool[p->version]);
   975 	}
   976 
   977 	r = (unsigned long *) set->property_pool.data + package->provides;
   978 	properties = set->provides.data;
   979 	while (~*r) {
   980 		p = &properties[*r++];
   981 		razor_importer_add_provides(importer,
   982 					    &pool[p->name], &pool[p->version]);
   983 	}
   984 
   985 	razor_importer_finish_package(importer);
   986 }
   987 
   988 /* Add packages from 'upstream' to 'set'.  The packages to add are
   989  * specified by the 'packages' array, which is a sorted list of
   990  * package indexes.  Returns a newly allocated package set.  Does not
   991  * enforce validity of the resulting package set. */
   992 
   993 /* FIXME: We can do this in a linear sweep instead of using an
   994  * importer and the sorting that incurs: build the new package list
   995  * sorted, build up a map from package index in old set to package
   996  * index in new set for both sets. ~0 means 'not in new set'.  build
   997  * new string pool as we go, probably just re-use that part of the
   998  * importer.  as we build the package list, fill out a bitvector of
   999  * the properties that are referenced by the pacakges in the new
  1000  * set. then do a parallel loop through the properties and emit them
  1001  * to the new set and build a map from indices in the old set to
  1002  * indices in the new set. then loop through the packages again and
  1003  * emit the property lists. */
  1004 
  1005 struct razor_set *
  1006 razor_set_add(struct razor_set *set, struct razor_set *upstream,
  1007 	      struct array *packages)
  1008 {
  1009 	struct razor_importer *importer;
  1010 	struct razor_package *upstream_packages, *p, *s, *send;
  1011 	char *spool, *upool;
  1012 	unsigned long *u, *uend;
  1013 	int cmp;
  1014 
  1015 	importer = razor_importer_new();
  1016 	upstream_packages = upstream->packages.data;
  1017 	u = packages->data;
  1018 	uend = packages->data + packages->size;
  1019 	upool = upstream->string_pool.data;
  1020 	s = set->packages.data;
  1021 	send = set->packages.data + set->packages.size;
  1022 	spool = set->string_pool.data;
  1023 
  1024 	while (s < send) {
  1025 		p = upstream_packages + *u;
  1026 		cmp = strcmp(&spool[s->name], &upool[p->name]);
  1027 		if (cmp < 0 || u == uend) {
  1028 			add_package(importer, s, set);
  1029 			s++;
  1030 		} else if (cmp == 0) {
  1031 			add_package(importer, p, upstream);
  1032 			s++;
  1033 			u++;
  1034 		} else {
  1035 			add_package(importer, p, upstream);
  1036 			u++;
  1037 		}
  1038 	}
  1039 
  1040 	return razor_importer_finish(importer);
  1041 }
  1042 
  1043 struct razor_set *
  1044 razor_set_update(struct razor_set *set, struct razor_set *upstream,
  1045 		 int count, const char **packages)
  1046 {
  1047 	struct razor_package *p;
  1048 	struct array list;
  1049 	unsigned long *r;
  1050 	int i;
  1051 
  1052 	array_init(&list);
  1053 	for (i = 0; i < count; i++) {
  1054 		p = razor_set_get_package(upstream, packages[i]);
  1055 		r = array_add(&list, sizeof *r);
  1056 		*r = p - (struct razor_package *) upstream->packages.data;
  1057 	}
  1058 
  1059 	return razor_set_add(set, upstream, &list);
  1060 }
  1061 
  1062 void
  1063 razor_set_info(struct razor_set *set)
  1064 {
  1065 	unsigned int offset, size;
  1066 	int i;
  1067 
  1068 	for (i = 0; i < set->header->sections[i].type; i++) {
  1069 		offset = set->header->sections[i].offset;
  1070 		size = set->header->sections[i].size;
  1071 
  1072 		switch (set->header->sections[i].type) {
  1073 		case RAZOR_PACKAGES:
  1074 			printf("package section:\t%dkb\n", size / 1024);
  1075 			break;
  1076 		case RAZOR_REQUIRES:
  1077 			printf("requires section:\t%dkb\n", size / 1024);
  1078 			break;
  1079 		case RAZOR_PROVIDES:
  1080 			printf("provides section:\t%dkb\n", size / 1024);
  1081 			break;
  1082 		case RAZOR_STRING_POOL:
  1083 			printf("string pool:\t\t%dkb\n", size / 1024);
  1084 			break;
  1085 		case RAZOR_PROPERTY_POOL:
  1086 			printf("properties section:\t%dkb\n", size / 1024);
  1087 			break;
  1088 		}
  1089 	}
  1090 }
  1091 
  1092 static int
  1093 usage(void)
  1094 {
  1095 	printf("usage: razor [ import FILES | lookup <key> | "
  1096 	       "list | list-requires | list-provides | eat-yum | info ]\n");
  1097 	exit(1);
  1098 }
  1099 
  1100 static const char *repo_filename = "system.repo";
  1101 static const char rawhide_repo_filename[] = "rawhide.repo";
  1102 
  1103 int
  1104 main(int argc, const char *argv[])
  1105 {
  1106 	struct razor_set *set, *upstream, *new;
  1107 	struct stat statbuf;
  1108 	char *repo;
  1109 
  1110 	repo = getenv("RAZOR_REPO");
  1111 	if (repo != NULL)
  1112 		repo_filename = repo;
  1113 
  1114 	if (argc < 2) {
  1115 		usage();
  1116 	} else if (strcmp(argv[1], "import") == 0) {
  1117 		if (stat("set", &statbuf) && mkdir("set", 0777)) {
  1118 			fprintf(stderr, "could not create directory 'set'\n");
  1119 			exit(-1);
  1120 		}
  1121 			
  1122 		set = razor_import_rzr_files(argc - 2, argv + 2);
  1123 
  1124 		printf("pool size: %d\n", set->string_pool.size);
  1125 		printf("pool allocation: %d\n", set->string_pool.alloc);
  1126 		printf("packages: %d\n",
  1127 		       set->packages.size / sizeof(struct razor_package));
  1128 		printf("requires: %d\n",
  1129 		       set->requires.size / sizeof(struct razor_property));
  1130 		printf("provides: %d\n",
  1131 		       set->provides.size / sizeof(struct razor_property));
  1132 
  1133 		razor_set_write(set, repo_filename);
  1134 
  1135 		razor_set_destroy(set);
  1136 	} else if (strcmp(argv[1], "list") == 0) {
  1137 		set = razor_set_open(repo_filename);
  1138 		razor_set_list(set);
  1139 		razor_set_destroy(set);
  1140 	} else if (strcmp(argv[1], "list-requires") == 0) {
  1141 		set = razor_set_open(repo_filename);
  1142 		razor_set_list_requires(set, argv[2]);
  1143 		razor_set_destroy(set);
  1144 	} else if (strcmp(argv[1], "list-provides") == 0) {
  1145 		set = razor_set_open(repo_filename);
  1146 		razor_set_list_provides(set, argv[2]);
  1147 		razor_set_destroy(set);
  1148 	} else if (strcmp(argv[1], "what-requires") == 0) {
  1149 		set = razor_set_open(repo_filename);
  1150 		razor_set_list_property_packages(set, &set->requires,
  1151 						 argv[2], argv[3]);
  1152 		razor_set_destroy(set);
  1153 	} else if (strcmp(argv[1], "what-provides") == 0) {
  1154 		set = razor_set_open(repo_filename);
  1155 		razor_set_list_property_packages(set, &set->provides,
  1156 						 argv[2], argv[3]);
  1157 		razor_set_destroy(set);
  1158 	} else if (strcmp(argv[1], "info") == 0) {
  1159 		set = razor_set_open(repo_filename);
  1160 		razor_set_info(set);
  1161 		razor_set_destroy(set);
  1162 	} else if (strcmp(argv[1], "eat-yum") == 0) {
  1163 		set = razor_set_create_from_yum_filelist(STDIN_FILENO);
  1164 		if (set == NULL)
  1165 			return 1;
  1166 		razor_set_write(set, rawhide_repo_filename);
  1167 		razor_set_destroy(set);
  1168 		printf("wrote %s\n", rawhide_repo_filename);
  1169 	} else if (strcmp(argv[1], "import-rpmdb") == 0) {
  1170 		set = razor_set_create_from_rpmdb();
  1171 		if (set == NULL)
  1172 			return 1;
  1173 		razor_set_write(set, repo_filename);
  1174 		razor_set_destroy(set);
  1175 		printf("wrote %s\n", repo_filename);
  1176 	} else if (strcmp(argv[1], "validate") == 0) {
  1177 		set = razor_set_open(repo_filename);
  1178 		if (set == NULL)
  1179 			return 1;
  1180 		razor_set_list_unsatisfied(set);
  1181 		razor_set_destroy(set);
  1182 	} else if (strcmp(argv[1], "update") == 0) {
  1183 		set = razor_set_open(repo_filename);
  1184 		upstream = razor_set_open(rawhide_repo_filename);
  1185 		if (set == NULL || upstream == NULL)
  1186 			return 1;
  1187 		new = razor_set_update(set, upstream, argc - 2, argv + 2);
  1188 		razor_set_write(new, "system-updated.repo");
  1189 		razor_set_destroy(new);
  1190 		razor_set_destroy(set);
  1191 		razor_set_destroy(upstream);
  1192 	} else {
  1193 		usage();
  1194 	}
  1195 
  1196 	return 0;
  1197 }