razor.c
author Kristian H?gsberg <krh@redhat.com>
Tue Sep 11 19:25:32 2007 -0400 (2007-09-11)
changeset 18 b2bf852ca8d1
parent 17 67029e580a0e
child 19 d2a716dd92bd
permissions -rw-r--r--
Implement property (requires/provides) to package mapping.
     1 #define _GNU_SOURCE
     2 
     3 #include <stdlib.h>
     4 #include <stdio.h>
     5 #include <string.h>
     6 #include <sys/types.h>
     7 #include <sys/stat.h>
     8 #include <sys/mman.h>
     9 #include <unistd.h>
    10 #include <fcntl.h>
    11 #include <errno.h>
    12 
    13 #include <expat.h>
    14 #include "sha1.h"
    15 
    16 struct array {
    17 	void *data;
    18 	int size, alloc;
    19 };
    20 
    21 static void
    22 array_init(struct array *array)
    23 {
    24 	memset(array, 0, sizeof *array);
    25 }
    26 
    27 static void
    28 array_release(struct array *array)
    29 {
    30 	free(array->data);
    31 }
    32 
    33 static void *
    34 array_add(struct array *array, int size)
    35 {
    36 	int alloc;
    37 	void *data, *p;
    38 
    39 	if (array->alloc > 0)
    40 		alloc = array->alloc;
    41 	else
    42 		alloc = 16;
    43 
    44 	while (alloc < array->size + size)
    45 		alloc *= 2;
    46 
    47 	if (array->alloc < alloc) {
    48 		data = realloc(array->data, alloc);
    49 		if (data == NULL)
    50 			return 0;
    51 		array->data = data;
    52 		array->alloc = alloc;
    53 	}
    54 
    55 	p = array->data + array->size;
    56 	array->size += size;
    57 
    58 	return p;
    59 }
    60 
    61 static int
    62 write_to_fd(int fd, void *p, size_t size)
    63 {
    64 	int rest, len;
    65 
    66 	rest = size;
    67 	while (rest > 0) {
    68 		len = write(fd, p, rest);
    69 		if (len < 0)
    70 			return -1;
    71 		rest -= len;
    72 	}
    73 
    74 	return 0;
    75 }
    76 
    77 static int
    78 write_to_file(const char *filename, void *p, size_t size)
    79 {
    80 	int fd, err;
    81 
    82 	fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666);
    83 	if (fd < 0)
    84 		return -1;
    85 	err = write_to_fd(fd, p, size);
    86 	close(fd);
    87 
    88 	return err;
    89 }
    90 
    91 static void *
    92 zalloc(size_t size)
    93 {
    94 	void *p;
    95 
    96 	p = malloc(size);
    97 	memset(p, 0, size);
    98 
    99 	return p;
   100 }
   101 
   102 struct razor_set_header {
   103 	unsigned int magic;
   104 	unsigned int version;
   105 	struct { unsigned int type, offset, size; } sections[0];
   106 };
   107 
   108 #define RAZOR_MAGIC 0x7a7a7a7a
   109 #define RAZOR_VERSION 1
   110 
   111 #define RAZOR_BUCKETS 1
   112 #define RAZOR_STRINGS 2
   113 #define RAZOR_PACKAGES 3
   114 #define RAZOR_REQUIRES 4
   115 #define RAZOR_PROVIDES 5
   116 #define RAZOR_PROPERTIES 6
   117 
   118 struct razor_package {
   119 	unsigned long name;
   120 	unsigned long version;
   121 	unsigned long requires;
   122 	unsigned long provides;
   123 };
   124 
   125 struct razor_property {
   126 	unsigned long name;
   127 	unsigned long version;
   128 	unsigned long packages;
   129 };
   130 
   131 struct razor_set {
   132 	struct array buckets;
   133 	struct array string_pool;
   134 	struct array property_pool;
   135  	struct array packages;
   136  	struct array requires;
   137  	struct array provides;
   138 	struct razor_set_header *header;
   139 };
   140 
   141 struct razor_set *
   142 razor_set_create(void)
   143 {
   144 	return zalloc(sizeof(struct razor_set));
   145 }
   146 
   147 struct razor_set *
   148 razor_set_open(const char *filename)
   149 {
   150 	struct razor_set *set;
   151 	struct stat stat;
   152 	unsigned int size, offset;
   153 	int fd, i;
   154 
   155 	set = zalloc(sizeof *set);
   156 	fd = open(filename, O_RDONLY);
   157 	if (fstat(fd, &stat) < 0)
   158 		return NULL;
   159 	set->header = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
   160 	if (set->header == MAP_FAILED) {
   161 		free(set);
   162 		return NULL;
   163 	}
   164 
   165 	for (i = 0; i < set->header->sections[i].type; i++) {
   166 		offset = set->header->sections[i].offset;
   167 		size = set->header->sections[i].size;
   168 
   169 		switch (set->header->sections[i].type) {
   170 		case RAZOR_STRINGS:
   171 			set->string_pool.data = (void *) set->header + offset;
   172 			set->string_pool.size = size;
   173 			set->string_pool.alloc = size;
   174 			break;
   175 		case RAZOR_PACKAGES:
   176 			set->packages.data = (void *) set->header + offset;
   177 			set->packages.size = size;
   178 			set->packages.size = size;
   179 			break;
   180 		case RAZOR_REQUIRES:
   181 			set->requires.data = (void *) set->header + offset;
   182 			set->requires.size = size;
   183 			set->requires.size = size;
   184 			break;
   185 		case RAZOR_PROVIDES:
   186 			set->provides.data = (void *) set->header + offset;
   187 			set->provides.size = size;
   188 			set->provides.size = size;
   189 			break;
   190 		case RAZOR_PROPERTIES:
   191 			set->property_pool.data = (void *) set->header + offset;
   192 			set->property_pool.size = size;
   193 			set->property_pool.size = size;
   194 			break;
   195 		}
   196 	}
   197 	close(fd);
   198 
   199 	return set;
   200 }
   201 
   202 void
   203 razor_set_destroy(struct razor_set *set)
   204 {
   205 	unsigned int size;
   206 	int i;
   207 
   208 	if (set->header) {
   209 		for (i = 0; set->header->sections[i].type; i++)
   210 			;
   211 		size = set->header->sections[i].type;
   212 		munmap(set->header, size);
   213 		free(set->buckets.data);
   214 	} else {
   215 		free(set->buckets.data);
   216 		free(set->string_pool.data);
   217 		free(set->packages.data);
   218 		free(set->requires.data);
   219 		free(set->provides.data);
   220 		free(set->property_pool.data);
   221 	}
   222 
   223 	free(set);
   224 }
   225 
   226 static int
   227 razor_set_write(struct razor_set *set, const char *filename)
   228 {
   229 	char data[4096];
   230 	struct razor_set_header *header = (struct razor_set_header *) data;
   231 	unsigned long offset;
   232 	int i, fd;
   233 	struct { int type; struct array *array; } sections[] = {
   234 		{ RAZOR_STRINGS, &set->string_pool },
   235 		{ RAZOR_PACKAGES, &set->packages },
   236 		{ RAZOR_REQUIRES, &set->requires },
   237 		{ RAZOR_PROVIDES, &set->provides },
   238 		{ RAZOR_PROPERTIES, &set->property_pool },
   239 		{ 0 }
   240 	};
   241 
   242 	memset(data, 0, sizeof data);
   243 	header->magic = RAZOR_MAGIC;
   244 	header->version = RAZOR_VERSION;
   245 	offset = sizeof data;
   246 
   247 	for (i = 0; sections[i].type != 0; i++) {
   248 		header->sections[i].type = sections[i].type;
   249 		header->sections[i].offset = offset;
   250 		header->sections[i].size = sections[i].array->size;
   251 		offset += (sections[i].array->size + 4095) & ~4095;
   252 	}
   253 
   254 	header->sections[i].type = 0;
   255 	header->sections[i].offset = 0;
   256 	header->sections[i].size = 0;
   257 
   258 	fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666);
   259 	if (fd < 0)
   260 		return -1;
   261 
   262 	write_to_fd(fd, data, sizeof data);
   263 	for (i = 0; sections[i].type != 0; i++)
   264 		write_to_fd(fd, sections[i].array->data,
   265 			    (sections[i].array->size + 4095) & ~4095);
   266 
   267 	close(fd);
   268 
   269 	return 0;
   270 }
   271 
   272 static unsigned int
   273 hash_string(const char *key)
   274 {
   275 	const char *p;
   276 	unsigned int hash = 0;
   277 
   278 	for (p = key; *p; p++)
   279 		hash = (hash * 617) ^ *p;
   280 
   281 	return hash;
   282 }
   283 
   284 unsigned long
   285 razor_set_lookup(struct razor_set *set, const char *key)
   286 {
   287 	unsigned int mask, start, i;
   288 	unsigned long *b;
   289 	char *pool;
   290 
   291 	pool = set->string_pool.data;
   292 	mask = set->buckets.alloc - 1;
   293 	start = hash_string(key) * sizeof(unsigned long);
   294 
   295 	for (i = 0; i < set->buckets.alloc; i += sizeof *b) {
   296 		b = set->buckets.data + ((start + i) & mask);
   297 
   298 		if (*b == 0)
   299 			return 0;
   300 
   301 		if (strcmp(key, &pool[*b]) == 0)
   302 			return *b;
   303 	}
   304 
   305 	return 0;
   306 }
   307 
   308 static unsigned long
   309 add_to_string_pool(struct razor_set *set, const char *key)
   310 {
   311 	int len;
   312 	char *p;
   313 
   314 	len = strlen(key) + 1;
   315 	p = array_add(&set->string_pool, len);
   316 	memcpy(p, key, len);
   317 
   318 	return p - (char *) set->string_pool.data;
   319 }
   320 
   321 static unsigned long
   322 add_to_property_pool(struct razor_set *set, struct array *properties)
   323 {
   324 	unsigned long  *p;
   325 
   326 	p = array_add(properties, sizeof *p);
   327 	*p = ~0ul;
   328 	p = array_add(&set->property_pool, properties->size);
   329 	memcpy(p, properties->data, properties->size);
   330 
   331 	return p - (unsigned long *) set->property_pool.data;
   332 }
   333 
   334 static void
   335 do_insert(struct razor_set *set, unsigned long value)
   336 {
   337 	unsigned int mask, start, i;
   338 	unsigned long *b;
   339 	const char *key;
   340 
   341 	key = (char *) set->string_pool.data + value;
   342 	mask = set->buckets.alloc - 1;
   343 	start = hash_string(key) * sizeof(unsigned long);
   344 
   345 	for (i = 0; i < set->buckets.alloc; i += sizeof *b) {
   346 		b = set->buckets.data + ((start + i) & mask);
   347 		if (*b == 0) {
   348 			*b = value;
   349 			break;
   350 		}
   351 	}
   352 }
   353 
   354 unsigned long
   355 razor_set_insert(struct razor_set *set, const char *key)
   356 {
   357 	unsigned long value, *buckets, *b, *end;
   358 	int alloc;
   359 
   360 	alloc = set->buckets.alloc;
   361 	array_add(&set->buckets, 4 * sizeof *buckets);
   362 	if (alloc != set->buckets.alloc) {
   363 		end = set->buckets.data + alloc;
   364 		memset(end, 0, set->buckets.alloc - alloc);
   365 		for (b = set->buckets.data; b < end; b++) {
   366 			value = *b;
   367 			if (value != 0) {
   368 				*b = 0;
   369 				do_insert(set, value);
   370 			}
   371 		}
   372 	}
   373 
   374 	value = add_to_string_pool(set, key);
   375 	do_insert (set, value);
   376 
   377 	return value;
   378 }
   379 
   380 unsigned long
   381 razor_set_tokenize(struct razor_set *set, const char *string)
   382 {
   383 	unsigned long token;
   384 
   385 	if (string == NULL)
   386 		return razor_set_tokenize(set, "");
   387 
   388 	token = razor_set_lookup(set, string);
   389 	if (token != 0)
   390 		return token;
   391 
   392 	return razor_set_insert(set, string);
   393 }
   394 
   395 struct import_property_context {
   396 	struct array all;
   397 	struct array package;
   398 };
   399 
   400 struct import_context {
   401 	struct razor_set *set;
   402 	struct import_property_context requires;
   403 	struct import_property_context provides;
   404 	struct array packages;
   405 	struct import_package *package;
   406 	unsigned long *requires_map;
   407 	unsigned long *provides_map;
   408 };
   409 
   410 struct import_package {
   411 	unsigned long name;
   412 	unsigned long version;
   413 	unsigned long requires;
   414 	unsigned long provides;
   415 	unsigned long index;
   416 };
   417 
   418 struct import_property {
   419 	unsigned long name;
   420 	unsigned long version;
   421 	unsigned long package;
   422 	unsigned long index;
   423 	unsigned long unique_index;
   424 };
   425 
   426 static void
   427 import_context_add_package(struct import_context *ctx,
   428 			   const char *name, const char *version)
   429 {
   430 	struct import_package *p;
   431 
   432 	p = array_add(&ctx->packages, sizeof *p);
   433 	p->name = razor_set_tokenize(ctx->set, name);
   434 	p->version = razor_set_tokenize(ctx->set, version);
   435 	p->index = p - (struct import_package *) ctx->packages.data;
   436 
   437 	ctx->package = p;
   438 	array_init(&ctx->requires.package);
   439 	array_init(&ctx->provides.package);
   440 }
   441 
   442 void
   443 import_context_finish_package(struct import_context *ctx)
   444 {
   445 	struct import_package *p;
   446 
   447 	p = ctx->package;
   448 	p->requires = add_to_property_pool(ctx->set, &ctx->requires.package);
   449 	p->provides = add_to_property_pool(ctx->set, &ctx->provides.package);
   450 
   451 	array_release(&ctx->requires.package);
   452 	array_release(&ctx->provides.package);
   453 }
   454 
   455 static void
   456 import_context_add_property(struct import_context *ctx,
   457 			    struct import_property_context *pctx,
   458 			    const char *name, const char *version)
   459 {
   460 	struct import_property *p;
   461 	unsigned long *r;
   462 
   463 	p = array_add(&pctx->all, sizeof *p);
   464 	p->name = razor_set_tokenize(ctx->set, name);
   465 	p->version = razor_set_tokenize(ctx->set, version);
   466 	p->package = ctx->package->index;
   467 	p->index = p - (struct import_property *) pctx->all.data;
   468 
   469 	r = array_add(&pctx->package, sizeof *r);
   470 	*r = p->index;
   471 }
   472 
   473 static void
   474 parse_package(struct import_context *ctx, const char **atts, void *data)
   475 {
   476 	const char *name = NULL, *version = NULL;
   477 	int i;
   478 
   479 	for (i = 0; atts[i]; i += 2) {
   480 		if (strcmp(atts[i], "name") == 0)
   481 			name = atts[i + 1];
   482 		else if (strcmp(atts[i], "version") == 0)
   483 			version = atts[i + 1];
   484 	}
   485 
   486 	if (name == NULL || version == NULL) {
   487 		fprintf(stderr, "invalid package tag, "
   488 			"missing name or version attributes\n");
   489 		return;
   490 	}
   491 
   492 	import_context_add_package(ctx, name, version);
   493 }
   494 
   495 static void
   496 parse_property(struct import_context *ctx, const char **atts, void *data)
   497 {
   498 	const char *name = NULL, *version = NULL;
   499 	int i;
   500 
   501 	for (i = 0; atts[i]; i += 2) {
   502 		if (strcmp(atts[i], "name") == 0)
   503 			name = atts[i + 1];
   504 		if (strcmp(atts[i], "version") == 0)
   505 			version = atts[i + 1];
   506 	}
   507 	
   508 	if (name == NULL) {
   509 		fprintf(stderr, "invalid tag, missing name attribute\n");
   510 		return;
   511 	}
   512 
   513 	import_context_add_property(ctx, data, name, version);
   514 }
   515 
   516 static void
   517 start_element(void *data, const char *name, const char **atts)
   518 {
   519 	struct import_context *ctx = data;
   520 
   521 	if (strcmp(name, "package") == 0)
   522 		parse_package(ctx, atts, NULL);
   523 	else if (strcmp(name, "requires") == 0)
   524 		parse_property(ctx, atts, &ctx->requires);
   525 	else if (strcmp(name, "provides") == 0)
   526 		parse_property(ctx, atts, &ctx->provides);
   527 }
   528 
   529 static void
   530 end_element (void *data, const char *name)
   531 {
   532 	struct import_context *ctx = data;
   533 
   534 	if (strcmp(name, "package") == 0)
   535 		import_context_finish_package(ctx);
   536 }
   537 
   538 static char *
   539 sha1_to_hex(const unsigned char *sha1)
   540 {
   541 	static int bufno;
   542 	static char hexbuffer[4][50];
   543 	static const char hex[] = "0123456789abcdef";
   544 	char *buffer = hexbuffer[3 & ++bufno], *buf = buffer;
   545 	int i;
   546 
   547 	for (i = 0; i < 20; i++) {
   548 		unsigned int val = *sha1++;
   549 		*buf++ = hex[val >> 4];
   550 		*buf++ = hex[val & 0xf];
   551 	}
   552 	*buf = '\0';
   553 
   554 	return buffer;
   555 }
   556 
   557 static void
   558 razor_prepare_import(struct import_context *ctx)
   559 {
   560 	memset(ctx, 0, sizeof *ctx);
   561 	ctx->set = razor_set_create();
   562 }
   563 
   564 static int
   565 razor_import(struct import_context *ctx, const char *filename)
   566 {
   567 	SHA_CTX sha1;
   568 	XML_Parser parser;
   569 	int fd;
   570 	void *p;
   571 	struct stat stat;
   572 	char buf[128];
   573 	unsigned char hash[20];
   574 
   575 	fd = open(filename, O_RDONLY);
   576 	if (fstat(fd, &stat) < 0)
   577 		return -1;
   578 	p = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
   579 	if (p == MAP_FAILED)
   580 		return -1;
   581 
   582 	parser = XML_ParserCreate(NULL);
   583 	XML_SetUserData(parser, ctx);
   584 	XML_SetElementHandler(parser, start_element, end_element);
   585 	if (XML_Parse(parser, p, stat.st_size, 1) == XML_STATUS_ERROR) {
   586 		fprintf(stderr,
   587 			"%s at line %d, %s\n",
   588 			XML_ErrorString(XML_GetErrorCode(parser)),
   589 			XML_GetCurrentLineNumber(parser),
   590 			filename);
   591 		return 1;
   592 	}
   593 
   594 	XML_ParserFree(parser);
   595 
   596 	SHA1_Init(&sha1);
   597 	SHA1_Update(&sha1, p, stat.st_size);
   598 	SHA1_Final(hash, &sha1);
   599 
   600 	close(fd);
   601 
   602 	snprintf(buf, sizeof buf, "set/%s", sha1_to_hex(hash));
   603 	if (write_to_file(buf, p, stat.st_size) < 0)
   604 		return -1;
   605 	munmap(p, stat.st_size);
   606 
   607 	return 0;
   608 }
   609 
   610 static struct razor_set *qsort_set;
   611 
   612 static int
   613 compare_packages(const void *p1, const void *p2)
   614 {
   615 	const struct import_package *pkg1 = p1, *pkg2 = p2;
   616 	char *pool = qsort_set->string_pool.data;
   617 
   618 	return strcmp(&pool[pkg1->name], &pool[pkg2->name]);
   619 }
   620 
   621 static int
   622 compare_properties(const void *p1, const void *p2)
   623 {
   624 	const struct import_property *prop1 = p1, *prop2 = p2;
   625 	char *pool = qsort_set->string_pool.data;
   626 	int result;
   627 
   628 	result = strcmp(&pool[prop1->name], &pool[prop2->name]);
   629 	if (result == 0)
   630 		return strcmp(&pool[prop1->version], &pool[prop2->version]);
   631 	else
   632 		return result;
   633 }
   634 
   635 static unsigned long *
   636 uniqueify_properties(struct razor_set *set,
   637 		     struct array *in, struct array *out)
   638 {
   639 	struct import_property *ip, *end;
   640 	struct razor_property *rp, *rp_end;
   641 	struct array *pkgs, *p;
   642 	unsigned long *map, *r;
   643 	int i, count, unique;
   644 
   645 	count = in->size / sizeof(struct import_property);
   646 	qsort(in->data, count,
   647 	      sizeof(struct import_property), compare_properties);
   648 
   649 	rp = NULL;
   650 	end = in->data + in->size;
   651 	for (ip = in->data; ip < end; ip++) {
   652 		if (rp == NULL ||
   653 		    ip->name != rp->name || ip->version != rp->version) {
   654 			rp = array_add(out, sizeof *rp);
   655 			rp->name = ip->name;
   656 			rp->version = ip->version;
   657 		}
   658 		ip->unique_index = rp - (struct razor_property *) out->data;
   659 	}
   660 
   661 	map = malloc(count * sizeof (unsigned long));
   662 	ip = in->data;
   663 	for (i = 0; i < count; i++)
   664 		map[ip[i].index] = ip[i].unique_index;
   665 
   666 	unique = out->size / sizeof(*rp);
   667 	pkgs = zalloc(unique * sizeof *pkgs);
   668 	for (ip = in->data; ip < end; ip++) {
   669 		r = array_add(&pkgs[ip->unique_index], sizeof *r);
   670 		*r = ip->package;
   671 	}
   672 
   673 	rp_end = out->data + out->size;
   674 	for (rp = out->data, p = pkgs; rp < rp_end; rp++, p++)
   675 		rp->packages = add_to_property_pool(set, p);
   676 
   677 	free(pkgs);
   678 
   679 	return map;
   680 }
   681 
   682 static void
   683 remap_package_links(struct import_context *ctx)
   684 {
   685 	struct import_package *p, *end;
   686 	unsigned long *pool, *r;
   687 
   688 	pool = ctx->set->property_pool.data;
   689 	end = ctx->packages.data + ctx->packages.size;
   690 	for (p = ctx->packages.data; p < end; p++) {
   691 		for (r = &pool[p->requires]; ~*r; r++)
   692 			*r = ctx->requires_map[*r];
   693 		for (r = &pool[p->provides]; ~*r; r++)
   694 			*r = ctx->provides_map[*r];
   695 	}
   696 }
   697 
   698 static void
   699 remap_property_links(struct import_context *ctx)
   700 {
   701 	struct razor_property *p, *end;
   702 	struct import_package *ip;
   703 	unsigned long *map, *pool, *r;
   704 	int i, count;
   705 
   706 	pool = ctx->set->property_pool.data;
   707 	count = ctx->packages.size / sizeof(struct import_package);
   708 	map = malloc(count * sizeof *map);
   709 	ip = ctx->packages.data;
   710 	for (i = 0; i < count; i++)
   711 		map[ip[i].index] = i;
   712 
   713 	/* FIXME: This will break if we implement package list sharing
   714 	 * for all properties, since we'll remap those lists more than
   715 	 * once. We should just have a separate pool for property
   716 	 * lists and a separate pool for package lists and remap it as
   717 	 * a flat pool.  Right now, as property lists and package
   718 	 * lists are mixed, we can't do that. */
   719 
   720 	end = ctx->set->requires.data + ctx->set->requires.size;
   721 	for (p = ctx->set->requires.data; p < end; p++)
   722 		for (r = &pool[p->packages]; ~*r; r++)
   723 			*r = map[*r];
   724 
   725 	end = ctx->set->provides.data + ctx->set->provides.size;
   726 	for (p = ctx->set->provides.data; p < end; p++)
   727 		for (r = &pool[p->packages]; ~*r; r++)
   728 			*r = map[*r];
   729 
   730 	free(map);
   731 }
   732 
   733 static struct razor_set *
   734 razor_finish_import(struct import_context *ctx)
   735 {
   736 	struct import_package *ip;
   737 	struct razor_package *rp;
   738 	int i, count;
   739 
   740 	qsort_set = ctx->set;
   741 
   742 	ctx->requires_map =
   743 		uniqueify_properties(ctx->set, 
   744 				     &ctx->requires.all,
   745 				     &ctx->set->requires);
   746 	ctx->provides_map =
   747 		uniqueify_properties(ctx->set,
   748 				     &ctx->provides.all,
   749 				     &ctx->set->provides);
   750 
   751 	remap_package_links(ctx);
   752 
   753 	count = ctx->packages.size / sizeof(struct import_package);
   754 	qsort(ctx->packages.data, count,
   755 	      sizeof(struct import_package), compare_packages);
   756 
   757 	ip = ctx->packages.data;
   758 	for (i = 0; i < count; i++, ip++, rp++) {
   759 		rp = array_add(&ctx->set->packages, sizeof *rp);
   760 		rp->name = ip->name;
   761 		rp->version = ip->version;
   762 		rp->requires = ip->requires;
   763 		rp->provides = ip->provides;
   764 	}
   765 
   766 	remap_property_links(ctx);
   767 
   768 	free(ctx->requires.all.data);
   769 	free(ctx->provides.all.data);
   770 	free(ctx->requires_map);
   771 	free(ctx->provides_map);
   772 		
   773 	fprintf(stderr, "parsed %d requires, %d unique\n",
   774 		ctx->requires.all.size / sizeof(struct import_property),
   775 		ctx->set->requires.size / sizeof(struct razor_property));
   776 	fprintf(stderr, "parsed %d provides, %d unique\n",
   777 		ctx->provides.all.size / sizeof(struct import_property),
   778 		ctx->set->provides.size / sizeof(struct razor_property));
   779 
   780 	return ctx->set; 
   781 }
   782 
   783 /* Import a yum filelist as a razor package set. */
   784 
   785 enum {
   786 	YUM_STATE_BEGIN,
   787 	YUM_STATE_PACKAGE_NAME
   788 };
   789 
   790 struct yum_context {
   791 	struct import_context ctx;
   792 	struct import_property_context *current_property_context;
   793 	char *name;
   794 	int state;
   795 };
   796 
   797 static void
   798 yum_start_element(void *data, const char *name, const char **atts)
   799 {
   800 	struct yum_context *ctx = data;
   801 	const char *n, *version;
   802 	int i;
   803 
   804 	if (strcmp(name, "name") == 0) {
   805 		ctx->state = YUM_STATE_PACKAGE_NAME;
   806 	} else if (strcmp(name, "version") == 0) {
   807 		for (i = 0; atts[i]; i += 2) {
   808 			if (strcmp(atts[i], "ver") == 0)
   809 				version = atts[i + 1];
   810 		}
   811 		import_context_add_package(&ctx->ctx, ctx->name, version);
   812 	} else if (strcmp(name, "rpm:requires") == 0) {
   813 		ctx->current_property_context = &ctx->ctx.requires;
   814 	} else if (strcmp(name, "rpm:provides") == 0) {
   815 		ctx->current_property_context = &ctx->ctx.provides;
   816 	} else if (strcmp(name, "rpm:entry") == 0 &&
   817 		   ctx->current_property_context != NULL) {
   818 		n = NULL;
   819 		version = NULL;
   820 		for (i = 0; atts[i]; i += 2) {
   821 			if (strcmp(atts[i], "name") == 0)
   822 				n = atts[i + 1];
   823 			else if (strcmp(atts[i], "ver") == 0)
   824 				version = atts[i + 1];
   825 		}
   826 
   827 		if (n == NULL) {
   828 			fprintf(stderr, "invalid rpm:entry, "
   829 				"missing name or version attributes\n");
   830 			return;
   831 		}
   832 
   833 		import_context_add_property(&ctx->ctx,
   834 					    ctx->current_property_context,
   835 					    n, version);
   836 	}
   837 }
   838 
   839 static void
   840 yum_end_element (void *data, const char *name)
   841 {
   842 	struct yum_context *ctx = data;
   843 
   844 	if (strcmp(name, "package") == 0) {
   845 		free(ctx->name);
   846 		import_context_finish_package(&ctx->ctx);
   847 	} else if (strcmp(name, "name") == 0) {
   848 		ctx->state = 0;
   849 	} else if (strcmp(name, "rpm:requires") == 0) {
   850 		ctx->current_property_context = NULL;
   851 	} else if (strcmp(name, "rpm:provides") == 0) {
   852 		ctx->current_property_context = NULL;
   853 	}
   854 }
   855 
   856 static void
   857 yum_character_data (void *data, const XML_Char *s, int len)
   858 {
   859 	struct yum_context *ctx = data;
   860 
   861 	if (ctx->state == YUM_STATE_PACKAGE_NAME)
   862 		ctx->name = strndup(s, len);
   863 }
   864 
   865 static struct razor_set *
   866 razor_set_create_from_yum_filelist(int fd)
   867 {
   868 	struct yum_context ctx;
   869 	XML_Parser parser;
   870 	char buf[4096];
   871 	int len;
   872 
   873 	razor_prepare_import(&ctx.ctx);
   874 
   875 	parser = XML_ParserCreate(NULL);
   876 	XML_SetUserData(parser, &ctx);
   877 	XML_SetElementHandler(parser, yum_start_element, yum_end_element);
   878 	XML_SetCharacterDataHandler(parser, yum_character_data);
   879 
   880 	while (1) {
   881 		len = read(fd, buf, sizeof buf);
   882 		if (len < 0) {
   883 			fprintf(stderr,
   884 				"couldn't read input: %s\n", strerror(errno));
   885 			return NULL;
   886 		} else if (len == 0)
   887 			break;
   888 
   889 		if (XML_Parse(parser, buf, len, 0) == XML_STATUS_ERROR) {
   890 			fprintf(stderr,
   891 				"%s at line %d\n",
   892 				XML_ErrorString(XML_GetErrorCode(parser)),
   893 				XML_GetCurrentLineNumber(parser));
   894 			return NULL;
   895 		}
   896 	}
   897 
   898 	XML_ParserFree(parser);
   899 
   900 	return razor_finish_import(&ctx.ctx);
   901 }
   902 
   903 void
   904 razor_set_list(struct razor_set *set)
   905 {
   906 	struct razor_package *p, *end;
   907 	char *pool;
   908 
   909 	pool = set->string_pool.data;
   910 	end = set->packages.data + set->packages.size;
   911 	for (p = set->packages.data; p < end; p++)
   912 		printf("%s %s\n", &pool[p->name], &pool[p->version]);
   913 }
   914 
   915 struct razor_set *bsearch_set;
   916 
   917 static int
   918 compare_package_name(const void *key, const void *data)
   919 {
   920 	const struct razor_package *p = data;
   921 	char *pool;
   922 
   923 	pool = bsearch_set->string_pool.data;
   924 
   925 	return strcmp(key, &pool[p->name]);
   926 }
   927 
   928 struct razor_package *
   929 razor_set_get_package(struct razor_set *set, const char *package)
   930 {
   931 	bsearch_set = set;
   932 	return bsearch(package, set->packages.data,
   933 		       set->packages.size / sizeof(struct razor_package),
   934 		       sizeof(struct razor_package), compare_package_name);
   935 }
   936 
   937 static int
   938 compare_property_name(const void *key, const void *data)
   939 {
   940 	const struct razor_property *p = data;
   941 	char *pool;
   942 
   943 	pool = bsearch_set->string_pool.data;
   944 
   945 	return strcmp(key, &pool[p->name]);
   946 }
   947 
   948 struct razor_property *
   949 razor_set_get_property(struct razor_set *set,
   950 		       struct array *properties,
   951 		       const char *property)
   952 {
   953 	struct razor_property *p, *start;
   954 
   955 	bsearch_set = set;
   956 	p = bsearch(property, properties->data,
   957 		    properties->size / sizeof(struct razor_property),
   958 		    sizeof(struct razor_property), compare_property_name);
   959 
   960 	start = properties->data;
   961 	while (p > start && (p - 1)->name == p->name)
   962 		p--;
   963 
   964 	return p;
   965 }
   966 
   967 static void
   968 razor_set_list_all_properties(struct razor_set *set, struct array *properties)
   969 {
   970 	struct razor_property *p, *end;
   971 	char *pool;
   972 
   973 	pool = set->string_pool.data;
   974 	end = properties->data + properties->size;
   975 	for (p = properties->data; p < end; p++)
   976 		printf("%s %s\n", &pool[p->name], &pool[p->version]);
   977 }
   978 
   979 void
   980 razor_set_list_requires(struct razor_set *set, const char *name)
   981 {
   982 	struct razor_property *p, *requires;
   983 	struct razor_package *package;
   984 	unsigned long *r;
   985 	char *pool;
   986 
   987 	if (name) {
   988 		package = razor_set_get_package(set, name);
   989 		r = (unsigned long *) set->property_pool.data +
   990 			package->requires;
   991 		requires = set->requires.data;
   992 		pool = set->string_pool.data;
   993 		while (~*r) {
   994 			p = &requires[*r++];
   995 			printf("%s %s\n", &pool[p->name], &pool[p->version]);
   996 		}
   997 	} else
   998 		razor_set_list_all_properties(set, &set->requires);
   999 }
  1000 
  1001 void
  1002 razor_set_list_provides(struct razor_set *set, const char *name)
  1003 {
  1004 	struct razor_property *p, *provides;
  1005 	struct razor_package *package;
  1006 	unsigned long *r;
  1007 	char *pool;
  1008 
  1009 	if (name) {
  1010 		package = razor_set_get_package(set, name);
  1011 		r = (unsigned long *) set->property_pool.data +
  1012 			package->provides;
  1013 		provides = set->provides.data;
  1014 		pool = set->string_pool.data;
  1015 		while (~*r) {
  1016 			p = &provides[*r++];
  1017 			printf("%s %s\n", &pool[p->name], &pool[p->version]);
  1018 		}
  1019 	} else 
  1020 		razor_set_list_all_properties(set, &set->provides);
  1021 }
  1022 
  1023 void
  1024 razor_set_list_property_packages(struct razor_set *set,
  1025 				 struct array *properties,
  1026 				 const char *name)
  1027 {
  1028 	struct razor_property *property, *end;
  1029 	struct razor_package *p, *packages;
  1030 	unsigned long *r;
  1031 	char *pool;
  1032 
  1033 	if (name == NULL)
  1034 		return;
  1035 
  1036 	property = razor_set_get_property(set, properties, name);
  1037 	packages = set->packages.data;
  1038 	pool = set->string_pool.data;
  1039 	end = properties->data + properties->size;
  1040 	while (property < end && strcmp(name, &pool[property->name]) == 0) {
  1041 		r = (unsigned long *)
  1042 			set->property_pool.data + property->packages;
  1043 		while (~*r) {
  1044 			p = &packages[*r++];
  1045 			printf("%s %s\n",
  1046 			       &pool[p->name], &pool[p->version]);
  1047 		}
  1048 		property++;
  1049 	}
  1050 }
  1051 
  1052 void
  1053 razor_set_info(struct razor_set *set)
  1054 {
  1055 	unsigned int offset, size;
  1056 	int i;
  1057 
  1058 	for (i = 0; i < set->header->sections[i].type; i++) {
  1059 		offset = set->header->sections[i].offset;
  1060 		size = set->header->sections[i].size;
  1061 
  1062 		switch (set->header->sections[i].type) {
  1063 		case RAZOR_STRINGS:
  1064 			printf("string pool:\t\t%dkb\n", size / 1024);
  1065 			break;
  1066 		case RAZOR_PACKAGES:
  1067 			printf("package section:\t%dkb\n", size / 1024);
  1068 			break;
  1069 		case RAZOR_REQUIRES:
  1070 			printf("requires section:\t%dkb\n", size / 1024);
  1071 			break;
  1072 		case RAZOR_PROVIDES:
  1073 			printf("provides section:\t%dkb\n", size / 1024);
  1074 			break;
  1075 		case RAZOR_PROPERTIES:
  1076 			printf("properties section:\t%dkb\n", size / 1024);
  1077 			break;
  1078 		}
  1079 	}
  1080 }
  1081 
  1082 static int
  1083 usage(void)
  1084 {
  1085 	printf("usage: razor [ import FILES | lookup <key> | "
  1086 	       "list | list-requires | list-provides | eat-yum | info ]\n");
  1087 	exit(1);
  1088 }
  1089 
  1090 static const char *repo_filename = "system.repo";
  1091 static const char rawhide_repo_filename[] = "rawhide.repo";
  1092 
  1093 int
  1094 main(int argc, char *argv[])
  1095 {
  1096 	int i;
  1097 	struct razor_set *set;
  1098 	struct stat statbuf;
  1099 	struct import_context ctx;
  1100 	char *repo;
  1101 
  1102 	repo = getenv("RAZOR_REPO");
  1103 	if (repo != NULL)
  1104 		repo_filename = repo;
  1105 
  1106 	if (argc < 2) {
  1107 		usage();
  1108 	} else if (strcmp(argv[1], "import") == 0) {
  1109 		if (stat("set", &statbuf) && mkdir("set", 0777)) {
  1110 			fprintf(stderr, "could not create directory 'set'\n");
  1111 			exit(-1);
  1112 		}
  1113 			
  1114 		razor_prepare_import(&ctx);
  1115 
  1116 		for (i = 2; i < argc; i++) {
  1117 			if (razor_import(&ctx, argv[i]) < 0) {
  1118 				fprintf(stderr, "failed to import %s\n",
  1119 					argv[i]);
  1120 				exit(-1);
  1121 			}
  1122 		}
  1123 
  1124 		set = razor_finish_import(&ctx);
  1125 
  1126 		printf("bucket allocation: %d\n", set->buckets.alloc);
  1127 		printf("pool size: %d\n", set->string_pool.size);
  1128 		printf("pool allocation: %d\n", set->string_pool.alloc);
  1129 		printf("packages: %d\n",
  1130 		       set->packages.size / sizeof(struct razor_package));
  1131 		printf("requires: %d\n",
  1132 		       set->requires.size / sizeof(struct razor_property));
  1133 		printf("provides: %d\n",
  1134 		       set->provides.size / sizeof(struct razor_property));
  1135 
  1136 		razor_set_write(set, repo_filename);
  1137 
  1138 		razor_set_destroy(set);
  1139 	} else if (strcmp(argv[1], "lookup") == 0) {
  1140 		set = razor_set_open(repo_filename);
  1141 		printf("%s is %lu\n", argv[2],
  1142 		       razor_set_lookup(set, argv[2]));
  1143 		razor_set_destroy(set);
  1144 	} else if (strcmp(argv[1], "list") == 0) {
  1145 		set = razor_set_open(repo_filename);
  1146 		razor_set_list(set);
  1147 		razor_set_destroy(set);
  1148 	} else if (strcmp(argv[1], "list-requires") == 0) {
  1149 		set = razor_set_open(repo_filename);
  1150 		razor_set_list_requires(set, argv[2]);
  1151 		razor_set_destroy(set);
  1152 	} else if (strcmp(argv[1], "list-provides") == 0) {
  1153 		set = razor_set_open(repo_filename);
  1154 		razor_set_list_provides(set, argv[2]);
  1155 		razor_set_destroy(set);
  1156 	} else if (strcmp(argv[1], "what-requires") == 0) {
  1157 		set = razor_set_open(repo_filename);
  1158 		razor_set_list_property_packages(set, &set->requires, argv[2]);
  1159 		razor_set_destroy(set);
  1160 	} else if (strcmp(argv[1], "what-provides") == 0) {
  1161 		set = razor_set_open(repo_filename);
  1162 		razor_set_list_property_packages(set, &set->provides, argv[2]);
  1163 		razor_set_destroy(set);
  1164 	} else if (strcmp(argv[1], "info") == 0) {
  1165 		set = razor_set_open(repo_filename);
  1166 		razor_set_info(set);
  1167 		razor_set_destroy(set);
  1168 	} else if (strcmp(argv[1], "eat-yum") == 0) {
  1169 		set = razor_set_create_from_yum_filelist(STDIN_FILENO);
  1170 		if (set == NULL)
  1171 			return 1;
  1172 		razor_set_write(set, rawhide_repo_filename);
  1173 		razor_set_destroy(set);
  1174 		printf("wrote %s\n", rawhide_repo_filename);
  1175 	} else {
  1176 		usage();
  1177 	}
  1178 
  1179 	return 0;
  1180 }