package.c 16.7 KB
Newer Older
1 2 3 4 5 6
/* package.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.
 *
7
 * This program is free software; you can redistribute it and/or modify it
8 9 10 11
 * 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.
 */

12
#include <errno.h>
13 14 15
#include <fcntl.h>
#include <ctype.h>
#include <stdio.h>
16
#include <limits.h>
17 18 19 20 21 22 23 24 25 26 27 28
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

#include "apk_defines.h"
#include "apk_archive.h"
#include "apk_package.h"
#include "apk_database.h"
#include "apk_state.h"

29
struct apk_package *apk_pkg_new(void)
30 31 32 33 34 35 36 37 38 39
{
	struct apk_package *pkg;

	pkg = calloc(1, sizeof(struct apk_package));
	if (pkg != NULL)
		list_init(&pkg->installed_pkgs_list);

	return pkg;
}

40 41 42 43 44 45
int apk_pkg_parse_name(apk_blob_t apkname,
		       apk_blob_t *name,
		       apk_blob_t *version)
{
	int i, dash = 0;

46 47 48
	if (APK_BLOB_IS_NULL(apkname))
		return -1;

49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
	for (i = apkname.len - 2; i >= 0; i--) {
		if (apkname.ptr[i] != '-')
			continue;
		if (isdigit(apkname.ptr[i+1]))
			break;
		if (++dash >= 2)
			return -1;
	}
	if (name != NULL)
		*name = APK_BLOB_PTR_LEN(apkname.ptr, i);
	if (version != NULL)
		*version = APK_BLOB_PTR_PTR(&apkname.ptr[i+1],
					    &apkname.ptr[apkname.len-1]);

	return 0;
}

Timo Teräs's avatar
Timo Teräs committed
66
static apk_blob_t trim(apk_blob_t str)
67 68
{
	if (str.ptr == NULL || str.len < 1)
Timo Teräs's avatar
Timo Teräs committed
69
		return str;
70

71 72
	if (str.ptr[str.len-1] == '\n') {
		str.ptr[str.len-1] = 0;
Timo Teräs's avatar
Timo Teräs committed
73
		return APK_BLOB_PTR_LEN(str.ptr, str.len-1);
74
	}
75

Timo Teräs's avatar
Timo Teräs committed
76
	return str;
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94
}

int apk_deps_add(struct apk_dependency_array **depends,
		 struct apk_dependency *dep)
{
	struct apk_dependency_array *deps = *depends;
	int i;

	if (deps != NULL) {
		for (i = 0; i < deps->num; i++) {
			if (deps->item[i].name == dep->name)
				return 0;
		}
	}

	*apk_dependency_array_add(depends) = *dep;
	return 0;
}
Timo Teräs's avatar
Timo Teräs committed
95

96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
void apk_deps_del(struct apk_dependency_array **pdeps,
		  struct apk_name *name)
{
	struct apk_dependency_array *deps = *pdeps;
	int i;

	if (deps == NULL)
		return;

	for (i = 0; i < deps->num; i++) {
		if (deps->item[i].name != name)
			continue;

		deps->item[i] = deps->item[deps->num-1];
		*pdeps = apk_dependency_array_resize(deps, deps->num-1);
		break;
	}
}

115 116 117 118 119 120 121 122 123 124
struct parse_depend_ctx {
	struct apk_database *db;
	struct apk_dependency_array **depends;
};

static int parse_depend(void *ctx, apk_blob_t blob)
{
	struct parse_depend_ctx *pctx = (struct parse_depend_ctx *) ctx;
	struct apk_dependency *dep;
	struct apk_name *name;
125 126
	apk_blob_t bname, bop, bver = APK_BLOB_NULL;
	int mask = APK_VERSION_LESS | APK_VERSION_EQUAL | APK_VERSION_GREATER;
127 128 129 130

	if (blob.len == 0)
		return 0;

131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
	/* [!]name[<,<=,=,>=,>]ver */
	if (blob.ptr[0] == '!') {
		mask = 0;
		blob.ptr++;
		blob.len--;
	}
	if (apk_blob_cspn(blob, "<>=", &bname, &bop)) {
		int i;

		if (mask == 0)
			return -1;
		if (!apk_blob_spn(bop, "<>=", &bop, &bver))
			return -1;
		for (i = 0; i < blob.len; i++) {
			switch (blob.ptr[i]) {
			case '<':
				mask |= APK_VERSION_LESS;
				break;
			case '>':
				mask |= APK_VERSION_GREATER;
				break;
			case '=':
				mask |= APK_VERSION_EQUAL;
				break;
			}
		}
		if ((mask & (APK_VERSION_LESS|APK_VERSION_GREATER))
		    == (APK_VERSION_LESS|APK_VERSION_GREATER))
			return -1;

		if (!apk_version_validate(bver))
			return -1;
	}

165 166 167 168 169 170 171 172 173 174
	name = apk_db_get_name(pctx->db, blob);
	if (name == NULL)
		return -1;

	dep = apk_dependency_array_add(pctx->depends);
	if (dep == NULL)
		return -1;

	*dep = (struct apk_dependency){
		.name = name,
175 176
		.version = APK_BLOB_IS_NULL(bver) ? NULL : apk_blob_cstr(bver),
		.result_mask = mask,
177 178 179 180 181
	};

	return 0;
}

182 183 184 185
void apk_deps_parse(struct apk_database *db,
		    struct apk_dependency_array **depends,
		    apk_blob_t blob)
{
186
	struct parse_depend_ctx ctx = { db, depends };
187

188 189
	if (blob.len > 1 && blob.ptr[blob.len-1] == '\n')
		blob.len--;
190

191
	apk_blob_for_each_segment(blob, " ", parse_depend, &ctx);
192 193
}

194 195
int apk_deps_write(struct apk_dependency_array *deps, struct apk_ostream *os)
{
196
	int i, r, n = 0;
197 198 199 200 201 202 203 204 205 206 207

	if (deps == NULL)
		return 0;

	for (i = 0; i < deps->num; i++) {
		if (i) {
			if (os->write(os, " ", 1) != 1)
				return -1;
			n += 1;
		}

208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
		if (deps->item[i].result_mask == APK_DEPMASK_CONFLICT) {
			if (os->write(os, "!", 1) != 1)
				return -1;
			n += 1;
		}

		r = apk_ostream_write_string(os, deps->item[i].name->name);
		if (r < 0)
			return r;
		n += r;

		if (deps->item[i].result_mask != APK_DEPMASK_CONFLICT &&
		    deps->item[i].result_mask != APK_DEPMASK_REQUIRE) {
			r = apk_ostream_write_string(os, apk_version_op_string(deps->item[i].result_mask));
			if (r < 0)
				return r;
			n += r;

			r = apk_ostream_write_string(os, deps->item[i].version);
			if (r < 0)
				return r;
			n += r;
		}
231 232 233 234 235
	}

	return n;
}

Timo Teräs's avatar
Timo Teräs committed
236
const char *apk_script_types[] = {
237 238 239 240 241 242 243 244 245 246 247 248
	[APK_SCRIPT_PRE_INSTALL]	= "pre-install",
	[APK_SCRIPT_POST_INSTALL]	= "post-install",
	[APK_SCRIPT_PRE_DEINSTALL]	= "pre-deinstall",
	[APK_SCRIPT_POST_DEINSTALL]	= "post-deinstall",
	[APK_SCRIPT_PRE_UPGRADE]	= "pre-upgrade",
	[APK_SCRIPT_POST_UPGRADE]	= "post-upgrade",
};

int apk_script_type(const char *name)
{
	int i;

Timo Teräs's avatar
Timo Teräs committed
249 250 251
	for (i = 0; i < ARRAY_SIZE(apk_script_types); i++)
		if (apk_script_types[i] &&
		    strcmp(apk_script_types[i], name) == 0)
252 253
			return i;

254
	return APK_SCRIPT_INVALID;
255 256 257 258 259
}

struct read_info_ctx {
	struct apk_database *db;
	struct apk_package *pkg;
260 261 262 263 264 265 266
	const EVP_MD *md;
	int version, action;
	int has_signature : 1;
	int has_install : 1;
	int has_data_checksum : 1;
	int data_started : 1;
	int in_signatures : 1;
267 268
};

269 270
int apk_pkg_add_info(struct apk_database *db, struct apk_package *pkg,
		     char field, apk_blob_t value)
Timo Teräs's avatar
Timo Teräs committed
271 272 273
{
	switch (field) {
	case 'P':
274
		pkg->name = apk_db_get_name(db, value);
Timo Teräs's avatar
Timo Teräs committed
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
		break;
	case 'V':
		pkg->version = apk_blob_cstr(value);
		break;
	case 'T':
		pkg->description = apk_blob_cstr(value);
		break;
	case 'U':
		pkg->url = apk_blob_cstr(value);
		break;
	case 'L':
		pkg->license = apk_blob_cstr(value);
		break;
	case 'D':
		apk_deps_parse(db, &pkg->depends, value);
		break;
	case 'C':
Timo Teräs's avatar
Timo Teräs committed
292
		apk_blob_pull_csum(&value, &pkg->csum);
Timo Teräs's avatar
Timo Teräs committed
293 294
		break;
	case 'S':
295
		pkg->size = apk_blob_pull_uint(&value, 10);
Timo Teräs's avatar
Timo Teräs committed
296 297
		break;
	case 'I':
298
		pkg->installed_size = apk_blob_pull_uint(&value, 10);
Timo Teräs's avatar
Timo Teräs committed
299
		break;
300 301
	default:
		return -1;
Timo Teräs's avatar
Timo Teräs committed
302
	}
303 304
	if (APK_BLOB_IS_NULL(value))
		return -1;
Timo Teräs's avatar
Timo Teräs committed
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
	return 0;
}

static int read_info_line(void *ctx, apk_blob_t line)
{
	static struct {
		const char *str;
		char field;
	} fields[] = {
		{ "pkgname", 'P' },
		{ "pkgver",  'V' },
		{ "pkgdesc", 'T' },
		{ "url",     'U' },
		{ "size",    'I' },
		{ "license", 'L' },
		{ "depend",  'D' },
	};
	struct read_info_ctx *ri = (struct read_info_ctx *) ctx;
	apk_blob_t l, r;
	int i;

	if (line.ptr == NULL || line.len < 1 || line.ptr[0] == '#')
		return 0;

329
	if (!apk_blob_split(line, APK_BLOB_STR(" = "), &l, &r))
Timo Teräs's avatar
Timo Teräs committed
330 331 332
		return 0;

	for (i = 0; i < ARRAY_SIZE(fields); i++) {
333
		if (apk_blob_compare(APK_BLOB_STR(fields[i].str), l) == 0) {
334
			apk_pkg_add_info(ri->db, ri->pkg, fields[i].field, r);
335
			return 0;
Timo Teräs's avatar
Timo Teräs committed
336 337 338
		}
	}

339 340 341 342
	if (ri->data_started == 0 &&
	    apk_blob_compare(APK_BLOB_STR("sha256"), l) == 0)
		ri->has_data_checksum = 1;

Timo Teräs's avatar
Timo Teräs committed
343 344 345
	return 0;
}

346
static int read_info_entry(void *ctx, const struct apk_file_info *ae,
347
			   struct apk_istream *is)
348
{
Timo Teräs's avatar
Timo Teräs committed
349 350 351 352 353
	static struct {
		const char *str;
		char field;
	} fields[] = {
		{ "DESC",	'T' },
354
		{ "WWW",	'U' },
Timo Teräs's avatar
Timo Teräs committed
355 356 357
		{ "LICENSE",	'L' },
		{ "DEPEND", 	'D' },
	};
358 359 360 361
	struct read_info_ctx *ri = (struct read_info_ctx *) ctx;
	struct apk_database *db = ri->db;
	struct apk_package *pkg = ri->pkg;
	apk_blob_t name, version;
362
	char *slash;
Timo Teräs's avatar
Timo Teräs committed
363
	int i;
364

Timo Teräs's avatar
Timo Teräs committed
365
	/* Meta info and scripts */
366 367 368 369
	if (ri->in_signatures && strncmp(ae->name, ".SIGN.", 6) != 0)
		ri->in_signatures = 0;

	if (ri->data_started == 0 && ae->name[0] == '.') {
Timo Teräs's avatar
Timo Teräs committed
370 371
		/* APK 2.0 format */
		if (strcmp(ae->name, ".PKGINFO") == 0) {
372
			apk_blob_t blob = apk_blob_from_istream(is, ae->size);
Timo Teräs's avatar
Timo Teräs committed
373 374
			apk_blob_for_each_segment(blob, "\n", read_info_line, ctx);
			free(blob.ptr);
375 376 377 378
			ri->version = 2;
		} else if (strncmp(ae->name, ".SIGN.", 6) == 0) {
			ri->has_signature = 1;
		} else if (strcmp(ae->name, ".INSTALL") == 0) {
379 380
			apk_warning("Package '%s-%s' contains deprecated .INSTALL",
				    pkg->name->name, pkg->version);
Timo Teräs's avatar
Timo Teräs committed
381
		}
382 383 384 385 386
		return 0;
	}

	ri->data_started = 1;
	if (strncmp(ae->name, "var/db/apk/", 11) == 0) {
Timo Teräs's avatar
Timo Teräs committed
387
		/* APK 1.0 format */
388
		ri->version = 1;
Timo Teräs's avatar
Timo Teräs committed
389 390
		if (!S_ISREG(ae->mode))
			return 0;
391

Timo Teräs's avatar
Timo Teräs committed
392 393 394
		slash = strchr(&ae->name[11], '/');
		if (slash == NULL)
			return 0;
395

Timo Teräs's avatar
Timo Teräs committed
396 397 398
		if (apk_pkg_parse_name(APK_BLOB_PTR_PTR(&ae->name[11], slash-1),
				       &name, &version) < 0)
			return -1;
399

400 401
		if (pkg->name == NULL)
			pkg->name = apk_db_get_name(db, name);
Timo Teräs's avatar
Timo Teräs committed
402 403 404 405 406
		if (pkg->version == NULL)
			pkg->version = apk_blob_cstr(version);

		for (i = 0; i < ARRAY_SIZE(fields); i++) {
			if (strcmp(fields[i].str, slash+1) == 0) {
407
				apk_blob_t blob = apk_blob_from_istream(is, ae->size);
408 409
				apk_pkg_add_info(ri->db, ri->pkg, fields[i].field,
						 trim(blob));
Timo Teräs's avatar
Timo Teräs committed
410 411 412
				free(blob.ptr);
				break;
			}
413
		}
Timo Teräs's avatar
Timo Teräs committed
414 415 416
		if (apk_script_type(slash+1) == APK_SCRIPT_POST_INSTALL ||
		    apk_script_type(slash+1) == APK_SCRIPT_PRE_INSTALL)
			ri->has_install = 1;
417
	} else if (ri->version < 2) {
418 419
		/* Version 1.x packages do not contain installed size
		 * in metadata, so we calculate it here */
420
		pkg->installed_size += apk_calc_installed_size(ae->size);
Timo Teräs's avatar
Timo Teräs committed
421
	}
422 423 424 425

	return 0;
}

426 427 428 429 430 431
static int apk_pkg_gzip_part(void *ctx, EVP_MD_CTX *mdctx, int part)
{
	struct read_info_ctx *ri = (struct read_info_ctx *) ctx;

	switch (part) {
	case APK_MPART_BEGIN:
432
		EVP_DigestInit_ex(mdctx, ri->md, NULL);
433
		break;
434 435 436 437 438 439 440 441 442 443 444
	case APK_MPART_BOUNDARY:
		if (ri->in_signatures) {
			EVP_DigestFinal_ex(mdctx, ri->pkg->csum.data, NULL);
			EVP_DigestInit_ex(mdctx, ri->md, NULL);
			return 0;
		}

		if (ri->action == APK_SIGN_GENERATE_V1 ||
		    !ri->has_data_checksum)
			break;
		/* Fallthrough to calculate checksum */
445
	case APK_MPART_END:
Timo Teräs's avatar
Timo Teräs committed
446 447
		ri->pkg->csum.type = EVP_MD_CTX_size(mdctx);
		EVP_DigestFinal_ex(mdctx, ri->pkg->csum.data, NULL);
448
		return 1;
449 450 451 452
	}
	return 0;
}

453 454
struct apk_package *apk_pkg_read(struct apk_database *db, const char *file,
				 int action)
455 456
{
	struct read_info_ctx ctx;
457
	struct apk_file_info fi;
458
	struct apk_bstream *bs;
459
	struct apk_istream *tar;
460
	char realfile[PATH_MAX];
461
	int r;
462 463 464

	if (realpath(file, realfile) < 0)
		return NULL;
465 466
	if (apk_file_get_info(realfile, APK_CHECKSUM_NONE, &fi) < 0)
		return NULL;
467

468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484
	memset(&ctx, 0, sizeof(ctx));
	switch (action) {
	case APK_SIGN_VERIFY:
		ctx.in_signatures = 1;
		ctx.md = EVP_md_null();
		break;
	case APK_SIGN_GENERATE:
		ctx.in_signatures = 1;
		ctx.md = EVP_sha1();
		break;
	case APK_SIGN_GENERATE_V1:
		ctx.md = EVP_md5();
		break;
	default:
		return NULL;
	}

485
	ctx.pkg = apk_pkg_new();
486 487 488
	if (ctx.pkg == NULL)
		return NULL;

489
	bs = apk_bstream_from_file(realfile);
Timo Teräs's avatar
Timo Teräs committed
490
	if (bs == NULL)
491 492 493 494
		goto err;

	ctx.db = db;
	ctx.has_install = 0;
495
	ctx.action = action;
496
	ctx.pkg->size = fi.size;
497

498
	tar = apk_bstream_gunzip_mpart(bs, apk_pkg_gzip_part, &ctx);
499 500 501 502 503 504 505 506 507
	r = apk_tar_parse(tar, read_info_entry, &ctx);
	tar->close(tar);
	switch (r) {
	case 0:
		break;
	case -2:
		apk_error("File %s does not have a signature", file);
		goto err;
	default:
Timo Teräs's avatar
Timo Teräs committed
508
		apk_error("File %s is not an APK archive", file);
509 510 511
		goto err;
	}

Timo Teräs's avatar
Timo Teräs committed
512 513
	if (ctx.pkg->name == NULL) {
		apk_error("File %s is corrupted", file);
514
		goto err;
Timo Teräs's avatar
Timo Teräs committed
515
	}
516 517 518 519

	/* Add implicit busybox dependency if there is scripts */
	if (ctx.has_install) {
		struct apk_dependency dep = {
520
			.name = apk_db_get_name(db, APK_BLOB_STR("busybox")),
521 522 523
		};
		apk_deps_add(&ctx.pkg->depends, &dep);
	}
524
	ctx.pkg->filename = strdup(realfile);
525

526
	return apk_db_pkg_add(db, ctx.pkg);
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542
err:
	apk_pkg_free(ctx.pkg);
	return NULL;
}

void apk_pkg_free(struct apk_package *pkg)
{
	struct apk_script *script;
	struct hlist_node *c, *n;

	if (pkg == NULL)
		return;

	hlist_for_each_entry_safe(script, c, n, &pkg->scripts, script_list)
		free(script);

543 544
	if (pkg->depends)
		free(pkg->depends);
545 546 547 548 549 550 551 552 553 554 555 556 557
	if (pkg->version)
		free(pkg->version);
	if (pkg->url)
		free(pkg->url);
	if (pkg->description)
		free(pkg->description);
	if (pkg->license)
		free(pkg->license);
	free(pkg);
}

int apk_pkg_get_state(struct apk_package *pkg)
{
558
	if (list_hashed(&pkg->installed_pkgs_list))
559 560
		return APK_PKG_INSTALLED;
	return APK_PKG_NOT_INSTALLED;
561 562
}

563 564 565
void apk_pkg_set_state(struct apk_database *db, struct apk_package *pkg, int state)
{
	switch (state) {
566
	case APK_PKG_INSTALLED:
567 568 569 570 571 572
		if (!list_hashed(&pkg->installed_pkgs_list)) {
			db->installed.stats.packages++;
			list_add_tail(&pkg->installed_pkgs_list,
				      &db->installed.packages);
		}
		break;
573
	case APK_PKG_NOT_INSTALLED:
574 575 576 577 578 579 580 581
		if (list_hashed(&pkg->installed_pkgs_list)) {
			db->installed.stats.packages--;
			list_del(&pkg->installed_pkgs_list);
		}
		break;
	}
}

582
int apk_pkg_add_script(struct apk_package *pkg, struct apk_istream *is,
583 584 585 586 587 588 589 590
		       unsigned int type, unsigned int size)
{
	struct apk_script *script;
	int r;

	script = malloc(sizeof(struct apk_script) + size);
	script->type = type;
	script->size = size;
591
	r = is->read(is, script->script, size);
592 593 594 595 596 597 598 599 600
	if (r < 0) {
		free(script);
		return r;
	}

	hlist_add_head(&script->script_list, &pkg->scripts);
	return r;
}

601
int apk_pkg_run_script(struct apk_package *pkg, int root_fd,
602 603
		       unsigned int type)
{
604 605 606 607
	static const char * const environment[] = {
		"PATH=/usr/sbin:/usr/bin:/sbin:/bin",
		NULL
	};
608 609 610 611 612 613
	struct apk_script *script;
	struct hlist_node *c;
	int fd, status;
	pid_t pid;
	char fn[1024];

614
	fchdir(root_fd);
615
	hlist_for_each_entry(script, c, &pkg->scripts, script_list) {
616
		if (script->type != type)
617 618 619 620 621
			continue;

		snprintf(fn, sizeof(fn),
			"tmp/%s-%s.%s",
			pkg->name->name, pkg->version,
Timo Teräs's avatar
Timo Teräs committed
622
			apk_script_types[type]);
623 624 625 626 627 628 629 630 631 632 633 634
		fd = creat(fn, 0777);
		if (fd < 0)
			return fd;
		write(fd, script->script, script->size);
		close(fd);

		apk_message("Executing %s", &fn[4]);

		pid = fork();
		if (pid == -1)
			return -1;
		if (pid == 0) {
635 636
			if (chroot(".") < 0) {
				apk_error("chroot: %s", strerror(errno));
Timo Teräs's avatar
Timo Teräs committed
637
			} else {
Timo Teräs's avatar
Timo Teräs committed
638
				execle(fn, apk_script_types[type],
639
				       pkg->version, "", NULL, environment);
Timo Teräs's avatar
Timo Teräs committed
640
			}
641 642 643
			exit(1);
		}
		waitpid(pid, &status, 0);
Timo Teräs's avatar
Timo Teräs committed
644
		unlink(fn);
645 646 647 648 649 650 651 652
		if (WIFEXITED(status))
			return WEXITSTATUS(status);
		return -1;
	}

	return 0;
}

Timo Teräs's avatar
Timo Teräs committed
653
static int parse_index_line(void *ctx, apk_blob_t line)
654
{
Timo Teräs's avatar
Timo Teräs committed
655
	struct read_info_ctx *ri = (struct read_info_ctx *) ctx;
656

Timo Teräs's avatar
Timo Teräs committed
657 658
	if (line.len < 3 || line.ptr[1] != ':')
		return 0;
659

660
	apk_pkg_add_info(ri->db, ri->pkg, line.ptr[0], APK_BLOB_PTR_LEN(line.ptr+2, line.len-2));
661 662 663 664 665
	return 0;
}

struct apk_package *apk_pkg_parse_index_entry(struct apk_database *db, apk_blob_t blob)
{
Timo Teräs's avatar
Timo Teräs committed
666
	struct read_info_ctx ctx;
667

668
	ctx.pkg = apk_pkg_new();
Timo Teräs's avatar
Timo Teräs committed
669
	if (ctx.pkg == NULL)
670 671
		return NULL;

Timo Teräs's avatar
Timo Teräs committed
672
	ctx.db = db;
673
	ctx.version = 0;
Timo Teräs's avatar
Timo Teräs committed
674
	ctx.has_install = 0;
675

Timo Teräs's avatar
Timo Teräs committed
676 677 678 679
	apk_blob_for_each_segment(blob, "\n", parse_index_line, &ctx);

	if (ctx.pkg->name == NULL) {
		apk_pkg_free(ctx.pkg);
680 681
		apk_error("Failed to parse index entry: %.*s",
			  blob.len, blob.ptr);
Timo Teräs's avatar
Timo Teräs committed
682
		ctx.pkg = NULL;
683 684
	}

Timo Teräs's avatar
Timo Teräs committed
685
	return ctx.pkg;
686 687
}

688 689
int apk_pkg_write_index_entry(struct apk_package *info,
			      struct apk_ostream *os)
690
{
691
	char buf[512];
692 693 694 695
	apk_blob_t bbuf = APK_BLOB_BUF(buf);
	int r;

	apk_blob_push_blob(&bbuf, APK_BLOB_STR("C:"));
Timo Teräs's avatar
Timo Teräs committed
696
	apk_blob_push_csum(&bbuf, &info->csum);
697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713
	apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nP:"));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR(info->name->name));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nV:"));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR(info->version));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nS:"));
	apk_blob_push_uint(&bbuf, info->size, 10);
	apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nI:"));
	apk_blob_push_uint(&bbuf, info->installed_size, 10);
	apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nT:"));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR(info->description));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nU:"));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR(info->url));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR("\nL:"));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR(info->license));
	apk_blob_push_blob(&bbuf, APK_BLOB_STR("\n"));

	if (os->write(os, buf, bbuf.ptr - buf) != bbuf.ptr - buf)
714
		return -1;
715 716

	if (info->depends != NULL) {
717 718 719 720 721 722 723
		if (os->write(os, "D:", 2) != 2)
			return -1;
		r = apk_deps_write(info->depends, os);
		if (r < 0)
			return r;
		if (os->write(os, "\n", 1) != 1)
			return -1;
724 725
	}

726
	return 0;
727
}
728

729 730 731 732 733
int apk_pkg_version_compare(struct apk_package *a, struct apk_package *b)
{
	return apk_version_compare(a->version, b->version);
}

734 735 736 737 738 739 740 741 742 743 744 745 746
struct apk_dependency apk_dep_from_str(struct apk_database *db,
				       char *str)
{
	apk_blob_t name = APK_BLOB_STR(str);
	char *v = str;
	int mask = APK_DEPMASK_REQUIRE;

	v = strpbrk(str, "<>=");
	if (v != NULL) {
		name.len = v - str;
		mask = apk_version_result_mask(v++);
		if (*v == '=')
			v++;
747
	}
748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764
	return (struct apk_dependency) {
		.name = apk_db_get_name(db, name),
		.version = v,
		.result_mask = mask,
	};
}

struct apk_dependency apk_dep_from_pkg(struct apk_database *db,
				       struct apk_package *pkg)
{
	return (struct apk_dependency) {
		.name = apk_db_get_name(db, APK_BLOB_STR(pkg->name->name)),
		.version = pkg->version,
		.result_mask = APK_VERSION_EQUAL,
	};
}