Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Merge pull request #169 from infracaninophile/shlibs
Baptiste Daroussin committed 14 years ago
commit 8d4e509fb9e629b5baae33d23fafaf0a04dac183
parent 89c39fd
22 files changed +552 -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
added pkg/pkg-shlib.8
@@ -0,0 +1,71 @@
+
.\"
+
.\" FreeBSD pkg - a next generation package for the installation and maintenance
+
.\" of non-core utilities.
+
.\"
+
.\" Redistribution and use in source and binary forms, with or without
+
.\" modification, are permitted provided that the following conditions
+
.\" are met:
+
.\" 1. Redistributions of source code must retain the above copyright
+
.\"    notice, this list of conditions and the following disclaimer.
+
.\" 2. Redistributions in binary form must reproduce the above copyright
+
.\"    notice, this list of conditions and the following disclaimer in the
+
.\"    documentation and/or other materials provided with the distribution.
+
.\"
+
.\"
+
.\"     @(#)pkg.8
+
.\" $FreeBSD$
+
.\"
+
.Dd March 19, 2012
+
.Dt PKG-SHLIB 8
+
.Os
+
.Sh NAME
+
.Nm "pkg shlib"
+
.Nd displays which packages link to a specific shared library
+
.Pp
+
.Ar <library>
+
is the filename of the library, without any leading path, but
+
including the ABI version number.
+
Only exact matches are handled.
+
.Sh SYNOPSIS
+
.Nm
+
.Ar <library>
+
.Sh DESCRIPTION
+
.Nm
+
is used for displaying the packages that link to
+
.Ar <library>
+
.Sh OPTIONS
+
The following commands are supported by
+
.Nm :
+
.Bl -tag -width F1
+
.El
+
.Sh ENVIRONMENT
+
The following environment variables affect the execution of
+
.Nm .
+
See
+
.Xr pkg.conf 5
+
for further description.
+
.Bl -tag -width ".Ev NO_DESCRIPTIONS"
+
.It PKG_DBDIR
+
.El
+
.Sh FILES
+
See
+
.Xr pkg.conf 5 .
+
.Sh SEE ALSO
+
.Xr pkg 8 ,
+
.Xr pkg-add 8 ,
+
.Xr pkg-autoremove 8 ,
+
.Xr pkg-search 8 ,
+
.Xr pkg-backup 8 ,
+
.Xr pkg-install 8 ,
+
.Xr pkg-delete 8 ,
+
.Xr pkg-info 8 ,
+
.Xr pkg-register 8 ,
+
.Xr pkg-repo 8 ,
+
.Xr pkg-set 8 ,
+
.Xr pkg-update 8 ,
+
.Xr pkg-updating 8 ,
+
.Xr pkg-upgrade 8 ,
+
.Xr pkg-version 8 ,
+
.Xr pkg-which 8 ,
+
.Xr pkg-create 8 ,
+
.Xr pkg.conf 5
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 *sanitize(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;
added pkg/shlib.c
@@ -0,0 +1,121 @@
+
/*
+
 * Copyright (c) 2012 Matthew Seaman <matthew@FreeBSD.org>
+
 * All rights reserved.
+
 * 
+
 * Redistribution and use in source and binary forms, with or without
+
 * modification, are permitted provided that the following conditions
+
 * are met:
+
 * 1. Redistributions of source code must retain the above copyright
+
 *    notice, this list of conditions and the following disclaimer
+
 *    in this position and unchanged.
+
 * 2. Redistributions in binary form must reproduce the above copyright
+
 *    notice, this list of conditions and the following disclaimer in the
+
 *    documentation and/or other materials provided with the distribution.
+
 * 
+
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+
 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
 */
+

+
#include <sys/param.h>
+

+
#include <err.h>
+
#include <stdio.h>
+
#include <pkg.h>
+
#include <libgen.h>
+
#include <stdlib.h>
+
#include <string.h>
+
#include <unistd.h>
+
#include <sysexits.h>
+
#include <ctype.h>
+

+
#include "pkgcli.h"
+

+
void
+
usage_shlib(void)
+
{
+
	fprintf(stderr, "usage: pkg shlib <library>\n\n");
+
	fprintf(stderr, "<library> should be a filename without leading path.\n");
+
	fprintf(stderr, "For more information see 'pkg help shlib'.\n");
+
}
+

+
char*
+
sanitize(char *target, const char *source, size_t size)
+
{
+
	size_t i;
+
	int s;
+
	char *rc = target;
+

+
	for (i = 0; i < size - 1; i++) {
+
		s = source[i];
+
		if (s == '\0')
+
			break;
+
		if (isascii(s) && (isspace(s) || s == '/')) {
+
			rc = NULL;
+
			break;
+
		} else {
+
			target[i] = s;
+
		}
+
	}
+
	target[i] = '\0';
+

+
	return (rc);
+
}
+

+
int
+
exec_shlib(int argc, char **argv)
+
{
+
	struct pkgdb *db = NULL;
+
	struct pkgdb_it *it = NULL;
+
	struct pkg *pkg = NULL;
+
	char libname[MAXPATHLEN + 1];
+
	int ret = EPKG_OK, retcode = EPKG_OK, count = 0;
+
	const char *name, *version;
+

+
	if (argc != 2) {
+
		usage_shlib();
+
		return (EX_USAGE);
+
	}
+

+
	if (sanitize(libname, argv[1], sizeof(libname)) == NULL) {
+
		usage_shlib();
+
		return (EX_USAGE);
+
	}
+

+
	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK) {
+
		pkgdb_close(db);
+
		return (EX_IOERR);
+
	}
+

+
	if ((it = pkgdb_query_shlib(db, libname)) == NULL) {
+
		return (EX_IOERR);
+
	}
+

+
	while (( ret = pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC)) == EPKG_OK) {
+
		if (count == 0)
+
			printf("%s is linked to by the folowing packages:\n", libname);
+
		count++;
+
		pkg_get(pkg, PKG_NAME, &name, PKG_VERSION, &version);
+
		printf("%s-%s\n", name, version);
+
	}
+

+
        if (ret != EPKG_END) {
+
		retcode = EPKG_WARN;
+
	} else if (count == 0) {
+
		printf("%s was not found in the database\n", libname);
+
		retcode = EPKG_WARN;
+
	}
+
		
+
	pkg_free(pkg);
+
	pkgdb_it_free(it);
+

+
	pkgdb_close(db);
+
	return (retcode);
+
}
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);