state.c 22.6 KB
Newer Older
1 2 3 4 5 6
/* state.c - Alpine Package Keeper (APK)
 *
 * Copyright (C) 2005-2008 Natanael Copa <n@tanael.org>
 * Copyright (C) 2008 Timo Teräs <timo.teras@iki.fi>
 * All rights reserved.
 *
Timo Teräs's avatar
Timo Teräs committed
7
 * This program is free software; you can redistribute it and/or modify it
8 9 10 11 12
 * under the terms of the GNU General Public License version 2 as published
 * by the Free Software Foundation. See http://www.gnu.org/ for details.
 */

#include <stdio.h>
13
#include <stdlib.h>
14
#include <unistd.h>
15 16 17 18
#include <malloc.h>

#include "apk_state.h"
#include "apk_database.h"
Natanael Copa's avatar
Natanael Copa committed
19
#include "apk_print.h"
20

21 22 23 24 25 26 27 28 29 30 31 32
struct apk_name_choices {
	unsigned short refs, num;
	struct apk_package *pkgs[];
};

#if 0
struct apk_deferred_state {
	unsigned int preference;
	struct apk_state *state;
};
#endif

33 34 35 36 37
int apk_state_prune_dependency(struct apk_state *state,
			       struct apk_dependency *dep);

#define APK_NS_LOCKED			0x00000001
#define APK_NS_PENDING			0x00000002
38 39 40 41 42 43 44
#define APK_NS_ERROR			0x00000004

static void apk_state_record_conflict(struct apk_state *state,
				      struct apk_package *pkg)
{
	struct apk_name *name = pkg->name;

Timo Teräs's avatar
Timo Teräs committed
45
	state->name[name->id] = (void*) (((intptr_t) pkg) | APK_NS_ERROR | APK_NS_LOCKED);
46 47
	*apk_package_array_add(&state->conflicts) = pkg;
}
48

49 50
static int inline ns_locked(apk_name_state_t name)
{
51 52 53 54 55 56 57 58
	if (((intptr_t) name) & APK_NS_LOCKED)
		return TRUE;
	return FALSE;
}

static int inline ns_pending(apk_name_state_t name)
{
	if (((intptr_t) name) & APK_NS_PENDING)
59 60 61 62
		return TRUE;
	return FALSE;
}

63 64 65 66 67 68 69
static int inline ns_error(apk_name_state_t name)
{
	if (((intptr_t) name) & APK_NS_ERROR)
		return TRUE;
	return FALSE;
}

70 71 72 73 74 75 76
static int ns_empty(apk_name_state_t name)
{
	return name == NULL;
}

static apk_name_state_t ns_from_pkg(struct apk_package *pkg)
{
77 78 79 80 81 82
	return (apk_name_state_t) (((intptr_t) pkg) | APK_NS_LOCKED | APK_NS_PENDING);
}

static apk_name_state_t ns_from_pkg_non_pending(struct apk_package *pkg)
{
	return (apk_name_state_t) (((intptr_t) pkg) | APK_NS_LOCKED);
83 84 85 86
}

static struct apk_package *ns_to_pkg(apk_name_state_t name)
{
87
	return (struct apk_package *)
88
		(((intptr_t) name) & ~(APK_NS_LOCKED | APK_NS_PENDING | APK_NS_ERROR));
89 90 91 92 93 94 95 96 97 98 99 100 101 102
}

static apk_name_state_t ns_from_choices(struct apk_name_choices *nc)
{
	if (nc == NULL)
		return ns_from_pkg(NULL);
	return (apk_name_state_t) nc;
}

static struct apk_name_choices *ns_to_choices(apk_name_state_t name)
{
	return (struct apk_name_choices *) name;
}

103 104
static struct apk_name_choices *name_choices_new(struct apk_database *db,
						 struct apk_name *name)
105 106
{
	struct apk_name_choices *nc;
107
	int i, j;
108

Timo Teräs's avatar
Timo Teräs committed
109
	if (name->pkgs->num == 0)
110 111 112 113 114 115 116 117 118 119 120
		return NULL;

	nc = malloc(sizeof(struct apk_name_choices) +
		    name->pkgs->num * sizeof(struct apk_package *));
	if (nc == NULL)
		return NULL;

	nc->refs = 1;
	nc->num = name->pkgs->num;
	memcpy(nc->pkgs, name->pkgs->item,
	       name->pkgs->num * sizeof(struct apk_package *));
121

122 123 124
	if (name->flags & APK_NAME_TOPLEVEL_OVERRIDE)
		return nc;

125
	/* Check for global dependencies */
Timo Teräs's avatar
Timo Teräs committed
126
	for (i = 0; i < db->world->num; i++) {
127 128 129 130 131 132 133 134 135 136
		struct apk_dependency *dep = &db->world->item[i];

		if (dep->name != name)
			continue;

		for (j = 0; j < nc->num; ) {
			if (apk_version_compare(nc->pkgs[j]->version, dep->version)
			    & dep->result_mask) {
			    	j++;
			} else {
Timo Teräs's avatar
Timo Teräs committed
137
				nc->pkgs[j] = nc->pkgs[nc->num - 1];
138 139 140 141 142
				nc->num--;
			}
		}
	}

143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
	return nc;
}

static void name_choices_unref(struct apk_name_choices *nc)
{
	if (--nc->refs == 0)
		free(nc);
}

static struct apk_name_choices *name_choices_writable(struct apk_name_choices *nc)
{
	struct apk_name_choices *n;

	if (nc->refs == 1)
		return nc;

	n = malloc(sizeof(struct apk_name_choices) +
		   nc->num * sizeof(struct apk_package *));
	if (n == NULL)
		return NULL;

	n->refs = 1;
	n->num = nc->num;
	memcpy(n->pkgs, nc->pkgs, nc->num * sizeof(struct apk_package *));
	name_choices_unref(nc);

	return n;
}

static void ns_free(apk_name_state_t name)
{
	if (!ns_empty(name) && !ns_locked(name))
		name_choices_unref(ns_to_choices(name));
}

178 179 180
static inline int apk_state_pkg_available(struct apk_state *state,
					  struct apk_package *pkg)
{
181 182
	if (pkg->ipkg != NULL)
		return TRUE;
183 184
	if (pkg->installed_size == 0)
		return TRUE;
185 186 187 188 189 190 191
	if (pkg->filename != NULL)
		return TRUE;
	if (apk_db_select_repo(state->db, pkg) != NULL)
		return TRUE;
	return FALSE;
}

192 193 194
struct apk_state *apk_state_new(struct apk_database *db)
{
	struct apk_state *state;
195
	int num_bytes;
196

197
	num_bytes = sizeof(struct apk_state) + db->name_id * sizeof(char *);
198 199
	state = (struct apk_state*) calloc(1, num_bytes);
	state->refs = 1;
200
	state->num_names = db->name_id;
201
	state->db = db;
202
	list_init(&state->change_list_head);
203

Timo Teräs's avatar
Timo Teräs committed
204 205 206
	apk_name_array_init(&state->missing);
	apk_package_array_init(&state->conflicts);

207 208 209 210 211 212 213 214 215 216 217 218 219
	return state;
}

struct apk_state *apk_state_dup(struct apk_state *state)
{
	state->refs++;
	return state;
}

void apk_state_unref(struct apk_state *state)
{
	if (--state->refs > 0)
		return;
Timo Teräs's avatar
Timo Teräs committed
220 221 222

	apk_package_array_free(&state->conflicts);
	apk_name_array_free(&state->missing);
223 224 225
	free(state);
}

226 227 228 229 230 231 232 233 234 235 236
static int apk_state_add_change(struct apk_state *state,
				struct apk_package *oldpkg,
				struct apk_package *newpkg)
{
	struct apk_change *change;

	change = (struct apk_change *) malloc(sizeof(struct apk_change));
	if (change == NULL)
		return -1;

	list_init(&change->change_list);
237
	list_add_tail(&change->change_list, &state->change_list_head);
238 239 240 241 242 243
	change->oldpkg = oldpkg;
	change->newpkg = newpkg;

	return 0;
}

244 245 246 247 248 249 250
/* returns:
 *   -1 error
 *    0 locked entry matches and is ok
 *   +n this many candidates on apk_name_choices for the name
 */
int apk_state_prune_dependency(struct apk_state *state,
			       struct apk_dependency *dep)
251
{
252 253 254 255
	struct apk_name *name = dep->name;
	struct apk_name_choices *c;
	int i;

256 257 258
	if (name->id >= state->num_names)
		return -1;

259
	if (ns_empty(state->name[name->id])) {
260 261 262 263
		if (dep->result_mask == APK_DEPMASK_CONFLICT) {
			state->name[name->id] = ns_from_pkg(NULL);
			return 1;
		}
264 265 266

		/* This name has not been visited yet.
		 * Construct list of candidates. */
267
		state->name[name->id] = ns_from_choices(name_choices_new(state->db, name));
268 269 270 271 272 273 274 275
	}

	if (ns_locked(state->name[name->id])) {
		/* Locked: check that selected package provides
		 * requested version. */
		struct apk_package *pkg = ns_to_pkg(state->name[name->id]);

		/* Locked to not-installed / remove? */
276 277 278
		if (ns_error(state->name[name->id])) {
			return -1;
		} else if (pkg == NULL) {
279 280 281 282 283
			if (dep->result_mask != APK_DEPMASK_CONFLICT) {
				if (ns_pending(state->name[name->id])) {
					state->name[name->id] = ns_from_pkg_non_pending(NULL);
					*apk_name_array_add(&state->missing) = name;
				}
284
				return -1;
285
			}
286 287 288 289
		} else {
			if (!(apk_version_compare(pkg->version, dep->version)
			      & dep->result_mask))
				return -1;
290 291
		}

292 293
		if (ns_pending(state->name[name->id]))
			return 1;
294

295
		return 0;
296 297 298 299
	}

	/* Multiple candidates: prune incompatible versions. */
	c = ns_to_choices(state->name[name->id]);
300 301
	i = 0;
	while (i < c->num) {
302
		if (apk_version_compare(c->pkgs[i]->version, dep->version)
303 304
		    & dep->result_mask) {
		    	i++;
305
			continue;
306
		}
307 308 309 310 311

		c = name_choices_writable(c);
		c->pkgs[i] = c->pkgs[c->num - 1];
		c->num--;
	}
312
	if (c->num == 1 && apk_state_pkg_available(state, c->pkgs[0])) {
313 314
		struct apk_package *pkg = c->pkgs[0];
		name_choices_unref(c);
315 316
		state->name[name->id] = ns_from_pkg(pkg);
		return 1;
317
	}
318 319
	if (c->num <= 1) {
		name_choices_unref(c);
Timo Teräs's avatar
Timo Teräs committed
320
		state->name[name->id] = ns_from_pkg(NULL);
321
		*apk_name_array_add(&state->missing) = name;
322 323
		return -1;
	}
324

325
	state->name[name->id] = ns_from_choices(c);
326 327
	return c->num;
}
328

329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
int apk_state_lock_dependency(struct apk_state *state,
			      struct apk_dependency *dep)
{
	struct apk_name *name = dep->name;
	struct apk_name_choices *c;
        struct apk_package *installed = NULL, *latest = NULL, *use;
	int i, r;

	r = apk_state_prune_dependency(state, dep);
	if (r <= 0)
		return r;

	if (ns_pending(state->name[name->id]))
		return apk_state_lock_name(state, name, ns_to_pkg(state->name[name->id]));

	c = ns_to_choices(state->name[name->id]);
345 346
#if 1
	/* Get latest and installed packages */
347
	for (i = 0; i < c->num; i++) {
348 349
		struct apk_package *pkg = c->pkgs[i];

350
		if (pkg->ipkg != NULL)
351
			installed = pkg;
352
		else if (!apk_state_pkg_available(state, pkg))
353 354
			continue;

Timo Teräs's avatar
Timo Teräs committed
355 356 357 358 359
		if (latest == NULL) {
			latest = pkg;
			continue;
		}

360 361
		if ((apk_flags & APK_PREFER_AVAILABLE) ||
		    (name->flags & APK_NAME_REINSTALL)) {
Timo Teräs's avatar
Timo Teräs committed
362 363 364 365 366 367 368 369 370 371 372 373 374
			if (latest->repos != 0 && pkg->repos == 0)
				continue;

			if (latest->repos == 0 && pkg->repos != 0) {
				latest = pkg;
				continue;
			}

			/* Otherwise both are not available, or both are
			 * available and we just compare the versions then */
		}

		if (apk_pkg_version_compare(pkg, latest) == APK_VERSION_GREATER)
375 376
			latest = pkg;
	}
377

378 379 380 381 382
	/* Choose the best looking candidate.
	 * FIXME: We should instead try all alternatives. */
	if (apk_flags & APK_UPGRADE) {
		use = latest;
	} else {
383 384 385
		if (installed != NULL &&
		    (installed->repos != 0 ||
		     !(name->flags & APK_NAME_REINSTALL)))
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
			use = installed;
		else
			use = latest;
	}
	if (use == NULL)
		return -1;

	return apk_state_lock_name(state, name, use);
#else
	/* If any of the choices is installed, we are good. Otherwise,
	 * the caller needs to install this dependency. */
	for (i = 0; i < c->num; i++)
		if (apk_pkg_get_state(c->pkgs[i]) == APK_PKG_INSTALLED)
			return 0;

	/* Queue for deferred solution. */
	return 0;
#endif
}

static int apk_state_fix_package(struct apk_state *state,
				 struct apk_package *pkg)
{
409 410
	int i, r, ret = 0;

Timo Teräs's avatar
Timo Teräs committed
411
	if (pkg == NULL)
412
		return 0;
413 414 415 416 417

	for (i = 0; i < pkg->depends->num; i++) {
		r = apk_state_lock_dependency(state,
					      &pkg->depends->item[i]);
		if (r != 0)
418
			ret = -1;
419
	}
420 421

	return ret;
422 423
}

Timo Teräs's avatar
Timo Teräs committed
424 425 426 427
static int call_if_dependency_broke(struct apk_state *state,
				    struct apk_package *pkg,
				    struct apk_name *dep_name,
				    int (*cb)(struct apk_state *state,
428 429 430 431
					      struct apk_package *pkg,
					      struct apk_dependency *dep,
					      void *ctx),
				    void *ctx)
Timo Teräs's avatar
Timo Teräs committed
432 433 434 435 436 437 438 439 440 441 442 443 444
{
	struct apk_package *dep_pkg;
	int k;

	dep_pkg = ns_to_pkg(state->name[dep_name->id]);
	for (k = 0; k < pkg->depends->num; k++) {
		struct apk_dependency *dep = &pkg->depends->item[k];
		if (dep->name != dep_name)
			continue;
		if (dep_pkg == NULL &&
		    dep->result_mask == APK_DEPMASK_CONFLICT)
			continue;
		if (dep_pkg != NULL &&
445
		    (apk_version_compare(dep_pkg->version, dep->version)
Timo Teräs's avatar
Timo Teräs committed
446 447
		     & dep->result_mask))
			continue;
448
		return cb(state, pkg, dep, ctx);
Timo Teräs's avatar
Timo Teräs committed
449 450 451 452 453 454 455 456
	}

	return 0;
}

static int for_each_broken_reverse_depency(struct apk_state *state,
					   struct apk_name *name,
					   int (*cb)(struct apk_state *state,
457 458 459 460
						     struct apk_package *pkg,
						     struct apk_dependency *dep,
						     void *ctx),
					   void *ctx)
Timo Teräs's avatar
Timo Teräs committed
461 462 463 464 465 466 467 468 469 470 471
{
	struct apk_package *pkg0;
	int i, j, r;

	for (i = 0; i < name->rdepends->num; i++) {
		struct apk_name *name0 = name->rdepends->item[i];

		if (ns_locked(state->name[name0->id])) {
			pkg0 = ns_to_pkg(state->name[name0->id]);
			if (pkg0 == NULL)
				continue;
472 473
			r = call_if_dependency_broke(state, pkg0, name,
						     cb, ctx);
474 475 476
			if (r != 0)
				return r;
		} else if (!ns_empty(state->name[name0->id])) {
Timo Teräs's avatar
Timo Teräs committed
477 478 479 480
			struct apk_name_choices *ns =
				ns_to_choices(state->name[name0->id]);

			for (j = 0; j < ns->num; j++) {
481
				if (ns->pkgs[j]->ipkg == NULL)
Timo Teräs's avatar
Timo Teräs committed
482 483 484
					continue;
				r = call_if_dependency_broke(state,
							     ns->pkgs[j],
485
							     name, cb, ctx);
Timo Teräs's avatar
Timo Teräs committed
486 487
				if (r != 0)
					return r;
488
				break;
Timo Teräs's avatar
Timo Teräs committed
489
			}
490 491 492
		} else {
			for (j = 0; j < name0->pkgs->num; j++) {
				pkg0 = name0->pkgs->item[j];
Timo Teräs's avatar
Timo Teräs committed
493

494
				if (pkg0->ipkg == NULL)
495
					continue;
Timo Teräs's avatar
Timo Teräs committed
496

497 498
				r = call_if_dependency_broke(state,
							     name0->pkgs->item[j],
499
							     name, cb, ctx);
500 501 502 503
				if (r != 0)
					return r;
				break;
			}
Timo Teräs's avatar
Timo Teräs committed
504 505 506 507 508 509 510
		}
	}

	return 0;
}

static int delete_broken_package(struct apk_state *state,
511 512 513
				 struct apk_package *pkg,
				 struct apk_dependency *dep,
				 void *ctx)
Timo Teräs's avatar
Timo Teräs committed
514 515 516 517 518
{
	return apk_state_lock_name(state, pkg->name, NULL);
}

static int reinstall_broken_package(struct apk_state *state,
519 520 521
				    struct apk_package *pkg,
				    struct apk_dependency *dep,
				    void *ctx)
Timo Teräs's avatar
Timo Teräs committed
522 523

{
524
	struct apk_dependency dep0 = {
Timo Teräs's avatar
Timo Teräs committed
525 526 527
		.name = pkg->name,
		.result_mask = APK_DEPMASK_REQUIRE,
	};
528
	return apk_state_lock_dependency(state, &dep0);
Timo Teräs's avatar
Timo Teräs committed
529 530
}

531 532 533
int apk_state_lock_name(struct apk_state *state,
			struct apk_name *name,
			struct apk_package *newpkg)
534
{
535
	struct apk_package *oldpkg = NULL;
Timo Teräs's avatar
Timo Teräs committed
536
	int i, r;
537

538 539 540
	if (name->id >= state->num_names)
		return -1;

541
	ns_free(state->name[name->id]);
542
	state->name[name->id] = ns_from_pkg_non_pending(newpkg);
543

Timo Teräs's avatar
Timo Teräs committed
544 545 546 547 548
	for (i = 0; i < name->pkgs->num; i++) {
		struct apk_package *pkg = name->pkgs->item[i];
		if (name->pkgs->item[i]->name == name &&
		    pkg->ipkg != NULL)
			oldpkg = pkg;
549 550 551 552
	}

	/* First we need to make sure the dependants of the old package
	 * still have their dependencies ok. */
Timo Teräs's avatar
Timo Teräs committed
553 554 555 556
	if (oldpkg != NULL) {
		r = for_each_broken_reverse_depency(state, name,
						    newpkg == NULL ?
						    delete_broken_package :
557 558 559 560
						    reinstall_broken_package,
						    NULL);
		if (r != 0) {
			apk_state_record_conflict(state, newpkg);
Timo Teräs's avatar
Timo Teräs committed
561
			return r;
562
		}
563 564 565
	}

	/* Check that all other dependencies hold for the new package. */
566 567 568 569
	r = apk_state_fix_package(state, newpkg);
	if (r != 0) {
		apk_state_record_conflict(state, newpkg);
		return r;
570 571
	}

572
	/* If the chosen package is installed, all is done here */
Timo Teräs's avatar
Timo Teräs committed
573 574 575
	if (oldpkg == newpkg &&
	    (newpkg == NULL ||
	     !(newpkg->name->flags & APK_NAME_REINSTALL)))
576 577
		return 0;

578 579 580 581 582 583
	/* Track change */
	r = apk_state_add_change(state, oldpkg, newpkg);
	if (r != 0)
		return r;

	return 0;
584 585
}

586 587
static void apk_print_change(struct apk_database *db,
			     struct apk_package *oldpkg,
588 589
			     struct apk_package *newpkg,
			     int num, int total)
590 591
{
	const char *msg = NULL;
592 593
	int r;
	struct apk_name *name;
594 595 596 597
	char status[64];

	snprintf(status, sizeof(status), "(%i/%i)", num, total);
	status[sizeof(status) - 1] = '\0';
598

599 600 601 602
	if (oldpkg != NULL)
		name = oldpkg->name;
	else
		name = newpkg->name;
603 604

	if (oldpkg == NULL) {
605 606
		apk_message("%s Installing %s (%s)",
			    status, name->name, newpkg->version);
607
	} else if (newpkg == NULL) {
608 609
		apk_message("%s Purging %s (%s)",
			    status, name->name, oldpkg->version);
610
	} else {
611
		r = apk_pkg_version_compare(newpkg, oldpkg);
612 613 614 615 616
		switch (r) {
		case APK_VERSION_LESS:
			msg = "Downgrading";
			break;
		case APK_VERSION_EQUAL:
617 618 619 620 621 622
			if (newpkg == oldpkg) {
				msg = "Re-installing";
				break;
			}
			/* fallthrough - equal version, but different
			 * package is counted as upgrade */
623 624 625 626
		case APK_VERSION_GREATER:
			msg = "Upgrading";
			break;
		}
627 628
		apk_message("%s %s %s (%s -> %s)",
			    status, msg, name->name, oldpkg->version,
629
			    newpkg->version);
630 631 632
	}
}

633 634 635 636
struct apk_stats {
	unsigned int bytes;
	unsigned int packages;
};
637

638 639 640 641 642
static void apk_count_change(struct apk_change *change, struct apk_stats *stats)
{
	if (change->newpkg != NULL) {
		stats->bytes += change->newpkg->installed_size;
		stats->packages ++;
643
	}
644 645 646
	if (change->oldpkg != NULL)
		stats->packages ++;
}
647

648
static inline void apk_draw_progress(int percent, int last)
649
{
650 651
	char tmp[128];
	char reset[128];
652
	int i;
653

654 655
	snprintf(tmp, sizeof(tmp), "-[                    ]- %3i%%", percent);
	for (i = 0; (i < (percent/5)) && (i < (sizeof(tmp)-2)); i++)
656
		tmp[2+i] = '#';
657 658 659 660 661 662
	memset(reset, '\b', strlen(tmp));
	fwrite(tmp, strlen(tmp), 1, stderr);
	if (!last)
		fwrite(reset, strlen(tmp), 1, stderr);
	else if (apk_verbosity > 0)
		fwrite("\n", 1, 1, stderr);
663
	fflush(stderr);
664 665
}

666 667 668 669 670 671 672 673
struct progress {
	struct apk_stats done;
	struct apk_stats total;
	struct apk_package *pkg;
	size_t count;
};

static void progress_cb(void *ctx, size_t progress)
674
{
675 676
	struct progress *prog = (struct progress *) ctx;
	size_t partial = 0, count;
677

678 679
	if (prog->pkg != NULL)
		partial = muldiv(progress, prog->pkg->installed_size, APK_PROGRESS_SCALE);
680

681
        count = muldiv(100, prog->done.bytes + prog->done.packages + partial,
682
		       prog->total.bytes + prog->total.packages);
683

684 685 686
	if (prog->count != count)
		apk_draw_progress(count, 0);
	prog->count = count;
687 688
}

689 690 691
static int dump_packages(struct apk_state *state,
			 int (*cmp)(struct apk_change *change),
			 const char *msg)
692
{
693
	struct apk_change *change;
694
	struct apk_name *name;
Timo Teräs's avatar
Timo Teräs committed
695 696 697
	struct apk_indent indent = { 0, 2 };
	char tmp[256];
	int match = 0, i;
698

699
	list_for_each_entry(change, &state->change_list_head, change_list) {
700 701 702
		if (!cmp(change))
			continue;
		if (match == 0)
Timo Teräs's avatar
Timo Teräs committed
703
			printf("%s:\n ", msg);
704 705 706 707 708
		if (change->newpkg != NULL)
			name = change->newpkg->name;
		else
			name = change->oldpkg->name;

Timo Teräs's avatar
Timo Teräs committed
709 710 711
		i = snprintf(tmp, sizeof(tmp), "%s%s", name->name,
			     (name->flags & APK_NAME_TOPLEVEL) ? "*" : "");
		apk_print_indented(&indent, APK_BLOB_PTR_LEN(tmp, i));
712
		match++;
713
	}
714
	if (match)
Timo Teräs's avatar
Timo Teräs committed
715
		printf("\n");
716
	return match;
717 718
}

719
static int cmp_remove(struct apk_change *change)
720
{
721 722
	return change->newpkg == NULL;
}
723

724 725 726 727
static int cmp_new(struct apk_change *change)
{
	return change->oldpkg == NULL;
}
728

729 730 731
static int cmp_downgrade(struct apk_change *change)
{
	if (change->newpkg == NULL || change->oldpkg == NULL)
732
		return 0;
733
	if (apk_pkg_version_compare(change->newpkg, change->oldpkg)
734 735
	    & APK_VERSION_LESS)
		return 1;
736
	return 0;
737 738
}

739
static int cmp_upgrade(struct apk_change *change)
740
{
741
	if (change->newpkg == NULL || change->oldpkg == NULL)
742
		return 0;
743

744 745 746 747
	/* Count swapping package as upgrade too - this can happen if
	 * same package version is used after it was rebuilt against
	 * newer libraries. Basically, different (and probably newer)
	 * package, but equal version number. */
748 749 750
	if ((apk_pkg_version_compare(change->newpkg, change->oldpkg) &
	     (APK_VERSION_GREATER | APK_VERSION_EQUAL)) &&
	    (change->newpkg != change->oldpkg))
751
		return 1;
752

753 754 755
	return 0;
}

Timo Teräs's avatar
Timo Teräs committed
756
static int fail_if_something_broke(struct apk_state *state,
757 758 759
				   struct apk_package *pkg,
				   struct apk_dependency *dep,
				   void *ctx)
Timo Teräs's avatar
Timo Teräs committed
760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781

{
	return 1;
}

static int apk_state_autoclean(struct apk_state *state,
			       struct apk_package *pkg)
{
	apk_name_state_t oldns;
	int i, r;

	for (i = 0; i < pkg->depends->num; i++) {
		struct apk_name *n = pkg->depends->item[i].name;

		if (ns_locked(state->name[n->id]))
			continue;
		if (n->flags & APK_NAME_TOPLEVEL)
			continue;

		oldns = state->name[n->id];
		state->name[n->id] = ns_from_pkg(NULL);
		r = for_each_broken_reverse_depency(state, n,
782 783
						    fail_if_something_broke,
						    NULL);
Timo Teräs's avatar
Timo Teräs committed
784 785 786 787 788 789 790 791 792 793 794
		state->name[n->id] = oldns;

		if (r == 0) {
			r = apk_state_lock_name(state, n, NULL);
			if (r != 0)
				return r;
		}
	}
	return 0;
}

795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832
struct error_state {
	struct apk_indent indent;
	struct apk_package *prevpkg;
};

static int print_dep(struct apk_state *state,
		     struct apk_package *pkg,
		     struct apk_dependency *dep,
		     void *ctx)
{
	struct error_state *es = (struct error_state *) ctx;
	apk_blob_t blob;
	char buf[256];
	int len;

	if (pkg != es->prevpkg) {
		printf("\n");
		es->indent.x = 0;
		len = snprintf(buf, sizeof(buf), "%s-%s:",
			       pkg->name->name, pkg->version);
		apk_print_indented(&es->indent, APK_BLOB_PTR_LEN(buf, len));
		es->prevpkg = pkg;
	}

	blob = APK_BLOB_BUF(buf);
	apk_blob_push_dep(&blob, dep);
	blob = apk_blob_pushed(APK_BLOB_BUF(buf), blob);
	apk_print_indented(&es->indent, blob);

	return 0;
}

void apk_state_print_errors(struct apk_state *state)
{
	struct apk_package *pkg;
	struct error_state es;
	int i, j, r;

Timo Teräs's avatar
Timo Teräs committed
833
	for (i = 0; i < state->conflicts->num; i++) {
834 835
		if (i == 0)
			apk_error("Unable to satisfy all dependencies:");
836 837

		es.prevpkg = pkg = state->conflicts->item[i];
Timo Teräs's avatar
Timo Teräs committed
838
		es.indent.x =
839
		printf("  %s-%s:", pkg->name->name, pkg->version);
Timo Teräs's avatar
Timo Teräs committed
840
		es.indent.indent = es.indent.x + 1;
Timo Teräs's avatar
Timo Teräs committed
841
		for (j = 0; j < pkg->depends->num; j++) {
842 843 844 845 846 847 848 849 850 851 852
			r = apk_state_lock_dependency(state,
						      &pkg->depends->item[j]);
			if (r != 0)
				print_dep(state, pkg, &pkg->depends->item[j], &es);
		}

		/* Print conflicting reverse deps */
		for_each_broken_reverse_depency(state, pkg->name,
						print_dep, &es);
		printf("\n");
	}
853

Timo Teräs's avatar
Timo Teräs committed
854
	for (i = 0; i < state->missing->num; i++) {
855 856 857 858 859 860 861 862 863 864
		struct apk_name *name = state->missing->item[i];
		if (i == 0) {
			apk_error("Missing packages:");
			es.indent.x = 0;
			es.indent.indent = 2;
		}
		apk_print_indented(&es.indent, APK_BLOB_STR(name->name));
	}
	if (i != 0)
		printf("\n");
865 866
}

867 868
int apk_state_commit(struct apk_state *state,
		     struct apk_database *db)
869
{
870 871
	struct progress prog;
	struct apk_change *change;
872
	int r = 0, size_diff = 0, toplevel = FALSE, deleteonly = TRUE;
873
	int n = 0, numpkg = 0;
874

875 876
	/* Count what needs to be done */
	memset(&prog, 0, sizeof(prog));
Timo Teräs's avatar
Timo Teräs committed
877
	list_for_each_entry(change, &state->change_list_head, change_list) {
878
		numpkg++;
879 880 881 882 883
		if (change->newpkg == NULL) {
			if (change->oldpkg->name->flags & APK_NAME_TOPLEVEL)
				toplevel = TRUE;
		} else
			deleteonly = FALSE;
884 885
		if (change->oldpkg != NULL)
			apk_state_autoclean(state, change->oldpkg);
886
		apk_count_change(change, &prog.total);
887 888 889 890
		if (change->newpkg)
			size_diff += change->newpkg->installed_size;
		if (change->oldpkg)
			size_diff -= change->oldpkg->installed_size;
Timo Teräs's avatar
Timo Teräs committed
891
	}
892
	size_diff /= 1024;
893

894 895 896 897 898 899 900 901 902 903 904
	if (toplevel &&
	    (apk_flags & (APK_INTERACTIVE | APK_RECURSIVE_DELETE)) == 0) {
		if (!deleteonly)
			return -1;

		dump_packages(state, cmp_remove,
			      "The top-level dependencies have been updated "
			      "but the following packages are not removed");
		goto update_state;
	}

905
	if (apk_verbosity > 1 || (apk_flags & APK_INTERACTIVE)) {
906 907 908 909
		r = dump_packages(state, cmp_remove,
				  "The following packages will be REMOVED");
		r += dump_packages(state, cmp_downgrade,
				   "The following packages will be DOWNGRADED");
910
		if (r || (apk_flags & APK_INTERACTIVE) || apk_verbosity > 2) {
911 912 913 914
			dump_packages(state, cmp_new,
				      "The following NEW packages will be installed");
			dump_packages(state, cmp_upgrade,
				      "The following packages will be upgraded");
915 916 917 918
			printf("%d kB of %s\n", abs(size_diff),
			       (size_diff < 0) ?
			       "disk space will be freed" :
			       "additional disk space will be used");
919 920
		}
		if (apk_flags & APK_INTERACTIVE) {
Timo Teräs's avatar
Timo Teräs committed
921 922
			printf("Do you want to continue [Y/n]? ");
			fflush(stdout);
923
			r = fgetc(stdin);
924
			if (r != 'y' && r != 'Y' && r != '\n')
925 926 927 928 929
				return -1;
		}
	}

	/* Go through changes */
930
	r = 0;
931
	n = 0;
932
	list_for_each_entry(change, &state->change_list_head, change_list) {
933 934
		n++;
		apk_print_change(db, change->oldpkg, change->newpkg, n, numpkg);
935 936 937 938 939 940 941
		prog.pkg = change->newpkg;

		if (!(apk_flags & APK_SIMULATE)) {
			r = apk_db_install_pkg(db,
					       change->oldpkg, change->newpkg,
					       (apk_flags & APK_PROGRESS) ? progress_cb : NULL,
					       &prog);
942
			if (r != 0)
943
				break;
944 945 946 947 948 949 950

			if (change->oldpkg != NULL &&
			    change->newpkg == NULL &&
			    change->oldpkg->name->flags & APK_NAME_TOPLEVEL) {
				change->oldpkg->name->flags &= ~APK_NAME_TOPLEVEL;
				apk_deps_del(&db->world, change->oldpkg->name);
			}
951
		}
952 953

		apk_count_change(change, &prog.done);
954
	}
955
	if (apk_flags & APK_PROGRESS)
956
		apk_draw_progress(100, 1);
957

958
update_state:
959 960
	apk_db_run_triggers(db);
	apk_db_write_config(db);
961

962
	if (r == 0)
963 964 965 966
		apk_message("OK: %d packages, %d dirs, %d files",
			    db->installed.stats.packages,
			    db->installed.stats.dirs,
			    db->installed.stats.files);
967

968
	return r;
969
}