Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Record shared library usage in pkgdb and add tools for querying same
Matthew Seaman committed 14 years ago
commit d8fa8b54cd7f74b3a848259ce632430b364cbfef
parent d860a8b
20 files changed +360 -14
modified libpkg/backup.c
@@ -42,7 +42,7 @@ pkgdb_dump(struct pkgdb *db, char *dest)
	int ret = EPKG_OK;
	int query_flags = PKG_LOAD_DEPS | PKG_LOAD_FILES | PKG_LOAD_CATEGORIES |
	    PKG_LOAD_DIRS | PKG_LOAD_SCRIPTS | PKG_LOAD_OPTIONS |
-
	    PKG_LOAD_MTREE | PKG_LOAD_LICENSES;
+
	    PKG_LOAD_MTREE | PKG_LOAD_LICENSES | PKG_LOAD_SHLIBS;

	packing_init(&pack, dest ? dest : "./pkgdump", TXZ);

modified libpkg/pkg.c
@@ -77,6 +77,7 @@ pkg_new(struct pkg **pkg, pkg_t type)
	STAILQ_INIT(&(*pkg)->options);
	STAILQ_INIT(&(*pkg)->users);
	STAILQ_INIT(&(*pkg)->groups);
+
	STAILQ_INIT(&(*pkg)->shlibs);

	(*pkg)->automatic = false;
	(*pkg)->type = type;
@@ -112,6 +113,7 @@ pkg_reset(struct pkg *pkg, pkg_t type)
	pkg_list_free(pkg, PKG_OPTIONS);
	pkg_list_free(pkg, PKG_USERS);
	pkg_list_free(pkg, PKG_GROUPS);
+
	pkg_list_free(pkg, PKG_SHLIBS);

	pkg->rowid = 0;
	pkg->type = type;
@@ -136,6 +138,7 @@ pkg_free(struct pkg *pkg)
	pkg_list_free(pkg, PKG_OPTIONS);
	pkg_list_free(pkg, PKG_USERS);
	pkg_list_free(pkg, PKG_GROUPS);
+
	pkg_list_free(pkg, PKG_SHLIBS);

	free(pkg);
}
@@ -419,6 +422,15 @@ pkg_options(struct pkg *pkg, struct pkg_option **o)
}

int
+

+
pkg_shlibs(struct pkg *pkg, struct pkg_shlib **s)
+
{
+
	assert(pkg != NULL);
+

+
	PKG_LIST_NEXT(&pkg->shlibs, *s);
+
}
+

+
int
pkg_addlicense(struct pkg *pkg, const char *name)
{
	struct pkg_license *l = NULL;
@@ -801,6 +813,30 @@ pkg_addoption(struct pkg *pkg, const char *key, const char *value)
}

int
+
pkg_addshlib(struct pkg *pkg, const char *name)
+
{
+
	struct pkg_shlib *s = NULL;
+

+
	assert(pkg != NULL);
+
	assert(name != NULL && name[0] != '\0');
+

+
	while (pkg_shlibs(pkg, &s) == EPKG_OK) {
+
		if (strcmp(name, pkg_shlib_name(s)) == 0) {
+
			pkg_emit_error("duplicate shared library listing: %s, ignoring", name);
+
			return (EPKG_OK);
+
		}
+
	}
+

+
	pkg_shlib_new(&s);
+

+
	sbuf_set(&s->name, name);
+

+
	STAILQ_INSERT_TAIL(&pkg->shlibs, s, next);
+

+
	return (EPKG_OK);
+
}
+

+
int
pkg_list_is_empty(struct pkg *pkg, pkg_list list) {
	switch (list) {
		case PKG_DEPS:
@@ -823,6 +859,8 @@ pkg_list_is_empty(struct pkg *pkg, pkg_list list) {
			return (STAILQ_EMPTY(&pkg->groups));
		case PKG_SCRIPTS:
			return (STAILQ_EMPTY(&pkg->scripts));
+
		case PKG_SHLIBS:
+
			return (STAILQ_EMPTY(&pkg->shlibs));
	}
	
	return (0);
@@ -839,6 +877,7 @@ pkg_list_free(struct pkg *pkg, pkg_list list) {
	struct pkg_user *u;
	struct pkg_group *g;
	struct pkg_script *s;
+
	struct pkg_shlib *sl;

	switch (list) {
		case PKG_DEPS:
@@ -881,6 +920,10 @@ pkg_list_free(struct pkg *pkg, pkg_list list) {
			LIST_FREE(&pkg->scripts, s, pkg_script_free);
			pkg->flags &= ~PKG_LOAD_SCRIPTS;
			break;
+
		case PKG_SHLIBS:
+
			LIST_FREE(&pkg->shlibs, sl, pkg_shlib_free);
+
			pkg->flags &= ~PKG_LOAD_SHLIBS;
+
			break;
	}
}

modified libpkg/pkg.h
@@ -47,6 +47,7 @@ struct pkg_option;
struct pkg_license;
struct pkg_user;
struct pkg_group;
+
struct pkg_shlib;

struct pkgdb;
struct pkgdb_it;
@@ -198,7 +199,8 @@ typedef enum {
	PKG_DIRS,
	PKG_USERS,
	PKG_GROUPS,
-
	PKG_SCRIPTS
+
	PKG_SCRIPTS,
+
	PKG_SHLIBS
} pkg_list;

/**
@@ -390,6 +392,13 @@ int pkg_scripts(struct pkg *, struct pkg_script **script);
int pkg_options(struct pkg *, struct pkg_option **option);

/**
+
 * Iterates over the shared libraries used by the package.
+
 * @param Must be set to NULL for the first call.
+
 * @return An error code
+
 */
+
int pkg_shlibs(struct pkg *pkg, struct pkg_shlib **shlib);
+

+
/**
 * @todo Document
 */
int pkg_analyse_files(struct pkgdb *, struct pkg *);
@@ -509,6 +518,12 @@ int pkg_appendscript(struct pkg *pkg, const char *cmd, pkg_script_t type);
int pkg_addoption(struct pkg *pkg, const char *name, const char *value);

/**
+
 * Add a shared library
+
 * @return An error code.
+
 */
+
int pkg_addshlib(struct pkg *pkg, const char *name);
+

+
/**
 * Parse a manifest and set the attributes of pkg accordingly.
 * @param buf An NULL-terminated buffer containing the manifest data.
 * @return An error code.
@@ -550,6 +565,9 @@ pkg_script_t pkg_script_type(struct pkg_script *);
const char *pkg_option_opt(struct pkg_option *);
const char *pkg_option_value(struct pkg_option *);

+
/* pkg_shlib */
+
const char *pkg_shlib_name(struct pkg_shlib *);
+

/**
 * Create a repository database.
 * @param path The path where the repository live.
@@ -622,6 +640,8 @@ struct pkgdb_it *pkgdb_query_autoremove(struct pkgdb *db);
 */
struct pkgdb_it * pkgdb_query_which(struct pkgdb *db, const char *path);

+
struct pkgdb_it * pkgdb_query_shlib(struct pkgdb *db, const char *shlib);
+

#define PKG_LOAD_BASIC 0
#define PKG_LOAD_DEPS (1<<0)
#define PKG_LOAD_RDEPS (1<<1)
@@ -634,6 +654,7 @@ struct pkgdb_it * pkgdb_query_which(struct pkgdb *db, const char *path);
#define PKG_LOAD_LICENSES (1<<8)
#define PKG_LOAD_USERS (1<<9)
#define PKG_LOAD_GROUPS (1<<10)
+
#define PKG_LOAD_SHLIBS (1<<11)

/**
 * Get the next pkg.
modified libpkg/pkg_attributes.c
@@ -357,3 +357,32 @@ pkg_option_value(struct pkg_option *option)
{
	return (sbuf_get(option->value));
}
+

+
/*
+
 * Shared Libraries
+
 */
+
int
+
pkg_shlib_new(struct pkg_shlib **sl)
+
{
+
	if (( *sl = calloc(1, sizeof(struct pkg_shlib))) == NULL)
+
		return (EPKG_FATAL);
+

+
	return (EPKG_OK);
+
}
+

+
const char *
+
pkg_shlib_name(struct pkg_shlib *sl)
+
{
+
	return (sbuf_get(sl->name));
+
}
+

+
void
+
pkg_shlib_free(struct pkg_shlib *sl)
+
{
+

+
	if (sl == NULL)
+
		return;
+

+
	sbuf_free(sl->name);
+
	free(sl);
+
}
modified libpkg/pkg_create.c
@@ -184,7 +184,7 @@ pkg_create_installed(const char *outdir, pkg_formats format, const char *rootdir
	struct packing *pkg_archive;
	int required_flags = PKG_LOAD_DEPS | PKG_LOAD_FILES | PKG_LOAD_CATEGORIES |
	    PKG_LOAD_DIRS | PKG_LOAD_SCRIPTS | PKG_LOAD_OPTIONS |
-
	    PKG_LOAD_MTREE | PKG_LOAD_LICENSES;
+
	    PKG_LOAD_MTREE | PKG_LOAD_LICENSES ;

	assert(pkg->type == PKG_INSTALLED);

modified libpkg/pkg_manifest.c
@@ -52,6 +52,7 @@
#define PKG_USERS -9
#define PKG_GROUPS -10
#define PKG_DIRECTORIES -11
+
#define PKG_SHLIBS -12

static int pkg_set_from_node(struct pkg *, yaml_node_t *, yaml_document_t *, int);
static int pkg_set_flatsize_from_node(struct pkg *, yaml_node_t *, yaml_document_t *, int);
@@ -93,6 +94,7 @@ static struct manifest_key {
	{ "users", PKG_USERS, YAML_MAPPING_NODE, parse_mapping},
	{ "groups", PKG_GROUPS, YAML_SEQUENCE_NODE, parse_sequence},
	{ "groups", PKG_GROUPS, YAML_MAPPING_NODE, parse_mapping}, /* compatibility with old format */
+
	{ "shlibs", PKG_SHLIBS, YAML_SEQUENCE_NODE, parse_sequence},
	{ NULL, -99, -99, NULL}
};

@@ -264,6 +266,12 @@ parse_sequence(struct pkg * pkg, yaml_node_t *node, yaml_document_t *doc, int at
					parse_mapping(pkg, val, doc, attr);
				else
					pkg_emit_error("Skipping malformed dirs");
+
				break;
+
			case PKG_SHLIBS:
+
				if (val->type != YAML_SCALAR_NODE || val->data.scalar.length <= 0)
+
					pkg_emit_error("Skipping malformed shared library");
+
				else 
+
					pkg_addshlib(pkg, val->data.scalar.value);
		}
		++item;
	}
@@ -655,6 +663,7 @@ pkg_emit_manifest(struct pkg *pkg, char **dest)
	struct pkg_license *license = NULL;
	struct pkg_user *user = NULL;
	struct pkg_group *group = NULL;
+
	struct pkg_shlib *shlib = NULL;
	struct sbuf *tmpsbuf = NULL;
	int rc = EPKG_OK;
	int mapping;
@@ -763,6 +772,10 @@ pkg_emit_manifest(struct pkg *pkg, char **dest)
		manifest_append_kv(groups, pkg_group_name(group), group->gidstr);
	}*/

+
	seq = -1;
+
	while (pkg_shlibs(pkg, &shlib) == EPKG_OK)
+
		manifest_append_seqval(&doc, mapping, &seq, "shlibs", pkg_shlib_name(shlib));
+
 
	map = -1;
	while (pkg_options(pkg, &option) == EPKG_OK) {
		if (map == -1)
modified libpkg/pkg_repo.c
@@ -227,6 +227,7 @@ pkg_create_repo(char *path, void (progress)(struct pkg *pkg, void *data), void *
	struct pkg_category *category = NULL;
	struct pkg_license *license = NULL;
	struct pkg_option *option = NULL;
+
	struct pkg_shlib *shlib = NULL;
	struct sbuf *manifest = sbuf_new_auto();
	char *ext = NULL;

@@ -238,6 +239,8 @@ pkg_create_repo(char *path, void (progress)(struct pkg *pkg, void *data), void *
	sqlite3_stmt *stmt_cat1 = NULL;
	sqlite3_stmt *stmt_cat2 = NULL;
	sqlite3_stmt *stmt_opts = NULL;
+
	sqlite3_stmt *stmt_shlib1 = NULL;
+
	sqlite3_stmt *stmt_shlib2 = NULL;

	int64_t package_id;
	char *errmsg = NULL;
@@ -299,6 +302,15 @@ pkg_create_repo(char *path, void (progress)(struct pkg *pkg, void *data), void *
			"value TEXT,"
			"UNIQUE (package_id, option)"
		");"
+
	  	"CREATE TABLE shlibs ("
+
			"id INTEGER PRIMARY KEY,"
+
			"name TEXT NOT NULL UNIQUE "
+
		");"
+
		"CREATE TABLE pkg_shlibs ("
+
			"package_id INTEGER REFERENCES packages(id), "
+
			"shlib_id INTEGER REFERENCES shlibs(id), "
+
			"UNIQUE(package_id, shlib_id)"
+
		");"
		"PRAGMA user_version=2;"
		;
	const char pkgsql[] = ""
@@ -318,6 +330,9 @@ pkg_create_repo(char *path, void (progress)(struct pkg *pkg, void *data), void *
		"VALUES (?1, (SELECT id FROM categories WHERE name = ?2));";
	const char addoption[] = "INSERT OR ROLLBACK INTO options (option, value, package_id) "
		"VALUES (?1, ?2, ?3);";
+
	const char shlibsql[] = "INSERT OR IGNORE INTO shlibs(name) VALUES(?1);";
+
	const char addshlibsql[] = "INSERT OR ROLLBACK INTO pkg_shlibs(package_id, shlib_id) "
+
		"VALUES (?1, (SELECT id FROM shlibs WHERE name = ?2))";

	if (!is_dir(path)) {
		pkg_emit_error("%s is not a directory", path);
@@ -401,6 +416,18 @@ pkg_create_repo(char *path, void (progress)(struct pkg *pkg, void *data), void *
		goto cleanup;
	}

+
	if (sqlite3_prepare_v2(sqlite, shlibsql, -1, &stmt_shlib1, NULL) != SQLITE_OK) {
+
		ERROR_SQLITE(sqlite);
+
		retcode = EPKG_FATAL;
+
		goto cleanup;
+
	}
+

+
	if (sqlite3_prepare_v2(sqlite, addshlibsql, -1, &stmt_shlib2, NULL) != SQLITE_OK) {
+
		ERROR_SQLITE(sqlite);
+
		retcode = EPKG_FATAL;
+
		goto cleanup;
+
	}
+

	while ((ent = fts_read(fts)) != NULL) {
		const char *name, *version, *origin, *comment, *desc;
		const char *arch, *osversion, *maintainer, *www, *prefix;
@@ -540,6 +567,27 @@ pkg_create_repo(char *path, void (progress)(struct pkg *pkg, void *data), void *
			}
			sqlite3_reset(stmt_opts);
		}
+

+
		shlib = NULL;
+
		while (pkg_shlibs(pkg, &shlib) == EPKG_OK) {
+
			sqlite3_bind_text(stmt_shlib1, 1, pkg_shlib_name(shlib), -1, SQLITE_STATIC);
+
			sqlite3_bind_int64(stmt_shlib2, 1, package_id);
+
			sqlite3_bind_text(stmt_shlib2, 2, pkg_shlib_name(shlib), -1, SQLITE_STATIC);
+

+
			if (sqlite3_step(stmt_shlib1) != SQLITE_DONE) {
+
				ERROR_SQLITE(sqlite);
+
				retcode = EPKG_FATAL;
+
				goto cleanup;
+
			}
+

+
			if (sqlite3_step(stmt_shlib2) != SQLITE_DONE) {
+
				ERROR_SQLITE(sqlite);
+
				retcode = EPKG_FATAL;
+
				goto cleanup;
+
			}
+
			sqlite3_reset(stmt_shlib1);
+
			sqlite3_reset(stmt_shlib2);
+
		}
	}

	if (sqlite3_exec(sqlite, "COMMIT;", NULL, NULL, &errmsg) != SQLITE_OK) {
@@ -575,6 +623,12 @@ pkg_create_repo(char *path, void (progress)(struct pkg *pkg, void *data), void *
	if (stmt_opts != NULL)
		sqlite3_finalize(stmt_opts);

+
	if (stmt_shlib1 != NULL)
+
		sqlite3_finalize(stmt_shlib1);
+

+
	if (stmt_shlib2 != NULL)
+
		sqlite3_finalize(stmt_shlib2);
+

	if (sqlite != NULL)
		sqlite3_close(sqlite);

modified libpkg/pkgdb.c
@@ -49,7 +49,7 @@
#include "private/utils.h"

#include "private/db_upgrades.h"
-
#define DBVERSION 8
+
#define DBVERSION 9

#define sql_clean_stmt(stmt) do { \
		if (stmt != NULL) \
@@ -444,8 +444,19 @@ pkgdb_init(sqlite3 *sdb)
			" ON UPDATE RESTRICT,"
		"UNIQUE(package_id, group_id)"
	");"
+
	"CREATE TABLE shlibs ("
+
	        "id INTEGER PRIMARY KEY,"
+
	        "name TEXT NOT NULL UNIQUE"
+
	");"
+
	"CREATE TABLE pkg_shlibs ("
+
	        "package_id INTEGER REFERENCES packages(id) ON DELETE CASCADE"
+
	                " ON UPDATE CASCADE,"
+
	        "shlib_id INTEGER REFERENCES shlibs(id) ON DELETE RESTRICT"
+
	  		" ON UPDATE RESTRICT,"
+
		"PRIMARY KEY (package_id, shlib_id)"
+
	");"
	"CREATE INDEX deporigini on deps(origin);"
-
	"PRAGMA user_version = 8;"
+
	"PRAGMA user_version = 9;"
	"COMMIT;"
	;

@@ -711,6 +722,10 @@ pkgdb_it_next(struct pkgdb_it *it, struct pkg **pkg_p, int flags)
			if ((ret = pkgdb_load_group(it->db, pkg)) != EPKG_OK)
				return (ret);

+
		if (flags & PKG_LOAD_SHLIBS)
+
			if ((ret = pkgdb_load_shlib(it->db, pkg)) != EPKG_OK)
+
				return (ret);
+

		return (EPKG_OK);
	case SQLITE_DONE:
		return (EPKG_END);
@@ -850,6 +865,31 @@ pkgdb_query_which(struct pkgdb *db, const char *path)
	return (pkgdb_it_new(db, stmt, PKG_INSTALLED));
}

+
struct pkgdb_it *
+
pkgdb_query_shlib(struct pkgdb *db, const char *shlib)
+
{
+
	sqlite3_stmt *stmt;
+
	const char sql[] = ""
+
		"SELECT p.id, p.origin, p.name, p.version, p.comment, p.desc, "
+
			"p.message, p.arch, p.osversion, p.maintainer, p.www, "
+
			"p.prefix, p.flatsize "
+
			"FROM packages AS p, pkg_shlibs AS ps, shlibs AS s "
+
			"WHERE p.id = ps.package_id "
+
	  			"AND ps.shlib_id = s.id "
+
				"AND s.name = ?1;";
+

+
	assert(db != NULL);
+

+
	if (sqlite3_prepare_v2(db->sqlite, sql, -1, &stmt, NULL) != SQLITE_OK) {
+
		ERROR_SQLITE(db->sqlite);
+
		return (NULL);
+
	}
+

+
	sqlite3_bind_text(stmt, 1, shlib, -1, SQLITE_TRANSIENT);
+

+
	return (pkgdb_it_new(db, stmt, PKG_INSTALLED));
+
}
+

int
pkgdb_is_dir_used(struct pkgdb *db, const char *dir, int64_t *res)
{
@@ -1158,6 +1198,29 @@ pkgdb_load_group(struct pkgdb *db, struct pkg *pkg)
}

int
+
pkgdb_load_shlib(struct pkgdb *db, struct pkg *pkg)
+
{
+
	char sql[BUFSIZ];
+
	const char *reponame = NULL;
+
	const char *basesql = ""
+
			"SELECT name "
+
			"FROM '%s'.pkg_shlibs, '%s'.shlibs AS s "
+
			"WHERE package_id = ?1 "
+
			"AND shlib_id = s.id "
+
			"ORDER by name DESC";
+

+
	assert(db != NULL && pkg != NULL);
+

+
	if (pkg->type == PKG_REMOTE) {
+
		pkg_get(pkg, PKG_REPONAME, &reponame);
+
		snprintf(sql, sizeof(sql), basesql, reponame, reponame);
+
	} else
+
		snprintf(sql, sizeof(sql), basesql, "main", "main");
+

+
	return (load_val(db->sqlite, pkg, sql, PKG_LOAD_SHLIBS, pkg_addshlib, PKG_SHLIBS));
+
}
+

+
int
pkgdb_load_scripts(struct pkgdb *db, struct pkg *pkg)
{
	sqlite3_stmt *stmt = NULL;
@@ -1270,6 +1333,7 @@ pkgdb_register_pkg(struct pkgdb *db, struct pkg *pkg, int complete)
	struct pkg_license *license = NULL;
	struct pkg_user *user = NULL;
	struct pkg_group *group = NULL;
+
	struct pkg_shlib *shlib = NULL;
	struct pkgdb_it *it = NULL;

	sqlite3 *s;
@@ -1289,6 +1353,8 @@ pkgdb_register_pkg(struct pkgdb *db, struct pkg *pkg, int complete)
	sqlite3_stmt *stmt_users = NULL;
	sqlite3_stmt *stmt_groups = NULL;
	sqlite3_stmt *stmt_group = NULL;
+
	sqlite3_stmt *stmt_shlibs = NULL;
+
	sqlite3_stmt *stmt_shlib = NULL;
	sqlite3_stmt *stmt_upd_deps = NULL;

	int ret;
@@ -1336,6 +1402,10 @@ pkgdb_register_pkg(struct pkgdb *db, struct pkg *pkg, int complete)
	const char sql_groups[] = ""
		"INSERT OR ROLLBACK INTO pkg_groups(package_id, group_id) "
		"VALUES (?1, (SELECT id FROM groups WHERE name = ?2));";
+
	const char sql_shlib[] = "INSERT OR IGNORE INTO shlibs(name) VALUES(?1);";
+
	const char sql_shlibs[] = ""
+
		"INSERT OR ROLLBACK INTO pkg_shlibs(package_id, shlib_id) "
+
		"VALUES (?1, (SELECT id FROM shlibs WHERE name = ?2))";
	const char sql_deps_update[] = ""
		"UPDATE deps SET NAME=?1 , VERSION=?2 WHERE ORIGIN=?3;";

@@ -1694,6 +1764,41 @@ pkgdb_register_pkg(struct pkgdb *db, struct pkg *pkg, int complete)
		sqlite3_reset(stmt_option);
	}

+

+
	/*
+
	 * Insert shlibs
+
	 */
+

+
	if (sqlite3_prepare_v2(s, sql_shlib, -1, &stmt_shlib, NULL) != SQLITE_OK) {
+
		ERROR_SQLITE(s);
+
		goto cleanup;
+
	}
+
	if (sqlite3_prepare_v2(s, sql_shlibs, -1, &stmt_shlibs, NULL) != SQLITE_OK) {
+
		ERROR_SQLITE(s);
+
		goto cleanup;
+
	}
+

+
	while (pkg_shlibs(pkg, &shlib) == EPKG_OK) {
+
		sqlite3_bind_text(stmt_shlib, 1, pkg_shlib_name(shlib), -1, SQLITE_STATIC);
+
		sqlite3_bind_int64(stmt_shlibs, 1, package_id);
+
		sqlite3_bind_text(stmt_shlibs, 2, pkg_shlib_name(shlib), -1, SQLITE_STATIC);
+

+
		if ((ret = sqlite3_step(stmt_shlib)) != SQLITE_DONE) {
+
			if (ret == SQLITE_CONSTRAINT) {
+
				pkg_emit_error("sqlite: constraint violation on shlibs.name: %s",
+
						pkg_shlib_name(shlib));
+
			} else
+
				ERROR_SQLITE(s);
+
			goto cleanup;
+
		}
+
		if (( ret = sqlite3_step(stmt_shlibs)) != SQLITE_DONE) {
+
			ERROR_SQLITE(s);
+
			goto cleanup;
+
		}
+
		sqlite3_reset(stmt_shlib);
+
		sqlite3_reset(stmt_shlibs);
+
	}
+

	retcode = EPKG_OK;

	cleanup:
@@ -1714,6 +1819,8 @@ pkgdb_register_pkg(struct pkgdb *db, struct pkg *pkg, int complete)
	sql_clean_stmt(stmt_groups);
	sql_clean_stmt(stmt_users);
	sql_clean_stmt(stmt_upd_deps);
+
	sql_clean_stmt(stmt_shlib);
+
	sql_clean_stmt(stmt_shlibs);

	return (retcode);
}
@@ -1778,6 +1885,9 @@ pkgdb_unregister_pkg(struct pkgdb *db, const char *origin)
	if (sql_exec(db->sqlite, "DELETE FROM groups WHERE id NOT IN (SELECT DISTINCT group_id FROM pkg_groups);") != EPKG_OK)
		return (EPKG_FATAL);

+
	if (sql_exec(db->sqlite, "DELETE FROM shlibs WHERE id NOT IN (SELECT DISTINCT shlib_id FROM pkg_shlibs);") != EPKG_OK)
+
		return (EPKG_FATAL);
+

	return (EPKG_OK);
}

modified libpkg/private/db_upgrades.h
@@ -146,6 +146,19 @@ static struct db_upgrades {
	{8,
	"DROP TABLE conflicts;"
	},
+
	{9,
+
        "CREATE TABLE shlibs ("
+
                "id INTEGER PRIMARY KEY,"
+
                "name TEXT NOT NULL UNIQUE"
+
        ");"
+
        "CREATE TABLE pkg_shlibs ("
+
                "package_id INTEGER REFERENCES packages(id) ON DELETE CASCADE"
+
                        " ON UPDATE CASCADE,"
+
                "shlib_id INTEGER REFERENCES shlibs(id) ON DELETE RESTRICT"
+
                        " ON UPDATE RESTRICT,"
+
                "PRIMARY KEY (package_id, shlib_id)"
+
        ");"
+
	},

	/* Mark the end of the array */
	{ -1, NULL },
modified libpkg/private/pkg.h
@@ -70,6 +70,7 @@ struct pkg {
	STAILQ_HEAD(options, pkg_option) options;
	STAILQ_HEAD(users, pkg_user) users;
	STAILQ_HEAD(groups, pkg_group) groups;
+
	STAILQ_HEAD(shlibs, pkg_shlib) shlibs;
	int flags;
	int64_t rowid;
	lic_t licenselogic;
@@ -153,6 +154,11 @@ struct pkg_group {
	STAILQ_ENTRY(pkg_group) next;
};

+
struct pkg_shlib {
+
	struct sbuf *name;
+
	STAILQ_ENTRY(pkg_shlib) next;
+
};
+

/**
 * Remove and unregister the package.
 * @param pkg An installed package to delete
@@ -208,6 +214,9 @@ void pkg_group_free(struct pkg_group *);

int pkg_jobs_resolv(struct pkg_jobs *jobs);

+
int pkg_shlib_new(struct pkg_shlib **);
+
void pkg_shlib_free(struct pkg_shlib *);
+

struct packing;

int packing_init(struct packing **pack, const char *path, pkg_formats format);
@@ -243,6 +252,6 @@ int pkgdb_load_category(struct pkgdb *db, struct pkg *pkg);
int pkgdb_load_license(struct pkgdb *db, struct pkg *pkg);
int pkgdb_load_user(struct pkgdb *db, struct pkg *pkg);
int pkgdb_load_group(struct pkgdb *db, struct pkg *pkg);
-

+
int pkgdb_load_shlib(struct pkgdb *db, struct pkg *pkg);

#endif
modified pkg/Makefile
@@ -19,6 +19,7 @@ SRCS= add.c \
		upgrade.c \
		search.c \
		set.c \
+
		shlib.c \
		updating.c \
		utils.c \
		version.c \
@@ -58,6 +59,7 @@ MAN= pkg.8 \
	pkg-repo.8 \
	pkg-search.8 \
	pkg-set.8 \
+
	pkg-shlib.8 \
	pkg-update.8 \
	pkg-updating.8 \
	pkg-upgrade.8 \
modified pkg/create.c
@@ -57,7 +57,8 @@ pkg_create_matches(int argc, char **argv, match_t match, pkg_formats fmt, const
	char pkgpath[MAXPATHLEN];
	int query_flags = PKG_LOAD_DEPS | PKG_LOAD_FILES | PKG_LOAD_CATEGORIES |
	    PKG_LOAD_DIRS | PKG_LOAD_SCRIPTS | PKG_LOAD_OPTIONS |
-
	    PKG_LOAD_MTREE | PKG_LOAD_LICENSES | PKG_LOAD_USERS | PKG_LOAD_GROUPS;
+
	    PKG_LOAD_MTREE | PKG_LOAD_LICENSES | PKG_LOAD_USERS |
+
	    PKG_LOAD_GROUPS | PKG_LOAD_SHLIBS;

	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK) {
		pkgdb_close(db);
modified pkg/info.c
@@ -50,8 +50,8 @@ usage_info(void)
{
	fprintf(stderr, "usage: pkg info <pkg-name>\n");
	fprintf(stderr, "       pkg info -a\n");
-
	fprintf(stderr, "       pkg info [-egxXdrlsqOf] <pkg-name>\n");
-
	fprintf(stderr, "       pkg info [-drlsqfR] -F <pkg-file>\n\n");
+
	fprintf(stderr, "       pkg info [-egxXdrlBsqOf] <pkg-name>\n");
+
	fprintf(stderr, "       pkg info [-drlBsqfR] -F <pkg-file>\n\n");
	fprintf(stderr, "For more information see 'pkg help info'.\n");
}

@@ -81,7 +81,7 @@ exec_info(int argc, char **argv)
	int sign2 = 0;

	/* TODO: exclusive opts ? */
-
	while ((ch = getopt(argc, argv, "aegxXEdrlsqopOfF:R")) != -1) {
+
	while ((ch = getopt(argc, argv, "aegxXEdrlBsqopOfF:R")) != -1) {
		switch (ch) {
			case 'a':
				match = MATCH_ALL;
@@ -114,6 +114,10 @@ exec_info(int argc, char **argv)
				opt |= INFO_LIST_FILES;
				query_flags |= PKG_LOAD_FILES;
				break;
+
			case 'B':
+
				opt |= INFO_LIST_SHLIBS;
+
				query_flags |= PKG_LOAD_SHLIBS;
+
				break;
			case 's':
				opt |= INFO_SIZE;
				break;
@@ -136,7 +140,7 @@ exec_info(int argc, char **argv)
				break;
			case 'R':
				opt |= INFO_RAW;
-
				query_flags |= PKG_LOAD_FILES|PKG_LOAD_DIRS|PKG_LOAD_CATEGORIES|PKG_LOAD_LICENSES|PKG_LOAD_OPTIONS|PKG_LOAD_SCRIPTS|PKG_LOAD_USERS|PKG_LOAD_GROUPS|PKG_LOAD_DEPS;
+
				query_flags |= PKG_LOAD_FILES|PKG_LOAD_DIRS|PKG_LOAD_CATEGORIES|PKG_LOAD_LICENSES|PKG_LOAD_OPTIONS|PKG_LOAD_SCRIPTS|PKG_LOAD_USERS|PKG_LOAD_GROUPS|PKG_LOAD_DEPS|PKG_LOAD_SHLIBS;
				break;
			default:
				usage_info();
modified pkg/main.c
@@ -74,6 +74,7 @@ static struct commands {
	{ "set", "Modify local database informations", exec_set, usage_set},
	{ "register", "Registers a package into the local package database", exec_register, usage_register},
	{ "repo", "Creates a package database repository", exec_repo, usage_repo},
+
	{ "shlib", "Displays which package links against a specific shared library", exec_shlib, usage_shlib},
	{ "update", "Updates remote package repository databases", exec_update, usage_update},
	{ "updating", "Displays UPDATING information for a package", exec_updating, usage_updating},
	{ "upgrade", "Performs upgrades of package software distributions", exec_upgrade, usage_upgrade},
modified pkg/pkg-info.8
@@ -73,6 +73,9 @@ Display the list of packages which depend on
.It Fl l
Display all files installed by
.Ar <pkg-name> .
+
.It Fl B
+
Display all shared libraries used by
+
.Ar <pkg-name> .
.It Fl s
Display the total size of files installed by
.Ar <pkg-name>
modified pkg/pkg-query.8
@@ -106,6 +106,8 @@ for licenses
for users
.It Cm G
for groups
+
.It Cm B
+
for shared libraries
.El
.El
.Ss Multiline patterns:
@@ -148,6 +150,8 @@ Expands to the list of users needed by the matched package.
Expands to the list of groups needed by the matched package.
.It Cm \&%S
Expands to the list of scripts for the matching packages - install, deinstall, etc.
+
.It Cm \&%B
+
Expands to the list of shared libraries used by programs from the matched package.
.El
.Sh EVALUATION FORMAT
.Ss Variables
modified pkg/pkgcli.h
@@ -88,6 +88,11 @@ void usage_set(void);
int exec_search(int, char **);
void usage_search(void);

+
/* pkg shlib */
+
int exec_shlib(int, char **);
+
void usage_shlib(void);
+
char *sanatize(char *, const char *, size_t);
+

/* pkg update */
int exec_update(int, char **);
void usage_update(void);
@@ -132,6 +137,7 @@ void usage_which(void);
#define INFO_PREFIX (1<<8)
#define INFO_FULL (1<<9)
#define INFO_RAW (1<<10)
+
#define INFO_LIST_SHLIBS (1<<11)

bool query_yesno(const char *msg, ...);
int print_info(struct pkg * const pkg, unsigned int opt);
modified pkg/query.c
@@ -56,7 +56,8 @@ static struct query_flags {
        { 'L', "",		1, PKG_LOAD_LICENSES },
        { 'U', "",		1, PKG_LOAD_USERS },
        { 'G', "",		1, PKG_LOAD_GROUPS },
-
	{ '?', "drCFODLUGK",	1, PKG_LOAD_BASIC },	/* dbflags handled in analyse_query_string() */
+
	{ 'B', "",              1, PKG_LOAD_SHLIBS },
+
	{ '?', "drCFODLUGBK",	1, PKG_LOAD_BASIC },	/* dbflags handled in analyse_query_string() */
        { 's', "hb",		0, PKG_LOAD_BASIC }, 
        { 'n', "",		0, PKG_LOAD_BASIC },
        { 'v', "",		0, PKG_LOAD_BASIC },
@@ -171,6 +172,9 @@ format_str(struct pkg *pkg, struct sbuf *dest, const char *qstr, void *data)
						case 'G':
							sbuf_printf(dest, "%d", !pkg_list_is_empty(pkg, PKG_GROUPS));
							break;
+
						case 'B':
+
							sbuf_printf(dest, "%d", !pkg_list_is_empty(pkg, PKG_SHLIBS));
+
							break;
					}
					break;
				case 'l':
@@ -237,6 +241,9 @@ format_str(struct pkg *pkg, struct sbuf *dest, const char *qstr, void *data)
				case 'G':
					sbuf_cat(dest, pkg_group_name((struct pkg_group *)data));
					break;
+
				case 'B':
+
					sbuf_cat(dest, pkg_shlib_name((struct pkg_shlib *)data));
+
					break;
				case 'M':
					pkg_get(pkg, PKG_MESSAGE, &tmp);
					if (tmp != NULL)
@@ -278,6 +285,7 @@ format_str(struct pkg *pkg, struct sbuf *dest, const char *qstr, void *data)
	}
	sbuf_finish(dest);
}
+

static void
print_query(struct pkg *pkg, char *qstr, char multiline)
{
@@ -291,6 +299,7 @@ print_query(struct pkg *pkg, char *qstr, char multiline)
	struct pkg_user *user = NULL;
	struct pkg_group *group = NULL;
	struct pkg_script *scripts = NULL;
+
	struct pkg_shlib *shlib = NULL;

	switch (multiline) {
		case 'd':
@@ -353,6 +362,12 @@ print_query(struct pkg *pkg, char *qstr, char multiline)
				printf("%s\n", sbuf_data(output));
			}
			break;
+
		case 'B':
+
			while (pkg_shlibs(pkg, &shlib) == EPKG_OK) {
+
				format_str(pkg, output, qstr, shlib);
+
				printf("%s\n", sbuf_data(output));
+
			}
+
			break;
		default:
			format_str(pkg, output, qstr, dep);
			printf("%s\n", sbuf_data(output));
modified pkg/search.c
@@ -79,7 +79,7 @@ exec_search(int argc, char **argv)
				reponame = optarg;
			case 'f':
				opt |= INFO_FULL;
-
				flags |= PKG_LOAD_CATEGORIES|PKG_LOAD_LICENSES|PKG_LOAD_OPTIONS;
+
				flags |= PKG_LOAD_CATEGORIES|PKG_LOAD_LICENSES|PKG_LOAD_OPTIONS|PKG_LOAD_SHLIBS;
				break;
			case 'D':
				opt |= INFO_PRINT_DEP;
modified pkg/utils.c
@@ -137,6 +137,7 @@ print_info(struct pkg * const pkg, unsigned int opt)
	struct pkg_category *cat = NULL;
	struct pkg_license *lic = NULL;
	struct pkg_option *option = NULL;
+
	struct pkg_shlib *shlib = NULL;
	bool multirepos_enabled = false;
	char *m;
	char size[7];
@@ -151,7 +152,7 @@ print_info(struct pkg * const pkg, unsigned int opt)
	    PKG_ORIGIN, &origin, PKG_REPONAME, &reponame, PKG_REPOURL, &repourl,
	    PKG_MAINTAINER, &maintainer, PKG_WWW, &www, PKG_COMMENT, &comment,
	    PKG_DESC, &desc, PKG_FLATSIZE, &flatsize, PKG_NEW_FLATSIZE, &newflatsize,
-
	    PKG_NEW_PKGSIZE, &newpkgsize, PKG_LICENSE_LOGIC, &licenselogic);
+
		PKG_NEW_PKGSIZE, &newpkgsize, PKG_LICENSE_LOGIC, &licenselogic);

	if (opt & INFO_RAW) {
		pkg_emit_manifest(pkg, &m);
@@ -195,6 +196,13 @@ print_info(struct pkg * const pkg, unsigned int opt)
                                printf("\t%s: %s\n", pkg_option_opt(option), pkg_option_value(option));
                }

+
		if (!pkg_list_is_empty(pkg, PKG_SHLIBS)) {
+
			printf("%-15s:", "SharedLibraries");
+
			while (pkg_shlibs(pkg, &shlib) == EPKG_OK)
+
				printf(" %s", pkg_shlib_name(shlib));
+
			printf("\n");
+
		}
+

		if (pkg_type(pkg) == PKG_INSTALLED || pkg_type(pkg) == PKG_FILE) {
			humanize_number(size, sizeof(size), flatsize, "B", HN_AUTOSCALE, 0);
			printf("%-15s: %s\n", "Flat size", size);
@@ -237,6 +245,16 @@ print_info(struct pkg * const pkg, unsigned int opt)

                if (!(opt & INFO_QUIET))
                        printf("\n");
+
	} else if (opt & INFO_LIST_SHLIBS) {
+
		if (!(opt & INFO_QUIET))
+
			printf("%s-%s uses the following shared libraries:\n", name, version);
+

+
                while (pkg_shlibs(pkg, &shlib) == EPKG_OK) {
+
                        printf("%s\n", pkg_shlib_name(shlib));
+
                }
+

+
                if (!(opt & INFO_QUIET))
+
                        printf("\n");
        } else if (opt & INFO_SIZE) {
		if (pkg_type(pkg) == PKG_INSTALLED) {
			humanize_number(size, sizeof(size), flatsize, "B", HN_AUTOSCALE, 0);