razor.c
author Kristian H?gsberg <krh@redhat.com>
Fri Sep 07 11:56:04 2007 -0400 (2007-09-07)
changeset 13 6a715b310792
parent 12 71a410830f3d
child 14 d21d74d6e297
permissions -rw-r--r--
Clean up import logic and make it independent of XML parsing.
     1 #include <stdlib.h>
     2 #include <stdio.h>
     3 #include <string.h>
     4 #include <sys/types.h>
     5 #include <sys/stat.h>
     6 #include <sys/mman.h>
     7 #include <unistd.h>
     8 #include <fcntl.h>
     9 
    10 #include <expat.h>
    11 #include "sha1.h"
    12 
    13 struct array {
    14 	void *data;
    15 	int size, alloc;
    16 };
    17 
    18 static void
    19 array_init(struct array *array)
    20 {
    21 	memset(array, 0, sizeof *array);
    22 }
    23 
    24 static void
    25 array_release(struct array *array)
    26 {
    27 	free(array->data);
    28 }
    29 
    30 static void *
    31 array_add(struct array *array, int size)
    32 {
    33 	int alloc;
    34 	void *data, *p;
    35 
    36 	if (array->alloc > 0)
    37 		alloc = array->alloc;
    38 	else
    39 		alloc = 16;
    40 
    41 	while (alloc < array->size + size)
    42 		alloc *= 2;
    43 
    44 	if (array->alloc < alloc) {
    45 		data = realloc(array->data, alloc);
    46 		if (data == NULL)
    47 			return 0;
    48 		array->data = data;
    49 		array->alloc = alloc;
    50 	}
    51 
    52 	p = array->data + array->size;
    53 	array->size += size;
    54 
    55 	return p;
    56 }
    57 
    58 static int
    59 write_to_fd(int fd, void *p, size_t size)
    60 {
    61 	int rest, len;
    62 
    63 	rest = size;
    64 	while (rest > 0) {
    65 		len = write(fd, p, rest);
    66 		if (len < 0)
    67 			return -1;
    68 		rest -= len;
    69 	}
    70 
    71 	return 0;
    72 }
    73 
    74 static int
    75 write_to_file(const char *filename, void *p, size_t size)
    76 {
    77 	int fd, err;
    78 
    79 	fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666);
    80 	if (fd < 0)
    81 		return -1;
    82 	err = write_to_fd(fd, p, size);
    83 	close(fd);
    84 
    85 	return err;
    86 }
    87 
    88 static void *
    89 zalloc(size_t size)
    90 {
    91 	void *p;
    92 
    93 	p = malloc(size);
    94 	memset(p, 0, size);
    95 
    96 	return p;
    97 }
    98 
    99 struct razor_set_header {
   100 	unsigned int magic;
   101 	unsigned int version;
   102 	struct { unsigned int type, offset; } sections[0];
   103 };
   104 
   105 #define RAZOR_MAGIC 0x7a7a7a7a
   106 #define RAZOR_VERSION 1
   107 
   108 #define RAZOR_BUCKETS 1
   109 #define RAZOR_STRINGS 2
   110 #define RAZOR_PACKAGES 3
   111 #define RAZOR_REQUIRES 4
   112 #define RAZOR_PROVIDES 5
   113 #define RAZOR_PROPERTIES 6
   114 
   115 struct razor_package {
   116 	unsigned long name;
   117 	unsigned long version;
   118 	unsigned long requires;
   119 	unsigned long provides;
   120 };
   121 
   122 struct razor_property {
   123 	unsigned long name;
   124 	unsigned long version;
   125 	unsigned long packages;
   126 };
   127 
   128 struct razor_set {
   129 	struct array buckets;
   130 	struct array string_pool;
   131 	struct array property_pool;
   132  	struct array packages;
   133  	struct array requires;
   134  	struct array provides;
   135 	struct razor_set_header *header;
   136 };
   137 
   138 struct razor_set *
   139 razor_set_create(void)
   140 {
   141 	struct razor_set *set;
   142 	char *p;
   143 
   144 	set = zalloc(sizeof(struct razor_set));
   145 	p = array_add(&set->string_pool, 1);
   146 	*p = '\0';
   147 
   148 	return set;
   149 }
   150 
   151 struct razor_set *
   152 razor_set_open(const char *filename)
   153 {
   154 	struct razor_set *set;
   155 	struct stat stat;
   156 	unsigned int size, offset;
   157 	int fd, i;
   158 
   159 	set = zalloc(sizeof *set);
   160 	fd = open(filename, O_RDONLY);
   161 	if (fstat(fd, &stat) < 0)
   162 		return NULL;
   163 	set->header = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
   164 	if (set->header == MAP_FAILED) {
   165 		free(set);
   166 		return NULL;
   167 	}
   168 
   169 	for (i = 0; i < set->header->sections[i].type; i++) {
   170 		offset = set->header->sections[i].offset;
   171 		size = set->header->sections[i + 1].offset - offset;
   172 
   173 		switch (set->header->sections[i].type) {
   174 		case RAZOR_BUCKETS:
   175 			set->buckets.data = (void *) set->header + offset;
   176 			set->buckets.size = size;
   177 			set->buckets.alloc = size;
   178 			break;
   179 		case RAZOR_STRINGS:
   180 			set->string_pool.data = (void *) set->header + offset;
   181 			set->string_pool.size = size;
   182 			set->string_pool.alloc = size;
   183 			break;
   184 		case RAZOR_PACKAGES:
   185 			set->packages.data = (void *) set->header + offset;
   186 			set->packages.size = size;
   187 			set->packages.size = size;
   188 			break;
   189 		case RAZOR_REQUIRES:
   190 			set->requires.data = (void *) set->header + offset;
   191 			set->requires.size = size;
   192 			set->requires.size = size;
   193 			break;
   194 		case RAZOR_PROVIDES:
   195 			set->provides.data = (void *) set->header + offset;
   196 			set->provides.size = size;
   197 			set->provides.size = size;
   198 			break;
   199 		case RAZOR_PROPERTIES:
   200 			set->property_pool.data = (void *) set->header + offset;
   201 			set->property_pool.size = size;
   202 			set->property_pool.size = size;
   203 			break;
   204 		}
   205 	}
   206 	close(fd);
   207 
   208 	return set;
   209 }
   210 
   211 void
   212 razor_set_destroy(struct razor_set *set)
   213 {
   214 	unsigned int size;
   215 	int i;
   216 
   217 	if (set->header) {
   218 		for (i = 0; set->header->sections[i].type; i++)
   219 			;
   220 		size = set->header->sections[i].type;
   221 		munmap(set->header, size);
   222 	} else {
   223 		free(set->buckets.data);
   224 		free(set->string_pool.data);
   225 		free(set->packages.data);
   226 		free(set->requires.data);
   227 		free(set->provides.data);
   228 		free(set->property_pool.data);
   229 	}
   230 
   231 	free(set);
   232 }
   233 
   234 static int
   235 razor_set_write(struct razor_set *set, const char *filename)
   236 {
   237 	char data[4096];
   238 	struct razor_set_header *header = (struct razor_set_header *) data;
   239 	int fd, pool_size, packages_size, requires_size, provides_size;
   240 	int properties_size;
   241 
   242 	/* Align these to pages sizes */
   243 	pool_size = (set->string_pool.size + 4095) & ~4095;
   244 	packages_size = (set->packages.size + 4095) & ~4095;
   245 	requires_size = (set->requires.size + 4095) & ~4095;
   246 	provides_size = (set->provides.size + 4095) & ~4095;
   247 	properties_size = (set->property_pool.size + 4095) & ~4095;
   248 
   249 	memset(data, 0, sizeof data);
   250 	header->magic = RAZOR_MAGIC;
   251 	header->version = RAZOR_VERSION;
   252 
   253 	header->sections[0].type = RAZOR_BUCKETS;
   254 	header->sections[0].offset = sizeof data;
   255 
   256 	header->sections[1].type = RAZOR_STRINGS;
   257 	header->sections[1].offset =
   258 		header->sections[0].offset + set->buckets.alloc;
   259 
   260 	header->sections[2].type = RAZOR_PACKAGES;
   261 	header->sections[2].offset =
   262 		header->sections[1].offset + pool_size;
   263 
   264 	header->sections[3].type = RAZOR_REQUIRES;
   265 	header->sections[3].offset =
   266 		header->sections[2].offset + packages_size;
   267 
   268 	header->sections[4].type = RAZOR_PROVIDES;
   269 	header->sections[4].offset =
   270 		header->sections[3].offset + requires_size;
   271 
   272 	header->sections[5].type = RAZOR_PROPERTIES;
   273 	header->sections[5].offset =
   274 		header->sections[4].offset + provides_size;
   275 
   276 	header->sections[6].type = 0;
   277 	header->sections[6].offset =
   278 		header->sections[5].offset + properties_size;
   279 
   280 	fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666);
   281 	if (fd < 0)
   282 		return -1;
   283 
   284 	write_to_fd(fd, data, sizeof data);
   285 	write_to_fd(fd, set->buckets.data, set->buckets.alloc);
   286 	write_to_fd(fd, set->string_pool.data, pool_size);
   287 	write_to_fd(fd, set->packages.data, packages_size);
   288 	write_to_fd(fd, set->requires.data, requires_size);
   289 	write_to_fd(fd, set->provides.data, provides_size);
   290 	write_to_fd(fd, set->property_pool.data, properties_size);
   291 
   292 	return 0;
   293 }
   294 
   295 static unsigned int
   296 hash_string(const char *key)
   297 {
   298 	const char *p;
   299 	unsigned int hash = 0;
   300 
   301 	for (p = key; *p; p++)
   302 		hash = (hash * 617) ^ *p;
   303 
   304 	return hash;
   305 }
   306 
   307 unsigned long
   308 razor_set_lookup(struct razor_set *set, const char *key)
   309 {
   310 	unsigned int mask, start, i;
   311 	unsigned long *b;
   312 	char *pool;
   313 
   314 	pool = set->string_pool.data;
   315 	mask = set->buckets.alloc - 1;
   316 	start = hash_string(key) * sizeof(unsigned long);
   317 
   318 	for (i = 0; i < set->buckets.alloc; i += sizeof *b) {
   319 		b = set->buckets.data + ((start + i) & mask);
   320 
   321 		if (*b == 0)
   322 			return 0;
   323 
   324 		if (strcmp(key, &pool[*b]) == 0)
   325 			return *b;
   326 	}
   327 
   328 	return 0;
   329 }
   330 
   331 static unsigned long
   332 add_to_string_pool(struct razor_set *set, const char *key)
   333 {
   334 	int len;
   335 	char *p;
   336 
   337 	len = strlen(key) + 1;
   338 	p = array_add(&set->string_pool, len);
   339 	memcpy(p, key, len);
   340 
   341 	return p - (char *) set->string_pool.data;
   342 }
   343 
   344 static unsigned long
   345 add_to_property_pool(struct razor_set *set, struct array *properties)
   346 {
   347 	unsigned long  *p;
   348 
   349 	p = array_add(properties, sizeof *p);
   350 	*p = 0;
   351 	p = array_add(&set->property_pool, properties->size);
   352 	memcpy(p, properties->data, properties->size);
   353 
   354 	return p - (unsigned long *) set->property_pool.data;
   355 }
   356 
   357 static void
   358 do_insert(struct razor_set *set, unsigned long value)
   359 {
   360 	unsigned int mask, start, i;
   361 	unsigned long *b;
   362 	const char *key;
   363 
   364 	key = (char *) set->string_pool.data + value;
   365 	mask = set->buckets.alloc - 1;
   366 	start = hash_string(key) * sizeof(unsigned long);
   367 
   368 	for (i = 0; i < set->buckets.alloc; i += sizeof *b) {
   369 		b = set->buckets.data + ((start + i) & mask);
   370 		if (*b == 0) {
   371 			*b = value;
   372 			break;
   373 		}
   374 	}
   375 }
   376 
   377 unsigned long
   378 razor_set_insert(struct razor_set *set, const char *key)
   379 {
   380 	unsigned long value, *buckets, *b, *end;
   381 	int alloc;
   382 
   383 	alloc = set->buckets.alloc;
   384 	array_add(&set->buckets, 4 * sizeof *buckets);
   385 	if (alloc != set->buckets.alloc) {
   386 		end = set->buckets.data + alloc;
   387 		memset(end, 0, set->buckets.alloc - alloc);
   388 		for (b = set->buckets.data; b < end; b++) {
   389 			value = *b;
   390 			if (value != 0) {
   391 				*b = 0;
   392 				do_insert(set, value);
   393 			}
   394 		}
   395 	}
   396 
   397 	value = add_to_string_pool(set, key);
   398 	do_insert (set, value);
   399 
   400 	return value;
   401 }
   402 
   403 unsigned long
   404 razor_set_tokenize(struct razor_set *set, const char *string)
   405 {
   406 	unsigned long token;
   407 
   408 	if (string == NULL)
   409 		return 0;
   410 
   411 	token = razor_set_lookup(set, string);
   412 	if (token != 0)
   413 		return token;
   414 
   415 	return razor_set_insert(set, string);
   416 }
   417 
   418 struct import_property_context {
   419 	struct array all;
   420 	struct array package;
   421 };
   422 
   423 struct import_context {
   424 	struct razor_set *set;
   425 	struct import_property_context requires;
   426 	struct import_property_context provides;
   427 	unsigned long package;
   428 	unsigned long *requires_map;
   429 	unsigned long *provides_map;
   430 };
   431 
   432 struct import_property {
   433 	unsigned long name;
   434 	unsigned long version;
   435 	unsigned long package;
   436 	unsigned long index;
   437 	unsigned long unique_index;
   438 };
   439 
   440 static void
   441 import_context_add_package(struct import_context *ctx,
   442 			   const char *name, const char *version)
   443 {
   444 	struct razor_package *p;
   445 
   446 	p = array_add(&ctx->set->packages, sizeof *p);
   447 	p->name = razor_set_tokenize(ctx->set, name);
   448 	p->version = razor_set_tokenize(ctx->set, version);
   449 
   450 	ctx->package = p - (struct razor_package *) ctx->set->packages.data;
   451 	array_init(&ctx->requires.package);
   452 	array_init(&ctx->provides.package);
   453 }
   454 
   455 void
   456 import_context_finish_package(struct import_context *ctx)
   457 {
   458 	struct razor_package *p;
   459 
   460 	p = (struct razor_package *) ctx->set->packages.data + ctx->package;
   461 	p->requires = add_to_property_pool(ctx->set, &ctx->requires.package);
   462 	p->provides = add_to_property_pool(ctx->set, &ctx->provides.package);
   463 
   464 	array_release(&ctx->requires.package);
   465 	array_release(&ctx->provides.package);
   466 }
   467 
   468 static void
   469 import_context_add_property(struct import_context *ctx,
   470 			    struct import_property_context *pctx,
   471 			    const char *name, const char *version)
   472 {
   473 	struct import_property *p;
   474 	unsigned long *r;
   475 
   476 	p = array_add(&pctx->all, sizeof *p);
   477 	p->name = razor_set_tokenize(ctx->set, name);
   478 	p->version = razor_set_tokenize(ctx->set, version);
   479 	p->package = ctx->package;
   480 	p->index = p - (struct import_property *) pctx->all.data;
   481 
   482 	r = array_add(&pctx->package, sizeof *r);
   483 	*r = p->index;
   484 }
   485 
   486 static void
   487 parse_package(struct import_context *ctx, const char **atts, void *data)
   488 {
   489 	const char *name = NULL, *version = NULL;
   490 	int i;
   491 
   492 	for (i = 0; atts[i]; i += 2) {
   493 		if (strcmp(atts[i], "name") == 0)
   494 			name = atts[i + 1];
   495 		else if (strcmp(atts[i], "version") == 0)
   496 			version = atts[i + 1];
   497 	}
   498 
   499 	if (name == NULL || version == NULL) {
   500 		fprintf(stderr, "invalid package tag, "
   501 			"missing name or version attributes\n");
   502 		return;
   503 	}
   504 
   505 	import_context_add_package(ctx, name, version);
   506 }
   507 
   508 static void
   509 parse_property(struct import_context *ctx, const char **atts, void *data)
   510 {
   511 	const char *name = NULL, *version = NULL;
   512 	int i;
   513 
   514 	for (i = 0; atts[i]; i += 2) {
   515 		if (strcmp(atts[i], "name") == 0)
   516 			name = atts[i + 1];
   517 		if (strcmp(atts[i], "version") == 0)
   518 			version = atts[i + 1];
   519 	}
   520 	
   521 	if (name == NULL) {
   522 		fprintf(stderr, "invalid tag, missing name attribute\n");
   523 		return;
   524 	}
   525 
   526 	import_context_add_property(ctx, data, name, version);
   527 }
   528 
   529 static void
   530 start_element(void *data, const char *name, const char **atts)
   531 {
   532 	struct import_context *ctx = data;
   533 
   534 	if (strcmp(name, "package") == 0)
   535 		parse_package(ctx, atts, NULL);
   536 	else if (strcmp(name, "requires") == 0)
   537 		parse_property(ctx, atts, &ctx->requires);
   538 	else if (strcmp(name, "provides") == 0)
   539 		parse_property(ctx, atts, &ctx->provides);
   540 }
   541 
   542 static void
   543 end_element (void *data, const char *name)
   544 {
   545 	struct import_context *ctx = data;
   546 
   547 	if (strcmp(name, "package") == 0)
   548 		import_context_finish_package(ctx);
   549 }
   550 
   551 static char *
   552 sha1_to_hex(const unsigned char *sha1)
   553 {
   554 	static int bufno;
   555 	static char hexbuffer[4][50];
   556 	static const char hex[] = "0123456789abcdef";
   557 	char *buffer = hexbuffer[3 & ++bufno], *buf = buffer;
   558 	int i;
   559 
   560 	for (i = 0; i < 20; i++) {
   561 		unsigned int val = *sha1++;
   562 		*buf++ = hex[val >> 4];
   563 		*buf++ = hex[val & 0xf];
   564 	}
   565 	*buf = '\0';
   566 
   567 	return buffer;
   568 }
   569 
   570 static void
   571 razor_prepare_import(struct import_context *ctx)
   572 {
   573 	memset(ctx, 0, sizeof *ctx);
   574 	ctx->set = razor_set_create();
   575 }
   576 
   577 static int
   578 razor_import(struct import_context *ctx, const char *filename)
   579 {
   580 	SHA_CTX sha1;
   581 	XML_Parser parser;
   582 	int fd;
   583 	void *p;
   584 	struct stat stat;
   585 	char buf[128];
   586 	unsigned char hash[20];
   587 
   588 	fd = open(filename, O_RDONLY);
   589 	if (fstat(fd, &stat) < 0)
   590 		return -1;
   591 	p = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
   592 	if (p == MAP_FAILED)
   593 		return -1;
   594 
   595 	parser = XML_ParserCreate(NULL);
   596 	XML_SetUserData(parser, ctx);
   597 	XML_SetElementHandler(parser, start_element, end_element);
   598 	if (XML_Parse(parser, p, stat.st_size, 1) == XML_STATUS_ERROR) {
   599 		fprintf(stderr,
   600 			"%s at line %d, %s\n",
   601 			XML_ErrorString(XML_GetErrorCode(parser)),
   602 			XML_GetCurrentLineNumber(parser),
   603 			filename);
   604 		return 1;
   605 	}
   606 
   607 	XML_ParserFree(parser);
   608 
   609 	SHA1_Init(&sha1);
   610 	SHA1_Update(&sha1, p, stat.st_size);
   611 	SHA1_Final(hash, &sha1);
   612 
   613 	close(fd);
   614 
   615 	snprintf(buf, sizeof buf, "set/%s", sha1_to_hex(hash));
   616 	if (write_to_file(buf, p, stat.st_size) < 0)
   617 		return -1;
   618 	munmap(p, stat.st_size);
   619 
   620 	return 0;
   621 }
   622 
   623 static struct razor_set *qsort_set;
   624 
   625 static int
   626 compare_packages(const void *p1, const void *p2)
   627 {
   628 	const struct razor_package *pkg1 = p1, *pkg2 = p2;
   629 	char *pool = qsort_set->string_pool.data;
   630 
   631 	return strcmp(&pool[pkg1->name], &pool[pkg2->name]);
   632 }
   633 
   634 static int
   635 compare_properties(const void *p1, const void *p2)
   636 {
   637 	const struct import_property *prop1 = p1, *prop2 = p2;
   638 	char *pool = qsort_set->string_pool.data;
   639 	int result;
   640 
   641 	result = strcmp(&pool[prop1->name], &pool[prop2->name]);
   642 	if (result == 0)
   643 		return strcmp(&pool[prop1->version], &pool[prop2->version]);
   644 	else
   645 		return result;
   646 }
   647 
   648 static unsigned long *
   649 uniqueify_properties(struct array *in, struct array *out)
   650 {
   651 	struct import_property *ip, *end;
   652 	struct razor_property *rp;
   653 	unsigned long *map;
   654 	int i, count;
   655 
   656 	count = in->size / sizeof(struct import_property);
   657 	qsort(in->data, count,
   658 	      sizeof(struct import_property), compare_properties);
   659 
   660 	rp = NULL;
   661 	end = in->data + in->size;
   662 	for (ip = in->data; ip < end; ip++) {
   663 		if (rp == NULL ||
   664 		    ip->name != rp->name || ip->version != rp->version) {
   665 			rp = array_add(out, sizeof *rp);
   666 			rp->name = ip->name;
   667 			rp->version = ip->version;
   668 		}
   669 		ip->unique_index = rp - (struct razor_property *) out->data;
   670 	}
   671 
   672 	map = malloc(count * sizeof (unsigned long));
   673 	ip = in->data;
   674 	for (i = 0; i < count; i++)
   675 		map[ip[i].index] = ip[i].unique_index;
   676 
   677 	return map;
   678 }
   679 
   680 static void
   681 sort_packages(struct import_context *ctx)
   682 {
   683 	struct razor_package *p, *end;
   684 	unsigned long *pool, *r;
   685 
   686 	pool = ctx->set->property_pool.data;
   687 	end = ctx->set->packages.data + ctx->set->packages.size;
   688 	for (p = ctx->set->packages.data; p < end; p++) {
   689 		for (r = &pool[p->requires]; *r; r++)
   690 			*r = ctx->requires_map[*r];
   691 		for (r = &pool[p->provides]; *r; r++)
   692 			*r = ctx->provides_map[*r];
   693 	}
   694 
   695 	qsort(ctx->set->packages.data,
   696 	      ctx->set->packages.size / sizeof(struct razor_package),
   697 	      sizeof(struct razor_package), compare_packages);
   698 }
   699 
   700 static struct razor_set *
   701 razor_finish_import(struct import_context *ctx)
   702 {
   703 	struct razor_package *pkg;
   704 	struct razor_property *prop;
   705 
   706 	qsort_set = ctx->set;
   707 
   708 	ctx->requires_map =
   709 		uniqueify_properties(&ctx->requires.all, &ctx->set->requires);
   710 	ctx->provides_map =
   711 		uniqueify_properties(&ctx->provides.all, &ctx->set->provides);
   712 
   713 	sort_packages(ctx);
   714 
   715 	free(ctx->requires.all.data);
   716 	free(ctx->provides.all.data);
   717 	free(ctx->requires_map);
   718 	free(ctx->provides_map);
   719 
   720 	/* FIXME: We add sentinel package/props here, but we should
   721 	 * probably just have a size field in the header section. */
   722 	pkg = array_add(&ctx->set->packages, sizeof *pkg);
   723 	pkg->name = 0;
   724 	prop = array_add(&ctx->set->requires, sizeof *prop);
   725 	prop->name = 0;
   726 	prop = array_add(&ctx->set->provides, sizeof *prop);
   727 	prop->name = 0;
   728 		
   729 	fprintf(stderr, "parsed %d requires, %d unique\n",
   730 		ctx->requires.all.size / sizeof(struct import_property),
   731 		ctx->set->requires.size / sizeof(struct razor_property));
   732 	fprintf(stderr, "parsed %d provides, %d unique\n",
   733 		ctx->provides.all.size / sizeof(struct import_property),
   734 		ctx->set->provides.size / sizeof(struct razor_property));
   735 
   736 	return ctx->set; 
   737 }
   738 
   739 void
   740 razor_set_list(struct razor_set *set)
   741 {
   742 	struct razor_package *p, *end;
   743 	char *pool;
   744 
   745 	pool = set->string_pool.data;
   746 	end = set->packages.data + set->packages.size;
   747 	for (p = set->packages.data; p < end && p->name; p++)
   748 		printf("%s %s\n", &pool[p->name], &pool[p->version]);
   749 }
   750 
   751 struct razor_package *
   752 razor_set_get_package(struct razor_set *set, const char *package)
   753 {
   754 	unsigned long name;
   755 	struct razor_package *p, *end;
   756 
   757 	name = razor_set_lookup(set, package);
   758 	end = set->packages.data + set->packages.size;
   759 	for (p = set->packages.data; p < end && p->name; p++)
   760 		if (p->name == name)
   761 			return p;
   762 
   763 	return NULL;
   764 }
   765 
   766 static void
   767 razor_set_list_all_properties(struct razor_set *set, struct array *properties)
   768 {
   769 	struct razor_property *p, *end;
   770 	char *pool;
   771 
   772 	pool = set->string_pool.data;
   773 	end = properties->data + properties->size;
   774 	for (p = properties->data; p < end && p->name; p++)
   775 		printf("%s %s\n", &pool[p->name], &pool[p->version]);
   776 }
   777 
   778 void
   779 razor_set_list_requires(struct razor_set *set, const char *name)
   780 {
   781 	struct razor_property *p, *requires;
   782 	struct razor_package *package;
   783 	unsigned long *r;
   784 	char *pool;
   785 
   786 	if (name) {
   787 		package = razor_set_get_package(set, name);
   788 		r = (unsigned long *) set->property_pool.data +
   789 			package->requires;
   790 		requires = set->requires.data;
   791 		pool = set->string_pool.data;
   792 		while (*r) {
   793 			p = &requires[*r++];
   794 			printf("%s %s\n", &pool[p->name], &pool[p->version]);
   795 		}
   796 	} else
   797 		razor_set_list_all_properties(set, &set->requires);
   798 }
   799 
   800 void
   801 razor_set_list_provides(struct razor_set *set, const char *name)
   802 {
   803 	struct razor_property *p, *provides;
   804 	struct razor_package *package;
   805 	unsigned long *r;
   806 	char *pool;
   807 
   808 	if (name) {
   809 		package = razor_set_get_package(set, name);
   810 		r = (unsigned long *) set->property_pool.data +
   811 			package->provides;
   812 		provides = set->provides.data;
   813 		pool = set->string_pool.data;
   814 		while (*r) {
   815 			p = &provides[*r++];
   816 			printf("%s %s\n", &pool[p->name], &pool[p->version]);
   817 		}
   818 	} else 
   819 		razor_set_list_all_properties(set, &set->provides);
   820 }
   821 
   822 void
   823 razor_set_info(struct razor_set *set)
   824 {
   825 	unsigned int offset, size;
   826 	int i;
   827 
   828 	for (i = 0; i < set->header->sections[i].type; i++) {
   829 		offset = set->header->sections[i].offset;
   830 		size = set->header->sections[i + 1].offset - offset;
   831 
   832 		switch (set->header->sections[i].type) {
   833 		case RAZOR_BUCKETS:
   834 			printf("bucket section:\t\t%dkb\n", size / 1024);
   835 			break;
   836 		case RAZOR_STRINGS:
   837 			printf("string pool:\t\t%dkb\n", size / 1024);
   838 			break;
   839 		case RAZOR_PACKAGES:
   840 			printf("package section:\t%dkb\n", size / 1024);
   841 			break;
   842 		case RAZOR_REQUIRES:
   843 			printf("requires section:\t%dkb\n", size / 1024);
   844 			break;
   845 		case RAZOR_PROVIDES:
   846 			printf("provides section:\t%dkb\n", size / 1024);
   847 			break;
   848 		}
   849 	}
   850 }
   851 
   852 static int
   853 usage(void)
   854 {
   855 	printf("usage: razor [ import FILES | lookup <key> | "
   856 	       "list | list-requires | list-provides | info ]\n");
   857 	exit(1);
   858 }
   859 
   860 static const char repo_filename[] = "system.repo";
   861 
   862 int
   863 main(int argc, char *argv[])
   864 {
   865 	int i;
   866 	struct razor_set *set;
   867 	struct stat statbuf;
   868 	struct import_context ctx;
   869 
   870 	if (argc < 2) {
   871 		usage();
   872 	} else if (strcmp(argv[1], "import") == 0) {
   873 		if (stat("set", &statbuf) && mkdir("set", 0777)) {
   874 			fprintf(stderr, "could not create directory 'set'\n");
   875 			exit(-1);
   876 		}
   877 			
   878 		razor_prepare_import(&ctx);
   879 
   880 		for (i = 2; i < argc; i++) {
   881 			if (razor_import(&ctx, argv[i]) < 0) {
   882 				fprintf(stderr, "failed to import %s\n",
   883 					argv[i]);
   884 				exit(-1);
   885 			}
   886 		}
   887 
   888 		set = razor_finish_import(&ctx);
   889 
   890 		printf("bucket allocation: %d\n", set->buckets.alloc);
   891 		printf("pool size: %d\n", set->string_pool.size);
   892 		printf("pool allocation: %d\n", set->string_pool.alloc);
   893 		printf("packages: %d\n",
   894 		       set->packages.size / sizeof(struct razor_package));
   895 		printf("requires: %d\n",
   896 		       set->requires.size / sizeof(struct razor_property));
   897 		printf("provides: %d\n",
   898 		       set->provides.size / sizeof(struct razor_property));
   899 
   900 		razor_set_write(set, repo_filename);
   901 
   902 		razor_set_destroy(set);
   903 	} else if (strcmp(argv[1], "lookup") == 0) {
   904 		set = razor_set_open(repo_filename);
   905 		printf("%s is %lu\n", argv[2],
   906 		       razor_set_lookup(set, argv[2]));
   907 		razor_set_destroy(set);
   908 	} else if (strcmp(argv[1], "list") == 0) {
   909 		set = razor_set_open(repo_filename);
   910 		razor_set_list(set);
   911 		razor_set_destroy(set);
   912 	} else if (strcmp(argv[1], "list-requires") == 0) {
   913 		set = razor_set_open(repo_filename);
   914 		razor_set_list_requires(set, argv[2]);
   915 		razor_set_destroy(set);
   916 	} else if (strcmp(argv[1], "list-provides") == 0) {
   917 		set = razor_set_open(repo_filename);
   918 		razor_set_list_provides(set, argv[2]);
   919 		razor_set_destroy(set);
   920 	} else if (strcmp(argv[1], "info") == 0) {
   921 		set = razor_set_open(repo_filename);
   922 		razor_set_info(set);
   923 		razor_set_destroy(set);
   924 	} else {
   925 		usage();
   926 	}
   927 
   928 	return 0;
   929 }