Redo updates and removes in terms of a single razor_transaction abstraction
authorDan Winship <danw@gnome.org>
Fri Feb 29 11:53:15 2008 -0500 (2008-02-29)
changeset 1374722cd3437cb
parent 136 eef2b734f2cc
child 138 49deac048d07
Redo updates and removes in terms of a single razor_transaction abstraction

Also does versioned depsolving at least partially.
Update main.c and test-driver.c for that, and fix some unrelated test-driver
bugs.

Now gets up to testUpdateSinglePackageObsoletesOldRequirement, although
it really should not be passing the multilib tests; apparently they aren't
clever enough in their testing of the depsolving algorithm and are allowing
it to come up with the right answer for the wrong reason.
main.c
razor.c
razor.h
test-driver.c
     1.1 --- a/main.c	Fri Feb 29 11:51:58 2008 -0500
     1.2 +++ b/main.c	Fri Feb 29 11:53:15 2008 -0500
     1.3 @@ -14,10 +14,6 @@
     1.4  static const char *rawhide_repo_filename = "rawhide.repo";
     1.5  static const char *updated_repo_filename = "system-updated.repo";
     1.6  
     1.7 -static const char *relations[] = {
     1.8 -	"<", "<=", "=", ">=", ">"
     1.9 -};
    1.10 -
    1.11  static int
    1.12  command_list(int argc, const char *argv[])
    1.13  {
    1.14 @@ -67,8 +63,8 @@
    1.15  		if (version[0] == '\0')
    1.16  			printf("%s\n", name);
    1.17  		else
    1.18 -			printf("%s %s %s\n", name, relations[relation],
    1.19 -			       version);
    1.20 +			printf("%s %s %s\n", name,
    1.21 +			       razor_version_relations[relation], version);
    1.22  	}
    1.23  	razor_property_iterator_destroy(pi);
    1.24  
    1.25 @@ -325,12 +321,19 @@
    1.26  command_update(int argc, const char *argv[])
    1.27  {
    1.28  	struct razor_set *set, *upstream;
    1.29 +	struct razor_transaction *trans;
    1.30  
    1.31  	set = razor_set_open(repo_filename);
    1.32  	upstream = razor_set_open(rawhide_repo_filename);
    1.33  	if (set == NULL || upstream == NULL)
    1.34  		return 1;
    1.35 -	set = razor_set_update(set, upstream, argc, argv);
    1.36 +	trans = razor_transaction_create(set, upstream, argc, argv, 0, NULL);
    1.37 +	razor_transaction_describe(trans);
    1.38 +	if (trans->errors)
    1.39 +		return 1;
    1.40 +
    1.41 +	set = razor_transaction_run(trans);
    1.42 +	razor_transaction_destroy(trans);
    1.43  	razor_set_write(set, updated_repo_filename);
    1.44  	razor_set_destroy(set);
    1.45  	razor_set_destroy(upstream);
    1.46 @@ -343,11 +346,18 @@
    1.47  command_remove(int argc, const char *argv[])
    1.48  {
    1.49  	struct razor_set *set;
    1.50 +	struct razor_transaction *trans;
    1.51  
    1.52  	set = razor_set_open(repo_filename);
    1.53  	if (set == NULL)
    1.54  		return 1;
    1.55 -	set = razor_set_remove(set, argc, argv);
    1.56 +	trans = razor_transaction_create(set, NULL, 0, NULL, argc, argv);
    1.57 +	razor_transaction_describe(trans);
    1.58 +	if (trans->errors)
    1.59 +		return 1;
    1.60 +
    1.61 +	set = razor_transaction_run(trans);
    1.62 +	razor_transaction_destroy(trans);
    1.63  	razor_set_write(set, updated_repo_filename);
    1.64  	razor_set_destroy(set);
    1.65  	printf("wrote system-updated.repo\n");
     2.1 --- a/razor.c	Fri Feb 29 11:51:58 2008 -0500
     2.2 +++ b/razor.c	Fri Feb 29 11:53:15 2008 -0500
     2.3 @@ -382,7 +382,7 @@
     2.4  		__qsort_with_data(end, right, mend, ctx);
     2.5  }
     2.6  
     2.7 -uint32_t *
     2.8 +static uint32_t *
     2.9  qsort_with_data(void *base, size_t nelem, size_t size,
    2.10  		compare_with_data_func_t compare, void *data)
    2.11  {
    2.12 @@ -758,7 +758,7 @@
    2.13  	struct list *index;
    2.14  };
    2.15  
    2.16 -struct razor_package_iterator *
    2.17 +static struct razor_package_iterator *
    2.18  razor_package_iterator_create_with_index(struct razor_set *set,
    2.19  					 struct list *index)
    2.20  {
    2.21 @@ -1231,53 +1231,6 @@
    2.22  	}
    2.23  }
    2.24  
    2.25 -
    2.26 -/* Build the new package list sorted by merging the two package lists.
    2.27 - * Build new string pool as we go. */
    2.28 -static void
    2.29 -merge_packages(struct razor_merger *merger, struct array *packages)
    2.30 -{
    2.31 -	struct razor_package *upstream_packages, *p, *s, *send;
    2.32 -	struct source *source1, *source2;
    2.33 -	char *spool, *upool;
    2.34 -	uint32_t *u, *uend;
    2.35 -	int cmp;
    2.36 -
    2.37 -	source1 = &merger->source1;
    2.38 -	source2 = &merger->source2;
    2.39 -	upstream_packages = source2->set->packages.data;
    2.40 -
    2.41 -	u = packages->data;
    2.42 -	uend = packages->data + packages->size;
    2.43 -	upool = source2->set->string_pool.data;
    2.44 -
    2.45 -	s = source1->set->packages.data;
    2.46 -	send = source1->set->packages.data + source1->set->packages.size;
    2.47 -	spool = source1->set->string_pool.data;
    2.48 -
    2.49 -	while (s < send || u < uend) {
    2.50 -		p = upstream_packages + *u;
    2.51 -
    2.52 -		if (s < send && u < uend)
    2.53 -			cmp = strcmp(&spool[s->name], &upool[p->name]);
    2.54 -		else if (s < send)
    2.55 -			cmp = -1;
    2.56 -		else
    2.57 -			cmp = 1;
    2.58 -		if (cmp < 0) {
    2.59 -			add_package(merger, s, source1, 0);
    2.60 -			s++;
    2.61 -		} else if (cmp == 0) {
    2.62 -			add_package(merger, p, source2, UPSTREAM_SOURCE);
    2.63 -			s++;
    2.64 -			u++;
    2.65 -		} else {
    2.66 -			add_package(merger, p, source2, UPSTREAM_SOURCE);
    2.67 -			u++;
    2.68 -		}
    2.69 -	}
    2.70 -}
    2.71 -
    2.72  static uint32_t
    2.73  add_property(struct razor_merger *merger,
    2.74  	     const char *name, enum razor_version_relation relation,
    2.75 @@ -1648,7 +1601,7 @@
    2.76  	free(pkgs);
    2.77  }
    2.78  
    2.79 -struct razor_set *
    2.80 +static struct razor_set *
    2.81  razor_merger_finish(struct razor_merger *merger)
    2.82  {
    2.83  	struct razor_set *result;
    2.84 @@ -1698,112 +1651,58 @@
    2.85  	return result;
    2.86  }
    2.87  
    2.88 -/* Add packages from 'upstream' to 'set'.  The packages to add are
    2.89 - * specified by the 'packages' array, which is a sorted list of
    2.90 - * package indexes.  Returns a newly allocated package set.  Does not
    2.91 - * enforce validity of the resulting package set.
    2.92 - *
    2.93 - * This looks more complicated than it is.  An easy way to merge two
    2.94 - * package sets would be to just use a razor_importer, but that
    2.95 - * requires resorting, and is thus O(n log n).  We can do this in a
    2.96 - * linear sweep, but it gets a little more complicated.
    2.97 - */
    2.98 -struct razor_set *
    2.99 -razor_set_add(struct razor_set *set, struct razor_set *upstream,
   2.100 -	      struct array *packages)
   2.101 -{
   2.102 -	struct razor_merger *merger;
   2.103 -
   2.104 -	merger = razor_merger_create(set, upstream);
   2.105 -
   2.106 -	merge_packages(merger, packages);
   2.107 -
   2.108 -	return razor_merger_finish(merger);
   2.109 -}
   2.110 -
   2.111 -void
   2.112 -razor_set_satisfy(struct razor_set *set, struct array *unsatisfied,
   2.113 -		  struct razor_set *upstream, struct array *list)
   2.114 -{
   2.115 -	struct razor_property *requires, *r;
   2.116 -	struct razor_property *p, *pend;
   2.117 -	uint32_t *u, *end, *pkg;
   2.118 -	struct array *package_pool;
   2.119 -	char *pool, *upool;
   2.120 -
   2.121 -	end = unsatisfied->data + unsatisfied->size;
   2.122 -	requires = set->properties.data;
   2.123 -	pool = set->string_pool.data;
   2.124 -
   2.125 -	p = upstream->properties.data;
   2.126 -	pend = upstream->properties.data + upstream->properties.size;
   2.127 -	upool = upstream->string_pool.data;
   2.128 -	package_pool = &upstream->package_pool;
   2.129 -
   2.130 -	u = unsatisfied->data;
   2.131 -	while (u < end) {
   2.132 -		r = requires + *u;
   2.133 -
   2.134 -		while (p < pend &&
   2.135 -		       p->type != RAZOR_PROPERTY_PROVIDES &&
   2.136 -		       strcmp(&pool[r->name], &upool[p->name]) > 0)
   2.137 -			p++;
   2.138 -		/* If there is more than one version of a provides,
   2.139 -		 * seek to the end for the highest version. */
   2.140 -		while (p + 1 < pend && p->name == (p + 1)->name && p->type == (p + 1)->type)
   2.141 -			p++;
   2.142 -
   2.143 -		if (p != pend &&
   2.144 -		    p->type == RAZOR_PROPERTY_PROVIDES &&
   2.145 -		    strcmp(&pool[r->name], &upool[p->name]) == 0 &&
   2.146 -		    versioncmp(&pool[r->version], &upool[p->version]) <= 0) {
   2.147 -			pkg = array_add(list, sizeof *pkg);
   2.148 -			/* We just pull in the first package that provides */
   2.149 -			*pkg = list_first(&p->packages, package_pool)->data;
   2.150 -
   2.151 -			/* Remove this from the unsatisfied list */
   2.152 -			memmove (u, u + 1, end - (u + 1));
   2.153 -			end--;
   2.154 -		} else {
   2.155 -			/* Leave this in the unsatisfied list */
   2.156 -			u++;
   2.157 -		}
   2.158 -	}
   2.159 -	unsatisfied->size = (void *)end - unsatisfied->data;
   2.160 -}
   2.161 -
   2.162 -static void
   2.163 -find_packages(struct razor_set *set,
   2.164 -	      int count, const char **package_names, struct array *list)
   2.165 +static int
   2.166 +find_packages(struct razor_set *set, int count, const char **package_names,
   2.167 +	      struct array *package_array,
   2.168 +	      enum razor_transaction_package_state state)
   2.169  {
   2.170  	struct razor_package_iterator *pi;
   2.171  	struct razor_package *p, *packages;
   2.172  	const char *name, *version;
   2.173 -	uint32_t *r;
   2.174 -	int i;
   2.175 +	struct razor_transaction_package *tp;
   2.176 +	int i, *found, errors = 0;
   2.177  
   2.178  	packages = (struct razor_package *) set->packages.data;
   2.179  	pi = razor_package_iterator_create(set);
   2.180 +	found = zalloc(count * sizeof (int));
   2.181  
   2.182  	while (razor_package_iterator_next(pi, &p, &name, &version)) {
   2.183  		for (i = 0; i < count; i++) {
   2.184  			if (strcmp(name, package_names[i]) == 0) {
   2.185 -				r = array_add(list, sizeof *r);
   2.186 -				*r = p - packages;
   2.187 +				found[i] = 1;
   2.188 +				tp = array_add(package_array, sizeof *tp);
   2.189 +				memset(tp, 0, sizeof *tp);
   2.190 +				tp->package = p;
   2.191 +				tp->name = name;
   2.192 +				tp->version = version;
   2.193 +				tp->state = state;
   2.194  				break;
   2.195  			}
   2.196  		}
   2.197  	}
   2.198  
   2.199 +	for (i = 0; i < count; i++) {
   2.200 +		if (!found[i]) {
   2.201 +			tp = array_add(package_array, sizeof *tp);
   2.202 +			memset(tp, 0, sizeof *tp);
   2.203 +			tp->name = strdup(package_names[i]);
   2.204 +			tp->state = state | RAZOR_PACKAGE_UNAVAILABLE;
   2.205 +			errors++;
   2.206 +		}
   2.207 +	}
   2.208 +
   2.209  	razor_package_iterator_destroy(pi);
   2.210 +	free(found);
   2.211 +
   2.212 +	return errors;
   2.213  }
   2.214  
   2.215  static void
   2.216  find_all_packages(struct razor_set *set,
   2.217 -		  struct razor_set *upstream, struct array *list)
   2.218 +		  struct razor_set *upstream, struct array *package_array)
   2.219  {
   2.220 +	struct razor_transaction_package *tp;
   2.221  	struct razor_package *p, *u, *pend, *uend;
   2.222 -	uint32_t *r;
   2.223  	char *pool, *upool;
   2.224  
   2.225  	pend = set->packages.data + set->packages.size;
   2.226 @@ -1816,25 +1715,89 @@
   2.227  		while (u < uend && strcmp(&pool[p->name], &upool[u->name]) > 0)
   2.228  			u++;
   2.229  		if (strcmp(&pool[p->name], &upool[u->name]) == 0) {
   2.230 -			r = array_add(list, sizeof *r);
   2.231 -			*r = u - (struct razor_package *) upstream->packages.data;
   2.232 +			tp = array_add(package_array, sizeof *tp);
   2.233 +			memset(tp, 0, sizeof *tp);
   2.234 +			tp->name = &upool[u->name];
   2.235 +			tp->version = &upool[u->version];
   2.236 +			tp->state = RAZOR_PACKAGE_INSTALL;
   2.237  		}
   2.238  	}
   2.239  }
   2.240  
   2.241 +/* FIXME: wrong, need to compare names, not razor_package* */
   2.242  static int
   2.243 -find_provider(struct razor_set *set, const char *req_name,
   2.244 -	      enum razor_version_relation relation, const char *version)
   2.245 +find_transaction_package(struct array *package_array,
   2.246 +			 struct razor_package *package)
   2.247 +{
   2.248 +	struct razor_transaction_package *tps = package_array->data;
   2.249 +	int i, tpcount = package_array->size / sizeof *tps;
   2.250 +
   2.251 +	for (i = 0; i < tpcount; i++) {
   2.252 +		if (tps[i].package == package)
   2.253 +			return i;
   2.254 +	}
   2.255 +	return -1;
   2.256 +}
   2.257 +
   2.258 +static int
   2.259 +provider_satisfies_requirement(struct razor_property *provider,
   2.260 +			       const char *provider_strings,
   2.261 +			       struct razor_property *requirement,
   2.262 +			       const char *requirement_strings)
   2.263 +{
   2.264 +	int cmp, len;
   2.265 +	const char *provided = &provider_strings[provider->version];
   2.266 +	const char *required = &requirement_strings[requirement->version];
   2.267 +
   2.268 +	if (!*required)
   2.269 +		return 1;
   2.270 +
   2.271 +	cmp = versioncmp(provided, required);
   2.272 +
   2.273 +	switch (requirement->relation) {
   2.274 +	case RAZOR_VERSION_LESS:
   2.275 +		return cmp < 0;
   2.276 +
   2.277 +	case RAZOR_VERSION_LESS_OR_EQUAL:
   2.278 +		if (cmp <= 0)
   2.279 +			return 1;
   2.280 +		/* fall through: FIXME, make sure this is correct */
   2.281 +
   2.282 +	case RAZOR_VERSION_EQUAL:
   2.283 +		if (cmp == 0)
   2.284 +			return 1;
   2.285 +
   2.286 +		/* "foo == 1.1" is satisfied by "foo 1.1-2" */
   2.287 +		len = strlen(required);
   2.288 +		if (!strncmp(required, provided, len) && provided[len] == '-')
   2.289 +			return 1;
   2.290 +		return 0;
   2.291 +
   2.292 +	case RAZOR_VERSION_GREATER_OR_EQUAL:
   2.293 +		return cmp >= 0;
   2.294 +
   2.295 +	case RAZOR_VERSION_GREATER:
   2.296 +		return cmp > 0;
   2.297 +	}
   2.298 +
   2.299 +	/* shouldn't happen */
   2.300 +	return 0;
   2.301 +}
   2.302 +
   2.303 +static int
   2.304 +find_provider(struct razor_set *set, struct razor_property *requirement,
   2.305 +	      const char *requirement_strings)
   2.306  {
   2.307  	struct razor_property *props = set->properties.data;
   2.308 -	int p, hi, lo, cmp, pkg;
   2.309 +	int p, hi, lo, cmp;
   2.310  	char *pool = set->string_pool.data;
   2.311  
   2.312  	lo = 0;
   2.313  	hi = set->properties.size / sizeof *props;
   2.314  	while (lo < hi) {
   2.315  		p = (lo + hi) / 2;
   2.316 -		cmp = strcmp(&pool[props[p].name], req_name);
   2.317 +		cmp = strcmp(&pool[props[p].name],
   2.318 +			     &requirement_strings[requirement->name]);
   2.319  		if (cmp < 0)
   2.320  			lo = p + 1;
   2.321  		else if (cmp > 0)
   2.322 @@ -1868,35 +1831,9 @@
   2.323  		return -1;
   2.324  
   2.325  	do {
   2.326 -		pkg = list_first(&props[p].packages, &set->package_pool)->data;
   2.327 -		if (!*version)
   2.328 -			return pkg;
   2.329 -
   2.330 -		cmp = versioncmp(&pool[props[p].version], version);
   2.331 -
   2.332 -		switch (relation) {
   2.333 -		case RAZOR_VERSION_EQUAL:
   2.334 -		case RAZOR_VERSION_GREATER_OR_EQUAL:
   2.335 -		case RAZOR_VERSION_GREATER:
   2.336 -			if (cmp >= 0)
   2.337 -				return pkg;
   2.338 -			else {
   2.339 -				/* If the highest version doesn't pass,
   2.340 -				 * none of the others will either.
   2.341 -				 */
   2.342 -				return -1;
   2.343 -			}
   2.344 -
   2.345 -		case RAZOR_VERSION_LESS_OR_EQUAL:
   2.346 -			if (cmp <= 0)
   2.347 -				return pkg;
   2.348 -			break;
   2.349 -
   2.350 -		case RAZOR_VERSION_LESS:
   2.351 -			if (cmp < 0)
   2.352 -				return pkg;
   2.353 -			break;
   2.354 -		}
   2.355 +		if (provider_satisfies_requirement(&props[p], pool,
   2.356 +						   requirement, requirement_strings))
   2.357 +			return list_first(&props[p].packages, &set->package_pool)->data;
   2.358  
   2.359  		p--;
   2.360  	} while (p > lo && props[p].name == props[p + 1].name &&
   2.361 @@ -1921,78 +1858,79 @@
   2.362  		if (!strncmp(&upool[prop->name], "rpmlib(", 7))
   2.363  			continue;
   2.364  
   2.365 -		if (find_provider(system, &upool[prop->name],
   2.366 -				  prop->relation, &upool[prop->version]) == -1) {
   2.367 +		if (find_provider(system, prop, upool) == -1) {
   2.368  			new = array_add(new_requires, sizeof *new);
   2.369  			*new = p->data;
   2.370  		}
   2.371  	}
   2.372  }
   2.373  
   2.374 -struct razor_set *
   2.375 -razor_set_update(struct razor_set *set, struct razor_set *upstream,
   2.376 -		 int count, const char **packages)
   2.377 +static void
   2.378 +razor_transaction_satisfy_installs(struct razor_transaction *trans,
   2.379 +				   struct array *package_array,
   2.380 +				   int start, int end)
   2.381  {
   2.382 -	struct razor_set *new_set;
   2.383 -	struct array list;
   2.384  	struct razor_package *pkgs;
   2.385 -	int update_count, u, provider, already;
   2.386 -	uint32_t *update, *new, *new_end, *required;
   2.387 +	struct razor_transaction_package *packages, *tp;
   2.388 +	int p, provider, already;
   2.389 +	uint32_t *new, *new_end;
   2.390  	struct razor_property *props, *prop_end;
   2.391  	struct array new_requires;
   2.392  	char *pool;
   2.393  
   2.394 -	pkgs = upstream->packages.data;
   2.395 -	props = upstream->properties.data;
   2.396 -	prop_end = upstream->properties.data + set->properties.size;
   2.397 -	pool = upstream->string_pool.data;
   2.398 +	pkgs = trans->upstream->packages.data;
   2.399 +	props = trans->upstream->properties.data;
   2.400 +	prop_end = trans->upstream->properties.data + trans->upstream->properties.size;
   2.401 +	pool = trans->upstream->string_pool.data;
   2.402  
   2.403 -	array_init(&list);
   2.404 -	if (count > 0)
   2.405 -		find_packages(upstream, count, packages, &list);
   2.406 -	else
   2.407 -		find_all_packages(set, upstream, &list);
   2.408 +	packages = package_array->data;
   2.409 +	for (p = start; p < end; p++) {
   2.410 +		if (packages[p].state != RAZOR_PACKAGE_INSTALL)
   2.411 +			continue;
   2.412  
   2.413 -	update = list.data;
   2.414 -	update_count = list.size / sizeof (uint32_t);
   2.415 -	u = 0;
   2.416 -	while (u < update_count) {
   2.417  		array_init(&new_requires);
   2.418 -		for (; u < update_count; u++)
   2.419 -			gather_new_requires(set, upstream, &pkgs[update[u]], &new_requires);
   2.420 +		gather_new_requires(trans->system, trans->upstream,
   2.421 +				    packages[p].package, &new_requires);
   2.422  
   2.423  		new_end = new_requires.data + new_requires.size;
   2.424  		for (new = new_requires.data; new < new_end; new++) {
   2.425 -			provider = find_provider(upstream, &pool[props[*new].name],
   2.426 -						 props[*new].relation,
   2.427 -						 &pool[props[*new].version]);
   2.428 -
   2.429  			/* FIXME */
   2.430 -			if (provider == -1)
   2.431 +			if (pool[props[*new].name] == '/')
   2.432  				continue;
   2.433  
   2.434 -			update = list.data;
   2.435 -			update_count = list.size / sizeof (uint32_t);
   2.436 -			for (already = 0; already < update_count; already++) {
   2.437 -				if (provider == update[already])
   2.438 -					break;
   2.439 -			}
   2.440 -			if (already < update_count)
   2.441 +			provider = find_provider(trans->upstream,
   2.442 +						 &props[*new], pool);
   2.443 +			already = find_transaction_package(package_array,
   2.444 +							   &pkgs[provider]);
   2.445 +			if (already != -1 &&
   2.446 +			    (packages[already].state & RAZOR_PACKAGE_INSTALL))
   2.447  				continue;
   2.448  
   2.449 -			required = array_add(&list, sizeof *required);
   2.450 -			*required = provider;
   2.451 +			tp = array_add(package_array, sizeof *tp);
   2.452 +			memset(tp, 0, sizeof *tp);
   2.453 +			tp->req_package = packages[p].name;
   2.454 +			tp->req_property = &pool[props[*new].name];
   2.455 +			tp->req_relation = props[*new].relation;
   2.456 +			tp->req_version = &pool[props[*new].version];
   2.457 +
   2.458 +			if (provider != -1) {
   2.459 +				tp->package = &pkgs[provider];
   2.460 +				tp->name = &pool[tp->package->name];
   2.461 +				tp->version = &pool[tp->package->version];
   2.462 +				if (already != -1) {
   2.463 +					tp->state = RAZOR_PACKAGE_INSTALL_BLOCKED;
   2.464 +					trans->errors++;
   2.465 +				} else
   2.466 +					tp->state = RAZOR_PACKAGE_INSTALL;
   2.467 +			} else {
   2.468 +				tp->state = RAZOR_PACKAGE_INSTALL_UNSATISFIABLE;
   2.469 +				trans->errors++;
   2.470 +			}
   2.471 +
   2.472 +			packages = package_array->data;
   2.473  		}
   2.474  		array_release(&new_requires);
   2.475 -
   2.476 -		update = list.data;
   2.477 -		update_count = list.size / sizeof (uint32_t);
   2.478  	}
   2.479 -
   2.480 -	new_set = razor_set_add(set, upstream, &list);
   2.481 -	array_release(&list);
   2.482 -	razor_set_destroy(set);
   2.483 -	return new_set;
   2.484  }
   2.485  
   2.486  /* Look through pkg's PROVIDES, and for each one that no other package
   2.487 @@ -2019,101 +1957,102 @@
   2.488  	}
   2.489  }
   2.490  
   2.491 -/* Add the index of each package required by req that isn't already in
   2.492 - * lost_requires, to lost_requires
   2.493 - */
   2.494  static void
   2.495 -gather_lost_requires(struct razor_set *set, struct razor_property *req,
   2.496 -		     struct array *lost_requires)
   2.497 +lose_requirement(struct razor_transaction *trans, struct array *package_array,
   2.498 +		 const char *req_package, struct razor_property *req,
   2.499 +		 struct razor_property *lost_provider,
   2.500 +		 struct razor_property *first_provider)
   2.501  {
   2.502 +	struct razor_property *provider, *prop_end;
   2.503 +	struct razor_package *pkgs;
   2.504 +	char *pool = trans->system->string_pool.data;
   2.505  	struct list *p;
   2.506 -	uint32_t *lost, *already_lost, *already_end;
   2.507 +	struct razor_transaction_package *tp, *packages;;
   2.508 +	int already;
   2.509  
   2.510 -	for (p = list_first(&req->packages, &set->package_pool); p; p = list_next(p)) {
   2.511 -		already_end = lost_requires->data + lost_requires->size;
   2.512 -		for (already_lost = lost_requires->data; already_lost < already_end; already_lost++) {
   2.513 -			if (*already_lost == p->data)
   2.514 -				break;
   2.515 -		}
   2.516 +	pkgs = trans->system->packages.data;
   2.517 +	prop_end = trans->system->properties.data + trans->system->properties.size;
   2.518  
   2.519 -		if (already_lost == already_end) {
   2.520 -			lost = array_add(lost_requires, sizeof *lost);
   2.521 -			*lost = p->data;
   2.522 -		}
   2.523 +	/* See if any other provider satisfies req */
   2.524 +	for (provider = first_provider;
   2.525 +	     provider < prop_end && provider->type == RAZOR_PROPERTY_PROVIDES && provider->name == lost_provider->name;
   2.526 +	     provider++) {
   2.527 +		if (provider == lost_provider)
   2.528 +			continue;
   2.529 +
   2.530 +		if (provider_satisfies_requirement(provider, pool, req, pool))
   2.531 +			return;
   2.532 +	}
   2.533 +
   2.534 +	/* Remove each of the packages requiring req */
   2.535 +	for (p = list_first(&req->packages, &trans->system->package_pool); p; p = list_next(p)) {
   2.536 +		packages = package_array->data;
   2.537 +		already = find_transaction_package(package_array, &pkgs[p->data]);
   2.538 +		if (already != -1 &&
   2.539 +		    (packages[already].state & RAZOR_PACKAGE_REMOVE))
   2.540 +			continue;
   2.541 +
   2.542 +		tp = array_add(package_array, sizeof *tp);
   2.543 +		memset(tp, 0, sizeof *tp);
   2.544 +		tp->package = &pkgs[p->data];
   2.545 +		tp->name = &pool[tp->package->name];
   2.546 +		tp->version = &pool[tp->package->version];
   2.547 +		tp->req_package = req_package;
   2.548 +		tp->req_property = &pool[req->name];
   2.549 +		tp->req_relation = req->relation;
   2.550 +		tp->req_version = &pool[req->version];
   2.551 +		if (already != -1) {
   2.552 +			tp->state = RAZOR_PACKAGE_REMOVE_BLOCKED;
   2.553 +			trans->errors++;
   2.554 +		} else
   2.555 +			tp->state = RAZOR_PACKAGE_REMOVE;
   2.556  	}
   2.557  }
   2.558  
   2.559 -static struct razor_set *
   2.560 -razor_set_remove_internal(struct razor_set *set, struct array *list)
   2.561 +static void
   2.562 +razor_transaction_satisfy_removes(struct razor_transaction *trans,
   2.563 +				  struct array *package_array,
   2.564 +				  int start, int end)
   2.565  {
   2.566 -	struct razor_set *empty, *new;
   2.567 -	struct razor_merger *merger;
   2.568 +	struct razor_transaction_package *packages;
   2.569  	struct razor_package *pkgs;
   2.570 -	int pkg_count, remove_count, p, r;
   2.571 -	uint32_t *remove, *lost, *lost_end;
   2.572 -	struct razor_property *props, *prop_end, *req;
   2.573 +	int pkg_count, r;
   2.574 +	uint32_t *lost, *lost_end;
   2.575 +	struct razor_property *props, *prop_end, *req, *first_provider;
   2.576  	struct array lost_provides;
   2.577 +	const char *req_package;
   2.578  
   2.579 -	pkgs = set->packages.data;
   2.580 -	pkg_count = set->packages.size / sizeof (struct razor_package);
   2.581 -	props = set->properties.data;
   2.582 -	prop_end = set->properties.data + set->properties.size;
   2.583 +	pkgs = trans->system->packages.data;
   2.584 +	pkg_count = trans->system->packages.size / sizeof (struct razor_package);
   2.585 +	props = trans->system->properties.data;
   2.586 +	prop_end = trans->system->properties.data + trans->system->properties.size;
   2.587  
   2.588 -	remove = list->data;
   2.589 -	remove_count = list->size / sizeof (uint32_t);
   2.590 -	r = 0;
   2.591 -	while (r < remove_count) {
   2.592 +	for (r = start; r < end; r++) {
   2.593 +		packages = package_array->data;
   2.594 +		if (packages[r].state != RAZOR_PACKAGE_REMOVE)
   2.595 +			continue;
   2.596 +
   2.597  		array_init(&lost_provides);
   2.598 -		for (; r < remove_count; r++)
   2.599 -			gather_lost_provides(set, &pkgs[remove[r]], &lost_provides);
   2.600 +		req_package = packages[r].name;
   2.601 +		gather_lost_provides(trans->system, packages[r].package,
   2.602 +				     &lost_provides);
   2.603  
   2.604  		lost_end = lost_provides.data + lost_provides.size;
   2.605  		for (lost = lost_provides.data; lost < lost_end; lost++) {
   2.606  			/* Requires FOO will appear before Provides FOO */
   2.607  			for (req = &props[*lost]; req > props && req->name == props[*lost].name && req->type != RAZOR_PROPERTY_REQUIRES; req--)
   2.608  				;
   2.609 -			/* FIXME: versioned deps... */
   2.610 +			first_provider = req + 1;
   2.611 +
   2.612  			while (req > props && req->name == props[*lost].name) {
   2.613 -				gather_lost_requires(set, req, list);
   2.614 +				lose_requirement(trans, package_array,
   2.615 +						 req_package, req,
   2.616 +						 &props[*lost], first_provider);
   2.617  				req--;
   2.618  			}
   2.619  		}
   2.620  		array_release(&lost_provides);
   2.621 -
   2.622 -		remove = list->data;
   2.623 -		remove_count = list->size / sizeof (uint32_t);
   2.624  	}
   2.625 -
   2.626 -	empty = razor_set_create();
   2.627 -	merger = razor_merger_create(set, empty);
   2.628 -
   2.629 -	for (p = 0; p < pkg_count; p++) {
   2.630 -		for (r = 0; r < remove_count; r++) {
   2.631 -			if (p == remove[r])
   2.632 -				goto skip;
   2.633 -		}
   2.634 -		add_package(merger, &pkgs[p], &merger->source1, 0);
   2.635 -	skip:
   2.636 -		;
   2.637 -	}
   2.638 -
   2.639 -	new = razor_merger_finish(merger);
   2.640 -	razor_set_destroy(empty);
   2.641 -	return new;
   2.642 -}
   2.643 -
   2.644 -struct razor_set *
   2.645 -razor_set_remove(struct razor_set *set, int count, const char **packages)
   2.646 -{
   2.647 -	struct razor_set *new;
   2.648 -	struct array list;
   2.649 -
   2.650 -	array_init(&list);
   2.651 -	find_packages(set, count, packages, &list);
   2.652 -	new = razor_set_remove_internal(set, &list);
   2.653 -	array_release(&list);
   2.654 -	razor_set_destroy(set);
   2.655 -	return new;
   2.656  }
   2.657  
   2.658  /* The diff order matters.  We should sort the packages so that a
   2.659 @@ -2161,3 +2100,258 @@
   2.660  	razor_package_iterator_destroy(pi1);
   2.661  	razor_package_iterator_destroy(pi2);
   2.662  }
   2.663 +
   2.664 +struct razor_transaction *
   2.665 +razor_transaction_create(struct razor_set *system, struct razor_set *upstream,
   2.666 +			 int update_count, const char **update_packages,
   2.667 +			 int remove_count, const char **remove_packages)
   2.668 +{
   2.669 +	struct razor_transaction *trans;
   2.670 +	struct array packages;
   2.671 +	int start, end;
   2.672 +
   2.673 +	trans = zalloc(sizeof *trans);
   2.674 +	trans->system = system;
   2.675 +	trans->upstream = upstream ? upstream : razor_set_create();
   2.676 +	array_init(&packages);
   2.677 +
   2.678 +	/* Find initial upstream packages to be installed */
   2.679 +	if (update_count > 0) {
   2.680 +		trans->errors +=
   2.681 +			find_packages(upstream, update_count, update_packages,
   2.682 +				      &packages, RAZOR_PACKAGE_INSTALL);
   2.683 +	} else if (remove_count == 0)
   2.684 +		find_all_packages(system, upstream, &packages);
   2.685 +
   2.686 +	/* Find initial installed packages to remove. */
   2.687 +	if (remove_count > 0) {
   2.688 +		trans->errors +=
   2.689 +			find_packages(system, remove_count, remove_packages,
   2.690 +				      &packages, RAZOR_PACKAGE_REMOVE);
   2.691 +	}
   2.692 +
   2.693 +	start = 0;
   2.694 +	end = packages.size / sizeof (struct razor_transaction_package);
   2.695 +
   2.696 +	while (!trans->errors && start != end) {
   2.697 +		if (upstream)
   2.698 +			razor_transaction_satisfy_installs(trans, &packages, start, end);
   2.699 +		razor_transaction_satisfy_removes(trans, &packages, start, end);
   2.700 +
   2.701 +		start = end;
   2.702 +		end = packages.size / sizeof (struct razor_transaction_package);
   2.703 +	}
   2.704 +
   2.705 +	trans->packages = packages.data;
   2.706 +	trans->package_count = packages.size / sizeof (struct razor_transaction_package);
   2.707 +	return trans;
   2.708 +}
   2.709 +
   2.710 +const char * const razor_version_relations[] = {
   2.711 +	/* same order as enum razor_version_relation */
   2.712 +	"<", "<=", "=", ">=", ">"
   2.713 +};
   2.714 +
   2.715 +void
   2.716 +razor_transaction_describe(struct razor_transaction *trans)
   2.717 +{
   2.718 +	struct razor_transaction_package *p, *pend, *tps;
   2.719 +	int errors_only = 0;
   2.720 +
   2.721 +	tps = trans->packages;
   2.722 +	pend = trans->packages + trans->package_count;
   2.723 +	for (p = trans->packages; p < pend; p++) {
   2.724 +		switch (p->state) {
   2.725 +		case RAZOR_PACKAGE_INSTALL:
   2.726 +			if (errors_only)
   2.727 +				break;
   2.728 +
   2.729 +			printf ("Installing %s %s", p->name, p->version);
   2.730 +			if (p->req_package) {
   2.731 +				printf (" for %s", p->req_package);
   2.732 +				if (*p->req_version) {
   2.733 +					printf (", which requires %s %s %s",
   2.734 +						p->req_property,
   2.735 +						razor_version_relations[p->req_relation],
   2.736 +						p->req_version);
   2.737 +				} else if (strcmp(p->req_property, p->name) != 0) {
   2.738 +					printf (", which requires %s",
   2.739 +						p->req_property);
   2.740 +				}
   2.741 +			}
   2.742 +			printf("\n");
   2.743 +			break;
   2.744 +
   2.745 +		case RAZOR_PACKAGE_INSTALL_UNAVAILABLE:
   2.746 +			if (*p->req_version && strcmp(p->req_property, p->name) == 0) {
   2.747 +				printf ("Can't find %s %s %s, which is required by %s",
   2.748 +					p->name,
   2.749 +					razor_version_relations[p->req_relation],
   2.750 +					p->req_version,
   2.751 +					p->req_package);
   2.752 +			} else {
   2.753 +				printf ("Can't find %s", p->name);
   2.754 +				if (*p->version)
   2.755 +					printf (" %s", p->version);
   2.756 +				
   2.757 +				if (p->req_package) {
   2.758 +					printf ("  which is required by %s",
   2.759 +						p->req_package);
   2.760 +					if (strcmp(p->req_property, p->name) != 0)
   2.761 +						printf (" for %s", p->req_property);
   2.762 +				}
   2.763 +			}
   2.764 +			printf("\n");
   2.765 +			errors_only = 1;
   2.766 +			break;
   2.767 +
   2.768 +		case RAZOR_PACKAGE_INSTALL_BLOCKED:
   2.769 +			printf ("Cannot install %s, which is already marked for removal but is required by %s\n", p->name, p->req_package);
   2.770 +			errors_only = 1;
   2.771 +			break;
   2.772 +
   2.773 +		case RAZOR_PACKAGE_INSTALL_UNSATISFIABLE:
   2.774 +			printf ("Cannot find package for %s", p->req_property);
   2.775 +			if (*p->req_version) {
   2.776 +				printf (" %s %s",
   2.777 +					razor_version_relations[p->req_relation],
   2.778 +					p->req_version);
   2.779 +			}
   2.780 +			printf (" which is required by %s\n",
   2.781 +				p->req_package);
   2.782 +			errors_only = 1;
   2.783 +			break;
   2.784 +
   2.785 +		case RAZOR_PACKAGE_REMOVE:
   2.786 +			if (errors_only)
   2.787 +				break;
   2.788 +			printf ("Removing %s %s", p->name, p->version);
   2.789 +			if (p->req_package) {
   2.790 +				printf (" which required %s", p->req_package);
   2.791 +				if (strcmp(p->req_property, p->req_package) != 0)
   2.792 +					printf (" for %s", p->req_property);
   2.793 +			}
   2.794 +			printf("\n");
   2.795 +			break;
   2.796 +
   2.797 +		case RAZOR_PACKAGE_REMOVE_NOT_INSTALLED:
   2.798 +			printf ("Package %s is not installed\n", p->name);
   2.799 +			errors_only = 1;
   2.800 +			break;
   2.801 +
   2.802 +		case RAZOR_PACKAGE_REMOVE_BLOCKED:
   2.803 +			printf ("Cannot remove %s, which is marked for installation but requires %s\n", p->name, p->req_package);
   2.804 +			errors_only = 1;
   2.805 +			break;
   2.806 +
   2.807 +		default:
   2.808 +			/* Shouldn't actually happen */
   2.809 +			break;
   2.810 +		}
   2.811 +	}
   2.812 +}
   2.813 +
   2.814 +struct razor_set *
   2.815 +razor_transaction_run(struct razor_transaction *trans)
   2.816 +{
   2.817 +	struct array install_packages, remove_packages;
   2.818 +	struct razor_merger *merger;
   2.819 +	struct razor_package *pkg, *i, *iend, *r, *rend, *s, *send;
   2.820 +	struct source *source1, *source2;
   2.821 +	char *spool, *ipool, *rpool;
   2.822 +	uint32_t *map;
   2.823 +	int p, cmp;
   2.824 +
   2.825 +	/* FIXME */
   2.826 +	if (trans->errors)
   2.827 +		return NULL;
   2.828 +
   2.829 +	/* Sort the transaction packages into two arrays */
   2.830 +	array_init(&install_packages);
   2.831 +	array_init(&remove_packages);
   2.832 +	for (p = 0; p < trans->package_count; p++) {
   2.833 +		if (trans->packages[p].state & RAZOR_PACKAGE_INSTALL)
   2.834 +			pkg = array_add(&install_packages, sizeof *pkg);
   2.835 +		else
   2.836 +			pkg = array_add(&remove_packages, sizeof *pkg);
   2.837 +		*pkg = *trans->packages[p].package;
   2.838 +	}
   2.839 +	map = qsort_with_data(install_packages.data,
   2.840 +			      install_packages.size / sizeof *pkg,
   2.841 +			      sizeof *pkg,
   2.842 +			      compare_packages,
   2.843 +			      trans->upstream);
   2.844 +	free(map);
   2.845 +	map = qsort_with_data(remove_packages.data,
   2.846 +			      remove_packages.size / sizeof *pkg,
   2.847 +			      sizeof *pkg,
   2.848 +			      compare_packages,
   2.849 +			      trans->system);
   2.850 +	free(map);
   2.851 +
   2.852 +	merger = razor_merger_create(trans->system, trans->upstream);
   2.853 +
   2.854 +	source1 = &merger->source1;
   2.855 +	source2 = &merger->source2;
   2.856 +
   2.857 +	i = install_packages.data;
   2.858 +	iend = install_packages.data + install_packages.size;
   2.859 +	ipool = trans->upstream->string_pool.data;
   2.860 +
   2.861 +	r = remove_packages.data;
   2.862 +	rend = remove_packages.data + remove_packages.size;
   2.863 +	rpool = trans->system->string_pool.data;
   2.864 +
   2.865 +	s = trans->system->packages.data;
   2.866 +	send = trans->system->packages.data + trans->system->packages.size;
   2.867 +	spool = trans->system->string_pool.data;
   2.868 +
   2.869 +	while (s < send || i < iend) {
   2.870 +		/* Check if s is being removed */
   2.871 +		if (s < send && r < rend &&
   2.872 +		    s->name == r->name && s->version && r->version) {
   2.873 +			s++;
   2.874 +			r++;
   2.875 +			continue;
   2.876 +		}
   2.877 +
   2.878 +		if (s < send && i < iend)
   2.879 +			cmp = strcmp(&spool[s->name], &ipool[i->name]);
   2.880 +		else if (s < send)
   2.881 +			cmp = -1;
   2.882 +		else
   2.883 +			cmp = 1;
   2.884 +		if (cmp < 0) {
   2.885 +			add_package(merger, s, source1, 0);
   2.886 +			s++;
   2.887 +		} else if (cmp == 0) {
   2.888 +			add_package(merger, i, source2, UPSTREAM_SOURCE);
   2.889 +			s++;
   2.890 +			i++;
   2.891 +		} else {
   2.892 +			add_package(merger, i, source2, UPSTREAM_SOURCE);
   2.893 +			i++;
   2.894 +		}
   2.895 +	}
   2.896 +
   2.897 +	array_release(&install_packages);
   2.898 +	array_release(&remove_packages);
   2.899 +
   2.900 +	return razor_merger_finish(merger);
   2.901 +}
   2.902 +
   2.903 +void
   2.904 +razor_transaction_destroy(struct razor_transaction *trans)
   2.905 +{
   2.906 +	int p;
   2.907 +
   2.908 +	for (p = 0; p < trans->package_count; p++) {
   2.909 +		if (!trans->packages[p].req_package &&
   2.910 +		    (trans->packages[p].state == RAZOR_PACKAGE_INSTALL_UNAVAILABLE ||
   2.911 +		     trans->packages[p].state == RAZOR_PACKAGE_REMOVE_NOT_INSTALLED))
   2.912 +			free((char *)trans->packages[p].name);
   2.913 +	}
   2.914 +	free(trans);
   2.915 +
   2.916 +	/* FIXME: free upstream if it was created as an empty set */
   2.917 +}
     3.1 --- a/razor.h	Fri Feb 29 11:51:58 2008 -0500
     3.2 +++ b/razor.h	Fri Feb 29 11:53:15 2008 -0500
     3.3 @@ -21,6 +21,7 @@
     3.4  	RAZOR_VERSION_GREATER_OR_EQUAL,
     3.5  	RAZOR_VERSION_GREATER
     3.6  };
     3.7 +extern const char * const razor_version_relations[];
     3.8  
     3.9  struct razor_set *razor_set_create(void);
    3.10  struct razor_set *razor_set_open(const char *filename);
    3.11 @@ -62,11 +63,6 @@
    3.12  void razor_set_list_package_files(struct razor_set *set, const char *name);
    3.13  
    3.14  void razor_set_list_unsatisfied(struct razor_set *set);
    3.15 -struct razor_set *razor_set_update(struct razor_set *set,
    3.16 -				   struct razor_set *upstream,
    3.17 -				   int count, const char **packages);
    3.18 -struct razor_set *razor_set_remove(struct razor_set *set,
    3.19 -				   int count, const char **packages);
    3.20  
    3.21  typedef void (*razor_package_callback_t)(const char *name,
    3.22  					 const char *old_version,
    3.23 @@ -76,6 +72,51 @@
    3.24  razor_set_diff(struct razor_set *set, struct razor_set *upstream,
    3.25  	       razor_package_callback_t callback, void *data);
    3.26  
    3.27 +/* Package transactions */
    3.28 +
    3.29 +enum razor_transaction_package_state {
    3.30 +	/* Basic states */
    3.31 +	RAZOR_PACKAGE_INSTALL       = 0x01,
    3.32 +	RAZOR_PACKAGE_REMOVE        = 0x02,
    3.33 +
    3.34 +	/* (Flags used to define the error states) */
    3.35 +	RAZOR_PACKAGE_UNAVAILABLE   = 0x04,
    3.36 +	RAZOR_PACKAGE_UNSATISFIABLE = 0x08,
    3.37 +	RAZOR_PACKAGE_BLOCKED       = 0x10,
    3.38 +
    3.39 +	/* Error states */
    3.40 +	RAZOR_PACKAGE_INSTALL_UNAVAILABLE   = RAZOR_PACKAGE_INSTALL | RAZOR_PACKAGE_UNAVAILABLE,
    3.41 +	RAZOR_PACKAGE_INSTALL_UNSATISFIABLE = RAZOR_PACKAGE_INSTALL | RAZOR_PACKAGE_UNSATISFIABLE,
    3.42 +	RAZOR_PACKAGE_INSTALL_BLOCKED = RAZOR_PACKAGE_INSTALL | RAZOR_PACKAGE_BLOCKED,
    3.43 +	RAZOR_PACKAGE_REMOVE_NOT_INSTALLED  = RAZOR_PACKAGE_REMOVE | RAZOR_PACKAGE_UNAVAILABLE,
    3.44 +	RAZOR_PACKAGE_REMOVE_BLOCKED  = RAZOR_PACKAGE_REMOVE | RAZOR_PACKAGE_BLOCKED
    3.45 +};
    3.46 +
    3.47 +struct razor_transaction_package {
    3.48 +	struct razor_package *package;
    3.49 +	const char *name, *version;
    3.50 +	enum razor_transaction_package_state state;
    3.51 +
    3.52 +	const char *req_package;
    3.53 +	const char *req_property;
    3.54 +	enum razor_version_relation req_relation;
    3.55 +	const char *req_version;
    3.56 +};
    3.57 +
    3.58 +struct razor_transaction {
    3.59 +	int package_count, errors;
    3.60 +	struct razor_transaction_package *packages;
    3.61 +
    3.62 +	struct razor_set *system, *upstream;
    3.63 +};
    3.64 +
    3.65 +struct razor_transaction *
    3.66 +razor_transaction_create(struct razor_set *system, struct razor_set *upstream,
    3.67 +			 int update_count, const char **update_packages,
    3.68 +			 int remove_count, const char **remove_packages);
    3.69 +void razor_transaction_describe(struct razor_transaction *trans);
    3.70 +struct razor_set *razor_transaction_run(struct razor_transaction *trans);
    3.71 +void razor_transaction_destroy(struct razor_transaction *trans);
    3.72  
    3.73  /* Importer interface; for building a razor set from external sources,
    3.74   * like yum, rpmdb or razor package files. */
     4.1 --- a/test-driver.c	Fri Feb 29 11:51:58 2008 -0500
     4.2 +++ b/test-driver.c	Fri Feb 29 11:53:15 2008 -0500
     4.3 @@ -24,15 +24,17 @@
     4.4  	XML_SetElementHandler(parser, start, end);
     4.5  	XML_SetUserData(parser, data);
     4.6  
     4.7 -	buffer = XML_GetBuffer(parser, XML_BUFFER_SIZE);
     4.8 -
     4.9  	fd = open(filename, O_RDONLY);
    4.10  	if (fd < 0) {
    4.11  		fprintf(stderr, "failed to open %s: %m\n", filename);
    4.12  		exit(-1);
    4.13  	}
    4.14  
    4.15 -	while (len = read(fd, buffer, XML_BUFFER_SIZE), len > 0) {
    4.16 +	while (1) {
    4.17 +		buffer = XML_GetBuffer(parser, XML_BUFFER_SIZE);
    4.18 +		len = read(fd, buffer, XML_BUFFER_SIZE);
    4.19 +		if (len == 0)
    4.20 +			break;
    4.21  		err = XML_ParseBuffer(parser, len, len == 0);
    4.22  		if (err == XML_STATUS_ERROR) {
    4.23  			fprintf(stderr, "parse error at line %lu:\n%s\n",
    4.24 @@ -56,11 +58,15 @@
    4.25  	struct razor_importer *importer;
    4.26  	struct razor_set **importer_set;
    4.27  
    4.28 +	struct razor_transaction *trans;
    4.29 +	struct razor_transaction_package *unsat;
    4.30 +
    4.31  	char *install_pkgs[3], *remove_pkgs[3];
    4.32  	int n_install_pkgs, n_remove_pkgs;
    4.33  
    4.34  	int in_result, result_errors;
    4.35 -	int in_unsatisfiable;
    4.36 +
    4.37 +	int debug;
    4.38  };
    4.39  
    4.40  static void
    4.41 @@ -87,11 +93,11 @@
    4.42  {
    4.43  	if (!rel_str)
    4.44  		return -1;
    4.45 -	if (rel_str[0] == 'l')
    4.46 -		return rel_str[1] == 'e' ? RAZOR_VERSION_LESS_OR_EQUAL : RAZOR_VERSION_LESS;
    4.47 -	else if (rel_str[0] == 'g')
    4.48 -		return rel_str[1] == 'e' ? RAZOR_VERSION_GREATER_OR_EQUAL : RAZOR_VERSION_GREATER;
    4.49 -	else if (rel_str[0] == 'e' || rel_str[1] == 'q')
    4.50 +	if (rel_str[0] == 'L')
    4.51 +		return rel_str[1] == 'E' ? RAZOR_VERSION_LESS_OR_EQUAL : RAZOR_VERSION_LESS;
    4.52 +	else if (rel_str[0] == 'G')
    4.53 +		return rel_str[1] == 'E' ? RAZOR_VERSION_GREATER_OR_EQUAL : RAZOR_VERSION_GREATER;
    4.54 +	else if (rel_str[0] == 'E' || rel_str[1] == 'Q')
    4.55  		return RAZOR_VERSION_EQUAL;
    4.56  	else
    4.57  		return -1;
    4.58 @@ -125,6 +131,10 @@
    4.59  		razor_set_destroy(ctx->result_set);
    4.60  		ctx->result_set = NULL;
    4.61  	}
    4.62 +	if (ctx->trans) {
    4.63 +		razor_transaction_destroy(ctx->trans);
    4.64 +		ctx->trans = NULL;
    4.65 +	}
    4.66  }
    4.67  
    4.68  static void
    4.69 @@ -177,15 +187,43 @@
    4.70  }
    4.71  
    4.72  static void
    4.73 +add_property(struct test_context *ctx, enum razor_property_type type, const char *name, enum razor_version_relation rel, const char *version)
    4.74 +{
    4.75 +	razor_importer_add_property(ctx->importer, name,
    4.76 +				    rel, version, type);
    4.77 +}
    4.78 +
    4.79 +static void
    4.80 +check_unsatisfiable_property(struct test_context *ctx, enum razor_property_type type, const char *name, enum razor_version_relation rel, const char *version)
    4.81 +{
    4.82 +	if (!version)
    4.83 +		version = "";
    4.84 +
    4.85 +	for (; ctx->unsat < ctx->trans->packages + ctx->trans->package_count; ctx->unsat++) {
    4.86 +		if (ctx->unsat->state != RAZOR_PACKAGE_INSTALL_UNSATISFIABLE)
    4.87 +			continue;
    4.88 +		if (strcmp(name, ctx->unsat->req_property) != 0 ||
    4.89 +		    rel != ctx->unsat->req_relation ||
    4.90 +		    strcmp(version, ctx->unsat->req_version) != 0)
    4.91 +			continue;
    4.92 +
    4.93 +		/* OK, found it, so skip over it and continue */
    4.94 +		ctx->unsat++;
    4.95 +		return;
    4.96 +	}
    4.97 +
    4.98 +	fprintf(stderr, "  didn't get unsatisfiable '%s %s %s'\n",
    4.99 +		name, razor_version_relations[rel], version);
   4.100 +	exit(1);
   4.101 +}
   4.102 +
   4.103 +static void
   4.104  start_property(struct test_context *ctx, enum razor_property_type type, const char **atts)
   4.105  {
   4.106  	const char *name = NULL, *rel_str = NULL, *version = NULL;
   4.107  	enum razor_version_relation rel;
   4.108  
   4.109 -	if (ctx->in_unsatisfiable)
   4.110 -		return;
   4.111 -
   4.112 -	get_atts(atts, "name", &name, "rel", &rel_str, "version", &version, NULL);
   4.113 +	get_atts(atts, "name", &name, "relation", &rel_str, "version", &version, NULL);
   4.114  	if (name == NULL) {
   4.115  		fprintf(stderr, "  no name specified for property\n");
   4.116  		exit(1);
   4.117 @@ -199,8 +237,10 @@
   4.118  	} else
   4.119  		rel = RAZOR_VERSION_EQUAL;
   4.120  	
   4.121 -	razor_importer_add_property(ctx->importer, name,
   4.122 -				    rel, version, type);
   4.123 +	if (ctx->unsat)
   4.124 +		check_unsatisfiable_property(ctx, type, name, rel, version);
   4.125 +	else
   4.126 +		add_property(ctx, type, name, rel, version);
   4.127  }
   4.128  
   4.129  static void
   4.130 @@ -213,22 +253,27 @@
   4.131  static void
   4.132  end_transaction(struct test_context *ctx)
   4.133  {
   4.134 -	if (ctx->n_install_pkgs) {
   4.135 -		ctx->system_set = razor_set_update(ctx->system_set,
   4.136 -						   ctx->repo_set,
   4.137 -						   ctx->n_install_pkgs,
   4.138 -						   (const char **)ctx->install_pkgs);
   4.139 -	}
   4.140 -	if (ctx->n_remove_pkgs && ctx->system_set) {
   4.141 -		ctx->system_set = razor_set_remove(ctx->system_set,
   4.142 -						   ctx->n_remove_pkgs,
   4.143 -						   (const char **)ctx->remove_pkgs);
   4.144 +	ctx->trans = razor_transaction_create(ctx->system_set, ctx->repo_set,
   4.145 +					      ctx->n_install_pkgs,
   4.146 +					      (const char **)ctx->install_pkgs,
   4.147 +					      ctx->n_remove_pkgs,
   4.148 +					      (const char **)ctx->remove_pkgs);
   4.149 +	if (ctx->debug) {
   4.150 +		razor_transaction_describe(ctx->trans);
   4.151 +		printf("\n");
   4.152  	}
   4.153  
   4.154  	while (ctx->n_install_pkgs--)
   4.155  		free(ctx->install_pkgs[ctx->n_install_pkgs]);
   4.156  	while (ctx->n_remove_pkgs--)
   4.157  		free(ctx->remove_pkgs[ctx->n_remove_pkgs]);
   4.158 +
   4.159 +	if (!ctx->trans->errors) {
   4.160 +		struct razor_set *new;
   4.161 +		new = razor_transaction_run(ctx->trans);
   4.162 +		razor_set_destroy(ctx->system_set);
   4.163 +		ctx->system_set = new;
   4.164 +	}
   4.165  }
   4.166  
   4.167  static void
   4.168 @@ -301,20 +346,18 @@
   4.169  static void
   4.170  start_unsatisfiable(struct test_context *ctx, const char **atts)
   4.171  {
   4.172 -	if (ctx->system_set) {
   4.173 +	if (ctx->result_set) {
   4.174  		fprintf(stderr, "Expected to fail, but didn't\n");
   4.175  		exit(1);
   4.176  	}
   4.177  
   4.178 -	/* FIXME */
   4.179 -	fprintf(stderr, "  Not actually checking <unsatisfiable>\n");
   4.180 -	ctx->in_unsatisfiable = 1;
   4.181 +	ctx->unsat = ctx->trans->packages;
   4.182  }
   4.183  
   4.184  static void
   4.185  end_unsatisfiable(struct test_context *ctx)
   4.186  {
   4.187 -	ctx->in_unsatisfiable = 0;
   4.188 +	ctx->unsat = NULL;
   4.189  }
   4.190  
   4.191  static void
   4.192 @@ -379,14 +422,26 @@
   4.193  int main(int argc, char *argv[])
   4.194  {
   4.195  	struct test_context ctx;
   4.196 +	const char *test_file;
   4.197  
   4.198 -	if (argc != 2) {
   4.199 -		fprintf(stderr, "usage: %s TESTS-FILE\n", argv[0]);
   4.200 +	memset(&ctx, 0, sizeof ctx);
   4.201 +
   4.202 +	if (argc > 3) {
   4.203 +		fprintf(stderr, "usage: %s [-d] [TESTS-FILE]\n", argv[0]);
   4.204  		exit(-1);			
   4.205  	}
   4.206  
   4.207 -	memset(&ctx, 0, sizeof ctx);
   4.208 -	parse_xml_file(argv[1], start_test_element, end_test_element, &ctx);
   4.209 +	if (argc >= 2 && !strcmp (argv[1], "-d")) {
   4.210 +		ctx.debug = 1;
   4.211 +		argc--;
   4.212 +		argv++;
   4.213 +	}
   4.214 +	if (argc == 2)
   4.215 +		test_file = argv[1];
   4.216 +	else
   4.217 +		test_file = "test.xml";
   4.218 +
   4.219 +	parse_xml_file(test_file, start_test_element, end_test_element, &ctx);
   4.220  
   4.221  	return 0;
   4.222  }