razor.c
author Kristian H?gsberg <krh@redhat.com>
Wed Sep 19 15:26:25 2007 -0400 (2007-09-19)
changeset 30 702c01e59497
parent 29 28a13008d80b
child 32 1609eb5d93a1
permissions -rw-r--r--
Refactor the import interface and make it opaque.
     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 
    14 #include "razor.h"
    15 
    16 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
    17 
    18 struct array {
    19 	void *data;
    20 	int size, alloc;
    21 };
    22 
    23 struct razor_set_section {
    24 	unsigned int type;
    25 	unsigned int offset;
    26 	unsigned int size;
    27 };
    28 
    29 struct razor_set_header {
    30 	unsigned int magic;
    31 	unsigned int version;
    32 	struct razor_set_section sections[0];
    33 };
    34 
    35 #define RAZOR_MAGIC 0x7a7a7a7a
    36 #define RAZOR_VERSION 1
    37 
    38 #define RAZOR_PACKAGES 0
    39 #define RAZOR_REQUIRES 1
    40 #define RAZOR_PROVIDES 2
    41 #define RAZOR_STRING_POOL 3
    42 #define RAZOR_PROPERTY_POOL 4
    43 
    44 struct razor_package {
    45 	unsigned long name;
    46 	unsigned long version;
    47 	unsigned long requires;
    48 	unsigned long provides;
    49 };
    50 
    51 struct razor_property {
    52 	unsigned long name;
    53 	unsigned long version;
    54 	unsigned long packages;
    55 };
    56 
    57 struct razor_set {
    58 	struct array buckets;
    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 import_property_context requires;
    75 	struct import_property_context provides;
    76 	struct razor_package *package;
    77 	unsigned long *requires_map;
    78 	unsigned long *provides_map;
    79 };
    80 
    81 static void
    82 array_init(struct array *array)
    83 {
    84 	memset(array, 0, sizeof *array);
    85 }
    86 
    87 static void
    88 array_release(struct array *array)
    89 {
    90 	free(array->data);
    91 }
    92 
    93 static void *
    94 array_add(struct array *array, int size)
    95 {
    96 	int alloc;
    97 	void *data, *p;
    98 
    99 	if (array->alloc > 0)
   100 		alloc = array->alloc;
   101 	else
   102 		alloc = 16;
   103 
   104 	while (alloc < array->size + size)
   105 		alloc *= 2;
   106 
   107 	if (array->alloc < alloc) {
   108 		data = realloc(array->data, alloc);
   109 		if (data == NULL)
   110 			return 0;
   111 		array->data = data;
   112 		array->alloc = alloc;
   113 	}
   114 
   115 	p = array->data + array->size;
   116 	array->size += size;
   117 
   118 	return p;
   119 }
   120 
   121 static int
   122 write_to_fd(int fd, void *p, size_t size)
   123 {
   124 	int rest, len;
   125 
   126 	rest = size;
   127 	while (rest > 0) {
   128 		len = write(fd, p, rest);
   129 		if (len < 0)
   130 			return -1;
   131 		rest -= len;
   132 	}
   133 
   134 	return 0;
   135 }
   136 
   137 static void *
   138 zalloc(size_t size)
   139 {
   140 	void *p;
   141 
   142 	p = malloc(size);
   143 	memset(p, 0, size);
   144 
   145 	return p;
   146 }
   147 
   148 struct razor_set_section razor_sections[] = {
   149 	{ RAZOR_PACKAGES,	offsetof(struct razor_set, packages) },
   150 	{ RAZOR_REQUIRES,	offsetof(struct razor_set, requires) },
   151 	{ RAZOR_PROVIDES,	offsetof(struct razor_set, provides) },
   152 	{ RAZOR_STRING_POOL,	offsetof(struct razor_set, string_pool) },
   153 	{ RAZOR_PROPERTY_POOL,	offsetof(struct razor_set, property_pool) },
   154 };
   155 
   156 struct razor_set *
   157 razor_set_create(void)
   158 {
   159 	return zalloc(sizeof(struct razor_set));
   160 }
   161 
   162 struct razor_set *
   163 razor_set_open(const char *filename)
   164 {
   165 	struct razor_set *set;
   166 	struct razor_set_section *s;
   167 	struct stat stat;
   168 	struct array *array;
   169 	int fd;
   170 
   171 	set = zalloc(sizeof *set);
   172 	fd = open(filename, O_RDONLY);
   173 	if (fstat(fd, &stat) < 0)
   174 		return NULL;
   175 	set->header = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
   176 	if (set->header == MAP_FAILED) {
   177 		free(set);
   178 		return NULL;
   179 	}
   180 
   181 	for (s = set->header->sections; ~s->type; s++) {
   182 		if (s->type >= ARRAY_SIZE(razor_sections))
   183 			continue;
   184 		if (s->type != razor_sections[s->type].type)
   185 			continue;
   186 		array = (void *) set + razor_sections[s->type].offset;
   187 		array->data = (void *) set->header + s->offset;
   188 		array->size = s->size;
   189 		array->alloc = s->size;
   190 	}
   191 	close(fd);
   192 
   193 	return set;
   194 }
   195 
   196 void
   197 razor_set_destroy(struct razor_set *set)
   198 {
   199 	unsigned int size;
   200 	struct array *a;
   201 	int i;
   202 
   203 	if (set->header) {
   204 		for (i = 0; set->header->sections[i].type; i++)
   205 			;
   206 		size = set->header->sections[i].type;
   207 		munmap(set->header, size);
   208 		free(set->buckets.data);
   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 		free(set->buckets.data);
   215 	}
   216 
   217 	free(set);
   218 }
   219 
   220 static int
   221 razor_set_write(struct razor_set *set, const char *filename)
   222 {
   223 	char data[4096];
   224 	struct razor_set_header *header = (struct razor_set_header *) data;
   225 	struct array *a;
   226 	unsigned long offset;
   227 	int i, fd;
   228 
   229 	memset(data, 0, sizeof data);
   230 	header->magic = RAZOR_MAGIC;
   231 	header->version = RAZOR_VERSION;
   232 	offset = sizeof data;
   233 
   234 	for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
   235 		if (razor_sections[i].type != i)
   236 			continue;
   237 		a = (void *) set + razor_sections[i].offset;
   238 		header->sections[i].type = i;
   239 		header->sections[i].offset = offset;
   240 		header->sections[i].size = a->size;
   241 		offset += (a->size + 4095) & ~4095;
   242 	}
   243 
   244 	header->sections[i].type = ~0;
   245 	header->sections[i].offset = 0;
   246 	header->sections[i].size = 0;
   247 
   248 	fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666);
   249 	if (fd < 0)
   250 		return -1;
   251 
   252 	write_to_fd(fd, data, sizeof data);
   253 	for (i = 0; i < ARRAY_SIZE(razor_sections); i++) {
   254 		if (razor_sections[i].type != i)
   255 			continue;
   256 		a = (void *) set + razor_sections[i].offset;
   257 		write_to_fd(fd, a->data, (a->size + 4095) & ~4095);
   258 	}
   259 
   260 	close(fd);
   261 
   262 	return 0;
   263 }
   264 
   265 static unsigned int
   266 hash_string(const char *key)
   267 {
   268 	const char *p;
   269 	unsigned int hash = 0;
   270 
   271 	for (p = key; *p; p++)
   272 		hash = (hash * 617) ^ *p;
   273 
   274 	return hash;
   275 }
   276 
   277 unsigned long
   278 razor_set_lookup(struct razor_set *set, const char *key)
   279 {
   280 	unsigned int mask, start, i;
   281 	unsigned long *b;
   282 	char *pool;
   283 
   284 	pool = set->string_pool.data;
   285 	mask = set->buckets.alloc - 1;
   286 	start = hash_string(key) * sizeof(unsigned long);
   287 
   288 	for (i = 0; i < set->buckets.alloc; i += sizeof *b) {
   289 		b = set->buckets.data + ((start + i) & mask);
   290 
   291 		if (*b == 0)
   292 			return 0;
   293 
   294 		if (strcmp(key, &pool[*b]) == 0)
   295 			return *b;
   296 	}
   297 
   298 	return 0;
   299 }
   300 
   301 static unsigned long
   302 add_to_string_pool(struct razor_set *set, const char *key)
   303 {
   304 	int len;
   305 	char *p;
   306 
   307 	len = strlen(key) + 1;
   308 	p = array_add(&set->string_pool, len);
   309 	memcpy(p, key, len);
   310 
   311 	return p - (char *) set->string_pool.data;
   312 }
   313 
   314 static unsigned long
   315 add_to_property_pool(struct razor_set *set, struct array *properties)
   316 {
   317 	unsigned long  *p;
   318 
   319 	p = array_add(properties, sizeof *p);
   320 	*p = ~0ul;
   321 	p = array_add(&set->property_pool, properties->size);
   322 	memcpy(p, properties->data, properties->size);
   323 
   324 	return p - (unsigned long *) set->property_pool.data;
   325 }
   326 
   327 static void
   328 do_insert(struct razor_set *set, unsigned long value)
   329 {
   330 	unsigned int mask, start, i;
   331 	unsigned long *b;
   332 	const char *key;
   333 
   334 	key = (char *) set->string_pool.data + value;
   335 	mask = set->buckets.alloc - 1;
   336 	start = hash_string(key) * sizeof(unsigned long);
   337 
   338 	for (i = 0; i < set->buckets.alloc; i += sizeof *b) {
   339 		b = set->buckets.data + ((start + i) & mask);
   340 		if (*b == 0) {
   341 			*b = value;
   342 			break;
   343 		}
   344 	}
   345 }
   346 
   347 unsigned long
   348 razor_set_insert(struct razor_set *set, const char *key)
   349 {
   350 	unsigned long value, *buckets, *b, *end;
   351 	int alloc;
   352 
   353 	alloc = set->buckets.alloc;
   354 	array_add(&set->buckets, 4 * sizeof *buckets);
   355 	if (alloc != set->buckets.alloc) {
   356 		end = set->buckets.data + alloc;
   357 		memset(end, 0, set->buckets.alloc - alloc);
   358 		for (b = set->buckets.data; b < end; b++) {
   359 			value = *b;
   360 			if (value != 0) {
   361 				*b = 0;
   362 				do_insert(set, value);
   363 			}
   364 		}
   365 	}
   366 
   367 	value = add_to_string_pool(set, key);
   368 	do_insert (set, value);
   369 
   370 	return value;
   371 }
   372 
   373 static unsigned long
   374 razor_set_tokenize(struct razor_set *set, const char *string)
   375 {
   376 	unsigned long token;
   377 
   378 	if (string == NULL)
   379 		return razor_set_tokenize(set, "");
   380 
   381 	token = razor_set_lookup(set, string);
   382 	if (token != 0)
   383 		return token;
   384 
   385 	return razor_set_insert(set, string);
   386 }
   387 
   388 void
   389 razor_importer_begin_package(struct razor_importer *importer,
   390 			     const char *name, const char *version)
   391 {
   392 	struct razor_package *p;
   393 
   394 	p = array_add(&importer->set->packages, sizeof *p);
   395 	p->name = razor_set_tokenize(importer->set, name);
   396 	p->version = razor_set_tokenize(importer->set, version);
   397 
   398 	importer->package = p;
   399 	array_init(&importer->requires.package);
   400 	array_init(&importer->provides.package);
   401 }
   402 
   403 void
   404 razor_importer_finish_package(struct razor_importer *importer)
   405 {
   406 	struct razor_package *p;
   407 
   408 	p = importer->package;
   409 	p->requires = add_to_property_pool(importer->set,
   410 					   &importer->requires.package);
   411 	p->provides = add_to_property_pool(importer->set,
   412 					   &importer->provides.package);
   413 
   414 	array_release(&importer->requires.package);
   415 	array_release(&importer->provides.package);
   416 }
   417 
   418 static void
   419 razor_importer_add_property(struct razor_importer *importer,
   420 			    struct import_property_context *pctx,
   421 			    const char *name, const char *version)
   422 {
   423 	struct razor_property *p;
   424 	unsigned long *r;
   425 
   426 	p = array_add(pctx->all, sizeof *p);
   427 	p->name = razor_set_tokenize(importer->set, name);
   428 	p->version = razor_set_tokenize(importer->set, version);
   429 	p->packages = importer->package -
   430 		(struct razor_package *) importer->set->packages.data;
   431 
   432 	r = array_add(&pctx->package, sizeof *r);
   433 	*r = p - (struct razor_property *) pctx->all->data;
   434 }
   435 
   436 void
   437 razor_importer_add_requires(struct razor_importer *importer,
   438 			    const char *name, const char *version)
   439 {
   440 	razor_importer_add_property(importer,
   441 				    &importer->requires, name, version);
   442 }
   443 
   444 void
   445 razor_importer_add_provides(struct razor_importer *importer,
   446 			    const char *name, const char *version)
   447 {
   448 	razor_importer_add_property(importer,
   449 				    &importer->provides, name, version);
   450 }
   451 
   452 struct razor_importer *
   453 razor_importer_new(void)
   454 {
   455 	struct razor_importer *importer;
   456 
   457 	importer = zalloc(sizeof *importer);
   458 	importer->set = razor_set_create();
   459 	importer->requires.all = &importer->set->requires;
   460 	importer->provides.all = &importer->set->provides;
   461 
   462 	return importer;
   463 }
   464 
   465 typedef int (*compare_with_data_func_t)(const void *p1,
   466 					const void *p,
   467 					void *data);
   468 
   469 struct qsort_context {
   470 	size_t size;
   471 	compare_with_data_func_t compare;
   472 	void *data;
   473 };
   474 
   475 static void
   476 qsort_swap(void *p1, void *p2, size_t size)
   477 {
   478 	char buffer[size];
   479 
   480 	memcpy(buffer, p1, size);
   481 	memcpy(p1, p2, size);
   482 	memcpy(p2, buffer, size);
   483 }
   484 
   485 static void
   486 __qsort_with_data(void *base, size_t nelem, unsigned long *map,
   487 		  struct qsort_context *ctx)
   488 {
   489 	void *p, *start, *end, *pivot;
   490 	unsigned long *mp, *mstart, *mend, tmp;
   491 	int left, right, result;
   492 	size_t size = ctx->size;
   493 
   494 	p = base;
   495 	start = base;
   496 	end = base + nelem * size;
   497 	mp = map;
   498 	mstart = map;
   499 	mend = map + nelem;
   500 	pivot = base + (random() % nelem) * size;
   501 
   502 	while (p < end) {
   503 		result = ctx->compare(p, pivot, ctx->data);
   504 		if (result < 0) {
   505 			qsort_swap(p, start, size);
   506 			tmp = *mp;
   507 			*mp = *mstart;
   508 			*mstart = tmp;
   509 			if (start == pivot)
   510 				pivot = p;
   511 			start += size;
   512 			mstart++;
   513 			p += size;
   514 			mp++;
   515 		} else if (result == 0) {
   516 			p += size;
   517 			mp++;
   518 		} else {
   519  			end -= size;
   520 			mend--;
   521 			qsort_swap(p, end, size);
   522 			tmp = *mp;
   523 			*mp = *mend;
   524 			*mend = tmp;
   525 			if (end == pivot)
   526 				pivot = p;
   527 		}
   528 	}
   529 
   530 	left = (start - base) / size;
   531 	right = (base + nelem * size - end) / size;
   532 	if (left > 1)
   533 		__qsort_with_data(base, left, map, ctx);
   534 	if (right > 1)
   535 		__qsort_with_data(end, right, mend, ctx);
   536 }
   537 
   538 unsigned long *
   539 qsort_with_data(void *base, size_t nelem, size_t size,
   540 		compare_with_data_func_t compare, void *data)
   541 {
   542 	struct qsort_context ctx;
   543 	unsigned long *map;
   544 	int i;
   545 
   546 	ctx.size = size;
   547 	ctx.compare = compare;
   548 	ctx.data = data;
   549 
   550 	map = malloc(nelem * sizeof (unsigned long));
   551 	for (i = 0; i < nelem; i++)
   552 		map[i] = i;
   553 
   554 	__qsort_with_data(base, nelem, map, &ctx);
   555 
   556 	return map;
   557 }
   558 
   559 static int
   560 compare_packages(const void *p1, const void *p2, void *data)
   561 {
   562 	const struct razor_package *pkg1 = p1, *pkg2 = p2;
   563 	struct razor_set *set = data;
   564 	char *pool = set->string_pool.data;
   565 
   566 	if (pkg1->name == pkg2->name)
   567 		return 0;
   568 	else
   569 		return strcmp(&pool[pkg1->name], &pool[pkg2->name]);
   570 }
   571 
   572 static int
   573 compare_properties(const void *p1, const void *p2, void *data)
   574 {
   575 	const struct razor_property *prop1 = p1, *prop2 = p2;
   576 	struct razor_set *set = data;
   577 	char *pool = set->string_pool.data;
   578 
   579 	if (prop1->name == prop2->name)
   580 		return strcmp(&pool[prop1->version], &pool[prop2->version]);
   581 	else
   582 		return strcmp(&pool[prop1->name], &pool[prop2->name]);
   583 }
   584 
   585 static unsigned long *
   586 uniqueify_properties(struct razor_set *set, struct array *properties)
   587 {
   588 	struct razor_property *rp, *up, *rp_end;
   589 	struct array *pkgs, *p;
   590 	unsigned long *map, *rmap, *r;
   591 	int i, count, unique;
   592 
   593 	count = properties->size / sizeof(struct razor_property);
   594 	map = qsort_with_data(properties->data,
   595 			      count,
   596 			      sizeof(struct razor_property),
   597 			      compare_properties,
   598 			      set);
   599 
   600 	rp_end = properties->data + properties->size;
   601 	rmap = malloc(count * sizeof *map);
   602 	pkgs = zalloc(count * sizeof *pkgs);
   603 	for (rp = properties->data, up = rp, i = 0; rp < rp_end; rp++, i++) {
   604 		if (rp->name != up->name || rp->version != up->version) {
   605 			up++;
   606 			up->name = rp->name;
   607 			up->version = rp->version;
   608 		}
   609 
   610 		unique = up - (struct razor_property *) properties->data;
   611 		rmap[map[i]] = unique;
   612 		r = array_add(&pkgs[unique], sizeof *r);
   613 		*r = rp->packages;
   614 	}
   615 	free(map);
   616 
   617 	up++;
   618 	properties->size = (void *) up - properties->data;
   619 	rp_end = up;
   620 	for (rp = properties->data, p = pkgs; rp < rp_end; rp++, p++) {
   621 		rp->packages = add_to_property_pool(set, p);
   622 		array_release(p);
   623 	}
   624 
   625 	free(pkgs);
   626 
   627 	return rmap;
   628 }
   629 
   630 static void
   631 remap_package_links(struct razor_importer *importer)
   632 {
   633 	struct razor_package *p, *end;
   634 	unsigned long *pool, *r;
   635 
   636 	pool = importer->set->property_pool.data;
   637 	end = importer->set->packages.data + importer->set->packages.size;
   638 	for (p = importer->set->packages.data; p < end; p++) {
   639 		for (r = &pool[p->requires]; ~*r; r++)
   640 			*r = importer->requires_map[*r];
   641 		for (r = &pool[p->provides]; ~*r; r++)
   642 			*r = importer->provides_map[*r];
   643 	}
   644 }
   645 
   646 static void
   647 remap_property_links(struct razor_importer *importer, unsigned long *map)
   648 {
   649 	struct razor_property *p, *end;
   650 	struct razor_package *rp;
   651 	unsigned long *pool, *r, *rmap;
   652 	int i, count;
   653 
   654 	pool = importer->set->property_pool.data;
   655 	count = importer->set->packages.size / sizeof(struct razor_package);
   656 	rmap = malloc(count * sizeof *map);
   657 	rp = importer->set->packages.data;
   658 	for (i = 0; i < count; i++)
   659 		rmap[map[i]] = i;
   660 
   661 	/* FIXME: This will break if we implement package list sharing
   662 	 * for all properties, since we'll remap those lists more than
   663 	 * once. We should just have a separate pool for property
   664 	 * lists and a separate pool for package lists and remap it as
   665 	 * a flat pool.  Right now, as property lists and package
   666 	 * lists are mixed, we can't do that. */
   667 
   668 	end = importer->set->requires.data + importer->set->requires.size;
   669 	for (p = importer->set->requires.data; p < end; p++)
   670 		for (r = &pool[p->packages]; ~*r; r++)
   671 			*r = rmap[*r];
   672 
   673 	end = importer->set->provides.data + importer->set->provides.size;
   674 	for (p = importer->set->provides.data; p < end; p++)
   675 		for (r = &pool[p->packages]; ~*r; r++)
   676 			*r = rmap[*r];
   677 
   678 	free(rmap);
   679 }
   680 
   681 struct razor_set *
   682 razor_importer_finish(struct razor_importer *importer)
   683 {
   684 	struct razor_set *set;
   685 	unsigned long *map;
   686 	int count;
   687 
   688 	importer->requires_map = uniqueify_properties(importer->set,
   689 						      importer->requires.all);
   690 	importer->provides_map = uniqueify_properties(importer->set,
   691 						      importer->provides.all);
   692 	remap_package_links(importer);
   693 	free(importer->requires_map);
   694 	free(importer->provides_map);
   695 
   696 	count = importer->set->packages.size / sizeof(struct razor_package);
   697 	map = qsort_with_data(importer->set->packages.data,
   698 			      count,
   699 			      sizeof(struct razor_package),
   700 			      compare_packages,
   701 			      importer->set);
   702 	remap_property_links(importer, map);
   703 	free(map);
   704 
   705 	set = importer->set;
   706 	free(importer);
   707 
   708 	return set;
   709 }
   710 
   711 void
   712 razor_set_list(struct razor_set *set)
   713 {
   714 	struct razor_package *p, *end;
   715 	char *pool;
   716 
   717 	pool = set->string_pool.data;
   718 	end = set->packages.data + set->packages.size;
   719 	for (p = set->packages.data; p < end; p++)
   720 		printf("%s %s\n", &pool[p->name], &pool[p->version]);
   721 }
   722 
   723 struct razor_set *bsearch_set;
   724 
   725 static int
   726 compare_package_name(const void *key, const void *data)
   727 {
   728 	const struct razor_package *p = data;
   729 	char *pool;
   730 
   731 	pool = bsearch_set->string_pool.data;
   732 
   733 	return strcmp(key, &pool[p->name]);
   734 }
   735 
   736 struct razor_package *
   737 razor_set_get_package(struct razor_set *set, const char *package)
   738 {
   739 	bsearch_set = set;
   740 	return bsearch(package, set->packages.data,
   741 		       set->packages.size / sizeof(struct razor_package),
   742 		       sizeof(struct razor_package), compare_package_name);
   743 }
   744 
   745 static int
   746 compare_property_name(const void *key, const void *data)
   747 {
   748 	const struct razor_property *p = data;
   749 	char *pool;
   750 
   751 	pool = bsearch_set->string_pool.data;
   752 
   753 	return strcmp(key, &pool[p->name]);
   754 }
   755 
   756 struct razor_property *
   757 razor_set_get_property(struct razor_set *set,
   758 		       struct array *properties,
   759 		       const char *property)
   760 {
   761 	struct razor_property *p, *start;
   762 
   763 	bsearch_set = set;
   764 	p = bsearch(property, properties->data,
   765 		    properties->size / sizeof(struct razor_property),
   766 		    sizeof(struct razor_property), compare_property_name);
   767 
   768 	start = properties->data;
   769 	while (p > start && (p - 1)->name == p->name)
   770 		p--;
   771 
   772 	return p;
   773 }
   774 
   775 static void
   776 razor_set_list_all_properties(struct razor_set *set, struct array *properties)
   777 {
   778 	struct razor_property *p, *end;
   779 	char *pool;
   780 
   781 	pool = set->string_pool.data;
   782 	end = properties->data + properties->size;
   783 	for (p = properties->data; p < end; p++)
   784 		printf("%s %s\n", &pool[p->name], &pool[p->version]);
   785 }
   786 
   787 void
   788 razor_set_list_requires(struct razor_set *set, const char *name)
   789 {
   790 	struct razor_property *p, *requires;
   791 	struct razor_package *package;
   792 	unsigned long *r;
   793 	char *pool;
   794 
   795 	if (name) {
   796 		package = razor_set_get_package(set, name);
   797 		r = (unsigned long *) set->property_pool.data +
   798 			package->requires;
   799 		requires = set->requires.data;
   800 		pool = set->string_pool.data;
   801 		while (~*r) {
   802 			p = &requires[*r++];
   803 			printf("%s %s\n", &pool[p->name], &pool[p->version]);
   804 		}
   805 	} else
   806 		razor_set_list_all_properties(set, &set->requires);
   807 }
   808 
   809 void
   810 razor_set_list_provides(struct razor_set *set, const char *name)
   811 {
   812 	struct razor_property *p, *provides;
   813 	struct razor_package *package;
   814 	unsigned long *r;
   815 	char *pool;
   816 
   817 	if (name) {
   818 		package = razor_set_get_package(set, name);
   819 		r = (unsigned long *) set->property_pool.data +
   820 			package->provides;
   821 		provides = set->provides.data;
   822 		pool = set->string_pool.data;
   823 		while (~*r) {
   824 			p = &provides[*r++];
   825 			printf("%s %s\n", &pool[p->name], &pool[p->version]);
   826 		}
   827 	} else 
   828 		razor_set_list_all_properties(set, &set->provides);
   829 }
   830 
   831 void
   832 razor_set_list_property_packages(struct razor_set *set,
   833 				 struct array *properties,
   834 				 const char *name,
   835 				 const char *version)
   836 {
   837 	struct razor_property *property, *end;
   838 	struct razor_package *p, *packages;
   839 	unsigned long *r;
   840 	char *pool;
   841 
   842 	if (name == NULL)
   843 		return;
   844 
   845 	property = razor_set_get_property(set, properties, name);
   846 	packages = set->packages.data;
   847 	pool = set->string_pool.data;
   848 	end = properties->data + properties->size;
   849 	while (property < end && strcmp(name, &pool[property->name]) == 0) {
   850 		if (version && strcmp(version, &pool[property->version]) != 0)
   851 			goto next;
   852 		r = (unsigned long *)
   853 			set->property_pool.data + property->packages;
   854 		while (~*r) {
   855 			p = &packages[*r++];
   856 			printf("%s %s\n",
   857 			       &pool[p->name], &pool[p->version]);
   858 		}
   859 	next:
   860 		property++;
   861 	}
   862 }
   863 
   864 void
   865 razor_set_validate(struct razor_set *set, struct array *unsatisfied)
   866 {
   867 	struct razor_property *r, *p, *rend, *pend;
   868 	unsigned long *u;
   869 	char *pool;
   870 
   871 	r = set->requires.data;
   872 	p = set->provides.data;
   873 	rend = set->requires.data + set->requires.size;
   874 	pend = set->provides.data + set->provides.size;
   875 	pool = set->string_pool.data;
   876 	
   877 	while (r < rend) {
   878 		while (p < pend && strcmp(&pool[r->name], &pool[p->name]) > 0)
   879 			p++;
   880 		if (p == pend || strcmp(&pool[r->name], &pool[p->name]) != 0) {
   881 			u = array_add(unsatisfied, sizeof *u);
   882 			*u = r - (struct razor_property *) set->requires.data;
   883 		}
   884 		r++;
   885 	}
   886 }
   887 
   888 void
   889 razor_set_list_unsatisfied(struct razor_set *set)
   890 {
   891 	struct array unsatisfied;
   892 	struct razor_property *requires, *r;
   893 	unsigned long *u, *end;
   894 	char *pool;
   895 
   896 	array_init(&unsatisfied);
   897 	razor_set_validate(set, &unsatisfied);
   898 
   899 	end = unsatisfied.data + unsatisfied.size;
   900 	requires = set->requires.data;
   901 	pool = set->string_pool.data;
   902 
   903 	for (u = unsatisfied.data; u < end; u++) {
   904 		r = requires + *u;
   905 		printf("%s %s not satisfied\n",
   906 		       &pool[r->name], &pool[r->version]);
   907 	}
   908 
   909 	array_release(&unsatisfied);
   910 }
   911 
   912 void
   913 razor_set_info(struct razor_set *set)
   914 {
   915 	unsigned int offset, size;
   916 	int i;
   917 
   918 	for (i = 0; i < set->header->sections[i].type; i++) {
   919 		offset = set->header->sections[i].offset;
   920 		size = set->header->sections[i].size;
   921 
   922 		switch (set->header->sections[i].type) {
   923 		case RAZOR_PACKAGES:
   924 			printf("package section:\t%dkb\n", size / 1024);
   925 			break;
   926 		case RAZOR_REQUIRES:
   927 			printf("requires section:\t%dkb\n", size / 1024);
   928 			break;
   929 		case RAZOR_PROVIDES:
   930 			printf("provides section:\t%dkb\n", size / 1024);
   931 			break;
   932 		case RAZOR_STRING_POOL:
   933 			printf("string pool:\t\t%dkb\n", size / 1024);
   934 			break;
   935 		case RAZOR_PROPERTY_POOL:
   936 			printf("properties section:\t%dkb\n", size / 1024);
   937 			break;
   938 		}
   939 	}
   940 }
   941 
   942 static int
   943 usage(void)
   944 {
   945 	printf("usage: razor [ import FILES | lookup <key> | "
   946 	       "list | list-requires | list-provides | eat-yum | info ]\n");
   947 	exit(1);
   948 }
   949 
   950 static const char *repo_filename = "system.repo";
   951 static const char rawhide_repo_filename[] = "rawhide.repo";
   952 
   953 int
   954 main(int argc, const char *argv[])
   955 {
   956 	struct razor_set *set;
   957 	struct stat statbuf;
   958 	char *repo;
   959 
   960 	repo = getenv("RAZOR_REPO");
   961 	if (repo != NULL)
   962 		repo_filename = repo;
   963 
   964 	if (argc < 2) {
   965 		usage();
   966 	} else if (strcmp(argv[1], "import") == 0) {
   967 		if (stat("set", &statbuf) && mkdir("set", 0777)) {
   968 			fprintf(stderr, "could not create directory 'set'\n");
   969 			exit(-1);
   970 		}
   971 			
   972 		set = razor_import_rzr_files(argc - 2, argv + 2);
   973 
   974 		printf("bucket allocation: %d\n", set->buckets.alloc);
   975 		printf("pool size: %d\n", set->string_pool.size);
   976 		printf("pool allocation: %d\n", set->string_pool.alloc);
   977 		printf("packages: %d\n",
   978 		       set->packages.size / sizeof(struct razor_package));
   979 		printf("requires: %d\n",
   980 		       set->requires.size / sizeof(struct razor_property));
   981 		printf("provides: %d\n",
   982 		       set->provides.size / sizeof(struct razor_property));
   983 
   984 		razor_set_write(set, repo_filename);
   985 
   986 		razor_set_destroy(set);
   987 	} else if (strcmp(argv[1], "lookup") == 0) {
   988 		set = razor_set_open(repo_filename);
   989 		printf("%s is %lu\n", argv[2],
   990 		       razor_set_lookup(set, argv[2]));
   991 		razor_set_destroy(set);
   992 	} else if (strcmp(argv[1], "list") == 0) {
   993 		set = razor_set_open(repo_filename);
   994 		razor_set_list(set);
   995 		razor_set_destroy(set);
   996 	} else if (strcmp(argv[1], "list-requires") == 0) {
   997 		set = razor_set_open(repo_filename);
   998 		razor_set_list_requires(set, argv[2]);
   999 		razor_set_destroy(set);
  1000 	} else if (strcmp(argv[1], "list-provides") == 0) {
  1001 		set = razor_set_open(repo_filename);
  1002 		razor_set_list_provides(set, argv[2]);
  1003 		razor_set_destroy(set);
  1004 	} else if (strcmp(argv[1], "what-requires") == 0) {
  1005 		set = razor_set_open(repo_filename);
  1006 		razor_set_list_property_packages(set, &set->requires,
  1007 						 argv[2], argv[3]);
  1008 		razor_set_destroy(set);
  1009 	} else if (strcmp(argv[1], "what-provides") == 0) {
  1010 		set = razor_set_open(repo_filename);
  1011 		razor_set_list_property_packages(set, &set->provides,
  1012 						 argv[2], argv[3]);
  1013 		razor_set_destroy(set);
  1014 	} else if (strcmp(argv[1], "info") == 0) {
  1015 		set = razor_set_open(repo_filename);
  1016 		razor_set_info(set);
  1017 		razor_set_destroy(set);
  1018 	} else if (strcmp(argv[1], "eat-yum") == 0) {
  1019 		set = razor_set_create_from_yum_filelist(STDIN_FILENO);
  1020 		if (set == NULL)
  1021 			return 1;
  1022 		razor_set_write(set, rawhide_repo_filename);
  1023 		razor_set_destroy(set);
  1024 		printf("wrote %s\n", rawhide_repo_filename);
  1025 	} else if (strcmp(argv[1], "import-rpmdb") == 0) {
  1026 		set = razor_set_create_from_rpmdb();
  1027 		if (set == NULL)
  1028 			return 1;
  1029 		razor_set_write(set, repo_filename);
  1030 		razor_set_destroy(set);
  1031 		printf("wrote %s\n", repo_filename);
  1032 	} else if (strcmp(argv[1], "validate") == 0) {
  1033 		set = razor_set_open(repo_filename);
  1034 		if (set == NULL)
  1035 			return 1;
  1036 		razor_set_list_unsatisfied(set);
  1037 		razor_set_destroy(set);
  1038 	} else {
  1039 		usage();
  1040 	}
  1041 
  1042 	return 0;
  1043 }