librazor/importer.c
author James Bowes <jbowes@redhat.com>
Thu Jun 26 17:16:13 2008 -0400 (2008-06-26)
changeset 288 75da311eda45
parent 264 634e54ca476c
child 309 a69289c9080c
permissions -rw-r--r--
Add return codes to razor_set_open_files and open_details
     1 /*
     2  * Copyright (C) 2008  Kristian Høgsberg <krh@redhat.com>
     3  * Copyright (C) 2008  Red Hat, Inc
     4  *
     5  * This program is free software; you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License as published by
     7  * the Free Software Foundation; either version 2 of the License, or
     8  * (at your option) any later version.
     9  *
    10  * This program is distributed in the hope that it will be useful,
    11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  * GNU General Public License for more details.
    14  *
    15  * You should have received a copy of the GNU General Public License along
    16  * with this program; if not, write to the Free Software Foundation, Inc.,
    17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
    18  */
    19 
    20 #define _GNU_SOURCE
    21 
    22 #include <string.h>
    23 #include "razor-internal.h"
    24 #include "razor.h"
    25 
    26 RAZOR_EXPORT void
    27 razor_importer_begin_package(struct razor_importer *importer,
    28 			     const char *name,
    29 			     const char *version,
    30 			     const char *arch)
    31 {
    32 	struct razor_package *p;
    33 
    34 	p = array_add(&importer->set->packages, sizeof *p);
    35 	p->name = hashtable_tokenize(&importer->table, name);
    36 	p->flags = 0;
    37 	p->version = hashtable_tokenize(&importer->table, version);
    38 	p->arch = hashtable_tokenize(&importer->table, arch);
    39 
    40 	importer->package = p;
    41 	array_init(&importer->properties);
    42 }
    43 
    44 
    45 RAZOR_EXPORT void
    46 razor_importer_finish_package(struct razor_importer *importer)
    47 {
    48 	list_set_array(&importer->package->properties,
    49 		       &importer->set->property_pool,
    50 		       &importer->properties,
    51 		       1);
    52 
    53 	array_release(&importer->properties);
    54 }
    55 
    56 RAZOR_EXPORT void
    57 razor_importer_add_details(struct razor_importer *importer,
    58 			   const char *summary,
    59 			   const char *description,
    60 			   const char *url,
    61 			   const char *license)
    62 {
    63 	importer->package->summary = hashtable_tokenize(&importer->details_table, summary);
    64 	importer->package->description = hashtable_tokenize(&importer->details_table, description);
    65 	importer->package->url = hashtable_tokenize(&importer->details_table, url);
    66 	importer->package->license = hashtable_tokenize(&importer->details_table, license);
    67 }
    68 
    69 RAZOR_EXPORT void
    70 razor_importer_add_property(struct razor_importer *importer,
    71 			    const char *name,
    72 			    uint32_t flags,
    73 			    const char *version)
    74 {
    75 	struct razor_property *p;
    76 	uint32_t *r;
    77 
    78 	p = array_add(&importer->set->properties, sizeof *p);
    79 	p->name = hashtable_tokenize(&importer->table, name);
    80 	p->flags = flags;
    81 	p->version = hashtable_tokenize(&importer->table, version);
    82 	list_set_ptr(&p->packages, importer->package -
    83 		     (struct razor_package *) importer->set->packages.data);
    84 
    85 	r = array_add(&importer->properties, sizeof *r);
    86 	*r = p - (struct razor_property *) importer->set->properties.data;
    87 
    88 	if (((flags & RAZOR_PROPERTY_TYPE_MASK) == RAZOR_PROPERTY_REQUIRES) &&
    89 	    *name == '/') {
    90 		r = array_add(&importer->file_requires, sizeof *r);
    91 		*r = p->name;
    92 	}
    93 }
    94 
    95 RAZOR_EXPORT void
    96 razor_importer_add_file(struct razor_importer *importer, const char *name)
    97 {
    98 	struct import_entry *e;
    99 
   100 	e = array_add(&importer->files, sizeof *e);
   101 
   102 	e->package = importer->package -
   103 		(struct razor_package *) importer->set->packages.data;
   104 	e->name = strdup(name);
   105 }
   106 
   107 RAZOR_EXPORT struct razor_importer *
   108 razor_importer_create(void)
   109 {
   110 	struct razor_importer *importer;
   111 
   112 	importer = zalloc(sizeof *importer);
   113 	importer->set = razor_set_create();
   114 	hashtable_init(&importer->table, &importer->set->string_pool);
   115 	hashtable_init(&importer->details_table,
   116 		       &importer->set->details_string_pool);
   117 	hashtable_init(&importer->file_table,
   118 		       &importer->set->file_string_pool);
   119 
   120 	return importer;
   121 }
   122 
   123 /* Destroy an importer without creating the set. */
   124 RAZOR_EXPORT void
   125 razor_importer_destroy(struct razor_importer *importer)
   126 {
   127 	/* FIXME: write this */
   128 }
   129 
   130 static int
   131 compare_packages(const void *p1, const void *p2, void *data)
   132 {
   133 	const struct razor_package *pkg1 = p1, *pkg2 = p2;
   134 	struct razor_set *set = data;
   135 	char *pool = set->string_pool.data;
   136 
   137 	/* FIXME: what if the flags are different? */
   138 	if (pkg1->name == pkg2->name)
   139 		return razor_versioncmp(&pool[pkg1->version], &pool[pkg2->version]);
   140 	else
   141 		return strcmp(&pool[pkg1->name], &pool[pkg2->name]);
   142 }
   143 
   144 static int
   145 compare_properties(const void *p1, const void *p2, void *data)
   146 {
   147 	const struct razor_property *prop1 = p1, *prop2 = p2;
   148 	struct razor_set *set = data;
   149 	char *pool = set->string_pool.data;
   150 
   151 	if (prop1->name != prop2->name)
   152 		return strcmp(&pool[prop1->name], &pool[prop2->name]);
   153 	else if (prop1->flags != prop2->flags)
   154 		return prop1->flags - prop2->flags;
   155 	else if (prop1->version != prop2->version)
   156 		return razor_versioncmp(&pool[prop1->version], &pool[prop2->version]);
   157 	else
   158 		return prop1->packages.list_ptr - prop2->packages.list_ptr;
   159 }
   160 
   161 static uint32_t *
   162 uniqueify_properties(struct razor_set *set)
   163 {
   164 	struct razor_property *rp, *up, *rp_end;
   165 	struct array *pkgs, *p;
   166 	struct list_head *r;
   167 	uint32_t *map, *rmap;
   168 	int i, count, unique;
   169 
   170 	count = set->properties.size / sizeof(struct razor_property);
   171 	map = razor_qsort_with_data(set->properties.data,
   172 				    count,
   173 				    sizeof(struct razor_property),
   174 				    compare_properties,
   175 				    set);
   176 
   177 	rp_end = set->properties.data + set->properties.size;
   178 	rmap = malloc(count * sizeof *map);
   179 	pkgs = zalloc(count * sizeof *pkgs);
   180 	for (rp = set->properties.data, up = rp, i = 0; rp < rp_end; rp++, i++) {
   181 		if (rp->name != up->name ||
   182 		    rp->flags != up->flags ||
   183 		    rp->version != up->version) {
   184 			up++;
   185 			up->name = rp->name;
   186 			up->flags = rp->flags;
   187 			up->version = rp->version;
   188 		}
   189 
   190 		unique = up - (struct razor_property *) set->properties.data;
   191 		rmap[map[i]] = unique;
   192 		r = array_add(&pkgs[unique], sizeof *r);
   193 		*r = rp->packages;
   194 	}
   195 	free(map);
   196 
   197 	if (up != rp)
   198 		up++;
   199 	set->properties.size = (void *) up - set->properties.data;
   200 	rp_end = up;
   201 	for (rp = set->properties.data, p = pkgs; rp < rp_end; rp++, p++) {
   202 		list_set_array(&rp->packages, &set->package_pool, p, 0);
   203 		array_release(p);
   204 	}
   205 
   206 	free(pkgs);
   207 
   208 	return rmap;
   209 }
   210 
   211 static int
   212 compare_filenames(const void *p1, const void *p2, void *data)
   213 {
   214 	const struct import_entry *e1 = p1;
   215 	const struct import_entry *e2 = p2;
   216 	const char *n1 = e1->name;
   217 	const char *n2 = e2->name;
   218 
   219 	/* Need to make sure that the contents of a directory
   220 	 * are sorted immediately after it. So "foo/bar" has to
   221 	 * sort before "foo.conf"
   222 	 *
   223 	 * FIXME: this is about 60% slower than strcmp
   224 	 */
   225 	while (*n1 && *n2) {
   226 		if (*n1 < *n2)
   227 			return *n2 == '/' ? 1 : -1;
   228 		else if (*n1 > *n2)
   229 			return *n1 == '/' ? -1 : 1;
   230 		n1++;
   231 		n2++;
   232 	}
   233 	if (*n1)
   234 		return 1;
   235 	else if (*n2)
   236 		return -1;
   237 	else
   238 		return 0;
   239 }
   240 
   241 static void
   242 count_entries(struct import_directory *d)
   243 {
   244 	struct import_directory *p, *end;
   245 
   246 	p = d->files.data;
   247 	end = d->files.data + d->files.size;
   248 	d->count = 0;
   249 	while (p < end) {
   250 		count_entries(p);
   251 		d->count += p->count + 1;
   252 		p++;
   253 	}
   254 }
   255 
   256 static void
   257 serialize_files(struct razor_set *set,
   258 		struct import_directory *d, struct array *array)
   259 {
   260 	struct import_directory *p, *end;
   261 	struct razor_entry *e = NULL;
   262 	uint32_t s;
   263 
   264 	p = d->files.data;
   265 	end = d->files.data + d->files.size;
   266 	s = array->size / sizeof *e + d->files.size / sizeof *p;
   267 	while (p < end) {
   268 		e = array_add(array, sizeof *e);
   269 		e->name = p->name;
   270 		e->flags = 0;
   271 		e->start = p->count > 0 ? s : 0;
   272 		s += p->count;
   273 
   274 		list_set_array(&e->packages, &set->package_pool, &p->packages, 0);
   275 		array_release(&p->packages);
   276 		p++;
   277 	}
   278 	if (e != NULL)
   279 		e->flags |= RAZOR_ENTRY_LAST;
   280 
   281 	p = d->files.data;
   282 	end = d->files.data + d->files.size;
   283 	while (p < end) {
   284 		serialize_files(set, p, array);
   285 		p++;
   286 	}
   287 }
   288 
   289 static void
   290 remap_property_package_links(struct array *properties, uint32_t *rmap)
   291 {
   292 	struct razor_property *p, *end;
   293 
   294 	end = properties->data + properties->size;
   295 	for (p = properties->data; p < end; p++)
   296 		list_remap_head(&p->packages, rmap);
   297 }
   298 
   299 static void
   300 build_file_tree(struct razor_importer *importer)
   301 {
   302 	int count, i, length;
   303 	struct import_entry *filenames;
   304 	char *f, *end;
   305 	uint32_t name, *r;
   306 	char dirname[256];
   307 	struct import_directory *d, root;
   308 	struct razor_entry *e;
   309 
   310 	count = importer->files.size / sizeof (struct import_entry);
   311 	razor_qsort_with_data(importer->files.data,
   312 			      count,
   313 			      sizeof (struct import_entry),
   314 			      compare_filenames,
   315 			      NULL);
   316 
   317 	root.name = hashtable_tokenize(&importer->file_table, "");
   318 	array_init(&root.files);
   319 	array_init(&root.packages);
   320 	root.last = NULL;
   321 
   322 	filenames = importer->files.data;
   323 	for (i = 0; i < count; i++) {
   324 		f = filenames[i].name;
   325 		if (*f != '/')
   326 			continue;
   327 		f++;
   328 
   329 		d = &root;
   330 		while (*f) {
   331 			end = strchr(f, '/');
   332 			if (end == NULL)
   333 				end = f + strlen(f);
   334 			length = end - f;
   335 			memcpy(dirname, f, length);
   336 			dirname[length] ='\0';
   337 			name = hashtable_tokenize(&importer->file_table,
   338 						  dirname);
   339 			if (d->last == NULL || d->last->name != name) {
   340 				d->last = array_add(&d->files, sizeof *d);
   341 				d->last->name = name;
   342 				d->last->last = NULL;
   343 				array_init(&d->last->files);
   344 				array_init(&d->last->packages);
   345 			}
   346 			d = d->last;
   347 			f = end + 1;
   348 			if (*end == '\0')
   349 				break;
   350 		}
   351 
   352 		r = array_add(&d->packages, sizeof *r);
   353 		*r = filenames[i].package;
   354 		free(filenames[i].name);
   355 	}
   356 
   357 	count_entries(&root);
   358 	e = importer->set->files.data;
   359 	e->name = root.name;
   360 	e->flags = RAZOR_ENTRY_LAST;
   361 	e->start = importer->files.size ? 1 : 0;
   362 	list_set_empty(&e->packages);
   363 
   364 	serialize_files(importer->set, &root, &importer->set->files);
   365 
   366 	array_release(&importer->files);
   367 }
   368 
   369 static void
   370 list_to_array(struct list *list, struct array *array)
   371 {
   372 	uint32_t *item;
   373 
   374 	while (list) {
   375 		 item = array_add(array, sizeof *item);
   376 		 *item = list->data;
   377 		 list = list_next(list);
   378 	}
   379 }
   380 
   381 static int
   382 compare_file_requires(const void *p1, const void *p2, void *data)
   383 {
   384 	uint32_t *f1 = (void *)p1, *f2 = (void *)p2;
   385 	const char *pool = data;
   386 
   387 	return strcmp(&pool[*f1], &pool[*f2]);
   388 }
   389 
   390 static void
   391 find_file_provides(struct razor_importer *importer)
   392 {
   393 	struct razor_property *prop;
   394 	struct razor_entry *top, *entry;
   395 	struct razor_package *packages;
   396 	struct array pkgprops;
   397 	struct list *pkg;
   398 	uint32_t *req, *req_start, *req_end;
   399 	uint32_t *map, *newprop;
   400 	char *pool;
   401 
   402 	pool = importer->set->string_pool.data;
   403 	packages = importer->set->packages.data;
   404 	top = importer->set->files.data;
   405 
   406 	req = req_start = importer->file_requires.data;
   407 	req_end = importer->file_requires.data + importer->file_requires.size;
   408 	map = razor_qsort_with_data(req, req_end - req, sizeof *req,
   409 				    compare_file_requires, pool);
   410 	free(map);
   411 
   412 	for (req = req_start; req < req_end; req++) {
   413 		if (req > req_start && req[0] == req[-1])
   414 			continue;
   415 		entry = razor_set_find_entry(importer->set, top, &pool[*req]);
   416 		if (!entry)
   417 			continue;
   418 
   419 		for (pkg = list_first(&entry->packages, &importer->set->package_pool); pkg; pkg = list_next(pkg)) {
   420 			prop = array_add(&importer->set->properties, sizeof *prop);
   421 			prop->name = *req;
   422 			prop->flags =
   423 				RAZOR_PROPERTY_PROVIDES | RAZOR_PROPERTY_EQUAL;
   424 			prop->version = hashtable_tokenize(&importer->table, "");
   425 			list_set_ptr(&prop->packages, pkg->data);
   426 
   427 			/* Update property list of pkg */
   428 			array_init(&pkgprops);
   429 			list_to_array(list_first(&packages[pkg->data].properties, &importer->set->property_pool), &pkgprops);
   430 			newprop = array_add(&pkgprops, sizeof *newprop);
   431 			*newprop = prop - (struct razor_property *)importer->set->properties.data;
   432 			list_set_array(&packages[pkg->data].properties, &importer->set->property_pool, &pkgprops, 1);
   433 			array_release(&pkgprops);
   434 		}
   435 	}
   436 
   437 	array_release(&importer->file_requires);
   438 }
   439 
   440 static void
   441 build_package_file_lists(struct razor_set *set, uint32_t *rmap)
   442 {
   443 	struct razor_package *p, *packages;
   444 	struct array *pkgs;
   445 	struct razor_entry *e, *end;
   446 	struct list *r;
   447 	uint32_t *q;
   448 	int i, count;
   449 
   450 	count = set->packages.size / sizeof *p;
   451 	pkgs = zalloc(count * sizeof *pkgs);
   452 
   453 	end = set->files.data + set->files.size;
   454 	for (e = set->files.data; e < end; e++) {
   455 		list_remap_head(&e->packages, rmap);
   456 		r = list_first(&e->packages, &set->package_pool);
   457 		while (r) {
   458 			q = array_add(&pkgs[r->data], sizeof *q);
   459 			*q = e - (struct razor_entry *) set->files.data;
   460 			r = list_next(r);
   461 		}
   462 	}
   463 
   464 	packages = set->packages.data;
   465 	for (i = 0; i < count; i++) {
   466 		list_set_array(&packages[i].files, &set->file_pool, &pkgs[i], 0);
   467 		array_release(&pkgs[i]);
   468 	}
   469 	free(pkgs);
   470 }
   471 
   472 RAZOR_EXPORT struct razor_set *
   473 razor_importer_finish(struct razor_importer *importer)
   474 {
   475 	struct razor_set *set;
   476 	uint32_t *map, *rmap;
   477 	int i, count;
   478 
   479 	build_file_tree(importer);
   480 	find_file_provides(importer);
   481 
   482 	map = uniqueify_properties(importer->set);
   483 	list_remap_pool(&importer->set->property_pool, map);
   484 	free(map);
   485 
   486 	count = importer->set->packages.size / sizeof(struct razor_package);
   487 	map = razor_qsort_with_data(importer->set->packages.data,
   488 				    count,
   489 				    sizeof(struct razor_package),
   490 				    compare_packages,
   491 				    importer->set);
   492 
   493 	rmap = malloc(count * sizeof *rmap);
   494 	for (i = 0; i < count; i++)
   495 		rmap[map[i]] = i;
   496 	free(map);
   497 
   498 	list_remap_pool(&importer->set->package_pool, rmap);
   499 	build_package_file_lists(importer->set, rmap);
   500 	remap_property_package_links(&importer->set->properties, rmap);
   501 	free(rmap);
   502 
   503 	set = importer->set;
   504 	hashtable_release(&importer->table);
   505 	hashtable_release(&importer->details_table);
   506 	hashtable_release(&importer->file_table);
   507 	free(importer);
   508 
   509 	return set;
   510 }