package.c 16 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
	[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",
};

Timo Teräs's avatar
Timo Teräs committed
245
246
247
248
249
250
251
252
253
static const char *script_types2[] = {
	[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",
};

254
255
256
257
int apk_script_type(const char *name)
{
	int i;

Timo Teräs's avatar
Timo Teräs committed
258
259
260
	for (i = 0; i < ARRAY_SIZE(apk_script_types); i++)
		if (apk_script_types[i] &&
		    strcmp(apk_script_types[i], name) == 0)
261
262
			return i;

263
	return APK_SCRIPT_INVALID;
264
265
266
267
268
}

struct read_info_ctx {
	struct apk_database *db;
	struct apk_package *pkg;
269
	int version;
270
271
272
	int has_install;
};

273
274
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
275
276
277
{
	switch (field) {
	case 'P':
278
		pkg->name = apk_db_get_name(db, value);
Timo Teräs's avatar
Timo Teräs committed
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
		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
296
		apk_blob_pull_csum(&value, &pkg->csum);
Timo Teräs's avatar
Timo Teräs committed
297
298
		break;
	case 'S':
299
		pkg->size = apk_blob_pull_uint(&value, 10);
Timo Teräs's avatar
Timo Teräs committed
300
301
		break;
	case 'I':
302
		pkg->installed_size = apk_blob_pull_uint(&value, 10);
Timo Teräs's avatar
Timo Teräs committed
303
		break;
304
305
	default:
		return -1;
Timo Teräs's avatar
Timo Teräs committed
306
	}
307
308
	if (APK_BLOB_IS_NULL(value))
		return -1;
Timo Teräs's avatar
Timo Teräs committed
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
	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;

333
	if (!apk_blob_split(line, APK_BLOB_STR(" = "), &l, &r))
Timo Teräs's avatar
Timo Teräs committed
334
335
336
337
		return 0;

	for (i = 0; i < ARRAY_SIZE(fields); i++) {
		if (strncmp(fields[i].str, l.ptr, l.len) == 0) {
338
			apk_pkg_add_info(ri->db, ri->pkg, fields[i].field, r);
Timo Teräs's avatar
Timo Teräs committed
339
340
341
342
343
344
345
			break;
		}
	}

	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
366
367
	/* Meta info and scripts */
	if (ae->name[0] == '.') {
		/* APK 2.0 format */
368
		ri->version = 2;
Timo Teräs's avatar
Timo Teräs committed
369
		if (strcmp(ae->name, ".PKGINFO") == 0) {
370
			apk_blob_t blob = apk_blob_from_istream(is, ae->size);
Timo Teräs's avatar
Timo Teräs committed
371
372
			apk_blob_for_each_segment(blob, "\n", read_info_line, ctx);
			free(blob.ptr);
373
374
375
376
377
			return 0;
		}
		if (strcmp(ae->name, ".INSTALL") == 0) {
			apk_warning("Package '%s-%s' contains deprecated .INSTALL",
				    pkg->name->name, pkg->version);
378
			return 0;
Timo Teräs's avatar
Timo Teräs committed
379
380
381
		}
	} else if (strncmp(ae->name, "var/db/apk/", 11) == 0) {
		/* APK 1.0 format */
382
		ri->version = 1;
Timo Teräs's avatar
Timo Teräs committed
383
384
		if (!S_ISREG(ae->mode))
			return 0;
385

Timo Teräs's avatar
Timo Teräs committed
386
387
388
		slash = strchr(&ae->name[11], '/');
		if (slash == NULL)
			return 0;
389

Timo Teräs's avatar
Timo Teräs committed
390
391
392
		if (apk_pkg_parse_name(APK_BLOB_PTR_PTR(&ae->name[11], slash-1),
				       &name, &version) < 0)
			return -1;
393

394
395
		if (pkg->name == NULL)
			pkg->name = apk_db_get_name(db, name);
Timo Teräs's avatar
Timo Teräs committed
396
397
398
399
400
		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) {
401
				apk_blob_t blob = apk_blob_from_istream(is, ae->size);
402
403
				apk_pkg_add_info(ri->db, ri->pkg, fields[i].field,
						 trim(blob));
Timo Teräs's avatar
Timo Teräs committed
404
405
406
				free(blob.ptr);
				break;
			}
407
		}
Timo Teräs's avatar
Timo Teräs committed
408
409
410
		if (apk_script_type(slash+1) == APK_SCRIPT_POST_INSTALL ||
		    apk_script_type(slash+1) == APK_SCRIPT_PRE_INSTALL)
			ri->has_install = 1;
411
412
	} else if (ri->version == 2) {
		/* All metdata of version 2.x package handled */
413
		return 0;
Timo Teräs's avatar
Timo Teräs committed
414
	} else {
415
416
		/* Version 1.x packages do not contain installed size
		 * in metadata, so we calculate it here */
417
		pkg->installed_size += apk_calc_installed_size(ae->size);
Timo Teräs's avatar
Timo Teräs committed
418
	}
419
420
421
422

	return 0;
}

423
424
425
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:
		EVP_DigestInit_ex(mdctx, EVP_md5(), NULL);
		break;
	case APK_MPART_END:
Timo Teräs's avatar
Timo Teräs committed
432
433
		ri->pkg->csum.type = EVP_MD_CTX_size(mdctx);
		EVP_DigestFinal_ex(mdctx, ri->pkg->csum.data, NULL);
434
435
436
437
438
		break;
	}
	return 0;
}

439
440
441
struct apk_package *apk_pkg_read(struct apk_database *db, const char *file)
{
	struct read_info_ctx ctx;
442
	struct apk_bstream *bs;
443
	struct apk_istream *tar;
444
445
446
447
	char realfile[PATH_MAX];

	if (realpath(file, realfile) < 0)
		return NULL;
448

449
	ctx.pkg = apk_pkg_new();
450
451
452
	if (ctx.pkg == NULL)
		return NULL;

453
	bs = apk_bstream_from_file(realfile);
Timo Teräs's avatar
Timo Teräs committed
454
	if (bs == NULL)
455
456
457
458
		goto err;

	ctx.db = db;
	ctx.has_install = 0;
459
460
461

	tar = apk_bstream_gunzip_mpart(bs, FALSE, apk_pkg_gzip_part, &ctx);
	if (apk_parse_tar(tar, read_info_entry, &ctx) < 0) {
Timo Teräs's avatar
Timo Teräs committed
462
		apk_error("File %s is not an APK archive", file);
463
		bs->close(bs, NULL);
464
465
		goto err;
	}
466
	bs->close(bs, &ctx.pkg->size);
467

Timo Teräs's avatar
Timo Teräs committed
468
469
	if (ctx.pkg->name == NULL) {
		apk_error("File %s is corrupted", file);
470
		goto err;
Timo Teräs's avatar
Timo Teräs committed
471
	}
472
473
474
475

	/* Add implicit busybox dependency if there is scripts */
	if (ctx.has_install) {
		struct apk_dependency dep = {
476
			.name = apk_db_get_name(db, APK_BLOB_STR("busybox")),
477
478
479
		};
		apk_deps_add(&ctx.pkg->depends, &dep);
	}
480
	ctx.pkg->filename = strdup(realfile);
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498

	return ctx.pkg;
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);

499
500
	if (pkg->depends)
		free(pkg->depends);
501
502
503
504
505
506
507
508
509
510
511
512
513
	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)
{
514
	if (list_hashed(&pkg->installed_pkgs_list))
515
516
		return APK_PKG_INSTALLED;
	return APK_PKG_NOT_INSTALLED;
517
518
}

519
520
521
void apk_pkg_set_state(struct apk_database *db, struct apk_package *pkg, int state)
{
	switch (state) {
522
	case APK_PKG_INSTALLED:
523
524
525
526
527
528
		if (!list_hashed(&pkg->installed_pkgs_list)) {
			db->installed.stats.packages++;
			list_add_tail(&pkg->installed_pkgs_list,
				      &db->installed.packages);
		}
		break;
529
	case APK_PKG_NOT_INSTALLED:
530
531
532
533
534
535
536
537
		if (list_hashed(&pkg->installed_pkgs_list)) {
			db->installed.stats.packages--;
			list_del(&pkg->installed_pkgs_list);
		}
		break;
	}
}

538
int apk_pkg_add_script(struct apk_package *pkg, struct apk_istream *is,
539
540
541
542
543
544
545
546
		       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;
547
	r = is->read(is, script->script, size);
548
549
550
551
552
553
554
555
556
	if (r < 0) {
		free(script);
		return r;
	}

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

557
int apk_pkg_run_script(struct apk_package *pkg, int root_fd,
558
559
		       unsigned int type)
{
560
561
562
563
	static const char * const environment[] = {
		"PATH=/usr/sbin:/usr/bin:/sbin:/bin",
		NULL
	};
564
565
566
567
568
569
	struct apk_script *script;
	struct hlist_node *c;
	int fd, status;
	pid_t pid;
	char fn[1024];

570
	fchdir(root_fd);
571
	hlist_for_each_entry(script, c, &pkg->scripts, script_list) {
Timo Teräs's avatar
Timo Teräs committed
572
573
		if (script->type != type &&
		    script->type != APK_SCRIPT_GENERIC)
574
575
576
577
578
			continue;

		snprintf(fn, sizeof(fn),
			"tmp/%s-%s.%s",
			pkg->name->name, pkg->version,
Timo Teräs's avatar
Timo Teräs committed
579
			apk_script_types[type]);
580
581
582
583
584
585
586
587
588
589
590
591
		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) {
592
593
			if (chroot(".") < 0) {
				apk_error("chroot: %s", strerror(errno));
Timo Teräs's avatar
Timo Teräs committed
594
595
596
597
			} else if (script->type == APK_SCRIPT_GENERIC) {
				execle(fn, "INSTALL", script_types2[type],
				       pkg->version, "", NULL, environment);
			} else {
Timo Teräs's avatar
Timo Teräs committed
598
				execle(fn, apk_script_types[type],
599
				       pkg->version, "", NULL, environment);
Timo Teräs's avatar
Timo Teräs committed
600
			}
601
602
603
			exit(1);
		}
		waitpid(pid, &status, 0);
Timo Teräs's avatar
Timo Teräs committed
604
		unlink(fn);
605
606
607
608
609
610
611
612
		if (WIFEXITED(status))
			return WEXITSTATUS(status);
		return -1;
	}

	return 0;
}

Timo Teräs's avatar
Timo Teräs committed
613
static int parse_index_line(void *ctx, apk_blob_t line)
614
{
Timo Teräs's avatar
Timo Teräs committed
615
	struct read_info_ctx *ri = (struct read_info_ctx *) ctx;
616

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

620
	apk_pkg_add_info(ri->db, ri->pkg, line.ptr[0], APK_BLOB_PTR_LEN(line.ptr+2, line.len-2));
621
622
623
624
625
	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
626
	struct read_info_ctx ctx;
627

628
	ctx.pkg = apk_pkg_new();
Timo Teräs's avatar
Timo Teräs committed
629
	if (ctx.pkg == NULL)
630
631
		return NULL;

Timo Teräs's avatar
Timo Teräs committed
632
	ctx.db = db;
633
	ctx.version = 0;
Timo Teräs's avatar
Timo Teräs committed
634
	ctx.has_install = 0;
635

Timo Teräs's avatar
Timo Teräs committed
636
637
638
639
	apk_blob_for_each_segment(blob, "\n", parse_index_line, &ctx);

	if (ctx.pkg->name == NULL) {
		apk_pkg_free(ctx.pkg);
640
641
		apk_error("Failed to parse index entry: %.*s",
			  blob.len, blob.ptr);
Timo Teräs's avatar
Timo Teräs committed
642
		ctx.pkg = NULL;
643
644
	}

Timo Teräs's avatar
Timo Teräs committed
645
	return ctx.pkg;
646
647
}

648
649
int apk_pkg_write_index_entry(struct apk_package *info,
			      struct apk_ostream *os)
650
{
651
	char buf[512];
652
653
654
655
	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
656
	apk_blob_push_csum(&bbuf, &info->csum);
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
	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)
674
		return -1;
675
676

	if (info->depends != NULL) {
677
678
679
680
681
682
683
		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;
684
685
	}

686
	return 0;
687
}
688

689
690
691
692
693
int apk_pkg_version_compare(struct apk_package *a, struct apk_package *b)
{
	return apk_version_compare(a->version, b->version);
}

694
695
696
697
698
699
700
701
702
703
704
705
706
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++;
707
	}
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
	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,
	};
}