razor.c
author Kristian H?gsberg <krh@redhat.com>
Fri Sep 07 12:11:37 2007 -0400 (2007-09-07)
changeset 14 d21d74d6e297
parent 13 6a715b310792
child 15 228bd47c5dc4
permissions -rw-r--r--
Add a size field to the section headers to avoid sentinels.
     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, size; } 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].size;
   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 	header->sections[0].size = set->buckets.alloc;
   256 
   257 	header->sections[1].type = RAZOR_STRINGS;
   258 	header->sections[1].offset =
   259 		header->sections[0].offset + set->buckets.alloc;
   260 	header->sections[1].size = set->string_pool.size;
   261 
   262 	header->sections[2].type = RAZOR_PACKAGES;
   263 	header->sections[2].offset =
   264 		header->sections[1].offset + pool_size;
   265 	header->sections[2].size = set->packages.size;
   266 
   267 	header->sections[3].type = RAZOR_REQUIRES;
   268 	header->sections[3].offset =
   269 		header->sections[2].offset + packages_size;
   270 	header->sections[3].size = set->requires.size;
   271 
   272 	header->sections[4].type = RAZOR_PROVIDES;
   273 	header->sections[4].offset =
   274 		header->sections[3].offset + requires_size;
   275 	header->sections[4].size = set->provides.size;
   276 
   277 	header->sections[5].type = RAZOR_PROPERTIES;
   278 	header->sections[5].offset =
   279 		header->sections[4].offset + provides_size;
   280 	header->sections[5].size = set->property_pool.size;
   281 
   282 	header->sections[6].type = 0;
   283 	header->sections[6].offset =
   284 		header->sections[5].offset + properties_size;
   285 	header->sections[6].size = 0;
   286 
   287 	fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666);
   288 	if (fd < 0)
   289 		return -1;
   290 
   291 	write_to_fd(fd, data, sizeof data);
   292 	write_to_fd(fd, set->buckets.data, set->buckets.alloc);
   293 	write_to_fd(fd, set->string_pool.data, pool_size);
   294 	write_to_fd(fd, set->packages.data, packages_size);
   295 	write_to_fd(fd, set->requires.data, requires_size);
   296 	write_to_fd(fd, set->provides.data, provides_size);
   297 	write_to_fd(fd, set->property_pool.data, properties_size);
   298 
   299 	return 0;
   300 }
   301 
   302 static unsigned int
   303 hash_string(const char *key)
   304 {
   305 	const char *p;
   306 	unsigned int hash = 0;
   307 
   308 	for (p = key; *p; p++)
   309 		hash = (hash * 617) ^ *p;
   310 
   311 	return hash;
   312 }
   313 
   314 unsigned long
   315 razor_set_lookup(struct razor_set *set, const char *key)
   316 {
   317 	unsigned int mask, start, i;
   318 	unsigned long *b;
   319 	char *pool;
   320 
   321 	pool = set->string_pool.data;
   322 	mask = set->buckets.alloc - 1;
   323 	start = hash_string(key) * sizeof(unsigned long);
   324 
   325 	for (i = 0; i < set->buckets.alloc; i += sizeof *b) {
   326 		b = set->buckets.data + ((start + i) & mask);
   327 
   328 		if (*b == 0)
   329 			return 0;
   330 
   331 		if (strcmp(key, &pool[*b]) == 0)
   332 			return *b;
   333 	}
   334 
   335 	return 0;
   336 }
   337 
   338 static unsigned long
   339 add_to_string_pool(struct razor_set *set, const char *key)
   340 {
   341 	int len;
   342 	char *p;
   343 
   344 	len = strlen(key) + 1;
   345 	p = array_add(&set->string_pool, len);
   346 	memcpy(p, key, len);
   347 
   348 	return p - (char *) set->string_pool.data;
   349 }
   350 
   351 static unsigned long
   352 add_to_property_pool(struct razor_set *set, struct array *properties)
   353 {
   354 	unsigned long  *p;
   355 
   356 	p = array_add(properties, sizeof *p);
   357 	*p = 0;
   358 	p = array_add(&set->property_pool, properties->size);
   359 	memcpy(p, properties->data, properties->size);
   360 
   361 	return p - (unsigned long *) set->property_pool.data;
   362 }
   363 
   364 static void
   365 do_insert(struct razor_set *set, unsigned long value)
   366 {
   367 	unsigned int mask, start, i;
   368 	unsigned long *b;
   369 	const char *key;
   370 
   371 	key = (char *) set->string_pool.data + value;
   372 	mask = set->buckets.alloc - 1;
   373 	start = hash_string(key) * sizeof(unsigned long);
   374 
   375 	for (i = 0; i < set->buckets.alloc; i += sizeof *b) {
   376 		b = set->buckets.data + ((start + i) & mask);
   377 		if (*b == 0) {
   378 			*b = value;
   379 			break;
   380 		}
   381 	}
   382 }
   383 
   384 unsigned long
   385 razor_set_insert(struct razor_set *set, const char *key)
   386 {
   387 	unsigned long value, *buckets, *b, *end;
   388 	int alloc;
   389 
   390 	alloc = set->buckets.alloc;
   391 	array_add(&set->buckets, 4 * sizeof *buckets);
   392 	if (alloc != set->buckets.alloc) {
   393 		end = set->buckets.data + alloc;
   394 		memset(end, 0, set->buckets.alloc - alloc);
   395 		for (b = set->buckets.data; b < end; b++) {
   396 			value = *b;
   397 			if (value != 0) {
   398 				*b = 0;
   399 				do_insert(set, value);
   400 			}
   401 		}
   402 	}
   403 
   404 	value = add_to_string_pool(set, key);
   405 	do_insert (set, value);
   406 
   407 	return value;
   408 }
   409 
   410 unsigned long
   411 razor_set_tokenize(struct razor_set *set, const char *string)
   412 {
   413 	unsigned long token;
   414 
   415 	if (string == NULL)
   416 		return 0;
   417 
   418 	token = razor_set_lookup(set, string);
   419 	if (token != 0)
   420 		return token;
   421 
   422 	return razor_set_insert(set, string);
   423 }
   424 
   425 struct import_property_context {
   426 	struct array all;
   427 	struct array package;
   428 };
   429 
   430 struct import_context {
   431 	struct razor_set *set;
   432 	struct import_property_context requires;
   433 	struct import_property_context provides;
   434 	unsigned long package;
   435 	unsigned long *requires_map;
   436 	unsigned long *provides_map;
   437 };
   438 
   439 struct import_property {
   440 	unsigned long name;
   441 	unsigned long version;
   442 	unsigned long package;
   443 	unsigned long index;
   444 	unsigned long unique_index;
   445 };
   446 
   447 static void
   448 import_context_add_package(struct import_context *ctx,
   449 			   const char *name, const char *version)
   450 {
   451 	struct razor_package *p;
   452 
   453 	p = array_add(&ctx->set->packages, sizeof *p);
   454 	p->name = razor_set_tokenize(ctx->set, name);
   455 	p->version = razor_set_tokenize(ctx->set, version);
   456 
   457 	ctx->package = p - (struct razor_package *) ctx->set->packages.data;
   458 	array_init(&ctx->requires.package);
   459 	array_init(&ctx->provides.package);
   460 }
   461 
   462 void
   463 import_context_finish_package(struct import_context *ctx)
   464 {
   465 	struct razor_package *p;
   466 
   467 	p = (struct razor_package *) ctx->set->packages.data + ctx->package;
   468 	p->requires = add_to_property_pool(ctx->set, &ctx->requires.package);
   469 	p->provides = add_to_property_pool(ctx->set, &ctx->provides.package);
   470 
   471 	array_release(&ctx->requires.package);
   472 	array_release(&ctx->provides.package);
   473 }
   474 
   475 static void
   476 import_context_add_property(struct import_context *ctx,
   477 			    struct import_property_context *pctx,
   478 			    const char *name, const char *version)
   479 {
   480 	struct import_property *p;
   481 	unsigned long *r;
   482 
   483 	p = array_add(&pctx->all, sizeof *p);
   484 	p->name = razor_set_tokenize(ctx->set, name);
   485 	p->version = razor_set_tokenize(ctx->set, version);
   486 	p->package = ctx->package;
   487 	p->index = p - (struct import_property *) pctx->all.data;
   488 
   489 	r = array_add(&pctx->package, sizeof *r);
   490 	*r = p->index;
   491 }
   492 
   493 static void
   494 parse_package(struct import_context *ctx, const char **atts, void *data)
   495 {
   496 	const char *name = NULL, *version = NULL;
   497 	int i;
   498 
   499 	for (i = 0; atts[i]; i += 2) {
   500 		if (strcmp(atts[i], "name") == 0)
   501 			name = atts[i + 1];
   502 		else if (strcmp(atts[i], "version") == 0)
   503 			version = atts[i + 1];
   504 	}
   505 
   506 	if (name == NULL || version == NULL) {
   507 		fprintf(stderr, "invalid package tag, "
   508 			"missing name or version attributes\n");
   509 		return;
   510 	}
   511 
   512 	import_context_add_package(ctx, name, version);
   513 }
   514 
   515 static void
   516 parse_property(struct import_context *ctx, const char **atts, void *data)
   517 {
   518 	const char *name = NULL, *version = NULL;
   519 	int i;
   520 
   521 	for (i = 0; atts[i]; i += 2) {
   522 		if (strcmp(atts[i], "name") == 0)
   523 			name = atts[i + 1];
   524 		if (strcmp(atts[i], "version") == 0)
   525 			version = atts[i + 1];
   526 	}
   527 	
   528 	if (name == NULL) {
   529 		fprintf(stderr, "invalid tag, missing name attribute\n");
   530 		return;
   531 	}
   532 
   533 	import_context_add_property(ctx, data, name, version);
   534 }
   535 
   536 static void
   537 start_element(void *data, const char *name, const char **atts)
   538 {
   539 	struct import_context *ctx = data;
   540 
   541 	if (strcmp(name, "package") == 0)
   542 		parse_package(ctx, atts, NULL);
   543 	else if (strcmp(name, "requires") == 0)
   544 		parse_property(ctx, atts, &ctx->requires);
   545 	else if (strcmp(name, "provides") == 0)
   546 		parse_property(ctx, atts, &ctx->provides);
   547 }
   548 
   549 static void
   550 end_element (void *data, const char *name)
   551 {
   552 	struct import_context *ctx = data;
   553 
   554 	if (strcmp(name, "package") == 0)
   555 		import_context_finish_package(ctx);
   556 }
   557 
   558 static char *
   559 sha1_to_hex(const unsigned char *sha1)
   560 {
   561 	static int bufno;
   562 	static char hexbuffer[4][50];
   563 	static const char hex[] = "0123456789abcdef";
   564 	char *buffer = hexbuffer[3 & ++bufno], *buf = buffer;
   565 	int i;
   566 
   567 	for (i = 0; i < 20; i++) {
   568 		unsigned int val = *sha1++;
   569 		*buf++ = hex[val >> 4];
   570 		*buf++ = hex[val & 0xf];
   571 	}
   572 	*buf = '\0';
   573 
   574 	return buffer;
   575 }
   576 
   577 static void
   578 razor_prepare_import(struct import_context *ctx)
   579 {
   580 	memset(ctx, 0, sizeof *ctx);
   581 	ctx->set = razor_set_create();
   582 }
   583 
   584 static int
   585 razor_import(struct import_context *ctx, const char *filename)
   586 {
   587 	SHA_CTX sha1;
   588 	XML_Parser parser;
   589 	int fd;
   590 	void *p;
   591 	struct stat stat;
   592 	char buf[128];
   593 	unsigned char hash[20];
   594 
   595 	fd = open(filename, O_RDONLY);
   596 	if (fstat(fd, &stat) < 0)
   597 		return -1;
   598 	p = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
   599 	if (p == MAP_FAILED)
   600 		return -1;
   601 
   602 	parser = XML_ParserCreate(NULL);
   603 	XML_SetUserData(parser, ctx);
   604 	XML_SetElementHandler(parser, start_element, end_element);
   605 	if (XML_Parse(parser, p, stat.st_size, 1) == XML_STATUS_ERROR) {
   606 		fprintf(stderr,
   607 			"%s at line %d, %s\n",
   608 			XML_ErrorString(XML_GetErrorCode(parser)),
   609 			XML_GetCurrentLineNumber(parser),
   610 			filename);
   611 		return 1;
   612 	}
   613 
   614 	XML_ParserFree(parser);
   615 
   616 	SHA1_Init(&sha1);
   617 	SHA1_Update(&sha1, p, stat.st_size);
   618 	SHA1_Final(hash, &sha1);
   619 
   620 	close(fd);
   621 
   622 	snprintf(buf, sizeof buf, "set/%s", sha1_to_hex(hash));
   623 	if (write_to_file(buf, p, stat.st_size) < 0)
   624 		return -1;
   625 	munmap(p, stat.st_size);
   626 
   627 	return 0;
   628 }
   629 
   630 static struct razor_set *qsort_set;
   631 
   632 static int
   633 compare_packages(const void *p1, const void *p2)
   634 {
   635 	const struct razor_package *pkg1 = p1, *pkg2 = p2;
   636 	char *pool = qsort_set->string_pool.data;
   637 
   638 	return strcmp(&pool[pkg1->name], &pool[pkg2->name]);
   639 }
   640 
   641 static int
   642 compare_properties(const void *p1, const void *p2)
   643 {
   644 	const struct import_property *prop1 = p1, *prop2 = p2;
   645 	char *pool = qsort_set->string_pool.data;
   646 	int result;
   647 
   648 	result = strcmp(&pool[prop1->name], &pool[prop2->name]);
   649 	if (result == 0)
   650 		return strcmp(&pool[prop1->version], &pool[prop2->version]);
   651 	else
   652 		return result;
   653 }
   654 
   655 static unsigned long *
   656 uniqueify_properties(struct array *in, struct array *out)
   657 {
   658 	struct import_property *ip, *end;
   659 	struct razor_property *rp;
   660 	unsigned long *map;
   661 	int i, count;
   662 
   663 	count = in->size / sizeof(struct import_property);
   664 	qsort(in->data, count,
   665 	      sizeof(struct import_property), compare_properties);
   666 
   667 	rp = NULL;
   668 	end = in->data + in->size;
   669 	for (ip = in->data; ip < end; ip++) {
   670 		if (rp == NULL ||
   671 		    ip->name != rp->name || ip->version != rp->version) {
   672 			rp = array_add(out, sizeof *rp);
   673 			rp->name = ip->name;
   674 			rp->version = ip->version;
   675 		}
   676 		ip->unique_index = rp - (struct razor_property *) out->data;
   677 	}
   678 
   679 	map = malloc(count * sizeof (unsigned long));
   680 	ip = in->data;
   681 	for (i = 0; i < count; i++)
   682 		map[ip[i].index] = ip[i].unique_index;
   683 
   684 	return map;
   685 }
   686 
   687 static void
   688 sort_packages(struct import_context *ctx)
   689 {
   690 	struct razor_package *p, *end;
   691 	unsigned long *pool, *r;
   692 
   693 	pool = ctx->set->property_pool.data;
   694 	end = ctx->set->packages.data + ctx->set->packages.size;
   695 	for (p = ctx->set->packages.data; p < end; p++) {
   696 		for (r = &pool[p->requires]; *r; r++)
   697 			*r = ctx->requires_map[*r];
   698 		for (r = &pool[p->provides]; *r; r++)
   699 			*r = ctx->provides_map[*r];
   700 	}
   701 
   702 	qsort(ctx->set->packages.data,
   703 	      ctx->set->packages.size / sizeof(struct razor_package),
   704 	      sizeof(struct razor_package), compare_packages);
   705 }
   706 
   707 static struct razor_set *
   708 razor_finish_import(struct import_context *ctx)
   709 {
   710 	qsort_set = ctx->set;
   711 
   712 	ctx->requires_map =
   713 		uniqueify_properties(&ctx->requires.all, &ctx->set->requires);
   714 	ctx->provides_map =
   715 		uniqueify_properties(&ctx->provides.all, &ctx->set->provides);
   716 
   717 	sort_packages(ctx);
   718 
   719 	free(ctx->requires.all.data);
   720 	free(ctx->provides.all.data);
   721 	free(ctx->requires_map);
   722 	free(ctx->provides_map);
   723 		
   724 	fprintf(stderr, "parsed %d requires, %d unique\n",
   725 		ctx->requires.all.size / sizeof(struct import_property),
   726 		ctx->set->requires.size / sizeof(struct razor_property));
   727 	fprintf(stderr, "parsed %d provides, %d unique\n",
   728 		ctx->provides.all.size / sizeof(struct import_property),
   729 		ctx->set->provides.size / sizeof(struct razor_property));
   730 
   731 	return ctx->set; 
   732 }
   733 
   734 void
   735 razor_set_list(struct razor_set *set)
   736 {
   737 	struct razor_package *p, *end;
   738 	char *pool;
   739 
   740 	pool = set->string_pool.data;
   741 	end = set->packages.data + set->packages.size;
   742 	for (p = set->packages.data; p < end; p++)
   743 		printf("%s %s\n", &pool[p->name], &pool[p->version]);
   744 }
   745 
   746 struct razor_package *
   747 razor_set_get_package(struct razor_set *set, const char *package)
   748 {
   749 	unsigned long name;
   750 	struct razor_package *p, *end;
   751 
   752 	name = razor_set_lookup(set, package);
   753 	end = set->packages.data + set->packages.size;
   754 	for (p = set->packages.data; p < end; p++)
   755 		if (p->name == name)
   756 			return p;
   757 
   758 	return NULL;
   759 }
   760 
   761 static void
   762 razor_set_list_all_properties(struct razor_set *set, struct array *properties)
   763 {
   764 	struct razor_property *p, *end;
   765 	char *pool;
   766 
   767 	pool = set->string_pool.data;
   768 	end = properties->data + properties->size;
   769 	for (p = properties->data; p < end; p++)
   770 		printf("%s %s\n", &pool[p->name], &pool[p->version]);
   771 }
   772 
   773 void
   774 razor_set_list_requires(struct razor_set *set, const char *name)
   775 {
   776 	struct razor_property *p, *requires;
   777 	struct razor_package *package;
   778 	unsigned long *r;
   779 	char *pool;
   780 
   781 	if (name) {
   782 		package = razor_set_get_package(set, name);
   783 		r = (unsigned long *) set->property_pool.data +
   784 			package->requires;
   785 		requires = set->requires.data;
   786 		pool = set->string_pool.data;
   787 		while (*r) {
   788 			p = &requires[*r++];
   789 			printf("%s %s\n", &pool[p->name], &pool[p->version]);
   790 		}
   791 	} else
   792 		razor_set_list_all_properties(set, &set->requires);
   793 }
   794 
   795 void
   796 razor_set_list_provides(struct razor_set *set, const char *name)
   797 {
   798 	struct razor_property *p, *provides;
   799 	struct razor_package *package;
   800 	unsigned long *r;
   801 	char *pool;
   802 
   803 	if (name) {
   804 		package = razor_set_get_package(set, name);
   805 		r = (unsigned long *) set->property_pool.data +
   806 			package->provides;
   807 		provides = set->provides.data;
   808 		pool = set->string_pool.data;
   809 		while (*r) {
   810 			p = &provides[*r++];
   811 			printf("%s %s\n", &pool[p->name], &pool[p->version]);
   812 		}
   813 	} else 
   814 		razor_set_list_all_properties(set, &set->provides);
   815 }
   816 
   817 void
   818 razor_set_info(struct razor_set *set)
   819 {
   820 	unsigned int offset, size;
   821 	int i;
   822 
   823 	for (i = 0; i < set->header->sections[i].type; i++) {
   824 		offset = set->header->sections[i].offset;
   825 		size = set->header->sections[i + 1].offset - offset;
   826 
   827 		switch (set->header->sections[i].type) {
   828 		case RAZOR_BUCKETS:
   829 			printf("bucket section:\t\t%dkb\n", size / 1024);
   830 			break;
   831 		case RAZOR_STRINGS:
   832 			printf("string pool:\t\t%dkb\n", size / 1024);
   833 			break;
   834 		case RAZOR_PACKAGES:
   835 			printf("package section:\t%dkb\n", size / 1024);
   836 			break;
   837 		case RAZOR_REQUIRES:
   838 			printf("requires section:\t%dkb\n", size / 1024);
   839 			break;
   840 		case RAZOR_PROVIDES:
   841 			printf("provides section:\t%dkb\n", size / 1024);
   842 			break;
   843 		}
   844 	}
   845 }
   846 
   847 static int
   848 usage(void)
   849 {
   850 	printf("usage: razor [ import FILES | lookup <key> | "
   851 	       "list | list-requires | list-provides | info ]\n");
   852 	exit(1);
   853 }
   854 
   855 static const char repo_filename[] = "system.repo";
   856 
   857 int
   858 main(int argc, char *argv[])
   859 {
   860 	int i;
   861 	struct razor_set *set;
   862 	struct stat statbuf;
   863 	struct import_context ctx;
   864 
   865 	if (argc < 2) {
   866 		usage();
   867 	} else if (strcmp(argv[1], "import") == 0) {
   868 		if (stat("set", &statbuf) && mkdir("set", 0777)) {
   869 			fprintf(stderr, "could not create directory 'set'\n");
   870 			exit(-1);
   871 		}
   872 			
   873 		razor_prepare_import(&ctx);
   874 
   875 		for (i = 2; i < argc; i++) {
   876 			if (razor_import(&ctx, argv[i]) < 0) {
   877 				fprintf(stderr, "failed to import %s\n",
   878 					argv[i]);
   879 				exit(-1);
   880 			}
   881 		}
   882 
   883 		set = razor_finish_import(&ctx);
   884 
   885 		printf("bucket allocation: %d\n", set->buckets.alloc);
   886 		printf("pool size: %d\n", set->string_pool.size);
   887 		printf("pool allocation: %d\n", set->string_pool.alloc);
   888 		printf("packages: %d\n",
   889 		       set->packages.size / sizeof(struct razor_package));
   890 		printf("requires: %d\n",
   891 		       set->requires.size / sizeof(struct razor_property));
   892 		printf("provides: %d\n",
   893 		       set->provides.size / sizeof(struct razor_property));
   894 
   895 		razor_set_write(set, repo_filename);
   896 
   897 		razor_set_destroy(set);
   898 	} else if (strcmp(argv[1], "lookup") == 0) {
   899 		set = razor_set_open(repo_filename);
   900 		printf("%s is %lu\n", argv[2],
   901 		       razor_set_lookup(set, argv[2]));
   902 		razor_set_destroy(set);
   903 	} else if (strcmp(argv[1], "list") == 0) {
   904 		set = razor_set_open(repo_filename);
   905 		razor_set_list(set);
   906 		razor_set_destroy(set);
   907 	} else if (strcmp(argv[1], "list-requires") == 0) {
   908 		set = razor_set_open(repo_filename);
   909 		razor_set_list_requires(set, argv[2]);
   910 		razor_set_destroy(set);
   911 	} else if (strcmp(argv[1], "list-provides") == 0) {
   912 		set = razor_set_open(repo_filename);
   913 		razor_set_list_provides(set, argv[2]);
   914 		razor_set_destroy(set);
   915 	} else if (strcmp(argv[1], "info") == 0) {
   916 		set = razor_set_open(repo_filename);
   917 		razor_set_info(set);
   918 		razor_set_destroy(set);
   919 	} else {
   920 		usage();
   921 	}
   922 
   923 	return 0;
   924 }