From 57154db85d459a869bbfb7a3bd2bcb16699a0a7a Mon Sep 17 00:00:00 2001
From: Timo Teras <timo.teras@iki.fi>
Date: Thu, 6 Nov 2008 16:58:58 +0200
Subject: db: parse new style .PKGINFO

---
 src/apk_blob.h |   5 +-
 src/blob.c     |  19 ++++++-
 src/create.c   |   5 ++
 src/database.c |   4 +-
 src/package.c  | 154 ++++++++++++++++++++++++++++++++++++++-------------------
 5 files changed, 131 insertions(+), 56 deletions(-)

diff --git a/src/apk_blob.h b/src/apk_blob.h
index 033d331..a91f6b5 100644
--- a/src/apk_blob.h
+++ b/src/apk_blob.h
@@ -27,10 +27,13 @@ typedef struct apk_blob apk_blob_t;
 #define APK_BLOB_PTR_PTR(beg,end)	APK_BLOB_PTR_LEN((beg),(end)-(beg)+1)
 
 char *apk_blob_cstr(apk_blob_t str);
-int apk_blob_splitstr(apk_blob_t blob, char *split, apk_blob_t *l, apk_blob_t *r);
+int apk_blob_splitstr(apk_blob_t blob, const char *split, apk_blob_t *l, apk_blob_t *r);
 int apk_blob_rsplit(apk_blob_t blob, char split, apk_blob_t *l, apk_blob_t *r);
 unsigned apk_blob_uint(apk_blob_t blob, int base);
 
+int apk_blob_for_each_segment(apk_blob_t blob, const char *split,
+			      int (*cb)(void *ctx, apk_blob_t blob), void *ctx);
+
 int apk_hexdump_parse(apk_blob_t to, apk_blob_t from);
 int apk_hexdump_format(int tolen, char *to, apk_blob_t from);
 
diff --git a/src/blob.c b/src/blob.c
index f7c14b5..f166a7a 100644
--- a/src/blob.c
+++ b/src/blob.c
@@ -47,7 +47,7 @@ int apk_blob_rsplit(apk_blob_t blob, char split, apk_blob_t *l, apk_blob_t *r)
 	return 1;
 }
 
-int apk_blob_splitstr(apk_blob_t blob, char *split, apk_blob_t *l, apk_blob_t *r)
+int apk_blob_splitstr(apk_blob_t blob, const char *split, apk_blob_t *l, apk_blob_t *r)
 {
 	int splitlen = strlen(split);
 	char *pos = blob.ptr, *end = blob.ptr + blob.len - splitlen + 1;
@@ -71,6 +71,23 @@ int apk_blob_splitstr(apk_blob_t blob, char *split, apk_blob_t *l, apk_blob_t *r
 	}
 }
 
+int apk_blob_for_each_segment(apk_blob_t blob, const char *split,
+			      int (*cb)(void *ctx, apk_blob_t blob), void *ctx)
+{
+	apk_blob_t l, r;
+	int rc;
+
+	r = blob;
+	while (apk_blob_splitstr(r, split, &l, &r)) {
+		rc = cb(ctx, l);
+		if (rc != 0)
+			return rc;
+	}
+	if (r.len > 0)
+		return cb(ctx, r);
+	return 0;
+}
+
 static int dx(int c)
 {
 	if (c >= '0' && c <= '9')
diff --git a/src/create.c b/src/create.c
index a033170..c1dbea3 100644
--- a/src/create.c
+++ b/src/create.c
@@ -15,6 +15,11 @@
 
 static int create_main(int argc, char **argv)
 {
+	if (strcmp(apk_root, "/") == 0) {
+		apk_error("Will not recreate system root.");
+		return 1;
+	}
+
 	return apk_db_create(apk_root);
 }
 
diff --git a/src/database.c b/src/database.c
index eed584e..b43b360 100644
--- a/src/database.c
+++ b/src/database.c
@@ -476,9 +476,9 @@ int apk_db_open(struct apk_database *db, const char *root)
 			free(db->root);
 			return -1;
 		}
-
-		apk_db_add_repository(db, apk_repository);
 	}
+	if (apk_repository != NULL)
+		apk_db_add_repository(db, apk_repository);
 
 	return apk_db_read_config(db);
 }
diff --git a/src/package.c b/src/package.c
index 2f14c80..bc517ad 100644
--- a/src/package.c
+++ b/src/package.c
@@ -106,6 +106,7 @@ int apk_deps_add(struct apk_dependency_array **depends,
 	*apk_dependency_array_add(depends) = *dep;
 	return 0;
 }
+
 void apk_deps_parse(struct apk_database *db,
 		    struct apk_dependency_array **depends,
 		    apk_blob_t blob)
@@ -171,6 +172,79 @@ struct read_info_ctx {
 	int has_install;
 };
 
+static int add_info(struct apk_database *db, struct apk_package *pkg,
+		    char field, apk_blob_t value)
+{
+	char *str;
+
+	switch (field) {
+	case 'P':
+		str = apk_blob_cstr(value);
+		pkg->name = apk_db_get_name(db, str);
+		free(str);
+		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':
+		apk_hexdump_parse(APK_BLOB_BUF(pkg->csum), value);
+		break;
+	case 'S':
+		pkg->size = apk_blob_uint(value, 10);
+		break;
+	case 'I':
+		pkg->installed_size = apk_blob_uint(value, 10);
+		break;
+	}
+	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;
+
+	if (!apk_blob_splitstr(line, " = ", &l, &r))
+		return 0;
+
+	for (i = 0; i < ARRAY_SIZE(fields); i++) {
+		if (strncmp(fields[i].str, l.ptr, l.len) == 0) {
+			add_info(ri->db, ri->pkg, fields[i].field, r);
+			break;
+		}
+	}
+
+	return 0;
+}
+
 static int read_info_entry(struct apk_archive_entry *ae, void *ctx)
 {
 	struct read_info_ctx *ri = (struct read_info_ctx *) ctx;
@@ -180,6 +254,13 @@ static int read_info_entry(struct apk_archive_entry *ae, void *ctx)
 	apk_blob_t name, version;
 	char *slash, *str;
 
+	if (strcmp(ae->name, ".PKGINFO") == 0) {
+		apk_blob_t blob = apk_archive_entry_read(ae);
+		apk_blob_for_each_segment(blob, "\n", read_info_line, ctx);
+		free(blob.ptr);
+		return 0;
+	}
+
 	if (strncmp(ae->name, "var/db/apk/", 11) != 0) {
 		pkg->installed_size += (ae->size + bsize - 1) & ~(bsize - 1);
 		return 0;
@@ -248,14 +329,17 @@ struct apk_package *apk_pkg_read(struct apk_database *db, const char *file)
 	ctx.db = db;
 	ctx.pkg->size = st.st_size;
 	ctx.has_install = 0;
-	if (apk_parse_tar_gz(fd, read_info_entry, &ctx) != 0) {
+	if (apk_parse_tar_gz(fd, read_info_entry, &ctx) < 0) {
+		apk_error("File %s is not an APK archive", file);
 		pthread_join(tid, NULL);
 		goto err;
 	}
 	pthread_join(tid, NULL);
 
-	if (ctx.pkg->name == NULL)
+	if (ctx.pkg->name == NULL) {
+		apk_error("File %s is corrupted", file);
 		goto err;
+	}
 
 	close(fd);
 
@@ -372,71 +456,37 @@ int apk_pkg_run_script(struct apk_package *pkg, int root_fd,
 	return 0;
 }
 
-static int parse_index_line(struct apk_database *db, struct apk_package *pkg,
-			    apk_blob_t blob)
+static int parse_index_line(void *ctx, apk_blob_t line)
 {
-	apk_blob_t d;
-	char *str;
+	struct read_info_ctx *ri = (struct read_info_ctx *) ctx;
 
-	if (blob.len < 2 || blob.ptr[1] != ':')
-		return -1;
+	if (line.len < 3 || line.ptr[1] != ':')
+		return 0;
 
-	d = APK_BLOB_PTR_LEN(blob.ptr+2, blob.len-2);
-	switch (blob.ptr[0]) {
-	case 'P':
-		str = apk_blob_cstr(d);
-		pkg->name = apk_db_get_name(db, str);
-		free(str);
-		break;
-	case 'V':
-		pkg->version = apk_blob_cstr(d);
-		break;
-	case 'T':
-		pkg->description = apk_blob_cstr(d);
-		break;
-	case 'U':
-		pkg->url = apk_blob_cstr(d);
-		break;
-	case 'L':
-		pkg->license = apk_blob_cstr(d);
-		break;
-	case 'D':
-		apk_deps_parse(db, &pkg->depends, d);
-		break;
-	case 'C':
-		apk_hexdump_parse(APK_BLOB_BUF(pkg->csum), d);
-		break;
-	case 'S':
-		pkg->size = apk_blob_uint(d, 10);
-		break;
-	case 'I':
-		pkg->installed_size = apk_blob_uint(d, 10);
-		break;
-	}
+	add_info(ri->db, ri->pkg, line.ptr[0], APK_BLOB_PTR_LEN(line.ptr+2, line.len-2));
 	return 0;
 }
 
 struct apk_package *apk_pkg_parse_index_entry(struct apk_database *db, apk_blob_t blob)
 {
-	struct apk_package *pkg;
-	apk_blob_t l, r;
+	struct read_info_ctx ctx;
 
-	pkg = calloc(1, sizeof(struct apk_package));
-	if (pkg == NULL)
+	ctx.pkg = calloc(1, sizeof(struct apk_package));
+	if (ctx.pkg == NULL)
 		return NULL;
 
-	r = blob;
-	while (apk_blob_splitstr(r, "\n", &l, &r))
-		parse_index_line(db, pkg, l);
-	parse_index_line(db, pkg, r);
+	ctx.db = db;
+	ctx.has_install = 0;
 
-	if (pkg->name == NULL) {
-		apk_pkg_free(pkg);
+	apk_blob_for_each_segment(blob, "\n", parse_index_line, &ctx);
+
+	if (ctx.pkg->name == NULL) {
+		apk_pkg_free(ctx.pkg);
 		printf("%.*s\n", blob.len, blob.ptr);
-		pkg = NULL;
+		ctx.pkg = NULL;
 	}
 
-	return pkg;
+	return ctx.pkg;
 }
 
 apk_blob_t apk_pkg_format_index_entry(struct apk_package *info, int size,
-- 
cgit v1.2.3-70-g09d2