krh@15: #define _GNU_SOURCE krh@15: krh@0: #include krh@0: #include krh@0: #include krh@0: #include krh@0: #include krh@0: #include krh@0: #include krh@0: #include krh@15: #include krh@0: krh@0: #include krh@0: #include "sha1.h" krh@0: krh@6: struct array { krh@6: void *data; krh@6: int size, alloc; krh@6: }; krh@6: krh@13: static void krh@13: array_init(struct array *array) krh@13: { krh@13: memset(array, 0, sizeof *array); krh@13: } krh@13: krh@13: static void krh@13: array_release(struct array *array) krh@13: { krh@13: free(array->data); krh@13: } krh@13: krh@6: static void * krh@6: array_add(struct array *array, int size) krh@6: { krh@6: int alloc; krh@6: void *data, *p; krh@6: krh@6: if (array->alloc > 0) krh@6: alloc = array->alloc; krh@6: else krh@10: alloc = 16; krh@6: krh@6: while (alloc < array->size + size) krh@6: alloc *= 2; krh@6: krh@6: if (array->alloc < alloc) { krh@6: data = realloc(array->data, alloc); krh@6: if (data == NULL) krh@6: return 0; krh@6: array->data = data; krh@6: array->alloc = alloc; krh@6: } krh@6: krh@6: p = array->data + array->size; krh@6: array->size += size; krh@6: krh@6: return p; krh@6: } krh@6: krh@0: static int krh@0: write_to_fd(int fd, void *p, size_t size) krh@0: { krh@0: int rest, len; krh@0: krh@0: rest = size; krh@0: while (rest > 0) { krh@0: len = write(fd, p, rest); krh@0: if (len < 0) krh@0: return -1; krh@0: rest -= len; krh@0: } krh@0: krh@0: return 0; krh@0: } krh@0: krh@0: static int krh@0: write_to_file(const char *filename, void *p, size_t size) krh@0: { krh@0: int fd, err; krh@0: krh@0: fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666); krh@0: if (fd < 0) krh@0: return -1; krh@0: err = write_to_fd(fd, p, size); krh@0: close(fd); krh@0: krh@0: return err; krh@0: } krh@0: krh@0: static void * krh@0: zalloc(size_t size) krh@0: { krh@0: void *p; krh@0: krh@0: p = malloc(size); krh@0: memset(p, 0, size); krh@0: krh@0: return p; krh@0: } krh@0: krh@4: struct razor_set_header { krh@4: unsigned int magic; krh@4: unsigned int version; krh@14: struct { unsigned int type, offset, size; } sections[0]; krh@4: }; krh@4: krh@4: #define RAZOR_MAGIC 0x7a7a7a7a krh@4: #define RAZOR_VERSION 1 krh@8: krh@4: #define RAZOR_BUCKETS 1 krh@4: #define RAZOR_STRINGS 2 krh@4: #define RAZOR_PACKAGES 3 krh@8: #define RAZOR_REQUIRES 4 krh@8: #define RAZOR_PROVIDES 5 krh@10: #define RAZOR_PROPERTIES 6 krh@4: krh@4: struct razor_package { krh@4: unsigned long name; krh@4: unsigned long version; krh@10: unsigned long requires; krh@10: unsigned long provides; krh@4: }; krh@4: krh@8: struct razor_property { krh@7: unsigned long name; krh@7: unsigned long version; krh@9: unsigned long packages; krh@7: }; krh@7: krh@4: struct razor_set { krh@6: struct array buckets; krh@6: struct array string_pool; krh@10: struct array property_pool; krh@7: struct array packages; krh@8: struct array requires; krh@7: struct array provides; krh@4: struct razor_set_header *header; krh@4: }; krh@4: krh@4: struct razor_set * krh@4: razor_set_create(void) krh@0: { krh@18: return zalloc(sizeof(struct razor_set)); krh@0: } krh@0: krh@4: struct razor_set * krh@4: razor_set_open(const char *filename) krh@0: { krh@4: struct razor_set *set; krh@0: struct stat stat; krh@0: unsigned int size, offset; krh@0: int fd, i; krh@0: krh@4: set = zalloc(sizeof *set); krh@0: fd = open(filename, O_RDONLY); krh@0: if (fstat(fd, &stat) < 0) krh@0: return NULL; krh@4: set->header = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); krh@4: if (set->header == MAP_FAILED) { krh@4: free(set); krh@0: return NULL; krh@0: } krh@0: krh@4: for (i = 0; i < set->header->sections[i].type; i++) { krh@4: offset = set->header->sections[i].offset; krh@14: size = set->header->sections[i].size; krh@0: krh@4: switch (set->header->sections[i].type) { krh@4: case RAZOR_STRINGS: krh@6: set->string_pool.data = (void *) set->header + offset; krh@6: set->string_pool.size = size; krh@6: set->string_pool.alloc = size; krh@0: break; krh@4: case RAZOR_PACKAGES: krh@6: set->packages.data = (void *) set->header + offset; krh@6: set->packages.size = size; krh@6: set->packages.size = size; krh@3: break; krh@8: case RAZOR_REQUIRES: krh@8: set->requires.data = (void *) set->header + offset; krh@8: set->requires.size = size; krh@8: set->requires.size = size; krh@8: break; krh@7: case RAZOR_PROVIDES: krh@7: set->provides.data = (void *) set->header + offset; krh@7: set->provides.size = size; krh@7: set->provides.size = size; krh@7: break; krh@10: case RAZOR_PROPERTIES: krh@10: set->property_pool.data = (void *) set->header + offset; krh@10: set->property_pool.size = size; krh@10: set->property_pool.size = size; krh@10: break; krh@0: } krh@0: } krh@0: close(fd); krh@0: krh@4: return set; krh@0: } krh@0: krh@0: void krh@4: razor_set_destroy(struct razor_set *set) krh@0: { krh@0: unsigned int size; krh@0: int i; krh@0: krh@4: if (set->header) { krh@4: for (i = 0; set->header->sections[i].type; i++) krh@0: ; krh@4: size = set->header->sections[i].type; krh@4: munmap(set->header, size); krh@17: free(set->buckets.data); krh@0: } else { krh@6: free(set->buckets.data); krh@6: free(set->string_pool.data); krh@6: free(set->packages.data); krh@9: free(set->requires.data); krh@9: free(set->provides.data); krh@10: free(set->property_pool.data); krh@0: } krh@0: krh@4: free(set); krh@0: } krh@0: krh@0: static int krh@4: razor_set_write(struct razor_set *set, const char *filename) krh@0: { krh@0: char data[4096]; krh@4: struct razor_set_header *header = (struct razor_set_header *) data; krh@17: unsigned long offset; krh@17: int i, fd; krh@17: struct { int type; struct array *array; } sections[] = { krh@17: { RAZOR_STRINGS, &set->string_pool }, krh@17: { RAZOR_PACKAGES, &set->packages }, krh@17: { RAZOR_REQUIRES, &set->requires }, krh@17: { RAZOR_PROVIDES, &set->provides }, krh@17: { RAZOR_PROPERTIES, &set->property_pool }, krh@17: { 0 } krh@17: }; krh@0: krh@0: memset(data, 0, sizeof data); krh@4: header->magic = RAZOR_MAGIC; krh@4: header->version = RAZOR_VERSION; krh@17: offset = sizeof data; krh@0: krh@17: for (i = 0; sections[i].type != 0; i++) { krh@17: header->sections[i].type = sections[i].type; krh@17: header->sections[i].offset = offset; krh@17: header->sections[i].size = sections[i].array->size; krh@17: offset += (sections[i].array->size + 4095) & ~4095; krh@17: } krh@0: krh@17: header->sections[i].type = 0; krh@17: header->sections[i].offset = 0; krh@17: header->sections[i].size = 0; krh@10: krh@0: fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0666); krh@0: if (fd < 0) krh@0: return -1; krh@0: krh@0: write_to_fd(fd, data, sizeof data); krh@17: for (i = 0; sections[i].type != 0; i++) krh@17: write_to_fd(fd, sections[i].array->data, krh@17: (sections[i].array->size + 4095) & ~4095); krh@17: krh@17: close(fd); krh@0: krh@0: return 0; krh@0: } krh@0: krh@0: static unsigned int krh@0: hash_string(const char *key) krh@0: { krh@0: const char *p; krh@0: unsigned int hash = 0; krh@0: krh@0: for (p = key; *p; p++) krh@9: hash = (hash * 617) ^ *p; krh@0: krh@0: return hash; krh@0: } krh@0: krh@0: unsigned long krh@4: razor_set_lookup(struct razor_set *set, const char *key) krh@0: { krh@6: unsigned int mask, start, i; krh@6: unsigned long *b; krh@6: char *pool; krh@0: krh@6: pool = set->string_pool.data; krh@6: mask = set->buckets.alloc - 1; krh@6: start = hash_string(key) * sizeof(unsigned long); krh@0: krh@6: for (i = 0; i < set->buckets.alloc; i += sizeof *b) { krh@6: b = set->buckets.data + ((start + i) & mask); krh@6: krh@6: if (*b == 0) krh@0: return 0; krh@0: krh@6: if (strcmp(key, &pool[*b]) == 0) krh@6: return *b; krh@6: } krh@0: krh@0: return 0; krh@0: } krh@0: krh@0: static unsigned long krh@4: add_to_string_pool(struct razor_set *set, const char *key) krh@0: { krh@6: int len; krh@6: char *p; krh@0: krh@0: len = strlen(key) + 1; krh@6: p = array_add(&set->string_pool, len); krh@6: memcpy(p, key, len); krh@0: krh@6: return p - (char *) set->string_pool.data; krh@0: } krh@0: krh@10: static unsigned long krh@10: add_to_property_pool(struct razor_set *set, struct array *properties) krh@10: { krh@10: unsigned long *p; krh@10: krh@10: p = array_add(properties, sizeof *p); krh@18: *p = ~0ul; krh@10: p = array_add(&set->property_pool, properties->size); krh@10: memcpy(p, properties->data, properties->size); krh@10: krh@10: return p - (unsigned long *) set->property_pool.data; krh@10: } krh@10: krh@0: static void krh@4: do_insert(struct razor_set *set, unsigned long value) krh@0: { krh@6: unsigned int mask, start, i; krh@6: unsigned long *b; krh@0: const char *key; krh@0: krh@6: key = (char *) set->string_pool.data + value; krh@6: mask = set->buckets.alloc - 1; krh@6: start = hash_string(key) * sizeof(unsigned long); krh@6: krh@6: for (i = 0; i < set->buckets.alloc; i += sizeof *b) { krh@6: b = set->buckets.data + ((start + i) & mask); krh@6: if (*b == 0) { krh@6: *b = value; krh@0: break; krh@0: } krh@6: } krh@0: } krh@0: krh@0: unsigned long krh@4: razor_set_insert(struct razor_set *set, const char *key) krh@0: { krh@6: unsigned long value, *buckets, *b, *end; krh@6: int alloc; krh@0: krh@6: alloc = set->buckets.alloc; krh@6: array_add(&set->buckets, 4 * sizeof *buckets); krh@6: if (alloc != set->buckets.alloc) { krh@6: end = set->buckets.data + alloc; krh@6: memset(end, 0, set->buckets.alloc - alloc); krh@6: for (b = set->buckets.data; b < end; b++) { krh@6: value = *b; krh@6: if (value != 0) { krh@6: *b = 0; krh@4: do_insert(set, value); krh@6: } krh@0: } krh@0: } krh@0: krh@4: value = add_to_string_pool(set, key); krh@4: do_insert (set, value); krh@0: krh@0: return value; krh@0: } krh@0: krh@0: unsigned long krh@4: razor_set_tokenize(struct razor_set *set, const char *string) krh@0: { krh@0: unsigned long token; krh@0: krh@13: if (string == NULL) krh@18: return razor_set_tokenize(set, ""); krh@13: krh@4: token = razor_set_lookup(set, string); krh@0: if (token != 0) krh@0: return token; krh@0: krh@4: return razor_set_insert(set, string); krh@0: } krh@0: krh@13: struct import_property_context { krh@10: struct array all; krh@10: struct array package; krh@10: }; krh@10: krh@9: struct import_context { krh@4: struct razor_set *set; krh@13: struct import_property_context requires; krh@13: struct import_property_context provides; krh@18: struct array packages; krh@18: struct import_package *package; krh@10: unsigned long *requires_map; krh@10: unsigned long *provides_map; krh@10: }; krh@10: krh@18: struct import_package { krh@18: unsigned long name; krh@18: unsigned long version; krh@18: unsigned long requires; krh@18: unsigned long provides; krh@18: unsigned long index; krh@18: }; krh@18: krh@10: struct import_property { krh@10: unsigned long name; krh@10: unsigned long version; krh@10: unsigned long package; krh@10: unsigned long index; krh@10: unsigned long unique_index; krh@0: }; krh@0: krh@0: static void krh@13: import_context_add_package(struct import_context *ctx, krh@13: const char *name, const char *version) krh@13: { krh@18: struct import_package *p; krh@13: krh@18: p = array_add(&ctx->packages, sizeof *p); krh@13: p->name = razor_set_tokenize(ctx->set, name); krh@13: p->version = razor_set_tokenize(ctx->set, version); krh@18: p->index = p - (struct import_package *) ctx->packages.data; krh@13: krh@18: ctx->package = p; krh@13: array_init(&ctx->requires.package); krh@13: array_init(&ctx->provides.package); krh@13: } krh@13: krh@13: void krh@13: import_context_finish_package(struct import_context *ctx) krh@13: { krh@18: struct import_package *p; krh@13: krh@18: p = ctx->package; krh@13: p->requires = add_to_property_pool(ctx->set, &ctx->requires.package); krh@13: p->provides = add_to_property_pool(ctx->set, &ctx->provides.package); krh@13: krh@13: array_release(&ctx->requires.package); krh@13: array_release(&ctx->provides.package); krh@13: } krh@13: krh@13: static void krh@13: import_context_add_property(struct import_context *ctx, krh@13: struct import_property_context *pctx, krh@13: const char *name, const char *version) krh@13: { krh@13: struct import_property *p; krh@13: unsigned long *r; krh@13: krh@13: p = array_add(&pctx->all, sizeof *p); krh@13: p->name = razor_set_tokenize(ctx->set, name); krh@13: p->version = razor_set_tokenize(ctx->set, version); krh@18: p->package = ctx->package->index; krh@13: p->index = p - (struct import_property *) pctx->all.data; krh@13: krh@13: r = array_add(&pctx->package, sizeof *r); krh@13: *r = p->index; krh@13: } krh@13: krh@13: static void krh@9: parse_package(struct import_context *ctx, const char **atts, void *data) krh@3: { krh@13: const char *name = NULL, *version = NULL; krh@3: int i; krh@3: krh@3: for (i = 0; atts[i]; i += 2) { krh@3: if (strcmp(atts[i], "name") == 0) krh@13: name = atts[i + 1]; krh@3: else if (strcmp(atts[i], "version") == 0) krh@13: version = atts[i + 1]; krh@3: } krh@3: krh@13: if (name == NULL || version == NULL) { krh@3: fprintf(stderr, "invalid package tag, " krh@3: "missing name or version attributes\n"); krh@3: return; krh@3: } krh@3: krh@13: import_context_add_package(ctx, name, version); krh@3: } krh@3: krh@3: static void krh@9: parse_property(struct import_context *ctx, const char **atts, void *data) krh@8: { krh@13: const char *name = NULL, *version = NULL; krh@8: int i; krh@8: krh@8: for (i = 0; atts[i]; i += 2) { krh@8: if (strcmp(atts[i], "name") == 0) krh@13: name = atts[i + 1]; krh@9: if (strcmp(atts[i], "version") == 0) krh@13: version = atts[i + 1]; krh@8: } krh@8: krh@13: if (name == NULL) { krh@9: fprintf(stderr, "invalid tag, missing name attribute\n"); krh@8: return; krh@8: } krh@8: krh@13: import_context_add_property(ctx, data, name, version); krh@7: } krh@7: krh@7: static void krh@0: start_element(void *data, const char *name, const char **atts) krh@0: { krh@9: struct import_context *ctx = data; krh@0: krh@3: if (strcmp(name, "package") == 0) krh@9: parse_package(ctx, atts, NULL); krh@8: else if (strcmp(name, "requires") == 0) krh@9: parse_property(ctx, atts, &ctx->requires); krh@7: else if (strcmp(name, "provides") == 0) krh@9: parse_property(ctx, atts, &ctx->provides); krh@0: } krh@0: krh@0: static void krh@0: end_element (void *data, const char *name) krh@0: { krh@9: struct import_context *ctx = data; krh@4: krh@13: if (strcmp(name, "package") == 0) krh@13: import_context_finish_package(ctx); krh@0: } krh@0: krh@0: static char * krh@0: sha1_to_hex(const unsigned char *sha1) krh@0: { krh@0: static int bufno; krh@0: static char hexbuffer[4][50]; krh@0: static const char hex[] = "0123456789abcdef"; krh@0: char *buffer = hexbuffer[3 & ++bufno], *buf = buffer; krh@0: int i; krh@0: krh@0: for (i = 0; i < 20; i++) { krh@0: unsigned int val = *sha1++; krh@0: *buf++ = hex[val >> 4]; krh@0: *buf++ = hex[val & 0xf]; krh@0: } krh@0: *buf = '\0'; krh@0: krh@0: return buffer; krh@0: } krh@0: krh@9: static void krh@13: razor_prepare_import(struct import_context *ctx) krh@9: { krh@9: memset(ctx, 0, sizeof *ctx); krh@13: ctx->set = razor_set_create(); krh@9: } krh@9: krh@0: static int krh@13: razor_import(struct import_context *ctx, const char *filename) krh@0: { krh@0: SHA_CTX sha1; krh@0: XML_Parser parser; krh@0: int fd; krh@0: void *p; krh@0: struct stat stat; krh@0: char buf[128]; krh@0: unsigned char hash[20]; krh@0: krh@0: fd = open(filename, O_RDONLY); krh@0: if (fstat(fd, &stat) < 0) krh@0: return -1; krh@0: p = mmap(NULL, stat.st_size, PROT_READ, MAP_PRIVATE, fd, 0); krh@0: if (p == MAP_FAILED) krh@0: return -1; krh@0: krh@0: parser = XML_ParserCreate(NULL); krh@9: XML_SetUserData(parser, ctx); krh@0: XML_SetElementHandler(parser, start_element, end_element); krh@0: if (XML_Parse(parser, p, stat.st_size, 1) == XML_STATUS_ERROR) { krh@0: fprintf(stderr, krh@0: "%s at line %d, %s\n", krh@0: XML_ErrorString(XML_GetErrorCode(parser)), krh@0: XML_GetCurrentLineNumber(parser), krh@0: filename); krh@0: return 1; krh@0: } krh@0: krh@0: XML_ParserFree(parser); krh@0: krh@0: SHA1_Init(&sha1); krh@0: SHA1_Update(&sha1, p, stat.st_size); krh@0: SHA1_Final(hash, &sha1); krh@0: krh@0: close(fd); krh@0: krh@0: snprintf(buf, sizeof buf, "set/%s", sha1_to_hex(hash)); krh@0: if (write_to_file(buf, p, stat.st_size) < 0) krh@0: return -1; krh@0: munmap(p, stat.st_size); krh@0: krh@0: return 0; krh@0: } krh@0: krh@9: static struct razor_set *qsort_set; krh@9: krh@9: static int krh@9: compare_packages(const void *p1, const void *p2) krh@9: { krh@18: const struct import_package *pkg1 = p1, *pkg2 = p2; krh@9: char *pool = qsort_set->string_pool.data; krh@9: krh@9: return strcmp(&pool[pkg1->name], &pool[pkg2->name]); krh@9: } krh@9: krh@9: static int krh@9: compare_properties(const void *p1, const void *p2) krh@9: { krh@10: const struct import_property *prop1 = p1, *prop2 = p2; krh@9: char *pool = qsort_set->string_pool.data; krh@12: int result; krh@9: krh@12: result = strcmp(&pool[prop1->name], &pool[prop2->name]); krh@12: if (result == 0) krh@12: return strcmp(&pool[prop1->version], &pool[prop2->version]); krh@12: else krh@12: return result; krh@9: } krh@9: krh@10: static unsigned long * krh@18: uniqueify_properties(struct razor_set *set, krh@18: struct array *in, struct array *out) krh@9: { krh@10: struct import_property *ip, *end; krh@18: struct razor_property *rp, *rp_end; krh@18: struct array *pkgs, *p; krh@18: unsigned long *map, *r; krh@18: int i, count, unique; krh@9: krh@10: count = in->size / sizeof(struct import_property); krh@10: qsort(in->data, count, krh@10: sizeof(struct import_property), compare_properties); krh@9: krh@10: rp = NULL; krh@9: end = in->data + in->size; krh@10: for (ip = in->data; ip < end; ip++) { krh@10: if (rp == NULL || krh@10: ip->name != rp->name || ip->version != rp->version) { krh@10: rp = array_add(out, sizeof *rp); krh@10: rp->name = ip->name; krh@10: rp->version = ip->version; krh@10: } krh@10: ip->unique_index = rp - (struct razor_property *) out->data; krh@10: } krh@9: krh@10: map = malloc(count * sizeof (unsigned long)); krh@10: ip = in->data; krh@10: for (i = 0; i < count; i++) krh@10: map[ip[i].index] = ip[i].unique_index; krh@10: krh@18: unique = out->size / sizeof(*rp); krh@18: pkgs = zalloc(unique * sizeof *pkgs); krh@18: for (ip = in->data; ip < end; ip++) { krh@18: r = array_add(&pkgs[ip->unique_index], sizeof *r); krh@18: *r = ip->package; krh@18: } krh@18: krh@18: rp_end = out->data + out->size; krh@18: for (rp = out->data, p = pkgs; rp < rp_end; rp++, p++) krh@18: rp->packages = add_to_property_pool(set, p); krh@18: krh@18: free(pkgs); krh@18: krh@10: return map; krh@10: } krh@10: krh@10: static void krh@18: remap_package_links(struct import_context *ctx) krh@10: { krh@18: struct import_package *p, *end; krh@10: unsigned long *pool, *r; krh@10: krh@10: pool = ctx->set->property_pool.data; krh@18: end = ctx->packages.data + ctx->packages.size; krh@18: for (p = ctx->packages.data; p < end; p++) { krh@18: for (r = &pool[p->requires]; ~*r; r++) krh@10: *r = ctx->requires_map[*r]; krh@18: for (r = &pool[p->provides]; ~*r; r++) krh@10: *r = ctx->provides_map[*r]; krh@9: } krh@18: } krh@10: krh@18: static void krh@18: remap_property_links(struct import_context *ctx) krh@18: { krh@18: struct razor_property *p, *end; krh@18: struct import_package *ip; krh@18: unsigned long *map, *pool, *r; krh@18: int i, count; krh@18: krh@18: pool = ctx->set->property_pool.data; krh@18: count = ctx->packages.size / sizeof(struct import_package); krh@18: map = malloc(count * sizeof *map); krh@18: ip = ctx->packages.data; krh@18: for (i = 0; i < count; i++) krh@18: map[ip[i].index] = i; krh@18: krh@18: /* FIXME: This will break if we implement package list sharing krh@18: * for all properties, since we'll remap those lists more than krh@18: * once. We should just have a separate pool for property krh@18: * lists and a separate pool for package lists and remap it as krh@18: * a flat pool. Right now, as property lists and package krh@18: * lists are mixed, we can't do that. */ krh@18: krh@18: end = ctx->set->requires.data + ctx->set->requires.size; krh@18: for (p = ctx->set->requires.data; p < end; p++) krh@18: for (r = &pool[p->packages]; ~*r; r++) krh@18: *r = map[*r]; krh@18: krh@18: end = ctx->set->provides.data + ctx->set->provides.size; krh@18: for (p = ctx->set->provides.data; p < end; p++) krh@18: for (r = &pool[p->packages]; ~*r; r++) krh@18: *r = map[*r]; krh@18: krh@18: free(map); krh@9: } krh@9: krh@13: static struct razor_set * krh@13: razor_finish_import(struct import_context *ctx) krh@9: { krh@18: struct import_package *ip; krh@18: struct razor_package *rp; krh@18: int i, count; krh@18: krh@9: qsort_set = ctx->set; krh@9: krh@10: ctx->requires_map = krh@18: uniqueify_properties(ctx->set, krh@18: &ctx->requires.all, krh@18: &ctx->set->requires); krh@10: ctx->provides_map = krh@18: uniqueify_properties(ctx->set, krh@18: &ctx->provides.all, krh@18: &ctx->set->provides); krh@9: krh@18: remap_package_links(ctx); krh@18: krh@18: count = ctx->packages.size / sizeof(struct import_package); krh@18: qsort(ctx->packages.data, count, krh@18: sizeof(struct import_package), compare_packages); krh@18: krh@18: ip = ctx->packages.data; krh@18: for (i = 0; i < count; i++, ip++, rp++) { krh@18: rp = array_add(&ctx->set->packages, sizeof *rp); krh@18: rp->name = ip->name; krh@18: rp->version = ip->version; krh@18: rp->requires = ip->requires; krh@18: rp->provides = ip->provides; krh@18: } krh@18: krh@18: remap_property_links(ctx); krh@10: krh@10: free(ctx->requires.all.data); krh@10: free(ctx->provides.all.data); krh@10: free(ctx->requires_map); krh@10: free(ctx->provides_map); krh@13: krh@9: fprintf(stderr, "parsed %d requires, %d unique\n", krh@10: ctx->requires.all.size / sizeof(struct import_property), krh@9: ctx->set->requires.size / sizeof(struct razor_property)); krh@9: fprintf(stderr, "parsed %d provides, %d unique\n", krh@10: ctx->provides.all.size / sizeof(struct import_property), krh@9: ctx->set->provides.size / sizeof(struct razor_property)); krh@13: krh@13: return ctx->set; krh@9: } krh@9: krh@15: /* Import a yum filelist as a razor package set. */ krh@15: krh@15: enum { krh@15: YUM_STATE_BEGIN, krh@15: YUM_STATE_PACKAGE_NAME krh@15: }; krh@15: krh@15: struct yum_context { krh@15: struct import_context ctx; krh@15: struct import_property_context *current_property_context; krh@15: char *name; krh@15: int state; krh@15: }; krh@15: krh@15: static void krh@15: yum_start_element(void *data, const char *name, const char **atts) krh@15: { krh@15: struct yum_context *ctx = data; krh@15: const char *n, *version; krh@15: int i; krh@15: krh@15: if (strcmp(name, "name") == 0) { krh@15: ctx->state = YUM_STATE_PACKAGE_NAME; krh@15: } else if (strcmp(name, "version") == 0) { krh@15: for (i = 0; atts[i]; i += 2) { krh@15: if (strcmp(atts[i], "ver") == 0) krh@15: version = atts[i + 1]; krh@15: } krh@15: import_context_add_package(&ctx->ctx, ctx->name, version); krh@15: } else if (strcmp(name, "rpm:requires") == 0) { krh@15: ctx->current_property_context = &ctx->ctx.requires; krh@15: } else if (strcmp(name, "rpm:provides") == 0) { krh@15: ctx->current_property_context = &ctx->ctx.provides; krh@15: } else if (strcmp(name, "rpm:entry") == 0 && krh@15: ctx->current_property_context != NULL) { krh@15: n = NULL; krh@15: version = NULL; krh@15: for (i = 0; atts[i]; i += 2) { krh@15: if (strcmp(atts[i], "name") == 0) krh@15: n = atts[i + 1]; krh@15: else if (strcmp(atts[i], "ver") == 0) krh@15: version = atts[i + 1]; krh@15: } krh@15: krh@15: if (n == NULL) { krh@15: fprintf(stderr, "invalid rpm:entry, " krh@15: "missing name or version attributes\n"); krh@15: return; krh@15: } krh@15: krh@15: import_context_add_property(&ctx->ctx, krh@15: ctx->current_property_context, krh@15: n, version); krh@15: } krh@15: } krh@15: krh@15: static void krh@15: yum_end_element (void *data, const char *name) krh@15: { krh@15: struct yum_context *ctx = data; krh@15: krh@15: if (strcmp(name, "package") == 0) { krh@15: free(ctx->name); krh@15: import_context_finish_package(&ctx->ctx); krh@15: } else if (strcmp(name, "name") == 0) { krh@15: ctx->state = 0; krh@15: } else if (strcmp(name, "rpm:requires") == 0) { krh@15: ctx->current_property_context = NULL; krh@15: } else if (strcmp(name, "rpm:provides") == 0) { krh@15: ctx->current_property_context = NULL; krh@15: } krh@15: } krh@15: krh@15: static void krh@15: yum_character_data (void *data, const XML_Char *s, int len) krh@15: { krh@15: struct yum_context *ctx = data; krh@15: krh@15: if (ctx->state == YUM_STATE_PACKAGE_NAME) krh@15: ctx->name = strndup(s, len); krh@15: } krh@15: krh@15: static struct razor_set * krh@15: razor_set_create_from_yum_filelist(int fd) krh@15: { krh@15: struct yum_context ctx; krh@15: XML_Parser parser; krh@15: char buf[4096]; krh@15: int len; krh@15: krh@15: razor_prepare_import(&ctx.ctx); krh@15: krh@15: parser = XML_ParserCreate(NULL); krh@15: XML_SetUserData(parser, &ctx); krh@15: XML_SetElementHandler(parser, yum_start_element, yum_end_element); krh@15: XML_SetCharacterDataHandler(parser, yum_character_data); krh@15: krh@15: while (1) { krh@15: len = read(fd, buf, sizeof buf); krh@15: if (len < 0) { krh@15: fprintf(stderr, krh@15: "couldn't read input: %s\n", strerror(errno)); krh@15: return NULL; krh@15: } else if (len == 0) krh@15: break; krh@15: krh@15: if (XML_Parse(parser, buf, len, 0) == XML_STATUS_ERROR) { krh@15: fprintf(stderr, krh@15: "%s at line %d\n", krh@15: XML_ErrorString(XML_GetErrorCode(parser)), krh@15: XML_GetCurrentLineNumber(parser)); krh@15: return NULL; krh@15: } krh@15: } krh@15: krh@15: XML_ParserFree(parser); krh@15: krh@15: return razor_finish_import(&ctx.ctx); krh@15: } krh@15: krh@0: void krh@4: razor_set_list(struct razor_set *set) krh@3: { krh@6: struct razor_package *p, *end; krh@6: char *pool; krh@3: krh@6: pool = set->string_pool.data; krh@6: end = set->packages.data + set->packages.size; krh@14: for (p = set->packages.data; p < end; p++) krh@6: printf("%s %s\n", &pool[p->name], &pool[p->version]); krh@3: } krh@3: krh@16: struct razor_set *bsearch_set; krh@16: krh@16: static int krh@16: compare_package_name(const void *key, const void *data) krh@16: { krh@16: const struct razor_package *p = data; krh@16: char *pool; krh@16: krh@16: pool = bsearch_set->string_pool.data; krh@16: krh@16: return strcmp(key, &pool[p->name]); krh@16: } krh@16: krh@10: struct razor_package * krh@10: razor_set_get_package(struct razor_set *set, const char *package) krh@10: { krh@16: bsearch_set = set; krh@16: return bsearch(package, set->packages.data, krh@16: set->packages.size / sizeof(struct razor_package), krh@16: sizeof(struct razor_package), compare_package_name); krh@10: } krh@10: krh@18: static int krh@18: compare_property_name(const void *key, const void *data) krh@18: { krh@18: const struct razor_property *p = data; krh@18: char *pool; krh@18: krh@18: pool = bsearch_set->string_pool.data; krh@18: krh@18: return strcmp(key, &pool[p->name]); krh@18: } krh@18: krh@18: struct razor_property * krh@18: razor_set_get_property(struct razor_set *set, krh@18: struct array *properties, krh@18: const char *property) krh@18: { krh@18: struct razor_property *p, *start; krh@18: krh@18: bsearch_set = set; krh@18: p = bsearch(property, properties->data, krh@18: properties->size / sizeof(struct razor_property), krh@18: sizeof(struct razor_property), compare_property_name); krh@18: krh@18: start = properties->data; krh@18: while (p > start && (p - 1)->name == p->name) krh@18: p--; krh@18: krh@18: return p; krh@18: } krh@18: krh@10: static void krh@10: razor_set_list_all_properties(struct razor_set *set, struct array *properties) krh@8: { krh@8: struct razor_property *p, *end; krh@8: char *pool; krh@8: krh@8: pool = set->string_pool.data; krh@10: end = properties->data + properties->size; krh@14: for (p = properties->data; p < end; p++) krh@8: printf("%s %s\n", &pool[p->name], &pool[p->version]); krh@8: } krh@8: krh@8: void krh@10: razor_set_list_requires(struct razor_set *set, const char *name) krh@7: { krh@10: struct razor_property *p, *requires; krh@10: struct razor_package *package; krh@10: unsigned long *r; krh@7: char *pool; krh@7: krh@10: if (name) { krh@10: package = razor_set_get_package(set, name); krh@10: r = (unsigned long *) set->property_pool.data + krh@10: package->requires; krh@10: requires = set->requires.data; krh@10: pool = set->string_pool.data; krh@18: while (~*r) { krh@10: p = &requires[*r++]; krh@10: printf("%s %s\n", &pool[p->name], &pool[p->version]); krh@10: } krh@10: } else krh@10: razor_set_list_all_properties(set, &set->requires); krh@10: } krh@10: krh@10: void krh@10: razor_set_list_provides(struct razor_set *set, const char *name) krh@10: { krh@10: struct razor_property *p, *provides; krh@10: struct razor_package *package; krh@10: unsigned long *r; krh@10: char *pool; krh@10: krh@10: if (name) { krh@10: package = razor_set_get_package(set, name); krh@10: r = (unsigned long *) set->property_pool.data + krh@10: package->provides; krh@10: provides = set->provides.data; krh@10: pool = set->string_pool.data; krh@18: while (~*r) { krh@10: p = &provides[*r++]; krh@10: printf("%s %s\n", &pool[p->name], &pool[p->version]); krh@10: } krh@10: } else krh@10: razor_set_list_all_properties(set, &set->provides); krh@7: } krh@7: krh@7: void krh@18: razor_set_list_property_packages(struct razor_set *set, krh@18: struct array *properties, krh@18: const char *name) krh@18: { krh@18: struct razor_property *property, *end; krh@18: struct razor_package *p, *packages; krh@18: unsigned long *r; krh@18: char *pool; krh@18: krh@18: if (name == NULL) krh@18: return; krh@18: krh@18: property = razor_set_get_property(set, properties, name); krh@18: packages = set->packages.data; krh@18: pool = set->string_pool.data; krh@18: end = properties->data + properties->size; krh@18: while (property < end && strcmp(name, &pool[property->name]) == 0) { krh@18: r = (unsigned long *) krh@18: set->property_pool.data + property->packages; krh@18: while (~*r) { krh@18: p = &packages[*r++]; krh@18: printf("%s %s\n", krh@18: &pool[p->name], &pool[p->version]); krh@18: } krh@18: property++; krh@18: } krh@18: } krh@18: krh@18: void krh@4: razor_set_info(struct razor_set *set) krh@3: { krh@3: unsigned int offset, size; krh@3: int i; krh@3: krh@4: for (i = 0; i < set->header->sections[i].type; i++) { krh@4: offset = set->header->sections[i].offset; krh@18: size = set->header->sections[i].size; krh@3: krh@4: switch (set->header->sections[i].type) { krh@4: case RAZOR_STRINGS: krh@3: printf("string pool:\t\t%dkb\n", size / 1024); krh@3: break; krh@4: case RAZOR_PACKAGES: krh@3: printf("package section:\t%dkb\n", size / 1024); krh@3: break; krh@8: case RAZOR_REQUIRES: krh@8: printf("requires section:\t%dkb\n", size / 1024); krh@8: break; krh@7: case RAZOR_PROVIDES: krh@7: printf("provides section:\t%dkb\n", size / 1024); krh@7: break; krh@18: case RAZOR_PROPERTIES: krh@18: printf("properties section:\t%dkb\n", size / 1024); krh@18: break; krh@3: } krh@3: } krh@0: } krh@0: krh@0: static int krh@0: usage(void) krh@0: { krh@7: printf("usage: razor [ import FILES | lookup | " krh@15: "list | list-requires | list-provides | eat-yum | info ]\n"); krh@0: exit(1); krh@0: } krh@0: krh@15: static const char *repo_filename = "system.repo"; krh@15: static const char rawhide_repo_filename[] = "rawhide.repo"; krh@0: krh@0: int krh@0: main(int argc, char *argv[]) krh@0: { krh@0: int i; krh@4: struct razor_set *set; krh@0: struct stat statbuf; krh@9: struct import_context ctx; krh@15: char *repo; krh@15: krh@15: repo = getenv("RAZOR_REPO"); krh@15: if (repo != NULL) krh@15: repo_filename = repo; krh@0: krh@3: if (argc < 2) { krh@0: usage(); krh@0: } else if (strcmp(argv[1], "import") == 0) { krh@0: if (stat("set", &statbuf) && mkdir("set", 0777)) { krh@0: fprintf(stderr, "could not create directory 'set'\n"); krh@0: exit(-1); krh@0: } krh@0: krh@13: razor_prepare_import(&ctx); krh@9: krh@0: for (i = 2; i < argc; i++) { krh@13: if (razor_import(&ctx, argv[i]) < 0) { krh@0: fprintf(stderr, "failed to import %s\n", krh@0: argv[i]); krh@0: exit(-1); krh@0: } krh@0: } krh@0: krh@13: set = razor_finish_import(&ctx); krh@6: krh@6: printf("bucket allocation: %d\n", set->buckets.alloc); krh@6: printf("pool size: %d\n", set->string_pool.size); krh@6: printf("pool allocation: %d\n", set->string_pool.alloc); krh@7: printf("packages: %d\n", krh@7: set->packages.size / sizeof(struct razor_package)); krh@8: printf("requires: %d\n", krh@8: set->requires.size / sizeof(struct razor_property)); krh@7: printf("provides: %d\n", krh@8: set->provides.size / sizeof(struct razor_property)); krh@0: krh@4: razor_set_write(set, repo_filename); krh@0: krh@4: razor_set_destroy(set); krh@0: } else if (strcmp(argv[1], "lookup") == 0) { krh@4: set = razor_set_open(repo_filename); krh@0: printf("%s is %lu\n", argv[2], krh@4: razor_set_lookup(set, argv[2])); krh@4: razor_set_destroy(set); krh@3: } else if (strcmp(argv[1], "list") == 0) { krh@4: set = razor_set_open(repo_filename); krh@4: razor_set_list(set); krh@4: razor_set_destroy(set); krh@8: } else if (strcmp(argv[1], "list-requires") == 0) { krh@8: set = razor_set_open(repo_filename); krh@10: razor_set_list_requires(set, argv[2]); krh@8: razor_set_destroy(set); krh@7: } else if (strcmp(argv[1], "list-provides") == 0) { krh@7: set = razor_set_open(repo_filename); krh@10: razor_set_list_provides(set, argv[2]); krh@7: razor_set_destroy(set); krh@18: } else if (strcmp(argv[1], "what-requires") == 0) { krh@18: set = razor_set_open(repo_filename); krh@18: razor_set_list_property_packages(set, &set->requires, argv[2]); krh@18: razor_set_destroy(set); krh@18: } else if (strcmp(argv[1], "what-provides") == 0) { krh@18: set = razor_set_open(repo_filename); krh@18: razor_set_list_property_packages(set, &set->provides, argv[2]); krh@18: razor_set_destroy(set); krh@3: } else if (strcmp(argv[1], "info") == 0) { krh@4: set = razor_set_open(repo_filename); krh@4: razor_set_info(set); krh@4: razor_set_destroy(set); krh@15: } else if (strcmp(argv[1], "eat-yum") == 0) { krh@15: set = razor_set_create_from_yum_filelist(STDIN_FILENO); krh@15: if (set == NULL) krh@15: return 1; krh@15: razor_set_write(set, rawhide_repo_filename); krh@15: razor_set_destroy(set); krh@18: printf("wrote %s\n", rawhide_repo_filename); krh@0: } else { krh@0: usage(); krh@0: } krh@0: krh@0: return 0; krh@0: }