librazor/importer.c
author Kristian H?gsberg <krh@redhat.com>
Fri Jun 20 23:13:09 2008 -0400 (2008-06-20)
changeset 257 0c3db660514d
parent 249 061a5b815727
child 259 5b0601d184ed
permissions -rw-r--r--
When uniquifying properties, also sort them on the owning package.

This ensures that whenever two packages provide or (or require, obsolete
or conflict) the same property, they appear in the same order in the
propertys list of packages.
     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 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 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 void
    57 razor_importer_add_property(struct razor_importer *importer,
    58 			    const char *name,
    59 			    uint32_t flags,
    60 			    const char *version)
    61 {
    62 	struct razor_property *p;
    63 	uint32_t *r;
    64 
    65 	p = array_add(&importer->set->properties, sizeof *p);
    66 	p->name = hashtable_tokenize(&importer->table, name);
    67 	p->flags = flags;
    68 	p->version = hashtable_tokenize(&importer->table, version);
    69 	list_set_ptr(&p->packages, importer->package -
    70 		     (struct razor_package *) importer->set->packages.data);
    71 
    72 	r = array_add(&importer->properties, sizeof *r);
    73 	*r = p - (struct razor_property *) importer->set->properties.data;
    74 
    75 	if (((flags & RAZOR_PROPERTY_TYPE_MASK) == RAZOR_PROPERTY_REQUIRES) &&
    76 	    *name == '/') {
    77 		r = array_add(&importer->file_requires, sizeof *r);
    78 		*r = p->name;
    79 	}
    80 }
    81 
    82 void
    83 razor_importer_add_file(struct razor_importer *importer, const char *name)
    84 {
    85 	struct import_entry *e;
    86 
    87 	e = array_add(&importer->files, sizeof *e);
    88 
    89 	e->package = importer->package -
    90 		(struct razor_package *) importer->set->packages.data;
    91 	e->name = strdup(name);
    92 }
    93 
    94 struct razor_importer *
    95 razor_importer_create(void)
    96 {
    97 	struct razor_importer *importer;
    98 
    99 	importer = zalloc(sizeof *importer);
   100 	importer->set = razor_set_create();
   101 	hashtable_init(&importer->table, &importer->set->string_pool);
   102 
   103 	return importer;
   104 }
   105 
   106 /* Destroy an importer without creating the set. */
   107 void
   108 razor_importer_destroy(struct razor_importer *importer)
   109 {
   110 	/* FIXME: write this */
   111 }
   112 
   113 static int
   114 compare_packages(const void *p1, const void *p2, void *data)
   115 {
   116 	const struct razor_package *pkg1 = p1, *pkg2 = p2;
   117 	struct razor_set *set = data;
   118 	char *pool = set->string_pool.data;
   119 
   120 	/* FIXME: what if the flags are different? */
   121 	if (pkg1->name == pkg2->name)
   122 		return razor_versioncmp(&pool[pkg1->version], &pool[pkg2->version]);
   123 	else
   124 		return strcmp(&pool[pkg1->name], &pool[pkg2->name]);
   125 }
   126 
   127 static int
   128 compare_properties(const void *p1, const void *p2, void *data)
   129 {
   130 	const struct razor_property *prop1 = p1, *prop2 = p2;
   131 	struct razor_set *set = data;
   132 	char *pool = set->string_pool.data;
   133 
   134 	if (prop1->name != prop2->name)
   135 		return strcmp(&pool[prop1->name], &pool[prop2->name]);
   136 	else if (prop1->flags != prop2->flags)
   137 		return prop1->flags - prop2->flags;
   138 	else if (prop1->version != prop2->version)
   139 		return razor_versioncmp(&pool[prop1->version], &pool[prop2->version]);
   140 	else
   141 		return prop1->packages.list_ptr - prop2->packages.list_ptr;
   142 }
   143 
   144 static uint32_t *
   145 uniqueify_properties(struct razor_set *set)
   146 {
   147 	struct razor_property *rp, *up, *rp_end;
   148 	struct array *pkgs, *p;
   149 	struct list_head *r;
   150 	uint32_t *map, *rmap;
   151 	int i, count, unique;
   152 
   153 	count = set->properties.size / sizeof(struct razor_property);
   154 	map = razor_qsort_with_data(set->properties.data,
   155 				    count,
   156 				    sizeof(struct razor_property),
   157 				    compare_properties,
   158 				    set);
   159 
   160 	rp_end = set->properties.data + set->properties.size;
   161 	rmap = malloc(count * sizeof *map);
   162 	pkgs = zalloc(count * sizeof *pkgs);
   163 	for (rp = set->properties.data, up = rp, i = 0; rp < rp_end; rp++, i++) {
   164 		if (rp->name != up->name ||
   165 		    rp->flags != up->flags ||
   166 		    rp->version != up->version) {
   167 			up++;
   168 			up->name = rp->name;
   169 			up->flags = rp->flags;
   170 			up->version = rp->version;
   171 		}
   172 
   173 		unique = up - (struct razor_property *) set->properties.data;
   174 		rmap[map[i]] = unique;
   175 		r = array_add(&pkgs[unique], sizeof *r);
   176 		*r = rp->packages;
   177 	}
   178 	free(map);
   179 
   180 	if (up != rp)
   181 		up++;
   182 	set->properties.size = (void *) up - set->properties.data;
   183 	rp_end = up;
   184 	for (rp = set->properties.data, p = pkgs; rp < rp_end; rp++, p++) {
   185 		list_set_array(&rp->packages, &set->package_pool, p, 0);
   186 		array_release(p);
   187 	}
   188 
   189 	free(pkgs);
   190 
   191 	return rmap;
   192 }
   193 
   194 static int
   195 compare_filenames(const void *p1, const void *p2, void *data)
   196 {
   197 	const struct import_entry *e1 = p1;
   198 	const struct import_entry *e2 = p2;
   199 	const char *n1 = e1->name;
   200 	const char *n2 = e2->name;
   201 
   202 	/* Need to make sure that the contents of a directory
   203 	 * are sorted immediately after it. So "foo/bar" has to
   204 	 * sort before "foo.conf"
   205 	 *
   206 	 * FIXME: this is about 60% slower than strcmp
   207 	 */
   208 	while (*n1 && *n2) {
   209 		if (*n1 < *n2)
   210 			return *n2 == '/' ? 1 : -1;
   211 		else if (*n1 > *n2)
   212 			return *n1 == '/' ? -1 : 1;
   213 		n1++;
   214 		n2++;
   215 	}
   216 	if (*n1)
   217 		return 1;
   218 	else if (*n2)
   219 		return -1;
   220 	else
   221 		return 0;
   222 }
   223 
   224 static void
   225 count_entries(struct import_directory *d)
   226 {
   227 	struct import_directory *p, *end;
   228 
   229 	p = d->files.data;
   230 	end = d->files.data + d->files.size;
   231 	d->count = 0;
   232 	while (p < end) {
   233 		count_entries(p);
   234 		d->count += p->count + 1;
   235 		p++;
   236 	}
   237 }
   238 
   239 static void
   240 serialize_files(struct razor_set *set,
   241 		struct import_directory *d, struct array *array)
   242 {
   243 	struct import_directory *p, *end;
   244 	struct razor_entry *e = NULL;
   245 	uint32_t s;
   246 
   247 	p = d->files.data;
   248 	end = d->files.data + d->files.size;
   249 	s = array->size / sizeof *e + d->files.size / sizeof *p;
   250 	while (p < end) {
   251 		e = array_add(array, sizeof *e);
   252 		e->name = p->name;
   253 		e->flags = 0;
   254 		e->start = p->count > 0 ? s : 0;
   255 		s += p->count;
   256 
   257 		list_set_array(&e->packages, &set->package_pool, &p->packages, 0);
   258 		array_release(&p->packages);
   259 		p++;
   260 	}
   261 	if (e != NULL)
   262 		e->flags |= RAZOR_ENTRY_LAST;
   263 
   264 	p = d->files.data;
   265 	end = d->files.data + d->files.size;
   266 	while (p < end) {
   267 		serialize_files(set, p, array);
   268 		p++;
   269 	}
   270 }
   271 
   272 static void
   273 remap_property_package_links(struct array *properties, uint32_t *rmap)
   274 {
   275 	struct razor_property *p, *end;
   276 
   277 	end = properties->data + properties->size;
   278 	for (p = properties->data; p < end; p++)
   279 		list_remap_head(&p->packages, rmap);
   280 }
   281 
   282 static void
   283 build_file_tree(struct razor_importer *importer)
   284 {
   285 	int count, i, length;
   286 	struct import_entry *filenames;
   287 	char *f, *end;
   288 	uint32_t name, *r;
   289 	char dirname[256];
   290 	struct import_directory *d, root;
   291 	struct razor_entry *e;
   292 
   293 	count = importer->files.size / sizeof (struct import_entry);
   294 	razor_qsort_with_data(importer->files.data,
   295 			      count,
   296 			      sizeof (struct import_entry),
   297 			      compare_filenames,
   298 			      NULL);
   299 
   300 	root.name = hashtable_tokenize(&importer->table, "");
   301 	array_init(&root.files);
   302 	array_init(&root.packages);
   303 	root.last = NULL;
   304 
   305 	filenames = importer->files.data;
   306 	for (i = 0; i < count; i++) {
   307 		f = filenames[i].name;
   308 		if (*f != '/')
   309 			continue;
   310 		f++;
   311 
   312 		d = &root;
   313 		while (*f) {
   314 			end = strchr(f, '/');
   315 			if (end == NULL)
   316 				end = f + strlen(f);
   317 			length = end - f;
   318 			memcpy(dirname, f, length);
   319 			dirname[length] ='\0';
   320 			name = hashtable_tokenize(&importer->table, dirname);
   321 			if (d->last == NULL || d->last->name != name) {
   322 				d->last = array_add(&d->files, sizeof *d);
   323 				d->last->name = name;
   324 				d->last->last = NULL;
   325 				array_init(&d->last->files);
   326 				array_init(&d->last->packages);
   327 			}
   328 			d = d->last;
   329 			f = end + 1;
   330 			if (*end == '\0')
   331 				break;
   332 		}
   333 
   334 		r = array_add(&d->packages, sizeof *r);
   335 		*r = filenames[i].package;
   336 		free(filenames[i].name);
   337 	}
   338 
   339 	count_entries(&root);
   340 	e = importer->set->files.data;
   341 	e->name = root.name;
   342 	e->flags = RAZOR_ENTRY_LAST;
   343 	e->start = importer->files.size ? 1 : 0;
   344 	list_set_empty(&e->packages);
   345 
   346 	serialize_files(importer->set, &root, &importer->set->files);
   347 
   348 	array_release(&importer->files);
   349 }
   350 
   351 static void
   352 list_to_array(struct list *list, struct array *array)
   353 {
   354 	uint32_t *item;
   355 
   356 	while (list) {
   357 		 item = array_add(array, sizeof *item);
   358 		 *item = list->data;
   359 		 list = list_next(list);
   360 	}
   361 }
   362 
   363 static int
   364 compare_file_requires(const void *p1, const void *p2, void *data)
   365 {
   366 	uint32_t *f1 = (void *)p1, *f2 = (void *)p2;
   367 	const char *pool = data;
   368 
   369 	return strcmp(&pool[*f1], &pool[*f2]);
   370 }
   371 
   372 static void
   373 find_file_provides(struct razor_importer *importer)
   374 {
   375 	struct razor_property *prop;
   376 	struct razor_entry *top, *entry;
   377 	struct razor_package *packages;
   378 	struct array pkgprops;
   379 	struct list *pkg;
   380 	uint32_t *req, *req_start, *req_end;
   381 	uint32_t *map, *newprop;
   382 	char *pool;
   383 
   384 	pool = importer->set->string_pool.data;
   385 	packages = importer->set->packages.data;
   386 	top = importer->set->files.data;
   387 
   388 	req = req_start = importer->file_requires.data;
   389 	req_end = importer->file_requires.data + importer->file_requires.size;
   390 	map = razor_qsort_with_data(req, req_end - req, sizeof *req,
   391 				    compare_file_requires, pool);
   392 	free(map);
   393 
   394 	for (req = req_start; req < req_end; req++) {
   395 		if (req > req_start && req[0] == req[-1])
   396 			continue;
   397 		entry = razor_set_find_entry(importer->set, top, &pool[*req]);
   398 		if (!entry)
   399 			continue;
   400 
   401 		for (pkg = list_first(&entry->packages, &importer->set->package_pool); pkg; pkg = list_next(pkg)) {
   402 			prop = array_add(&importer->set->properties, sizeof *prop);
   403 			prop->name = *req;
   404 			prop->flags =
   405 				RAZOR_PROPERTY_PROVIDES | RAZOR_PROPERTY_EQUAL;
   406 			prop->version = hashtable_tokenize(&importer->table, "");
   407 			list_set_ptr(&prop->packages, pkg->data);
   408 
   409 			/* Update property list of pkg */
   410 			array_init(&pkgprops);
   411 			list_to_array(list_first(&packages[pkg->data].properties, &importer->set->property_pool), &pkgprops);
   412 			newprop = array_add(&pkgprops, sizeof *newprop);
   413 			*newprop = prop - (struct razor_property *)importer->set->properties.data;
   414 			list_set_array(&packages[pkg->data].properties, &importer->set->property_pool, &pkgprops, 1);
   415 			array_release(&pkgprops);
   416 		}
   417 	}
   418 
   419 	array_release(&importer->file_requires);
   420 }
   421 
   422 static void
   423 build_package_file_lists(struct razor_set *set, uint32_t *rmap)
   424 {
   425 	struct razor_package *p, *packages;
   426 	struct array *pkgs;
   427 	struct razor_entry *e, *end;
   428 	struct list *r;
   429 	uint32_t *q;
   430 	int i, count;
   431 
   432 	count = set->packages.size / sizeof *p;
   433 	pkgs = zalloc(count * sizeof *pkgs);
   434 
   435 	end = set->files.data + set->files.size;
   436 	for (e = set->files.data; e < end; e++) {
   437 		list_remap_head(&e->packages, rmap);
   438 		r = list_first(&e->packages, &set->package_pool);
   439 		while (r) {
   440 			q = array_add(&pkgs[r->data], sizeof *q);
   441 			*q = e - (struct razor_entry *) set->files.data;
   442 			r = list_next(r);
   443 		}
   444 	}
   445 
   446 	packages = set->packages.data;
   447 	for (i = 0; i < count; i++) {
   448 		list_set_array(&packages[i].files, &set->file_pool, &pkgs[i], 0);
   449 		array_release(&pkgs[i]);
   450 	}
   451 	free(pkgs);
   452 }
   453 
   454 struct razor_set *
   455 razor_importer_finish(struct razor_importer *importer)
   456 {
   457 	struct razor_set *set;
   458 	uint32_t *map, *rmap;
   459 	int i, count;
   460 
   461 	build_file_tree(importer);
   462 	find_file_provides(importer);
   463 
   464 	map = uniqueify_properties(importer->set);
   465 	list_remap_pool(&importer->set->property_pool, map);
   466 	free(map);
   467 
   468 	count = importer->set->packages.size / sizeof(struct razor_package);
   469 	map = razor_qsort_with_data(importer->set->packages.data,
   470 				    count,
   471 				    sizeof(struct razor_package),
   472 				    compare_packages,
   473 				    importer->set);
   474 
   475 	rmap = malloc(count * sizeof *rmap);
   476 	for (i = 0; i < count; i++)
   477 		rmap[map[i]] = i;
   478 	free(map);
   479 
   480 	list_remap_pool(&importer->set->package_pool, rmap);
   481 	build_package_file_lists(importer->set, rmap);
   482 	remap_property_package_links(&importer->set->properties, rmap);
   483 	free(rmap);
   484 
   485 	set = importer->set;
   486 	hashtable_release(&importer->table);
   487 	free(importer);
   488 
   489 	return set;
   490 }