razor.c
author Kristian H?gsberg <krh@redhat.com>
Thu Sep 06 15:11:49 2007 -0400 (2007-09-06)
changeset 8 7820b7d94662
parent 7 235f7daf817c
child 9 c4338a14dd9f
permissions -rw-r--r--
Also import requires.

Needs more work though, only 10% of requires are unique.
     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_add(struct array *array, int size)
    20 {
    21 	int alloc;
    22 	void *data, *p;
    23 
    24 	if (array->alloc > 0)
    25 		alloc = array->alloc;
    26 	else
    27 		alloc = 1024;
    28 
    29 	while (alloc < array->size + size)
    30 		alloc *= 2;
    31 
    32 	if (array->alloc < alloc) {
    33 		data = realloc(array->data, alloc);
    34 		if (data == NULL)
    35 			return 0;
    36 		array->data = data;
    37 		array->alloc = alloc;
    38 	}
    39 
    40 	p = array->data + array->size;
    41 	array->size += size;
    42 
    43 	return p;
    44 }
    45 
    46 static int
    47 write_to_fd(int fd, void *p, size_t size)
    48 {
    49 	int rest, len;
    50 
    51 	rest = size;
    52 	while (rest > 0) {
    53 		len = write(fd, p, rest);
    54 		if (len < 0)
    55 			return -1;
    56 		rest -= len;
    57 	}
    58 
    59 	return 0;
    60 }
    61 
    62 static int
    63 write_to_file(const char *filename, void *p, size_t size)
    64 {
    65 	int fd, err;
    66 
    67 	fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666);
    68 	if (fd < 0)
    69 		return -1;
    70 	err = write_to_fd(fd, p, size);
    71 	close(fd);
    72 
    73 	return err;
    74 }
    75 
    76 static void *
    77 zalloc(size_t size)
    78 {
    79 	void *p;
    80 
    81 	p = malloc(size);
    82 	memset(p, 0, size);
    83 
    84 	return p;
    85 }
    86 
    87 struct razor_set_header {
    88 	unsigned int magic;
    89 	unsigned int version;
    90 	struct { unsigned int type, offset; } sections[0];
    91 };
    92 
    93 #define RAZOR_MAGIC 0x7a7a7a7a
    94 #define RAZOR_VERSION 1
    95 
    96 #define RAZOR_BUCKETS 1
    97 #define RAZOR_STRINGS 2
    98 #define RAZOR_PACKAGES 3
    99 #define RAZOR_REQUIRES 4
   100 #define RAZOR_PROVIDES 5
   101 
   102 struct razor_package {
   103 	unsigned long name;
   104 	unsigned long version;
   105 };
   106 
   107 struct razor_property {
   108 	unsigned long name;
   109 	unsigned long version;
   110 };
   111 
   112 struct razor_set {
   113 	struct array buckets;
   114 	struct array string_pool;
   115  	struct array packages;
   116  	struct array requires;
   117  	struct array provides;
   118 	struct razor_set_header *header;
   119 };
   120 
   121 struct razor_set *
   122 razor_set_create(void)
   123 {
   124 	struct razor_set *set;
   125 	char *p;
   126 
   127 	set = zalloc(sizeof(struct razor_set));
   128 	p = array_add(&set->string_pool, 1);
   129 	*p = '\0';
   130 
   131 	return set;
   132 }
   133 
   134 struct razor_set *
   135 razor_set_open(const char *filename)
   136 {
   137 	struct razor_set *set;
   138 	struct stat stat;
   139 	unsigned int size, offset;
   140 	int fd, i;
   141 
   142 	set = zalloc(sizeof *set);
   143 	fd = open(filename, O_RDONLY);
   144 	if (fstat(fd, &stat) < 0)
   145 		return NULL;
   146 	set->header = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
   147 	if (set->header == MAP_FAILED) {
   148 		free(set);
   149 		return NULL;
   150 	}
   151 
   152 	for (i = 0; i < set->header->sections[i].type; i++) {
   153 		offset = set->header->sections[i].offset;
   154 		size = set->header->sections[i + 1].offset - offset;
   155 
   156 		switch (set->header->sections[i].type) {
   157 		case RAZOR_BUCKETS:
   158 			set->buckets.data = (void *) set->header + offset;
   159 			set->buckets.size = size;
   160 			set->buckets.alloc = size;
   161 			break;
   162 		case RAZOR_STRINGS:
   163 			set->string_pool.data = (void *) set->header + offset;
   164 			set->string_pool.size = size;
   165 			set->string_pool.alloc = size;
   166 			break;
   167 		case RAZOR_PACKAGES:
   168 			set->packages.data = (void *) set->header + offset;
   169 			set->packages.size = size;
   170 			set->packages.size = size;
   171 			break;
   172 		case RAZOR_REQUIRES:
   173 			set->requires.data = (void *) set->header + offset;
   174 			set->requires.size = size;
   175 			set->requires.size = size;
   176 			break;
   177 		case RAZOR_PROVIDES:
   178 			set->provides.data = (void *) set->header + offset;
   179 			set->provides.size = size;
   180 			set->provides.size = size;
   181 			break;
   182 		}
   183 	}
   184 	close(fd);
   185 
   186 	return set;
   187 }
   188 
   189 void
   190 razor_set_destroy(struct razor_set *set)
   191 {
   192 	unsigned int size;
   193 	int i;
   194 
   195 	if (set->header) {
   196 		for (i = 0; set->header->sections[i].type; i++)
   197 			;
   198 		size = set->header->sections[i].type;
   199 		munmap(set->header, size);
   200 	} else {
   201 		free(set->buckets.data);
   202 		free(set->string_pool.data);
   203 		free(set->packages.data);
   204 	}
   205 
   206 	free(set);
   207 }
   208 
   209 static int
   210 razor_set_write(struct razor_set *set, const char *filename)
   211 {
   212 	char data[4096];
   213 	struct razor_set_header *header = (struct razor_set_header *) data;
   214 	int fd, pool_size, packages_size, requires_size, provides_size;
   215 
   216 	/* Align these to pages sizes */
   217 	pool_size = (set->string_pool.size + 4095) & ~4095;
   218 	packages_size = (set->packages.size + 4095) & ~4095;
   219 	requires_size = (set->requires.size + 4095) & ~4095;
   220 	provides_size = (set->provides.size + 4095) & ~4095;
   221 
   222 	memset(data, 0, sizeof data);
   223 	header->magic = RAZOR_MAGIC;
   224 	header->version = RAZOR_VERSION;
   225 
   226 	header->sections[0].type = RAZOR_BUCKETS;
   227 	header->sections[0].offset = sizeof data;
   228 
   229 	header->sections[1].type = RAZOR_STRINGS;
   230 	header->sections[1].offset =
   231 		header->sections[0].offset + set->buckets.alloc;
   232 
   233 	header->sections[2].type = RAZOR_PACKAGES;
   234 	header->sections[2].offset =
   235 		header->sections[1].offset + pool_size;
   236 
   237 	header->sections[3].type = RAZOR_REQUIRES;
   238 	header->sections[3].offset =
   239 		header->sections[2].offset + packages_size;
   240 
   241 	header->sections[4].type = RAZOR_PROVIDES;
   242 	header->sections[4].offset =
   243 		header->sections[3].offset + requires_size;
   244 
   245 	header->sections[5].type = 0;
   246 	header->sections[5].offset =
   247 		header->sections[4].offset + provides_size;
   248 
   249 	fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666);
   250 	if (fd < 0)
   251 		return -1;
   252 
   253 	write_to_fd(fd, data, sizeof data);
   254 	write_to_fd(fd, set->buckets.data, set->buckets.alloc);
   255 	write_to_fd(fd, set->string_pool.data, pool_size);
   256 	write_to_fd(fd, set->packages.data, packages_size);
   257 	write_to_fd(fd, set->requires.data, requires_size);
   258 	write_to_fd(fd, set->provides.data, provides_size);
   259 
   260 	return 0;
   261 }
   262 
   263 static unsigned int
   264 hash_string(const char *key)
   265 {
   266 	const char *p;
   267 	unsigned int hash = 0;
   268 
   269 	for (p = key; *p; p++)
   270 		hash = (hash << 2) ^ *p;
   271 
   272 	return hash;
   273 }
   274 
   275 unsigned long
   276 razor_set_lookup(struct razor_set *set, const char *key)
   277 {
   278 	unsigned int mask, start, i;
   279 	unsigned long *b;
   280 	char *pool;
   281 
   282 	pool = set->string_pool.data;
   283 	mask = set->buckets.alloc - 1;
   284 	start = hash_string(key) * sizeof(unsigned long);
   285 
   286 	for (i = 0; i < set->buckets.alloc; i += sizeof *b) {
   287 		b = set->buckets.data + ((start + i) & mask);
   288 
   289 		if (*b == 0)
   290 			return 0;
   291 
   292 		if (strcmp(key, &pool[*b]) == 0)
   293 			return *b;
   294 	}
   295 
   296 	return 0;
   297 }
   298 
   299 static unsigned long
   300 add_to_string_pool(struct razor_set *set, const char *key)
   301 {
   302 	int len;
   303 	char *p;
   304 
   305 	len = strlen(key) + 1;
   306 	p = array_add(&set->string_pool, len);
   307 	memcpy(p, key, len);
   308 
   309 	return p - (char *) set->string_pool.data;
   310 }
   311 
   312 static void
   313 do_insert(struct razor_set *set, unsigned long value)
   314 {
   315 	unsigned int mask, start, i;
   316 	unsigned long *b;
   317 	const char *key;
   318 
   319 	key = (char *) set->string_pool.data + value;
   320 	mask = set->buckets.alloc - 1;
   321 	start = hash_string(key) * sizeof(unsigned long);
   322 
   323 	for (i = 0; i < set->buckets.alloc; i += sizeof *b) {
   324 		b = set->buckets.data + ((start + i) & mask);
   325 		if (*b == 0) {
   326 			*b = value;
   327 			break;
   328 		}
   329 	}
   330 }
   331 
   332 unsigned long
   333 razor_set_insert(struct razor_set *set, const char *key)
   334 {
   335 	unsigned long value, *buckets, *b, *end;
   336 	int alloc;
   337 
   338 	alloc = set->buckets.alloc;
   339 	array_add(&set->buckets, 4 * sizeof *buckets);
   340 	if (alloc != set->buckets.alloc) {
   341 		end = set->buckets.data + alloc;
   342 		memset(end, 0, set->buckets.alloc - alloc);
   343 		for (b = set->buckets.data; b < end; b++) {
   344 			value = *b;
   345 			if (value != 0) {
   346 				*b = 0;
   347 				do_insert(set, value);
   348 			}
   349 		}
   350 	}
   351 
   352 	value = add_to_string_pool(set, key);
   353 	do_insert (set, value);
   354 
   355 	return value;
   356 }
   357 
   358 static unsigned long
   359 razor_set_add_package(struct razor_set *set,
   360 		      unsigned long name, unsigned long version)
   361 {
   362 	struct razor_package *p;
   363 
   364 	p = array_add(&set->packages, sizeof *p);
   365 
   366 	p->name = name;
   367 	p->version = version;
   368 
   369 	return p - (struct razor_package *) set->packages.data;
   370 }
   371 
   372 static unsigned long
   373 razor_set_add_requires(struct razor_set *set,
   374 		       unsigned long name, unsigned long version)
   375 {
   376 	struct razor_property *p;
   377 
   378 	p = array_add(&set->requires, sizeof *p);
   379 
   380 	p->name = name;
   381 	p->version = version;
   382 
   383 	return p - (struct razor_property *) set->requires.data;
   384 }
   385 
   386 static unsigned long
   387 razor_set_add_provides(struct razor_set *set,
   388 		       unsigned long name, unsigned long version)
   389 {
   390 	struct razor_property *p;
   391 
   392 	p = array_add(&set->provides, sizeof *p);
   393 
   394 	p->name = name;
   395 	p->version = version;
   396 
   397 	return p - (struct razor_property *) set->provides.data;
   398 }
   399 
   400 unsigned long
   401 razor_set_tokenize(struct razor_set *set, const char *string)
   402 {
   403 	unsigned long token;
   404 
   405 	token = razor_set_lookup(set, string);
   406 	if (token != 0)
   407 		return token;
   408 
   409 	return razor_set_insert(set, string);
   410 }
   411 
   412 static struct razor_set *qsort_set;
   413 
   414 static int
   415 compare_packages(const void *p1, const void *p2)
   416 {
   417 	const struct razor_package *pkg1 = p1, *pkg2 = p2;
   418 	char *pool = qsort_set->string_pool.data;
   419 
   420 	return strcmp(&pool[pkg1->name], &pool[pkg2->name]);
   421 }
   422 
   423 static int
   424 compare_properties(const void *p1, const void *p2)
   425 {
   426 	const struct razor_property *prop1 = p1, *prop2 = p2;
   427 	char *pool = qsort_set->string_pool.data;
   428 
   429 	return strcmp(&pool[prop1->name], &pool[prop2->name]);
   430 }
   431 
   432 static void
   433 razor_set_sort(struct razor_set *set)
   434 {
   435 	qsort_set = set;
   436 	qsort(set->packages.data,
   437 	      set->packages.size / sizeof(struct razor_package),
   438 	      sizeof(struct razor_package), compare_packages);
   439 	qsort(set->requires.data,
   440 	      set->requires.size / sizeof(struct razor_property),
   441 	      sizeof(struct razor_property), compare_properties);
   442 	qsort(set->provides.data,
   443 	      set->provides.size / sizeof(struct razor_property),
   444 	      sizeof(struct razor_property), compare_properties);
   445 }
   446 
   447 struct parsing_context {
   448 	struct razor_set *set;
   449 	int pkg_id;
   450 };
   451 
   452 static void
   453 parse_package(struct parsing_context *ctx, const char **atts)
   454 {
   455 	unsigned long name = 0, version = 0;
   456 	int i;
   457 
   458 	for (i = 0; atts[i]; i += 2) {
   459 		if (strcmp(atts[i], "name") == 0)
   460 			name = razor_set_tokenize(ctx->set, atts[i + 1]);
   461 		else if (strcmp(atts[i], "version") == 0)
   462 			version = razor_set_tokenize(ctx->set, atts[i + 1]);
   463 	}
   464 
   465 	if (name == 0 || version == 0) {
   466 		fprintf(stderr, "invalid package tag, "
   467 			"missing name or version attributes\n");
   468 		return;
   469 	}
   470 
   471 	ctx->pkg_id = razor_set_add_package(ctx->set, name, version);
   472 
   473 	return;
   474 }
   475 
   476 static void
   477 parse_requires(struct parsing_context *ctx, const char **atts)
   478 {
   479 	unsigned long name = 0, version = 0;
   480 	int i;
   481 
   482 	for (i = 0; atts[i]; i += 2) {
   483 		if (strcmp(atts[i], "name") == 0)
   484 			name = razor_set_tokenize(ctx->set, atts[i + 1]);
   485 	}
   486 	
   487 	if (name == 0) {
   488 		fprintf(stderr, "invalid requires tag, "
   489 			"missing name attribute\n");
   490 		return;
   491 	}
   492 
   493 	ctx->pkg_id = razor_set_add_requires(ctx->set, name, version);
   494 }
   495 
   496 static void
   497 parse_provides(struct parsing_context *ctx, const char **atts)
   498 {
   499 	unsigned long name = 0, version = 0;
   500 	int i;
   501 
   502 	for (i = 0; atts[i]; i += 2) {
   503 		if (strcmp(atts[i], "name") == 0)
   504 			name = razor_set_tokenize(ctx->set, atts[i + 1]);
   505 	}
   506 	
   507 	if (name == 0) {
   508 		fprintf(stderr, "invalid provides tag, "
   509 			"missing name attribute\n");
   510 		return;
   511 	}
   512 
   513 	ctx->pkg_id = razor_set_add_provides(ctx->set, name, version);
   514 }
   515 
   516 static void
   517 start_element(void *data, const char *name, const char **atts)
   518 {
   519 	struct parsing_context *ctx = data;
   520 	int i;
   521 
   522 	if (strcmp(name, "package") == 0)
   523 		parse_package(ctx, atts);
   524 	else if (strcmp(name, "requires") == 0)
   525 		parse_requires(ctx, atts);
   526 	else if (strcmp(name, "provides") == 0)
   527 		parse_provides(ctx, atts);
   528 
   529 	for (i = 0; atts[i]; i += 2)
   530 		razor_set_tokenize(ctx->set, atts[i + 1]);
   531 }
   532 
   533 static void
   534 end_element (void *data, const char *name)
   535 {
   536 	struct parsing_context *ctx = data;
   537 
   538 	if (strcmp(name, "package") == 0)
   539 		ctx->pkg_id = 0;
   540 }
   541 
   542 static char *
   543 sha1_to_hex(const unsigned char *sha1)
   544 {
   545 	static int bufno;
   546 	static char hexbuffer[4][50];
   547 	static const char hex[] = "0123456789abcdef";
   548 	char *buffer = hexbuffer[3 & ++bufno], *buf = buffer;
   549 	int i;
   550 
   551 	for (i = 0; i < 20; i++) {
   552 		unsigned int val = *sha1++;
   553 		*buf++ = hex[val >> 4];
   554 		*buf++ = hex[val & 0xf];
   555 	}
   556 	*buf = '\0';
   557 
   558 	return buffer;
   559 }
   560 
   561 static int
   562 razor_set_import(struct razor_set *set, const char *filename)
   563 {
   564 	SHA_CTX sha1;
   565 	XML_Parser parser;
   566 	struct parsing_context ctx;
   567 	int fd;
   568 	void *p;
   569 	struct stat stat;
   570 	char buf[128];
   571 	unsigned char hash[20];
   572 
   573 	fd = open(filename, O_RDONLY);
   574 	if (fstat(fd, &stat) < 0)
   575 		return -1;
   576 	p = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
   577 	if (p == MAP_FAILED)
   578 		return -1;
   579 
   580 	parser = XML_ParserCreate(NULL);
   581 	ctx.set = set;
   582 	XML_SetUserData(parser, &ctx);
   583 	XML_SetElementHandler(parser, start_element, end_element);
   584 	if (XML_Parse(parser, p, stat.st_size, 1) == XML_STATUS_ERROR) {
   585 		fprintf(stderr,
   586 			"%s at line %d, %s\n",
   587 			XML_ErrorString(XML_GetErrorCode(parser)),
   588 			XML_GetCurrentLineNumber(parser),
   589 			filename);
   590 		return 1;
   591 	}
   592 
   593 	XML_ParserFree(parser);
   594 
   595 	SHA1_Init(&sha1);
   596 	SHA1_Update(&sha1, p, stat.st_size);
   597 	SHA1_Final(hash, &sha1);
   598 
   599 	close(fd);
   600 
   601 	snprintf(buf, sizeof buf, "set/%s", sha1_to_hex(hash));
   602 	if (write_to_file(buf, p, stat.st_size) < 0)
   603 		return -1;
   604 	munmap(p, stat.st_size);
   605 
   606 	return 0;
   607 }
   608 
   609 void
   610 razor_set_list(struct razor_set *set)
   611 {
   612 	struct razor_package *p, *end;
   613 	char *pool;
   614 
   615 	pool = set->string_pool.data;
   616 	end = set->packages.data + set->packages.size;
   617 	for (p = set->packages.data; p < end && p->name; p++)
   618 		printf("%s %s\n", &pool[p->name], &pool[p->version]);
   619 }
   620 
   621 void
   622 razor_set_list_requires(struct razor_set *set)
   623 {
   624 	struct razor_property *p, *end;
   625 	char *pool;
   626 
   627 	pool = set->string_pool.data;
   628 	end = set->requires.data + set->requires.size;
   629 	for (p = set->requires.data; p < end && p->name; p++)
   630 		printf("%s %s\n", &pool[p->name], &pool[p->version]);
   631 }
   632 
   633 void
   634 razor_set_list_provides(struct razor_set *set)
   635 {
   636 	struct razor_property *p, *end;
   637 	char *pool;
   638 
   639 	pool = set->string_pool.data;
   640 	end = set->provides.data + set->provides.size;
   641 	for (p = set->provides.data; p < end && p->name; p++)
   642 		printf("%s %s\n", &pool[p->name], &pool[p->version]);
   643 }
   644 
   645 void
   646 razor_set_info(struct razor_set *set)
   647 {
   648 	unsigned int offset, size;
   649 	int i;
   650 
   651 	for (i = 0; i < set->header->sections[i].type; i++) {
   652 		offset = set->header->sections[i].offset;
   653 		size = set->header->sections[i + 1].offset - offset;
   654 
   655 		switch (set->header->sections[i].type) {
   656 		case RAZOR_BUCKETS:
   657 			printf("bucket section:\t\t%dkb\n", size / 1024);
   658 			break;
   659 		case RAZOR_STRINGS:
   660 			printf("string pool:\t\t%dkb\n", size / 1024);
   661 			break;
   662 		case RAZOR_PACKAGES:
   663 			printf("package section:\t%dkb\n", size / 1024);
   664 			break;
   665 		case RAZOR_REQUIRES:
   666 			printf("requires section:\t%dkb\n", size / 1024);
   667 			break;
   668 		case RAZOR_PROVIDES:
   669 			printf("provides section:\t%dkb\n", size / 1024);
   670 			break;
   671 		}
   672 	}
   673 }
   674 
   675 static int
   676 usage(void)
   677 {
   678 	printf("usage: razor [ import FILES | lookup <key> | "
   679 	       "list | list-requires | list-provides | info ]\n");
   680 	exit(1);
   681 }
   682 
   683 static const char repo_filename[] = "system.repo";
   684 
   685 int
   686 main(int argc, char *argv[])
   687 {
   688 	int i;
   689 	struct razor_set *set;
   690 	struct stat statbuf;
   691 
   692 	if (argc < 2) {
   693 		usage();
   694 	} else if (strcmp(argv[1], "import") == 0) {
   695 		if (stat("set", &statbuf) && mkdir("set", 0777)) {
   696 			fprintf(stderr, "could not create directory 'set'\n");
   697 			exit(-1);
   698 		}
   699 			
   700 		set = razor_set_create();
   701 
   702 		for (i = 2; i < argc; i++) {
   703 			if (razor_set_import(set, argv[i]) < 0) {
   704 				fprintf(stderr, "failed to import %s\n",
   705 					argv[i]);
   706 				exit(-1);
   707 			}
   708 		}
   709 
   710 		razor_set_sort(set);
   711 
   712 		/* FIXME: We add a sentinel package here, but we
   713 		 * should probably just have a size field in the
   714 		 * header section. */
   715 		razor_set_add_package(set, 0, 0);
   716 		razor_set_add_requires(set, 0, 0);
   717 		razor_set_add_provides(set, 0, 0);
   718 
   719 		printf("bucket allocation: %d\n", set->buckets.alloc);
   720 		printf("pool size: %d\n", set->string_pool.size);
   721 		printf("pool allocation: %d\n", set->string_pool.alloc);
   722 		printf("packages: %d\n",
   723 		       set->packages.size / sizeof(struct razor_package));
   724 		printf("requires: %d\n",
   725 		       set->requires.size / sizeof(struct razor_property));
   726 		printf("provides: %d\n",
   727 		       set->provides.size / sizeof(struct razor_property));
   728 
   729 		razor_set_write(set, repo_filename);
   730 
   731 		razor_set_destroy(set);
   732 	} else if (strcmp(argv[1], "lookup") == 0) {
   733 		set = razor_set_open(repo_filename);
   734 		printf("%s is %lu\n", argv[2],
   735 		       razor_set_lookup(set, argv[2]));
   736 		razor_set_destroy(set);
   737 	} else if (strcmp(argv[1], "list") == 0) {
   738 		set = razor_set_open(repo_filename);
   739 		razor_set_list(set);
   740 		razor_set_destroy(set);
   741 	} else if (strcmp(argv[1], "list-requires") == 0) {
   742 		set = razor_set_open(repo_filename);
   743 		razor_set_list_requires(set);
   744 		razor_set_destroy(set);
   745 	} else if (strcmp(argv[1], "list-provides") == 0) {
   746 		set = razor_set_open(repo_filename);
   747 		razor_set_list_provides(set);
   748 		razor_set_destroy(set);
   749 	} else if (strcmp(argv[1], "info") == 0) {
   750 		set = razor_set_open(repo_filename);
   751 		razor_set_info(set);
   752 		razor_set_destroy(set);
   753 	} else {
   754 		usage();
   755 	}
   756 
   757 	return 0;
   758 }