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