Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
package locking -- user interface bits
Matthew Seaman committed 13 years ago
commit 7021b09dfbf88aa085b4f009fdc06f1b9e45cb5f
parent 40e09d3
43 files changed +560 -40
modified libpkg/pkg.c
@@ -70,6 +70,7 @@ pkg_new(struct pkg **pkg, pkg_t type)
	}

	(*pkg)->automatic = false;
+
	(*pkg)->locked = false;
	(*pkg)->type = type;
	(*pkg)->licenselogic = LICENSE_SINGLE;

@@ -95,6 +96,7 @@ pkg_reset(struct pkg *pkg, pkg_t type)
	pkg->new_pkgsize = 0;
	pkg->time = 0;
	pkg->automatic = false;
+
	pkg->locked = false;
	pkg->licenselogic = LICENSE_SINGLE;

	pkg_list_free(pkg, PKG_LICENSES);
@@ -199,6 +201,9 @@ pkg_vget(struct pkg const *const pkg, va_list ap)
		case PKG_AUTOMATIC:
			*va_arg(ap, bool *) = pkg->automatic;
			break;
+
		case PKG_LOCKED:
+
			*va_arg(ap, bool *) = pkg->locked;
+
			break;
		case PKG_TIME:
			*va_arg(ap, int64_t *) = pkg->time;
			break;
@@ -299,6 +304,9 @@ pkg_vset(struct pkg *pkg, va_list ap)
		case PKG_AUTOMATIC:
			pkg->automatic = (int)va_arg(ap, int64_t);
			break;
+
		case PKG_LOCKED:
+
			pkg->locked = (int)va_arg(ap, int64_t);
+
			break;
		case PKG_LICENSE_LOGIC:
			pkg->licenselogic = (lic_t)va_arg(ap, int64_t);
			break;
@@ -548,7 +556,7 @@ pkg_addgroup(struct pkg *pkg, const char *name)
}

int
-
pkg_adddep(struct pkg *pkg, const char *name, const char *origin, const char *version)
+
pkg_adddep(struct pkg *pkg, const char *name, const char *origin, const char *version, bool locked)
{
	struct pkg_dep *d = NULL;
	const char *n1, *v1;
@@ -571,6 +579,7 @@ pkg_adddep(struct pkg *pkg, const char *name, const char *origin, const char *ve
	sbuf_set(&d->origin, origin);
	sbuf_set(&d->name, name);
	sbuf_set(&d->version, version);
+
	d->locked = locked;

	HASH_ADD_KEYPTR(hh, pkg->deps, __DECONST(char *, pkg_dep_get(d, PKG_DEP_ORIGIN)),
	    strlen(pkg_dep_get(d, PKG_DEP_ORIGIN)), d);
@@ -579,7 +588,7 @@ pkg_adddep(struct pkg *pkg, const char *name, const char *origin, const char *ve
}

int
-
pkg_addrdep(struct pkg *pkg, const char *name, const char *origin, const char *version)
+
pkg_addrdep(struct pkg *pkg, const char *name, const char *origin, const char *version, bool locked)
{
	struct pkg_dep *d;

@@ -593,6 +602,7 @@ pkg_addrdep(struct pkg *pkg, const char *name, const char *origin, const char *v
	sbuf_set(&d->origin, origin);
	sbuf_set(&d->name, name);
	sbuf_set(&d->version, version);
+
	d->locked = locked;

	HASH_ADD_KEYPTR(hh, pkg->deps, __DECONST(char *, pkg_dep_get(d, PKG_DEP_ORIGIN)),
	    strlen(pkg_dep_get(d, PKG_DEP_ORIGIN)), d);
modified libpkg/pkg.h
@@ -182,6 +182,7 @@ typedef enum {
	PKG_NEW_PKGSIZE,
	PKG_LICENSE_LOGIC,
	PKG_AUTOMATIC,
+
	PKG_LOCKED,
	PKG_ROWID,
	PKG_TIME,
} pkg_attr;
@@ -189,6 +190,7 @@ typedef enum {
typedef enum {
	PKG_SET_FLATSIZE = 1U,
	PKG_SET_AUTOMATIC,
+
	PKG_SET_LOCKED,
	PKG_SET_DEPORIGIN,
	PKG_SET_ORIGIN
} pkg_set_attr;
@@ -525,9 +527,9 @@ int pkg_set_from_file(struct pkg *pkg, pkg_attr attr, const char *file);
 * @return An error code.
 */
int pkg_adddep(struct pkg *pkg, const char *name, const char *origin, const
-
			   char *version);
+
			   char *version, bool locked);
int pkg_addrdep(struct pkg *pkg, const char *name, const char *origin, const
-
			   char *version);
+
			   char *version, bool locked);

//int pkg_addfile(struct pkg *, const char *fmt);
/**
@@ -656,6 +658,7 @@ const char *pkg_dep_get(struct pkg_dep const * const , const pkg_dep_attr);
const char *pkg_dep_name(struct pkg_dep const * const);
const char *pkg_dep_origin(struct pkg_dep const * const);
const char *pkg_dep_version(struct pkg_dep const * const);
+
bool pkg_dep_is_locked(struct pkg_dep const * const);

/* pkg_file */
const char *pkg_file_get(struct pkg_file const * const, const pkg_file_attr);
modified libpkg/pkg_attributes.c
@@ -101,6 +101,14 @@ pkg_dep_version(struct pkg_dep const * const d)
	return (sbuf_get(d->version));
}

+
bool
+
pkg_dep_is_locked(struct pkg_dep const * const d)
+
{
+
	assert(d != NULL);
+

+
	return d->locked;
+
}
+

/*
 * File
 */
modified libpkg/pkg_elf.c
@@ -135,8 +135,10 @@ test_depends(void *actdata, struct pkg *pkg, const char *name)
	d = NULL;
	if (pkgdb_it_next(it, &d, PKG_LOAD_BASIC) == EPKG_OK) {
		found = false;
-
		pkg_get(d, PKG_ORIGIN, &deporigin, PKG_NAME, &depname,
-
		    PKG_VERSION, &depversion);
+
		pkg_get(d, PKG_ORIGIN,  &deporigin,
+
			   PKG_NAME,    &depname,
+
			   PKG_VERSION, &depversion,
+
			   PKG_LOCKED,  &deplocked);

		dep = NULL;
		found = false;
@@ -149,7 +151,7 @@ test_depends(void *actdata, struct pkg *pkg, const char *name)
		if (!found) {
			pkg_emit_error("adding forgotten depends (%s): %s-%s",
					pathbuf, depname, depversion);
-
			pkg_adddep(pkg, depname, deporigin, depversion);
+
			pkg_adddep(pkg, depname, deporigin, depversion, deplocked);
		}
		pkg_free(d);
	}
modified libpkg/pkg_manifest.c
@@ -589,7 +589,7 @@ pkg_set_deps_from_node(struct pkg *pkg, yaml_node_t *item, yaml_document_t *doc,
	}

	if (origin != NULL && version != NULL)
-
		pkg_adddep(pkg, depname, origin, version);
+
		pkg_adddep(pkg, depname, origin, version, false);
	else
		pkg_emit_error("Skipping malformed dependency %s", depname);

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

#include "private/db_upgrades.h"
-
#define DBVERSION 12
+
#define DBVERSION 13

#define PKGGT	(1U << 1)
#define PKGLT	(1U << 2)
@@ -103,6 +103,7 @@ static struct column_mapping {
	{ "pkgsize",	PKG_NEW_PKGSIZE },
	{ "licenselogic", PKG_LICENSE_LOGIC },
	{ "automatic",	PKG_AUTOMATIC },
+
	{ "locked",	PKG_LOCKED },
	{ "time",	PKG_TIME },
	{ "infos",	PKG_INFOS },
	{ "rowid",	PKG_ROWID },
@@ -479,6 +480,7 @@ pkgdb_init(sqlite3 *sdb)
		"prefix TEXT NOT NULL,"
		"flatsize INTEGER NOT NULL,"
		"automatic INTEGER NOT NULL,"
+
		"locked INTEGER NOT NULL DEFAULT 0,"
		"licenselogic INTEGER NOT NULL,"
		"infos TEXT, "
		"time INTEGER, "
@@ -1153,7 +1155,7 @@ pkgdb_query(struct pkgdb *db, const char *pattern, match_t match)
			"SELECT id, origin, name, version, comment, desc, "
				"message, arch, maintainer, www, "
				"prefix, flatsize, licenselogic, automatic, "
-
				"time, infos "
+
				"locked, time, infos "
			"FROM packages AS p%s "
			"ORDER BY p.name;", comp);

@@ -1249,7 +1251,6 @@ pkgdb_is_dir_used(struct pkgdb *db, const char *dir, int64_t *res)

	return (EPKG_OK);

-

}

int
@@ -1259,8 +1260,13 @@ pkgdb_load_deps(struct pkgdb *db, struct pkg *pkg)
	int		 ret = EPKG_OK;
	char		 sql[BUFSIZ];
	const char	*reponame = NULL;
-
	const char	*basesql = ""
-
		"SELECT d.name, d.origin, d.version "
+
	const char	*mainsql = ""
+
		"SELECT d.name, d.origin, d.version, p.locked "
+
		"FROM main.deps AS d, "
+
		"main.packages AS p "
+
		"WHERE d.package_id = ?1 AND p.origin = d.origin;";
+
	const char	*reposql = ""
+
		"SELECT d.name, d.origin, d.version, 0 "
		"FROM %Q.deps AS d "
		"WHERE d.package_id = ?1;";

@@ -1272,11 +1278,12 @@ pkgdb_load_deps(struct pkgdb *db, struct pkg *pkg)
	if (pkg->type == PKG_REMOTE) {
		assert(db->type == PKGDB_REMOTE);
		pkg_get(pkg, PKG_REPONAME, &reponame);
-
		sqlite3_snprintf(sizeof(sql), sql, basesql, reponame);
+
		sqlite3_snprintf(sizeof(sql), sql, reposql, reponame);
+
		ret = sqlite3_prepare_v2(db->sqlite, sql, -1, &stmt, NULL);
	} else
-
		sqlite3_snprintf(sizeof(sql), sql, basesql, "main");
+
		ret = sqlite3_prepare_v2(db->sqlite, mainsql, -1, &stmt, NULL);

-
	if (sqlite3_prepare_v2(db->sqlite, sql, -1, &stmt, NULL) != SQLITE_OK) {
+
	if (ret != SQLITE_OK) {
		ERROR_SQLITE(db->sqlite);
		return (EPKG_FATAL);
	}
@@ -1285,7 +1292,9 @@ pkgdb_load_deps(struct pkgdb *db, struct pkg *pkg)

	while ((ret = sqlite3_step(stmt)) == SQLITE_ROW) {
		pkg_adddep(pkg, sqlite3_column_text(stmt, 0),
-
		    sqlite3_column_text(stmt, 1), sqlite3_column_text(stmt, 2));
+
			   sqlite3_column_text(stmt, 1),
+
			   sqlite3_column_text(stmt, 2),
+
			   sqlite3_column_int(stmt, 3));
	}
	sqlite3_finalize(stmt);

@@ -1307,8 +1316,13 @@ pkgdb_load_rdeps(struct pkgdb *db, struct pkg *pkg)
	const char	*origin;
	const char	*reponame = NULL;
	char		 sql[BUFSIZ];
-
	const char	*basesql = ""
-
		"SELECT p.name, p.origin, p.version "
+
	const char	*mainsql = ""
+
		"SELECT p.name, p.origin, p.version, p.locked "
+
		"FROM main.packages AS p, main.deps AS d "
+
		"WHERE p.id = d.package_id "
+
			"AND d.origin = ?1;";
+
	const char	*reposql = ""
+
		"SELECT p.name, p.origin, p.version, 0 "
		"FROM %Q.packages AS p, %Q.deps AS d "
		"WHERE p.id = d.package_id "
			"AND d.origin = ?1;";
@@ -1321,11 +1335,12 @@ pkgdb_load_rdeps(struct pkgdb *db, struct pkg *pkg)
	if (pkg->type == PKG_REMOTE) {
		assert(db->type == PKGDB_REMOTE);
		pkg_get(pkg, PKG_REPONAME, &reponame);
-
		sqlite3_snprintf(sizeof(sql), sql, basesql, reponame, reponame);
+
		sqlite3_snprintf(sizeof(sql), sql, reposql, reponame, reponame);
+
		ret = sqlite3_prepare_v2(db->sqlite, sql, -1, &stmt, NULL);
	} else
-
		sqlite3_snprintf(sizeof(sql), sql, basesql, "main", "main");
+
		ret = sqlite3_prepare_v2(db->sqlite, mainsql, -1, &stmt, NULL);

-
	if (sqlite3_prepare_v2(db->sqlite, sql, -1, &stmt, NULL) != SQLITE_OK) {
+
	if (ret != SQLITE_OK) {
		ERROR_SQLITE(db->sqlite);
		return (EPKG_FATAL);
	}
@@ -1335,7 +1350,9 @@ pkgdb_load_rdeps(struct pkgdb *db, struct pkg *pkg)

	while ((ret = sqlite3_step(stmt)) == SQLITE_ROW) {
		pkg_addrdep(pkg, sqlite3_column_text(stmt, 0),
-
		    sqlite3_column_text(stmt, 1), sqlite3_column_text(stmt, 2));
+
			    sqlite3_column_text(stmt, 1),
+
			    sqlite3_column_text(stmt, 2),
+
			    sqlite3_column_int(stmt, 3));
	}
	sqlite3_finalize(stmt);

@@ -1880,7 +1897,6 @@ prstmt_finalize(struct pkgdb *db)
	return;
}

-

int
pkgdb_register_pkg(struct pkgdb *db, struct pkg *pkg, int complete)
{
@@ -3184,7 +3200,6 @@ pkgdb_rquery(struct pkgdb *db, const char *pattern, match_t match,
	return (pkgdb_it_new(db, stmt, PKG_REMOTE));
}

-

static int
pkgdb_search_build_search_query(struct sbuf *sql, match_t match,
    pkgdb_field field, pkgdb_field sort)
@@ -3270,7 +3285,6 @@ pkgdb_search(struct pkgdb *db, const char *pattern, match_t match,
	assert(pattern != NULL && pattern[0] != '\0');
	assert(db->type == PKGDB_REMOTE);

-

	sql = sbuf_new_auto();
	sbuf_cat(sql, basesql);

@@ -3523,7 +3537,7 @@ pkgdb_vset(struct pkgdb *db, int64_t id, va_list ap)
{
	int		 attr;
	sqlite3_stmt	*stmt;
-
	int64_t		 automatic, flatsize;
+
	int64_t		 automatic, flatsize, locked;
	char		*oldorigin;
	char		*neworigin;

@@ -3564,6 +3578,13 @@ pkgdb_vset(struct pkgdb *db, int64_t id, va_list ap)
			sqlite3_bind_int64(stmt, 1, automatic);
			sqlite3_bind_int64(stmt, 2, id);
			break;
+
		case PKG_SET_LOCKED:
+
			locked = (int64_t)va_arg(ap, int);
+
			if (locked != 0 && locked != 1)
+
				continue;
+
			sqlite3_bind_int64(stmt, 1, locked);
+
			sqlite3_bind_int64(stmt, 2, id);
+
			break;
		case PKG_SET_DEPORIGIN:
			oldorigin = va_arg(ap, char *);
			neworigin = va_arg(ap, char *);
@@ -3583,7 +3604,7 @@ pkgdb_vset(struct pkgdb *db, int64_t id, va_list ap)
			sqlite3_finalize(stmt);
			return (EPKG_FATAL);
		}
-
		
+

		sqlite3_finalize(stmt);
	}
	return (EPKG_OK);
@@ -3918,7 +3939,6 @@ pkgdb_stats(struct pkgdb *db, pkg_stats_t type)
		stats = sqlite3_column_int64(stmt, 0);
	}

-

	sbuf_finish(sql);
	sbuf_free(sql);
	sqlite3_finalize(stmt);
modified libpkg/private/db_upgrades.h
@@ -232,6 +232,39 @@ static struct db_upgrades {
	"CREATE INDEX pkg_shlibs_package_id ON pkg_shlibs (package_id);"
	"CREATE INDEX pkg_directories_directory_id ON pkg_directories (directory_id);"
	},
+
	{13,
+
	"ALTER TABLE packages RENAME TO oldpkgs;"
+
	"CREATE TABLE packages ("
+
		"id INTEGER PRIMARY KEY,"
+
		"origin TEXT UNIQUE NOT NULL,"
+
		"name TEXT NOT NULL,"
+
		"version TEXT NOT NULL,"
+
		"comment TEXT NOT NULL,"
+
		"desc TEXT NOT NULL,"
+
		"mtree_id INTEGER REFERENCES mtree(id) ON DELETE RESTRICT"
+
			" ON UPDATE CASCADE,"
+
		"message TEXT,"
+
		"arch TEXT NOT NULL,"
+
		"maintainer TEXT NOT NULL, "
+
		"www TEXT,"
+
		"prefix TEXT NOT NULL,"
+
		"flatsize INTEGER NOT NULL,"
+
		"automatic INTEGER NOT NULL,"
+
		"locked INTEGER NOT NULL DEFAULT 0,"
+
		"licenselogic INTEGER NOT NULL,"
+
		"infos TEXT, "
+
		"time INTEGER,"
+
		"pkg_format_version INTEGER"
+
	");"
+
	"INSERT INTO packages (id, origin, name, version, comment, desc, "
+
		"mtree_id, message, arch, maintainer, www, prefix, flatsize, "
+
		"automatic, licenselogic, pkg_format_version) "
+
		"SELECT id, origin, name, version, comment, desc, "
+
		"mtree_id, message, arch, maintainer, www, prefix, flatsize, "
+
		"automatic, licenselogic, pkg_format_version "
+
		"FROM oldpkgs;"
+
	"DROP TABLE oldpkgs;"
+
	},

	/* Mark the end of the array */
	{ -1, NULL },
modified libpkg/private/pkg.h
@@ -81,6 +81,7 @@
struct pkg {
	struct sbuf	*fields[PKG_NUM_FIELDS];
	bool		 automatic;
+
	bool		 locked;
	int64_t		 flatsize;
	int64_t		 new_flatsize;
	int64_t		 new_pkgsize;
@@ -108,7 +109,8 @@ struct pkg_dep {
	struct sbuf	*origin;
	struct sbuf	*name;
	struct sbuf	*version;
-
	UT_hash_handle	hh;
+
	UT_hash_handle	 hh;
+
	bool		 locked;
};

struct pkg_license {
modified pkg/Makefile
@@ -10,6 +10,7 @@ SRCS= add.c \
		event.c \
		info.c \
		install.c \
+
		lock.c \
		main.c \
		plugins.c \
		progressmeter.c \
@@ -65,6 +66,7 @@ MAN= pkg.8 \
	pkg-fetch.8 \
	pkg-info.8 \
	pkg-install.8 \
+
	pkg-lock.8 \
	pkg-query.8 \
	pkg-register.8 \
	pkg-repo.8 \
@@ -82,6 +84,7 @@ MAN= pkg.8 \
	pkg.conf.5

MLINKS=	pkg-delete.8 pkg-remove.8 \
+
	pkg-lock.8 pkg-unlock.8 \
	pkg.8 pkg-static.8

.PHONY: fix-xrefs
modified pkg/info.c
@@ -50,7 +50,7 @@ usage_info(void)
{
	fprintf(stderr, "usage: pkg info <pkg-name>\n");
	fprintf(stderr, "       pkg info -a\n");
-
	fprintf(stderr, "       pkg info [-BDdefgIlOqRrsXx] <pkg-name>\n");
+
	fprintf(stderr, "       pkg info [-BDdefgIklOqRrsXx] <pkg-name>\n");
	fprintf(stderr, "       pkg info [-BDdfIlqRrs] -F <pkg-file>\n\n");
	fprintf(stderr, "For more information see 'pkg help info'.\n");
}
@@ -83,7 +83,7 @@ exec_info(int argc, char **argv)
	bool origin_search = false;

	/* TODO: exclusive opts ? */
-
	while ((ch = getopt(argc, argv, "aDegxXEIdrlBsqopOfF:R")) != -1) {
+
	while ((ch = getopt(argc, argv, "aDegxXEIdrklBsqopOfF:R")) != -1) {
		switch (ch) {
		case 'a':
			match = MATCH_ALL;
@@ -116,6 +116,9 @@ exec_info(int argc, char **argv)
		case 'r':
			opt |= INFO_RDEPS;
			break;
+
		case 'k':
+
			opt |= INFO_LOCKED;
+
			break;
		case 'l':
			opt |= INFO_FILES;
			break;
added pkg/lock.c
@@ -0,0 +1,218 @@
+
/*
+
 * 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 <err.h>
+
#include <stdio.h>
+
#include <sysexits.h>
+
#include <unistd.h>
+
#include <libutil.h>
+

+
#include <pkg.h>
+

+
#include "pkgcli.h"
+

+
enum action {
+
	LOCK,
+
	UNLOCK,
+
};
+

+
static int exec_lock_unlock(int, char**, enum action);
+
static int do_lock(struct pkgdb *db, struct pkg *pkg);
+
static int do_unlock(struct pkgdb *db, struct pkg *pkg);
+

+
static bool yes = false;	/* Assume yes answer to questions */
+

+
void
+
usage_lock(void)
+
{
+
	fprintf(stderr, "usage: pkg lock [-gxXyq] <pkg-name>\n");
+
	fprintf(stderr, "       pkg lock [-yq] -a\n");
+
	fprintf(stderr, "       pkg unlock [-gxXyq] <pkg-name>\n");
+
	fprintf(stderr, "       pkg unlock [-yq] -a\n");
+
	fprintf(stderr, "For more information see 'pkg help lock'.\n");
+
}
+

+
static int
+
do_lock(struct pkgdb *db, struct pkg *pkg)
+
{
+
	bool locked;
+
	const char *pkgname;
+
	const char *pkgversion;
+

+
	pkg_get(pkg, PKG_NAME, &pkgname, PKG_VERSION, &pkgversion,
+
		PKG_LOCKED, &locked);
+
	
+
	if (locked) {
+
		if (!quiet)
+
			printf("%s-%s: already locked\n", pkgname, pkgversion);
+
		return (EPKG_OK);
+
	}
+

+
	if (!yes && !query_yesno("%s-%s: lock this package? [y/N]: ", pkgname, pkgversion))
+
		return (EPKG_OK);
+

+
	if (!quiet)
+
		printf("Locking %s-%s\n", pkgname, pkgversion);
+

+
	return (pkgdb_set(db, pkg, PKG_SET_LOCKED, true));
+
}
+

+

+
static int
+
do_unlock(struct pkgdb *db, struct pkg *pkg)
+
{
+
	bool locked;
+
	const char *pkgname;
+
	const char *pkgversion;
+

+
	pkg_get(pkg, PKG_NAME, &pkgname, PKG_VERSION, &pkgversion,
+
		PKG_LOCKED, &locked);
+

+

+
	if (!locked) {
+
		if (!quiet)
+
			printf("%s-%s: already unlocked\n", pkgname, pkgversion);
+
		return (EPKG_OK);
+
	}
+

+
	if (!yes && !query_yesno("%s-%s: unlock this package? [y/N]: ", pkgname, pkgversion))
+
		return (EPKG_OK);
+

+
	if (!quiet)
+
		printf("Unlocking %s-%s\n", pkgname, pkgversion);
+

+
	return (pkgdb_set(db, pkg, PKG_SET_LOCKED, false));
+
}
+

+
int
+
exec_lock(int argc, char **argv)
+
{
+
	return (exec_lock_unlock(argc, argv, LOCK));
+
}
+

+
int
+
exec_unlock(int argc, char **argv)
+
{
+
	return (exec_lock_unlock(argc, argv, UNLOCK));
+
}
+

+
static int
+
exec_lock_unlock(int argc, char **argv, enum action action)
+
{
+
	struct pkgdb *db = NULL;
+
	struct pkgdb_it *it = NULL;
+
	struct pkg *pkg = NULL;
+
	const char *pkgname;
+
	int match = MATCH_EXACT;
+
	int retcode;
+
	int exitcode = EX_OK;
+
	int ch;
+

+
	while ((ch = getopt(argc, argv, "agxXyq")) != -1) {
+
		switch (ch) {
+
		case 'a':
+
			match = MATCH_ALL;
+
			break;
+
		case 'g':
+
			match = MATCH_GLOB;
+
			break;
+
		case 'x':
+
			match = MATCH_REGEX;
+
			break;
+
		case 'X':
+
			match = MATCH_EREGEX;
+
			break;
+
		case 'y':
+
			yes = true;
+
			break;
+
		case 'q':
+
			quiet = true;
+
			break;
+
		default:
+
			usage_lock();
+
			return (EX_USAGE);
+
		}
+
        }
+
	argc -= optind;
+
	argv += optind;
+

+
	if (!(match == MATCH_ALL && argc == 0) && argc != 1) {
+
		usage_lock();
+
		return (EX_USAGE);
+
	}
+

+
	if (match == MATCH_ALL)
+
		pkgname = NULL;
+
	else
+
		pkgname = argv[0];
+

+
	if (geteuid() != 0) {
+
		warnx("lock and unlock can only be done as root");
+
		return (EX_NOPERM);
+
	}
+

+
	retcode = pkgdb_open(&db, PKGDB_DEFAULT);
+
	if (retcode == EPKG_ENODB) {
+
		if (match == MATCH_ALL)
+
			return (EX_OK);
+

+
		if (!quiet)
+
			printf("No packages installed.\n");
+

+
		return (EX_NOINPUT);
+
	}
+
	if (retcode != EPKG_OK)
+
		return (EX_IOERR);
+

+
	if ((it = pkgdb_query(db, pkgname, match)) == NULL) {
+
		exitcode = EX_IOERR;
+
		goto cleanup;
+
	}
+

+
	while ((retcode = pkgdb_it_next(it, &pkg, 0)) == EPKG_OK) {
+
		if (action == LOCK)
+
			retcode = do_lock(db, pkg);
+
		else
+
			retcode = do_unlock(db, pkg);
+

+
		if (retcode != EPKG_OK) {
+
			exitcode = EX_IOERR;
+
			goto cleanup;
+
		}
+
	}
+
	if (retcode != EPKG_END)
+
		exitcode = EX_IOERR;
+

+
cleanup:
+
	if (pkg != NULL)
+
		pkg_free(pkg);
+
	if (it != NULL)
+
		pkgdb_it_free(it);
+
	if (db != NULL)
+
		pkgdb_close(db);
+

+
	return (exitcode);
+
}
modified pkg/main.c
@@ -72,6 +72,7 @@ static struct commands {
	{ "help", "Displays help information", exec_help, usage_help},
	{ "info", "Displays information about installed packages", exec_info, usage_info},
	{ "install", "Installs packages from remote package repositories", exec_install, usage_install},
+
	{ "lock", "Locks package against modifications or deletion", exec_lock, usage_lock},
	{ "plugins", "Manages plugins and displays information about plugins", exec_plugins, usage_plugins},
	{ "query", "Queries information about installed packages", exec_query, usage_query},
	{ "register", "Registers a package into the local database", exec_register, usage_register},
modified pkg/pkg-add.8
@@ -61,6 +61,7 @@ See
.Xr pkg-fetch 8 ,
.Xr pkg-info 8 ,
.Xr pkg-install 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
modified pkg/pkg-audit.8
@@ -87,6 +87,7 @@ See
.Xr pkg-fetch 8 ,
.Xr pkg-info 8 ,
.Xr pkg-install 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
modified pkg/pkg-autoremove.8
@@ -68,6 +68,7 @@ See
.Xr pkg-fetch 8 ,
.Xr pkg-info 8 ,
.Xr pkg-install 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
modified pkg/pkg-backup.8
@@ -74,6 +74,7 @@ See
.Xr pkg-fetch 8 ,
.Xr pkg-info 8 ,
.Xr pkg-install 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
modified pkg/pkg-check.8
@@ -93,6 +93,7 @@ See
.Xr pkg-fetch 8 ,
.Xr pkg-info 8 ,
.Xr pkg-install 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
modified pkg/pkg-clean.8
@@ -79,6 +79,7 @@ See
.Xr pkg-fetch 8 ,
.Xr pkg-info 8 ,
.Xr pkg-install 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
modified pkg/pkg-create.8
@@ -296,6 +296,7 @@ Create package file for pkg:
.Xr pkg-fetch 8 ,
.Xr pkg-info 8 ,
.Xr pkg-install 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
modified pkg/pkg-delete.8
@@ -105,6 +105,7 @@ See
.Xr pkg-fetch 8 ,
.Xr pkg-info 8 ,
.Xr pkg-install 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
modified pkg/pkg-fetch.8
@@ -99,6 +99,7 @@ See
.Xr pkg-delete 8 ,
.Xr pkg-info 8 ,
.Xr pkg-install 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
modified pkg/pkg-info.8
@@ -27,7 +27,7 @@
.Nm
.Fl a
.Nm
-
.Op Fl BDdefgIlOqRrsXx
+
.Op Fl BDdefgIklOqRrsXx
.Ar <pkg-name>
.Nm
.Op Fl BDdfIlqRrs
@@ -72,6 +72,19 @@ Display the list of packages required by
.It Fl r
Display the list of packages which depend on
.Ar <pkg-name> .
+
.It Fl k
+
Show the locking status for 
+
.Ar <pkg-name> .
+
When used in combination with
+
.Fl d
+
or
+
.Fl r ,
+
indicate which packages, dependencies or requirements are locked
+
by '(*)'.
+
Locking status is only meaningful for installed packages; all locking
+
statuses will show as unlocked when information about a
+
.Ar <pkg-file>
+
is queried.
.It Fl l
Display all files installed by
.Ar <pkg-name> .
@@ -130,6 +143,7 @@ See
.Xr pkg-delete 8 ,
.Xr pkg-fetch 8 ,
.Xr pkg-install 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
modified pkg/pkg-install.8
@@ -176,6 +176,7 @@ See
.Xr pkg-delete 8 ,
.Xr pkg-fetch 8 ,
.Xr pkg-info 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
added pkg/pkg-lock.8
@@ -0,0 +1,131 @@
+
.\"
+
.\" 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 May 12, 2012
+
.Dt PKG-LOCK 8
+
.Os
+
.Sh NAME
+
.Nm "pkg lock" ,
+
.Nm "pkg unlock"
+
.Nd lock or unlock packages
+
.Sh SYNOPSIS
+
.Nm
+
.Op Fl yq
+
.Fl a
+
.Nm
+
.Op Fl gxXyq
+
.Ar <pkg-name>
+
.Nm "pkg unlock"
+
.Op Fl yq
+
.Fl a
+
.Nm "pkg unlock"
+
.Op Fl gxXyq
+
.Ar <pkg-name>
+
.Sh DESCRIPTION
+
.Nm
+
is used to lock packages against reinstallation,
+
modification or deletion.
+
.Nm "pkg unlock"
+
unlocks the named packages.
+
Either variant only has an effect on currently installed packages.
+
Consequently it is impossible to block installation of a new package
+
by using this mechanism, unless such an installation implies updating
+
a locked package.
+
.Pp
+
The impact of locking a package is wider than simply preventing
+
modifications to the package itself.
+
.Cm Any
+
operation implying modification of the locked package will be
+
blocked.
+
This includes:
+
.Pp
+
.Bl -bullet -compact
+
.It
+
Attempts to reinstall, up- or downgrade or delete the locked package
+
itself.
+
.It
+
Installation, up- or downgrade of a package where the resultant
+
package would have a dependency on a different version of the locked
+
package.
+
.It
+
Deletion, up- or downgrade of any package the locked package depends
+
upon, either directly or as a consequence of installing or upgrading
+
some third package.
+
.El
+
.Sh OPTIONS
+
The following options are supported by
+
.Nm :
+
.Bl -tag -width F1
+
.It Fl a
+
Lock or unlock all installed packages.
+
.It Fl q
+
Operate quietly: do not output anything other than confirmatory questions.
+
.It Fl y
+
Assume "yes" as the answer to all questions.
+
.It Fl g
+
Treat
+
.Ar <pkg-name>
+
as a shell glob pattern.
+
.It Fl x
+
Treat
+
.Ar <pkg-name>
+
as a regular expression.
+
.It Fl X
+
Treat
+
.Ar <pkg-name>
+
as an extended regular expression.
+
.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-audit 8 ,
+
.Xr pkg-autoremove 8 ,
+
.Xr pkg-backup 8 ,
+
.Xr pkg-check 8 ,
+
.Xr pkg-clean 8 ,
+
.Xr pkg-create 8 ,
+
.Xr pkg-delete 8 ,
+
.Xr pkg-fetch 8 ,
+
.Xr pkg-info 8,
+
.Xr pkg-install 8 ,
+
.Xr pkg-query 8 ,
+
.Xr pkg-register 8 ,
+
.Xr pkg-repo 8 ,
+
.Xr pkg-rquery 8 ,
+
.Xr pkg-search 8 ,
+
.Xr pkg-set 8 ,
+
.Xr pkg-shell 8 ,
+
.Xr pkg-shlib 8 ,
+
.Xr pkg-stats 8 ,
+
.Xr pkg-update 8 ,
+
.Xr pkg-updating 8 ,
+
.Xr pkg-upgrade 8 ,
+
.Xr pkg-version 8 ,
+
.Xr pkg-which 8 ,
+
.Xr pkg.conf 5
modified pkg/pkg-query.8
@@ -94,6 +94,8 @@ is in bytes, and
is in human readable format.
.It Cm \&%a
Returns 1 if the matched package is an orphan package and can be pkg-autoremove(1)'d, 0 otherwise
+
.It Cm \&%k
+
Returns 1 if the matched package is locked against modification or deletion, 0 otherwise
.It Cm \&%M
message contain in the matched package
.It Cm \&%t
@@ -221,6 +223,8 @@ WWW address of the package (type string)
Flatsize of the package (type integer)
.It Cm \&%a
Automatic status of the package (type integer)
+
.It Cm \&%k
+
Locking status of the package (type integer)
.It Cm \&%M
Message of the package (type string)
.It Cm \&%t
@@ -272,6 +276,7 @@ See
.Xr pkg-fetch 8 ,
.Xr pkg-info 8 ,
.Xr pkg-install 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
.Xr pkg-rquery 8 ,
modified pkg/pkg-register.8
@@ -78,6 +78,7 @@ See
.Xr pkg-fetch 8 ,
.Xr pkg-info 8 ,
.Xr pkg-install 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-query 8 ,
.Xr pkg-repo 8 ,
.Xr pkg-rquery 8 ,
modified pkg/pkg-repo.8
@@ -96,6 +96,7 @@ See
.Xr pkg-fetch 8 ,
.Xr pkg-info 8 ,
.Xr pkg-install 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-rquery 8 ,
modified pkg/pkg-rquery.8
@@ -227,6 +227,7 @@ See
.Xr pkg-fetch 8 ,
.Xr pkg-info 8 ,
.Xr pkg-install 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
modified pkg/pkg-search.8
@@ -399,6 +399,7 @@ See
.Xr pkg-fetch 8 ,
.Xr pkg-info 8 ,
.Xr pkg-install 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
modified pkg/pkg-set.8
@@ -100,6 +100,7 @@ Move the origin libglut to freeglut, and then reinstall everything depending on
.Xr pkg-fetch 8 ,
.Xr pkg-info 8 ,
.Xr pkg-install 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
modified pkg/pkg-shell.8
@@ -53,6 +53,7 @@ See
.Xr pkg-fetch 8 ,
.Xr pkg-info 8 ,
.Xr pkg-install 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
modified pkg/pkg-shlib.8
@@ -59,6 +59,7 @@ See
.Xr pkg-fetch 8 ,
.Xr pkg-info 8 ,
.Xr pkg-install 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
modified pkg/pkg-stats.8
@@ -62,6 +62,7 @@ See
.Xr pkg-fetch 8 ,
.Xr pkg-info 8 ,
.Xr pkg-install 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
modified pkg/pkg-update.8
@@ -89,6 +89,7 @@ See
.Xr pkg-fetch 8 ,
.Xr pkg-info 8 ,
.Xr pkg-install 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
modified pkg/pkg-updating.8
@@ -88,6 +88,7 @@ installed ports:
.Xr pkg-fetch 8 ,
.Xr pkg-info 8 ,
.Xr pkg-install 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
modified pkg/pkg-upgrade.8
@@ -103,6 +103,7 @@ See
.Xr pkg-fetch 8 ,
.Xr pkg-info 8 ,
.Xr pkg-install 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
modified pkg/pkg-version.8
@@ -186,6 +186,7 @@ The following command compares two package version strings:
.Xr pkg-fetch 8 ,
.Xr pkg-info 8 ,
.Xr pkg-install 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
modified pkg/pkg-which.8
@@ -64,6 +64,7 @@ See
.Xr pkg-fetch 8 ,
.Xr pkg-info 8 ,
.Xr pkg-install 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
modified pkg/pkg.8
@@ -170,6 +170,7 @@ See
.Xr pkg-fetch 8 ,
.Xr pkg-info 8 ,
.Xr pkg-install 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
modified pkg/pkg.conf.5
@@ -151,6 +151,7 @@ file always overrides the value of an option set in the file.
.Xr pkg-fetch 8 ,
.Xr pkg-info 8 ,
.Xr pkg-install 8 ,
+
.Xr pkg-lock 8 ,
.Xr pkg-query 8 ,
.Xr pkg-register 8 ,
.Xr pkg-repo 8 ,
modified pkg/pkgcli.h
@@ -76,6 +76,11 @@ void usage_install(void);
int exec_plugins(int, char **);
void usage_plugins(void);

+
/* pkg lock */
+
int exec_lock(int, char **);
+
int exec_unlock(int, char **);
+
void usage_lock(void);
+

/* pkg query */
int exec_query(int, char **);
void usage_query(void);
@@ -182,8 +187,9 @@ void usage_which(void);
#define INFO_GROUPS	(1<<21)
#define INFO_ARCH	(1<<22)
#define INFO_REPOURL	(1<<23)
+
#define INFO_LOCKED	(1<<24)

-
#define INFO_LASTFIELD	INFO_REPOURL
+
#define INFO_LASTFIELD	INFO_LOCKED
#define INFO_ALL	(((INFO_LASTFIELD) << 1) - 1)

/* Identifying tags */
modified pkg/query.c
@@ -64,6 +64,7 @@ static struct query_flags accepted_query_flags[] = {
	{ 'w', "",		0, PKG_LOAD_BASIC },
	{ 'l', "",		0, PKG_LOAD_BASIC },
	{ 'a', "",		0, PKG_LOAD_BASIC },
+
	{ 'k', "",              0, PKG_LOAD_BASIC },
	{ 'M', "",		0, PKG_LOAD_BASIC },
	{ 'i', "",		0, PKG_LOAD_BASIC },
	{ 't', "",		0, PKG_LOAD_BASIC },
@@ -74,7 +75,7 @@ format_str(struct pkg *pkg, struct sbuf *dest, const char *qstr, void *data)
{
	char size[7];
	const char *tmp;
-
	bool automatic;
+
	bool tmp2;
	int64_t flatsize;
	int64_t timestamp;
	lic_t licenselogic;
@@ -131,8 +132,12 @@ format_str(struct pkg *pkg, struct sbuf *dest, const char *qstr, void *data)
					sbuf_cat(dest, tmp);
				break;
			case 'a':
-
				pkg_get(pkg, PKG_AUTOMATIC, &automatic);
-
				sbuf_printf(dest, "%d", automatic);
+
				pkg_get(pkg, PKG_AUTOMATIC, &tmp2);
+
				sbuf_printf(dest, "%d", tmp2);
+
				break;
+
			case 'k':
+
				pkg_get(pkg, PKG_LOCKED, &tmp2);
+
				sbuf_printf(dest, "%d", tmp2);
				break;
			case 't':
				pkg_get(pkg, PKG_TIME, &timestamp);
@@ -467,6 +472,12 @@ format_sql_condition(const char *str, struct sbuf *sqlcond, bool for_remote)
					sbuf_cat(sqlcond, "automatic");
					state = OPERATOR_INT;
					break;
+
				case 'k':
+
					if (for_remote)
+
						goto bad_option;
+
					sbuf_cat(sqlcond, "locked");
+
					state = OPERATOR_INT;
+
					break;
				case 'M':
					if (for_remote)
						goto bad_option;
modified pkg/utils.c
@@ -179,6 +179,7 @@ print_info(struct pkg * const pkg, unsigned int options)
	struct pkg_user	    *user   = NULL;
	bool multirepos_enabled = false;
	bool print_tag = false;
+
	bool show_locks = false;
	char size[7];
	const char *name, *version, *prefix, *origin, *reponame, *repourl;
	const char *maintainer, *www, *comment, *desc, *message, *arch;
@@ -188,6 +189,7 @@ print_info(struct pkg * const pkg, unsigned int options)
	unsigned opt;
	int64_t flatsize, newflatsize, newpkgsize;
	lic_t licenselogic;
+
	bool locked;
	int cout = 0;		/* Number of characters output */
	int info_num;		/* Number of different data items to print */

@@ -210,7 +212,8 @@ print_info(struct pkg * const pkg, unsigned int options)
		PKG_LICENSE_LOGIC, &licenselogic,
		PKG_MESSAGE,       &message,
		PKG_ARCH,	   &arch,
-
		PKG_REPOPATH,	   &repopath);
+
		PKG_REPOPATH,	   &repopath
+
		PKG_LOCKED         &locked);

	if (!multirepos_enabled)
		pkg_config_string(PKG_CONFIG_REPO, &repourl);
@@ -224,6 +227,11 @@ print_info(struct pkg * const pkg, unsigned int options)
		return;
	}

+
	/* Show locking status when requested to display it and the
+
	   package is locally installed */
+
	if (pkg_type(pkg) == PKG_INSTALLED && (options & INFO_LOCKED) != 0)
+
		show_locks = true;
+

	if (!quiet) {
		/* Print a tag-line identifying the package -- either
		   NAMEVER, ORIGIN or NAME (in that order of
@@ -236,6 +244,10 @@ print_info(struct pkg * const pkg, unsigned int options)
			cout = printf("%s", origin);
		else if (options & INFO_TAG_NAME)
			cout = printf("%s", name);
+

+
		/* Show when locked as requested */
+
		if (show_locks && locked)
+
			cout += printf(" (*)");
	}

	/* Don't display a tab if quiet, retains compatibility. */
@@ -416,22 +428,30 @@ print_info(struct pkg * const pkg, unsigned int options)
			if (pkg_list_count(pkg, PKG_DEPS) > 0) {
				if (print_tag)
					printf("%-15s:\n", "Depends on");
-
				while (pkg_deps(pkg, &dep) == EPKG_OK)
+
				while (pkg_deps(pkg, &dep) == EPKG_OK) {
					printf("%s%s-%s\n",
					       tab,
					       pkg_dep_name(dep),
					       pkg_dep_version(dep));
+
					if (show_locks && pkg_dep_locked(dep))
+
						printf(" (*)");
+
					printf("\n");
+
				}
			}
			break;
		case INFO_RDEPS:
			if (pkg_list_count(pkg, PKG_RDEPS) > 0) {
				if (print_tag)
					printf("%-15s:\n", "Required by");
-
				while (pkg_rdeps(pkg, &dep) == EPKG_OK)
+
				while (pkg_rdeps(pkg, &dep) == EPKG_OK) {
					printf("%s%s-%s\n",
					       tab,
					       pkg_dep_name(dep),
					       pkg_dep_version(dep));
+
					if (show_locks && pkg_dep_locked(dep))
+
						printf(" (*)");
+
					printf("\n");
+
				}
			}
			break;
		case INFO_FILES: /* Installed pkgs only */
@@ -497,6 +517,11 @@ print_info(struct pkg * const pkg, unsigned int options)
			} else if (!print_tag)
				printf("\n");
			break;
+
		case INFO_LOCKED:
+
			if (print_tag)
+
				printf("%-15s: ", "Locked");
+
			printf("%s\n", locked ? "yes" : "no");
+
			break;
		}
	}
}