razor.c
author Kristian H?gsberg <krh@redhat.com>
Fri Sep 07 14:17:39 2007 -0400 (2007-09-07)
changeset 17 67029e580a0e
parent 16 78383b7bc4fa
child 18 b2bf852ca8d1
permissions -rw-r--r--
Refactor razor_set_write() a bit and stop writing the buckets section.
     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 	struct razor_set *set;
   145 	char *p;
   146 
   147 	set = zalloc(sizeof(struct razor_set));
   148 	p = array_add(&set->string_pool, 1);
   149 	*p = '\0';
   150 
   151 	return set;
   152 }
   153 
   154 struct razor_set *
   155 razor_set_open(const char *filename)
   156 {
   157 	struct razor_set *set;
   158 	struct stat stat;
   159 	unsigned int size, offset;
   160 	int fd, i;
   161 
   162 	set = zalloc(sizeof *set);
   163 	fd = open(filename, O_RDONLY);
   164 	if (fstat(fd, &stat) < 0)
   165 		return NULL;
   166 	set->header = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
   167 	if (set->header == MAP_FAILED) {
   168 		free(set);
   169 		return NULL;
   170 	}
   171 
   172 	for (i = 0; i < set->header->sections[i].type; i++) {
   173 		offset = set->header->sections[i].offset;
   174 		size = set->header->sections[i].size;
   175 
   176 		switch (set->header->sections[i].type) {
   177 		case RAZOR_STRINGS:
   178 			set->string_pool.data = (void *) set->header + offset;
   179 			set->string_pool.size = size;
   180 			set->string_pool.alloc = size;
   181 			break;
   182 		case RAZOR_PACKAGES:
   183 			set->packages.data = (void *) set->header + offset;
   184 			set->packages.size = size;
   185 			set->packages.size = size;
   186 			break;
   187 		case RAZOR_REQUIRES:
   188 			set->requires.data = (void *) set->header + offset;
   189 			set->requires.size = size;
   190 			set->requires.size = size;
   191 			break;
   192 		case RAZOR_PROVIDES:
   193 			set->provides.data = (void *) set->header + offset;
   194 			set->provides.size = size;
   195 			set->provides.size = size;
   196 			break;
   197 		case RAZOR_PROPERTIES:
   198 			set->property_pool.data = (void *) set->header + offset;
   199 			set->property_pool.size = size;
   200 			set->property_pool.size = size;
   201 			break;
   202 		}
   203 	}
   204 	close(fd);
   205 
   206 	return set;
   207 }
   208 
   209 void
   210 razor_set_destroy(struct razor_set *set)
   211 {
   212 	unsigned int size;
   213 	int i;
   214 
   215 	if (set->header) {
   216 		for (i = 0; set->header->sections[i].type; i++)
   217 			;
   218 		size = set->header->sections[i].type;
   219 		munmap(set->header, size);
   220 		free(set->buckets.data);
   221 	} else {
   222 		free(set->buckets.data);
   223 		free(set->string_pool.data);
   224 		free(set->packages.data);
   225 		free(set->requires.data);
   226 		free(set->provides.data);
   227 		free(set->property_pool.data);
   228 	}
   229 
   230 	free(set);
   231 }
   232 
   233 static int
   234 razor_set_write(struct razor_set *set, const char *filename)
   235 {
   236 	char data[4096];
   237 	struct razor_set_header *header = (struct razor_set_header *) data;
   238 	unsigned long offset;
   239 	int i, fd;
   240 	struct { int type; struct array *array; } sections[] = {
   241 		{ RAZOR_STRINGS, &set->string_pool },
   242 		{ RAZOR_PACKAGES, &set->packages },
   243 		{ RAZOR_REQUIRES, &set->requires },
   244 		{ RAZOR_PROVIDES, &set->provides },
   245 		{ RAZOR_PROPERTIES, &set->property_pool },
   246 		{ 0 }
   247 	};
   248 
   249 	memset(data, 0, sizeof data);
   250 	header->magic = RAZOR_MAGIC;
   251 	header->version = RAZOR_VERSION;
   252 	offset = sizeof data;
   253 
   254 	for (i = 0; sections[i].type != 0; i++) {
   255 		header->sections[i].type = sections[i].type;
   256 		header->sections[i].offset = offset;
   257 		header->sections[i].size = sections[i].array->size;
   258 		offset += (sections[i].array->size + 4095) & ~4095;
   259 	}
   260 
   261 	header->sections[i].type = 0;
   262 	header->sections[i].offset = 0;
   263 	header->sections[i].size = 0;
   264 
   265 	fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666);
   266 	if (fd < 0)
   267 		return -1;
   268 
   269 	write_to_fd(fd, data, sizeof data);
   270 	for (i = 0; sections[i].type != 0; i++)
   271 		write_to_fd(fd, sections[i].array->data,
   272 			    (sections[i].array->size + 4095) & ~4095);
   273 
   274 	close(fd);
   275 
   276 	return 0;
   277 }
   278 
   279 static unsigned int
   280 hash_string(const char *key)
   281 {
   282 	const char *p;
   283 	unsigned int hash = 0;
   284 
   285 	for (p = key; *p; p++)
   286 		hash = (hash * 617) ^ *p;
   287 
   288 	return hash;
   289 }
   290 
   291 unsigned long
   292 razor_set_lookup(struct razor_set *set, const char *key)
   293 {
   294 	unsigned int mask, start, i;
   295 	unsigned long *b;
   296 	char *pool;
   297 
   298 	pool = set->string_pool.data;
   299 	mask = set->buckets.alloc - 1;
   300 	start = hash_string(key) * sizeof(unsigned long);
   301 
   302 	for (i = 0; i < set->buckets.alloc; i += sizeof *b) {
   303 		b = set->buckets.data + ((start + i) & mask);
   304 
   305 		if (*b == 0)
   306 			return 0;
   307 
   308 		if (strcmp(key, &pool[*b]) == 0)
   309 			return *b;
   310 	}
   311 
   312 	return 0;
   313 }
   314 
   315 static unsigned long
   316 add_to_string_pool(struct razor_set *set, const char *key)
   317 {
   318 	int len;
   319 	char *p;
   320 
   321 	len = strlen(key) + 1;
   322 	p = array_add(&set->string_pool, len);
   323 	memcpy(p, key, len);
   324 
   325 	return p - (char *) set->string_pool.data;
   326 }
   327 
   328 static unsigned long
   329 add_to_property_pool(struct razor_set *set, struct array *properties)
   330 {
   331 	unsigned long  *p;
   332 
   333 	p = array_add(properties, sizeof *p);
   334 	*p = 0;
   335 	p = array_add(&set->property_pool, properties->size);
   336 	memcpy(p, properties->data, properties->size);
   337 
   338 	return p - (unsigned long *) set->property_pool.data;
   339 }
   340 
   341 static void
   342 do_insert(struct razor_set *set, unsigned long value)
   343 {
   344 	unsigned int mask, start, i;
   345 	unsigned long *b;
   346 	const char *key;
   347 
   348 	key = (char *) set->string_pool.data + value;
   349 	mask = set->buckets.alloc - 1;
   350 	start = hash_string(key) * sizeof(unsigned long);
   351 
   352 	for (i = 0; i < set->buckets.alloc; i += sizeof *b) {
   353 		b = set->buckets.data + ((start + i) & mask);
   354 		if (*b == 0) {
   355 			*b = value;
   356 			break;
   357 		}
   358 	}
   359 }
   360 
   361 unsigned long
   362 razor_set_insert(struct razor_set *set, const char *key)
   363 {
   364 	unsigned long value, *buckets, *b, *end;
   365 	int alloc;
   366 
   367 	alloc = set->buckets.alloc;
   368 	array_add(&set->buckets, 4 * sizeof *buckets);
   369 	if (alloc != set->buckets.alloc) {
   370 		end = set->buckets.data + alloc;
   371 		memset(end, 0, set->buckets.alloc - alloc);
   372 		for (b = set->buckets.data; b < end; b++) {
   373 			value = *b;
   374 			if (value != 0) {
   375 				*b = 0;
   376 				do_insert(set, value);
   377 			}
   378 		}
   379 	}
   380 
   381 	value = add_to_string_pool(set, key);
   382 	do_insert (set, value);
   383 
   384 	return value;
   385 }
   386 
   387 unsigned long
   388 razor_set_tokenize(struct razor_set *set, const char *string)
   389 {
   390 	unsigned long token;
   391 
   392 	if (string == NULL)
   393 		return 0;
   394 
   395 	token = razor_set_lookup(set, string);
   396 	if (token != 0)
   397 		return token;
   398 
   399 	return razor_set_insert(set, string);
   400 }
   401 
   402 struct import_property_context {
   403 	struct array all;
   404 	struct array package;
   405 };
   406 
   407 struct import_context {
   408 	struct razor_set *set;
   409 	struct import_property_context requires;
   410 	struct import_property_context provides;
   411 	unsigned long package;
   412 	unsigned long *requires_map;
   413 	unsigned long *provides_map;
   414 };
   415 
   416 struct import_property {
   417 	unsigned long name;
   418 	unsigned long version;
   419 	unsigned long package;
   420 	unsigned long index;
   421 	unsigned long unique_index;
   422 };
   423 
   424 static void
   425 import_context_add_package(struct import_context *ctx,
   426 			   const char *name, const char *version)
   427 {
   428 	struct razor_package *p;
   429 
   430 	p = array_add(&ctx->set->packages, sizeof *p);
   431 	p->name = razor_set_tokenize(ctx->set, name);
   432 	p->version = razor_set_tokenize(ctx->set, version);
   433 
   434 	ctx->package = p - (struct razor_package *) ctx->set->packages.data;
   435 	array_init(&ctx->requires.package);
   436 	array_init(&ctx->provides.package);
   437 }
   438 
   439 void
   440 import_context_finish_package(struct import_context *ctx)
   441 {
   442 	struct razor_package *p;
   443 
   444 	p = (struct razor_package *) ctx->set->packages.data + ctx->package;
   445 	p->requires = add_to_property_pool(ctx->set, &ctx->requires.package);
   446 	p->provides = add_to_property_pool(ctx->set, &ctx->provides.package);
   447 
   448 	array_release(&ctx->requires.package);
   449 	array_release(&ctx->provides.package);
   450 }
   451 
   452 static void
   453 import_context_add_property(struct import_context *ctx,
   454 			    struct import_property_context *pctx,
   455 			    const char *name, const char *version)
   456 {
   457 	struct import_property *p;
   458 	unsigned long *r;
   459 
   460 	p = array_add(&pctx->all, sizeof *p);
   461 	p->name = razor_set_tokenize(ctx->set, name);
   462 	p->version = razor_set_tokenize(ctx->set, version);
   463 	p->package = ctx->package;
   464 	p->index = p - (struct import_property *) pctx->all.data;
   465 
   466 	r = array_add(&pctx->package, sizeof *r);
   467 	*r = p->index;
   468 }
   469 
   470 static void
   471 parse_package(struct import_context *ctx, const char **atts, void *data)
   472 {
   473 	const char *name = NULL, *version = NULL;
   474 	int i;
   475 
   476 	for (i = 0; atts[i]; i += 2) {
   477 		if (strcmp(atts[i], "name") == 0)
   478 			name = atts[i + 1];
   479 		else if (strcmp(atts[i], "version") == 0)
   480 			version = atts[i + 1];
   481 	}
   482 
   483 	if (name == NULL || version == NULL) {
   484 		fprintf(stderr, "invalid package tag, "
   485 			"missing name or version attributes\n");
   486 		return;
   487 	}
   488 
   489 	import_context_add_package(ctx, name, version);
   490 }
   491 
   492 static void
   493 parse_property(struct import_context *ctx, const char **atts, void *data)
   494 {
   495 	const char *name = NULL, *version = NULL;
   496 	int i;
   497 
   498 	for (i = 0; atts[i]; i += 2) {
   499 		if (strcmp(atts[i], "name") == 0)
   500 			name = atts[i + 1];
   501 		if (strcmp(atts[i], "version") == 0)
   502 			version = atts[i + 1];
   503 	}
   504 	
   505 	if (name == NULL) {
   506 		fprintf(stderr, "invalid tag, missing name attribute\n");
   507 		return;
   508 	}
   509 
   510 	import_context_add_property(ctx, data, name, version);
   511 }
   512 
   513 static void
   514 start_element(void *data, const char *name, const char **atts)
   515 {
   516 	struct import_context *ctx = data;
   517 
   518 	if (strcmp(name, "package") == 0)
   519 		parse_package(ctx, atts, NULL);
   520 	else if (strcmp(name, "requires") == 0)
   521 		parse_property(ctx, atts, &ctx->requires);
   522 	else if (strcmp(name, "provides") == 0)
   523 		parse_property(ctx, atts, &ctx->provides);
   524 }
   525 
   526 static void
   527 end_element (void *data, const char *name)
   528 {
   529 	struct import_context *ctx = data;
   530 
   531 	if (strcmp(name, "package") == 0)
   532 		import_context_finish_package(ctx);
   533 }
   534 
   535 static char *
   536 sha1_to_hex(const unsigned char *sha1)
   537 {
   538 	static int bufno;
   539 	static char hexbuffer[4][50];
   540 	static const char hex[] = "0123456789abcdef";
   541 	char *buffer = hexbuffer[3 & ++bufno], *buf = buffer;
   542 	int i;
   543 
   544 	for (i = 0; i < 20; i++) {
   545 		unsigned int val = *sha1++;
   546 		*buf++ = hex[val >> 4];
   547 		*buf++ = hex[val & 0xf];
   548 	}
   549 	*buf = '\0';
   550 
   551 	return buffer;
   552 }
   553 
   554 static void
   555 razor_prepare_import(struct import_context *ctx)
   556 {
   557 	memset(ctx, 0, sizeof *ctx);
   558 	ctx->set = razor_set_create();
   559 }
   560 
   561 static int
   562 razor_import(struct import_context *ctx, const char *filename)
   563 {
   564 	SHA_CTX sha1;
   565 	XML_Parser parser;
   566 	int fd;
   567 	void *p;
   568 	struct stat stat;
   569 	char buf[128];
   570 	unsigned char hash[20];
   571 
   572 	fd = open(filename, O_RDONLY);
   573 	if (fstat(fd, &stat) < 0)
   574 		return -1;
   575 	p = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
   576 	if (p == MAP_FAILED)
   577 		return -1;
   578 
   579 	parser = XML_ParserCreate(NULL);
   580 	XML_SetUserData(parser, ctx);
   581 	XML_SetElementHandler(parser, start_element, end_element);
   582 	if (XML_Parse(parser, p, stat.st_size, 1) == XML_STATUS_ERROR) {
   583 		fprintf(stderr,
   584 			"%s at line %d, %s\n",
   585 			XML_ErrorString(XML_GetErrorCode(parser)),
   586 			XML_GetCurrentLineNumber(parser),
   587 			filename);
   588 		return 1;
   589 	}
   590 
   591 	XML_ParserFree(parser);
   592 
   593 	SHA1_Init(&sha1);
   594 	SHA1_Update(&sha1, p, stat.st_size);
   595 	SHA1_Final(hash, &sha1);
   596 
   597 	close(fd);
   598 
   599 	snprintf(buf, sizeof buf, "set/%s", sha1_to_hex(hash));
   600 	if (write_to_file(buf, p, stat.st_size) < 0)
   601 		return -1;
   602 	munmap(p, stat.st_size);
   603 
   604 	return 0;
   605 }
   606 
   607 static struct razor_set *qsort_set;
   608 
   609 static int
   610 compare_packages(const void *p1, const void *p2)
   611 {
   612 	const struct razor_package *pkg1 = p1, *pkg2 = p2;
   613 	char *pool = qsort_set->string_pool.data;
   614 
   615 	return strcmp(&pool[pkg1->name], &pool[pkg2->name]);
   616 }
   617 
   618 static int
   619 compare_properties(const void *p1, const void *p2)
   620 {
   621 	const struct import_property *prop1 = p1, *prop2 = p2;
   622 	char *pool = qsort_set->string_pool.data;
   623 	int result;
   624 
   625 	result = strcmp(&pool[prop1->name], &pool[prop2->name]);
   626 	if (result == 0)
   627 		return strcmp(&pool[prop1->version], &pool[prop2->version]);
   628 	else
   629 		return result;
   630 }
   631 
   632 static unsigned long *
   633 uniqueify_properties(struct array *in, struct array *out)
   634 {
   635 	struct import_property *ip, *end;
   636 	struct razor_property *rp;
   637 	unsigned long *map;
   638 	int i, count;
   639 
   640 	count = in->size / sizeof(struct import_property);
   641 	qsort(in->data, count,
   642 	      sizeof(struct import_property), compare_properties);
   643 
   644 	rp = NULL;
   645 	end = in->data + in->size;
   646 	for (ip = in->data; ip < end; ip++) {
   647 		if (rp == NULL ||
   648 		    ip->name != rp->name || ip->version != rp->version) {
   649 			rp = array_add(out, sizeof *rp);
   650 			rp->name = ip->name;
   651 			rp->version = ip->version;
   652 		}
   653 		ip->unique_index = rp - (struct razor_property *) out->data;
   654 	}
   655 
   656 	map = malloc(count * sizeof (unsigned long));
   657 	ip = in->data;
   658 	for (i = 0; i < count; i++)
   659 		map[ip[i].index] = ip[i].unique_index;
   660 
   661 	return map;
   662 }
   663 
   664 static void
   665 sort_packages(struct import_context *ctx)
   666 {
   667 	struct razor_package *p, *end;
   668 	unsigned long *pool, *r;
   669 
   670 	pool = ctx->set->property_pool.data;
   671 	end = ctx->set->packages.data + ctx->set->packages.size;
   672 	for (p = ctx->set->packages.data; p < end; p++) {
   673 		for (r = &pool[p->requires]; *r; r++)
   674 			*r = ctx->requires_map[*r];
   675 		for (r = &pool[p->provides]; *r; r++)
   676 			*r = ctx->provides_map[*r];
   677 	}
   678 
   679 	qsort(ctx->set->packages.data,
   680 	      ctx->set->packages.size / sizeof(struct razor_package),
   681 	      sizeof(struct razor_package), compare_packages);
   682 }
   683 
   684 static struct razor_set *
   685 razor_finish_import(struct import_context *ctx)
   686 {
   687 	qsort_set = ctx->set;
   688 
   689 	ctx->requires_map =
   690 		uniqueify_properties(&ctx->requires.all, &ctx->set->requires);
   691 	ctx->provides_map =
   692 		uniqueify_properties(&ctx->provides.all, &ctx->set->provides);
   693 
   694 	sort_packages(ctx);
   695 
   696 	free(ctx->requires.all.data);
   697 	free(ctx->provides.all.data);
   698 	free(ctx->requires_map);
   699 	free(ctx->provides_map);
   700 		
   701 	fprintf(stderr, "parsed %d requires, %d unique\n",
   702 		ctx->requires.all.size / sizeof(struct import_property),
   703 		ctx->set->requires.size / sizeof(struct razor_property));
   704 	fprintf(stderr, "parsed %d provides, %d unique\n",
   705 		ctx->provides.all.size / sizeof(struct import_property),
   706 		ctx->set->provides.size / sizeof(struct razor_property));
   707 
   708 	return ctx->set; 
   709 }
   710 
   711 /* Import a yum filelist as a razor package set. */
   712 
   713 enum {
   714 	YUM_STATE_BEGIN,
   715 	YUM_STATE_PACKAGE_NAME
   716 };
   717 
   718 struct yum_context {
   719 	struct import_context ctx;
   720 	struct import_property_context *current_property_context;
   721 	char *name;
   722 	int state;
   723 };
   724 
   725 static void
   726 yum_start_element(void *data, const char *name, const char **atts)
   727 {
   728 	struct yum_context *ctx = data;
   729 	const char *n, *version;
   730 	int i;
   731 
   732 	if (strcmp(name, "name") == 0) {
   733 		ctx->state = YUM_STATE_PACKAGE_NAME;
   734 	} else if (strcmp(name, "version") == 0) {
   735 		for (i = 0; atts[i]; i += 2) {
   736 			if (strcmp(atts[i], "ver") == 0)
   737 				version = atts[i + 1];
   738 		}
   739 		import_context_add_package(&ctx->ctx, ctx->name, version);
   740 	} else if (strcmp(name, "rpm:requires") == 0) {
   741 		ctx->current_property_context = &ctx->ctx.requires;
   742 	} else if (strcmp(name, "rpm:provides") == 0) {
   743 		ctx->current_property_context = &ctx->ctx.provides;
   744 	} else if (strcmp(name, "rpm:entry") == 0 &&
   745 		   ctx->current_property_context != NULL) {
   746 		n = NULL;
   747 		version = NULL;
   748 		for (i = 0; atts[i]; i += 2) {
   749 			if (strcmp(atts[i], "name") == 0)
   750 				n = atts[i + 1];
   751 			else if (strcmp(atts[i], "ver") == 0)
   752 				version = atts[i + 1];
   753 		}
   754 
   755 		if (n == NULL) {
   756 			fprintf(stderr, "invalid rpm:entry, "
   757 				"missing name or version attributes\n");
   758 			return;
   759 		}
   760 
   761 		import_context_add_property(&ctx->ctx,
   762 					    ctx->current_property_context,
   763 					    n, version);
   764 	}
   765 }
   766 
   767 static void
   768 yum_end_element (void *data, const char *name)
   769 {
   770 	struct yum_context *ctx = data;
   771 
   772 	if (strcmp(name, "package") == 0) {
   773 		free(ctx->name);
   774 		import_context_finish_package(&ctx->ctx);
   775 	} else if (strcmp(name, "name") == 0) {
   776 		ctx->state = 0;
   777 	} else if (strcmp(name, "rpm:requires") == 0) {
   778 		ctx->current_property_context = NULL;
   779 	} else if (strcmp(name, "rpm:provides") == 0) {
   780 		ctx->current_property_context = NULL;
   781 	}
   782 }
   783 
   784 static void
   785 yum_character_data (void *data, const XML_Char *s, int len)
   786 {
   787 	struct yum_context *ctx = data;
   788 
   789 	if (ctx->state == YUM_STATE_PACKAGE_NAME)
   790 		ctx->name = strndup(s, len);
   791 }
   792 
   793 static struct razor_set *
   794 razor_set_create_from_yum_filelist(int fd)
   795 {
   796 	struct yum_context ctx;
   797 	XML_Parser parser;
   798 	char buf[4096];
   799 	int len;
   800 
   801 	razor_prepare_import(&ctx.ctx);
   802 
   803 	parser = XML_ParserCreate(NULL);
   804 	XML_SetUserData(parser, &ctx);
   805 	XML_SetElementHandler(parser, yum_start_element, yum_end_element);
   806 	XML_SetCharacterDataHandler(parser, yum_character_data);
   807 
   808 	while (1) {
   809 		len = read(fd, buf, sizeof buf);
   810 		if (len < 0) {
   811 			fprintf(stderr,
   812 				"couldn't read input: %s\n", strerror(errno));
   813 			return NULL;
   814 		} else if (len == 0)
   815 			break;
   816 
   817 		if (XML_Parse(parser, buf, len, 0) == XML_STATUS_ERROR) {
   818 			fprintf(stderr,
   819 				"%s at line %d\n",
   820 				XML_ErrorString(XML_GetErrorCode(parser)),
   821 				XML_GetCurrentLineNumber(parser));
   822 			return NULL;
   823 		}
   824 	}
   825 
   826 	XML_ParserFree(parser);
   827 
   828 	return razor_finish_import(&ctx.ctx);
   829 }
   830 
   831 void
   832 razor_set_list(struct razor_set *set)
   833 {
   834 	struct razor_package *p, *end;
   835 	char *pool;
   836 
   837 	pool = set->string_pool.data;
   838 	end = set->packages.data + set->packages.size;
   839 	for (p = set->packages.data; p < end; p++)
   840 		printf("%s %s\n", &pool[p->name], &pool[p->version]);
   841 }
   842 
   843 struct razor_set *bsearch_set;
   844 
   845 static int
   846 compare_package_name(const void *key, const void *data)
   847 {
   848 	const struct razor_package *p = data;
   849 	char *pool;
   850 
   851 	pool = bsearch_set->string_pool.data;
   852 
   853 	return strcmp(key, &pool[p->name]);
   854 }
   855 
   856 struct razor_package *
   857 razor_set_get_package(struct razor_set *set, const char *package)
   858 {
   859 	bsearch_set = set;
   860 	return bsearch(package, set->packages.data,
   861 		       set->packages.size / sizeof(struct razor_package),
   862 		       sizeof(struct razor_package), compare_package_name);
   863 }
   864 
   865 static void
   866 razor_set_list_all_properties(struct razor_set *set, struct array *properties)
   867 {
   868 	struct razor_property *p, *end;
   869 	char *pool;
   870 
   871 	pool = set->string_pool.data;
   872 	end = properties->data + properties->size;
   873 	for (p = properties->data; p < end; p++)
   874 		printf("%s %s\n", &pool[p->name], &pool[p->version]);
   875 }
   876 
   877 void
   878 razor_set_list_requires(struct razor_set *set, const char *name)
   879 {
   880 	struct razor_property *p, *requires;
   881 	struct razor_package *package;
   882 	unsigned long *r;
   883 	char *pool;
   884 
   885 	if (name) {
   886 		package = razor_set_get_package(set, name);
   887 		r = (unsigned long *) set->property_pool.data +
   888 			package->requires;
   889 		requires = set->requires.data;
   890 		pool = set->string_pool.data;
   891 		while (*r) {
   892 			p = &requires[*r++];
   893 			printf("%s %s\n", &pool[p->name], &pool[p->version]);
   894 		}
   895 	} else
   896 		razor_set_list_all_properties(set, &set->requires);
   897 }
   898 
   899 void
   900 razor_set_list_provides(struct razor_set *set, const char *name)
   901 {
   902 	struct razor_property *p, *provides;
   903 	struct razor_package *package;
   904 	unsigned long *r;
   905 	char *pool;
   906 
   907 	if (name) {
   908 		package = razor_set_get_package(set, name);
   909 		r = (unsigned long *) set->property_pool.data +
   910 			package->provides;
   911 		provides = set->provides.data;
   912 		pool = set->string_pool.data;
   913 		while (*r) {
   914 			p = &provides[*r++];
   915 			printf("%s %s\n", &pool[p->name], &pool[p->version]);
   916 		}
   917 	} else 
   918 		razor_set_list_all_properties(set, &set->provides);
   919 }
   920 
   921 void
   922 razor_set_info(struct razor_set *set)
   923 {
   924 	unsigned int offset, size;
   925 	int i;
   926 
   927 	for (i = 0; i < set->header->sections[i].type; i++) {
   928 		offset = set->header->sections[i].offset;
   929 		size = set->header->sections[i + 1].offset - offset;
   930 
   931 		switch (set->header->sections[i].type) {
   932 		case RAZOR_STRINGS:
   933 			printf("string pool:\t\t%dkb\n", size / 1024);
   934 			break;
   935 		case RAZOR_PACKAGES:
   936 			printf("package section:\t%dkb\n", size / 1024);
   937 			break;
   938 		case RAZOR_REQUIRES:
   939 			printf("requires section:\t%dkb\n", size / 1024);
   940 			break;
   941 		case RAZOR_PROVIDES:
   942 			printf("provides section:\t%dkb\n", size / 1024);
   943 			break;
   944 		}
   945 	}
   946 }
   947 
   948 static int
   949 usage(void)
   950 {
   951 	printf("usage: razor [ import FILES | lookup <key> | "
   952 	       "list | list-requires | list-provides | eat-yum | info ]\n");
   953 	exit(1);
   954 }
   955 
   956 static const char *repo_filename = "system.repo";
   957 static const char rawhide_repo_filename[] = "rawhide.repo";
   958 
   959 int
   960 main(int argc, char *argv[])
   961 {
   962 	int i;
   963 	struct razor_set *set;
   964 	struct stat statbuf;
   965 	struct import_context ctx;
   966 	char *repo;
   967 
   968 	repo = getenv("RAZOR_REPO");
   969 	if (repo != NULL)
   970 		repo_filename = repo;
   971 
   972 	if (argc < 2) {
   973 		usage();
   974 	} else if (strcmp(argv[1], "import") == 0) {
   975 		if (stat("set", &statbuf) && mkdir("set", 0777)) {
   976 			fprintf(stderr, "could not create directory 'set'\n");
   977 			exit(-1);
   978 		}
   979 			
   980 		razor_prepare_import(&ctx);
   981 
   982 		for (i = 2; i < argc; i++) {
   983 			if (razor_import(&ctx, argv[i]) < 0) {
   984 				fprintf(stderr, "failed to import %s\n",
   985 					argv[i]);
   986 				exit(-1);
   987 			}
   988 		}
   989 
   990 		set = razor_finish_import(&ctx);
   991 
   992 		printf("bucket allocation: %d\n", set->buckets.alloc);
   993 		printf("pool size: %d\n", set->string_pool.size);
   994 		printf("pool allocation: %d\n", set->string_pool.alloc);
   995 		printf("packages: %d\n",
   996 		       set->packages.size / sizeof(struct razor_package));
   997 		printf("requires: %d\n",
   998 		       set->requires.size / sizeof(struct razor_property));
   999 		printf("provides: %d\n",
  1000 		       set->provides.size / sizeof(struct razor_property));
  1001 
  1002 		razor_set_write(set, repo_filename);
  1003 
  1004 		razor_set_destroy(set);
  1005 	} else if (strcmp(argv[1], "lookup") == 0) {
  1006 		set = razor_set_open(repo_filename);
  1007 		printf("%s is %lu\n", argv[2],
  1008 		       razor_set_lookup(set, argv[2]));
  1009 		razor_set_destroy(set);
  1010 	} else if (strcmp(argv[1], "list") == 0) {
  1011 		set = razor_set_open(repo_filename);
  1012 		razor_set_list(set);
  1013 		razor_set_destroy(set);
  1014 	} else if (strcmp(argv[1], "list-requires") == 0) {
  1015 		set = razor_set_open(repo_filename);
  1016 		razor_set_list_requires(set, argv[2]);
  1017 		razor_set_destroy(set);
  1018 	} else if (strcmp(argv[1], "list-provides") == 0) {
  1019 		set = razor_set_open(repo_filename);
  1020 		razor_set_list_provides(set, argv[2]);
  1021 		razor_set_destroy(set);
  1022 	} else if (strcmp(argv[1], "info") == 0) {
  1023 		set = razor_set_open(repo_filename);
  1024 		razor_set_info(set);
  1025 		razor_set_destroy(set);
  1026 	} else if (strcmp(argv[1], "eat-yum") == 0) {
  1027 		set = razor_set_create_from_yum_filelist(STDIN_FILENO);
  1028 		if (set == NULL)
  1029 			return 1;
  1030 		razor_set_write(set, rawhide_repo_filename);
  1031 		razor_set_destroy(set);
  1032 	} else {
  1033 		usage();
  1034 	}
  1035 
  1036 	return 0;
  1037 }