Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
/!\ MERGING MULTI_REPOS INTO MASTER BRANCH /!\
Marin Atanasov Nikolov committed 14 years ago
commit 8a63f25ff263234500e6044b2d82adef4a59bef5
parent 18cd76a
16 files changed +1161 -224
modified libpkg/pkg.c
@@ -1,7 +1,11 @@
+
#include <sys/types.h>
+

#include <archive.h>
#include <archive_entry.h>
#include <assert.h>
#include <errno.h>
+
#include <fcntl.h>
+
#include <libutil.h>
#include <string.h>
#include <stdlib.h>
#include <sysexits.h>
@@ -30,6 +34,8 @@ static struct _fields {
	[PKG_REPOPATH] = {PKG_REMOTE, 0},
	[PKG_CKSUM] = {PKG_REMOTE, 0},
	[PKG_NEWVERSION] = {PKG_REMOTE, 1},
+
	[PKG_REPONAME] = {PKG_REMOTE, 1},
+
	[PKG_REPOURL] = {PKG_REMOTE, 1},
};

int
@@ -182,6 +188,12 @@ pkg_set(struct pkg * pkg, pkg_attr attr, const char *value)
		return (EPKG_OK);
	}

+
	/* 
+
	 * Insert the repository URL as well when inserting the repo name
+
	 */
+
	if (attr == PKG_REPONAME)
+
		pkg_add_repo_url(pkg, value);
+

	return (sbuf_set(sbuf, value));
}

@@ -1074,3 +1086,29 @@ pkg_copy_tree(struct pkg *pkg, const char *src, const char *dest)
	return (packing_finish(pack));
}

+
int
+
pkg_add_repo_url(struct pkg *pkg, const char *reponame)
+
{
+
	properties p = NULL;
+
	int fd = -1;
+

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

+
	/* 
+
	 * We have the repo name, now we need to find it's URL
+
	 * Using properties(3) here, as we know the 'key' already
+
	 */
+
	if ((fd = open("/etc/pkg/repositories", O_RDONLY)) < 0) {
+
		pkg_emit_errno("open", "/etc/pkg/repositories");
+
		return (EPKG_FATAL);
+
	}
+

+
	p = properties_read(fd);
+

+
	pkg_set(pkg, PKG_REPOURL, property_find(p, reponame));
+

+
	properties_free(p);
+
	close(fd);
+

+
	return (EPKG_OK);
+
}
modified libpkg/pkg.h
@@ -24,6 +24,9 @@ struct pkgdb_it;

struct pkg_jobs;

+
struct pkg_repos;
+
struct pkg_repos_entry;
+

typedef enum {
	/**
	 * The license logic is OR (dual in the ports)
@@ -127,6 +130,8 @@ typedef enum {
	PKG_REPOPATH,
	PKG_CKSUM,
	PKG_NEWVERSION,
+
	PKG_REPONAME,
+
	PKG_REPOURL
} pkg_attr;

typedef enum {
@@ -593,15 +598,15 @@ int pkgdb_unregister_pkg(struct pkgdb *pkg, const char *origin);
struct pkgdb_it * pkgdb_query(struct pkgdb *db, const char *pattern,
							  match_t type);
struct pkgdb_it * pkgdb_rquery(struct pkgdb *db, const char *pattern,
-
		match_t type, unsigned int field);
+
		match_t type, unsigned int field, const char *reponame);

/**
 * 
 */
-
struct pkgdb_it *pkgdb_query_installs(struct pkgdb *db, match_t type, int nbpkgs, char **pkgs);
+
struct pkgdb_it *pkgdb_query_installs(struct pkgdb *db, match_t type, int nbpkgs, char **pkgs, const char *reponame);
+
struct pkgdb_it *pkgdb_query_upgrades(struct pkgdb *db, const char *reponame);
+
struct pkgdb_it *pkgdb_query_downgrades(struct pkgdb *db, const char *reponame);
struct pkgdb_it *pkgdb_query_delete(struct pkgdb *db, match_t type, int nbpkgs, char **pkgs, int recursive);
-
struct pkgdb_it *pkgdb_query_upgrades(struct pkgdb *db);
-
struct pkgdb_it *pkgdb_query_downgrades(struct pkgdb *db);
struct pkgdb_it *pkgdb_query_autoremove(struct pkgdb *db);

/**
@@ -756,6 +761,105 @@ int pkg_fetch_file(const char *url, const char *dest);
int ports_parse_plist(struct pkg *, char *);

/**
+
 * Creates a new repository object
+
 * This function is used for creating a repository
+
 * object that can later be used by pkg_repos_load()
+
 * for loading the repositories from file and pkg_repos_next()
+
 * for iterating over the repositories tail.
+
 * @return EPKG_OK on success and EPKG_FATAL on error
+
 */
+
int pkg_repos_new(struct pkg_repos **repos);
+

+
/**
+
 * Loads the remote repositories from file
+
 * @param repos A valid repository object as received from pkg_repos_new()
+
 * @return EPKG_OK on success and EPKG_FATAL on error
+
 */
+
int pkg_repos_load(struct pkg_repos *repos);
+

+
/**
+
 * Adds a repository entry found from the repositories file to the tail
+
 * @param repos A valid repository object as returned by pkg_repos_new()
+
 * @param re A valid repository entry object
+
 * @return EPKG_OK on success and EPKG_FATAL on error
+
 */
+
int pkg_repos_add(struct pkg_repos *repos, struct pkg_repos_entry *re);
+

+
/**
+
 * Get the next repository from the configuration file
+
 * @param repos A valid repository object as returned by pkg_repos_new()
+
 * @param re A pointer to a repository entry to save the result. Must be set to
+
 * NULL for the first repository entry
+
 * @return EPKG_OK on success and EPKG_END if end of tail is reached
+
 */
+
int pkg_repos_next(struct pkg_repos *repos, struct pkg_repos_entry **re);
+

+
/**
+
 * Switches to a single repository while running in multi-repos mode
+
 * @param repos A valid repository object as returned by pkg_repos_new()
+
 * @param reponame The name of the repository to switch to
+
 * @return EPKG_OK if switching to reponame was successful and EPKG_FATAL
+
 * in case of error, e.g. repository does not exists
+
 */
+
int pkg_repos_switch(struct pkg_repos *repos, const char *reponame);
+

+
/**
+
 * Switches back to multi-repos mode and resets any switchable repos
+
 * @param repos A valid repository object as returned by pkg_repos_new()
+
 * @return EPKG_OK on success
+
 */
+
int pkg_repos_switch_reset(struct pkg_repos *repos);
+

+
/**
+
 * Frees the memory used by the repository objects
+
 * @param repos A valid repository object as returned by pkg_repos_new()
+
 */
+
void pkg_repos_free(struct pkg_repos *repos);
+

+
/**
+
 * Check if an attached repository exists
+
 * @param repos A valid repository object as returned by pkg_repos_new()
+
 * @param reponame The name of the repository to be checked
+
 * @return EPKG_OK if repository exists and EPKG_FATAL otherwise
+
 */
+
int pkg_repos_exists(struct pkg_repos *repos, const char *reponame);
+

+
/**
+
 * Returns the name associated with a repository entry
+
 * @param re A valid repository entry object
+
 */
+
const char * pkg_repos_get_name(struct pkg_repos_entry *re);
+

+
/**
+
 * Returns the URL associated wth a repository entry
+
 * @param re A valid repository entry object
+
 */
+
const char * pkg_repos_get_url(struct pkg_repos_entry *re);
+

+
/**
+
 * Returns the line from the configuration file where a repository is defined
+
 * @param re A valid repository entry object
+
 */
+
unsigned int pkg_repos_get_line(struct pkg_repos_entry *re);
+

+
/**
+
 * Returns the next database, which is ATTACH'ed to the main one
+
 * @param it A valid pkgdb_it object as received from
+
 * pkgdb_repos_new() call
+
 * @return A string containing the next database attached
+
 * to the main one, or NULL if end of list is reached.
+
 */
+
const char * pkgdb_repos_next(struct pkgdb_it *it);
+

+
/**
+
 * Adds the URL associated with a repository to a package object
+
 * @param pkg A valid package object
+
 * @param reponame The name of the repository (attached database)
+
 * @return EPKG_OK on success and EPKG_FATAL on error
+
 */
+
int pkg_add_repo_url(struct pkg *pkg, const char *reponame);
+

+
/**
 * @todo Document
 */
int pkg_copy_tree(struct pkg *, const char *src, const char *dest);
modified libpkg/pkg_config.c
@@ -19,6 +19,7 @@ static struct _config {
	{ "PKG_CACHEDIR", "/var/cache/pkg", NULL},
	{ "PORTSDIR", "/usr/ports", NULL },
	{ "PUBKEY", "/etc/ssl/pkg.pub", NULL },
+
	{ "PKG_MULTIREPOS", NULL, NULL },
	{ "HANDLE_RC_SCRIPTS", NULL, NULL },
	{ "ASSUME_ALWAYS_YES", NULL, NULL }, 
	{ NULL, NULL, NULL}
modified libpkg/pkg_private.h
@@ -13,7 +13,7 @@

#include "pkg_util.h"

-
#define PKG_NUM_FIELDS 15
+
#define PKG_NUM_FIELDS 17

#define EXTRACT_ARCHIVE_FLAGS  (ARCHIVE_EXTRACT_OWNER |ARCHIVE_EXTRACT_PERM| \
		ARCHIVE_EXTRACT_TIME  |ARCHIVE_EXTRACT_ACL | \
@@ -112,10 +112,17 @@ struct pkg_jobs_node {
	LIST_ENTRY(pkg_jobs_node) entries;
};

-
struct pkg_remote_repo {
-
	char *name;
-
	char *url;
-
	STAILQ_ENTRY(pkg_remote_repo) entries;
+
struct pkg_repos {
+
	struct pkg_repos_entry {
+
		struct sbuf *name;
+
		struct sbuf *url;
+
		unsigned int line;
+
		unsigned int switched :1;
+
		STAILQ_ENTRY(pkg_repos_entry) entries;
+
	} re;
+

+
	unsigned int switchable :1;
+
	STAILQ_HEAD(repos, pkg_repos_entry) nodes;
};

struct pkg_user {
modified libpkg/pkg_repo.c
@@ -9,6 +9,7 @@
#include <libgen.h>
#include <sqlite3.h>
#include <string.h>
+
#include <stdbool.h>
#include <unistd.h>

#include <openssl/err.h>
@@ -20,6 +21,49 @@
#include "pkg_event.h"
#include "pkg_private.h"

+
static int pkg_repos_is_reserved_name(struct pkg_repos *repos, struct pkg_repos_entry *re);
+

+
static int
+
pkg_repos_is_reserved_name(struct pkg_repos *repos, struct pkg_repos_entry *re)
+
{
+
        struct pkg_repos_entry *next = NULL;
+
	const  char *repo_name = NULL;
+

+
        assert(repos != NULL && re != NULL);
+

+
        /* 
+
         * Find if a repository name already exists.
+
	 *
+
         * NOTE1: The 'repo' name is always reserved, 
+
         * as it is being used by default when 
+
	 * working on a single remote repository,
+
	 * which means that PACKAGESITE is defined.
+
	 *
+
	 * NOTE2: The 'main' and 'temp' names are always
+
	 * reserved, because they are the names of the 
+
	 * main and temp databases, 
+
	 * when working with ATTACH'ed databases in
+
	 * multi-repos mode.
+
	 *
+
	 * NOTE3: The 'local' name is always reserved,
+
	 * because this is the name of the local database.
+
         */
+
	
+
	repo_name = pkg_repos_get_name(re);
+

+
	if ((strcmp(repo_name, "repo") == 0) ||
+
	    (strcmp(repo_name, "main") == 0) ||
+
	    (strcmp(repo_name, "temp") == 0) ||
+
	    (strcmp(repo_name, "local") == 0))
+
		return (EPKG_FATAL);
+

+
        while (pkg_repos_next(repos, &next) == EPKG_OK)
+
                if ((strcmp(repo_name, pkg_repos_get_name(next)) == 0))
+
                        return (EPKG_FATAL);
+

+
        return (EPKG_OK);
+
}
+

int
pkg_repo_fetch(struct pkg *pkg)
{
@@ -33,11 +77,6 @@ pkg_repo_fetch(struct pkg *pkg)

	assert((pkg->type & PKG_REMOTE) == PKG_REMOTE);

-
	if ((packagesite = pkg_config("PACKAGESITE")) == NULL) {
-
		pkg_emit_error("pkg_repo_fetch(): %s", "PACKAGESITE is not defined");
-
		return (EPKG_FATAL);
-
	}
-

	snprintf(dest, sizeof(dest), "%s/%s", pkg_config("PKG_CACHEDIR"),
			 pkg_get(pkg, PKG_REPOPATH));

@@ -55,12 +94,27 @@ pkg_repo_fetch(struct pkg *pkg)
	if ((retcode = mkdirs(path)) != 0)
		goto cleanup;

+
	/* 
+
	 * In multi-repos the remote URL is stored in pkg[PKG_REPOURL]
+
	 * For a single attached database the repository URL should be
+
	 * defined by PACKAGESITE.
+
	 */
+
	if (strcasecmp(pkg_config("PKG_MULTIREPOS"), "yes") == 0) {
+
		packagesite = pkg_get(pkg, PKG_REPOURL);
+
	} else {
+
		packagesite = pkg_config("PACKAGESITE");
+
	}
+

+
	if (packagesite == NULL) {
+
		pkg_emit_error("PACKAGESITE is not defined");
+
		retcode = 1;
+
		goto cleanup;
+
	}
+

	if (packagesite[strlen(packagesite) - 1] == '/')
-
		snprintf(url, sizeof(url), "%s%s", packagesite,
-
				pkg_get(pkg, PKG_REPOPATH));
+
		snprintf(url, sizeof(url), "%s%s", packagesite, pkg_get(pkg, PKG_REPOPATH));
	else
-
		snprintf(url, sizeof(url), "%s/%s", packagesite,
-
				pkg_get(pkg, PKG_REPOPATH));
+
		snprintf(url, sizeof(url), "%s/%s", packagesite, pkg_get(pkg, PKG_REPOPATH));

	retcode = pkg_fetch_file(url, dest);
	fetched = 1;
@@ -91,6 +145,212 @@ pkg_repo_fetch(struct pkg *pkg)
	return (retcode);
}

+
int
+
pkg_repos_new(struct pkg_repos **repos)
+
{
+
        if ((*repos = calloc(1, sizeof(struct pkg_repos))) == NULL) {
+
                pkg_emit_errno("calloc", "pkg_repos");
+
                return (EPKG_FATAL);
+
        }
+

+
        STAILQ_INIT(&(*repos)->nodes);
+

+
        return (EPKG_OK);
+
}
+

+
int
+
pkg_repos_load(struct pkg_repos *repos)
+
{
+
        FILE *fp = NULL;
+
        char *repo_buf[MAXPATHLEN];
+
        char buf[MAXPATHLEN];
+
        char *token = NULL, *tmp = NULL;
+
        unsigned int count = 0, line = 0;
+
        struct pkg_repos_entry *re = NULL;
+

+
        assert(repos != NULL);
+

+
        if ((fp = fopen("/etc/pkg/repositories", "r")) == NULL) {
+
                pkg_emit_errno("fopen", "/etc/pkg/repositories");
+
                return (EPKG_FATAL);
+
        }
+

+
        while (fgets(buf, MAXPATHLEN, fp)) {
+
                line++;
+

+
                if (buf[0] == '\n' || buf[0] == '#' || buf[0] == ';')
+
                        continue;
+

+
                count = 0;
+

+
                buf[strlen(buf) - 1] = '\0';
+
                tmp = buf;
+

+
                /* get the repository entries */
+
                while ((token = strsep(&tmp, " \t=")) != NULL)
+
                        if (*token != '\0')
+
                                repo_buf[count++] = token;
+

+
		/* only name and url are needed for the repository */
+
                if (count != 2) {
+
                        pkg_emit_error("Wrong repository format at line %d (ignoring repository)", line);
+
                        continue;
+
                }
+

+
                if ((re = calloc(1, sizeof(struct pkg_repos_entry))) == NULL) {
+
                        pkg_emit_errno("calloc", "pkg_repos_entry");
+
                        return (EPKG_FATAL);
+
                }
+

+
		sbuf_set(&re->name, repo_buf[0]);
+
		sbuf_set(&re->url, repo_buf[1]);
+
                re->line = line;
+

+
                pkg_repos_add(repos, re);
+
        }
+

+
        fclose(fp);
+

+
        return (EPKG_OK);
+
}
+

+
int
+
pkg_repos_add(struct pkg_repos *repos, struct pkg_repos_entry *re)
+
{
+
        assert(repos != NULL && re != NULL);
+

+
        if (pkg_repos_is_reserved_name(repos, re) != EPKG_OK) {
+
                pkg_emit_error("Repository name '%s' is already reserved (ignoring repository at line %d)",
+
                                pkg_repos_get_name(re), pkg_repos_get_line(re));
+

+
                sbuf_free(re->name);
+
		sbuf_free(re->url);
+
                free(re);
+

+
                return (EPKG_FATAL);
+
        }
+

+
        STAILQ_INSERT_TAIL(&repos->nodes, re, entries);
+

+
        return (EPKG_OK);
+
}
+

+
int
+
pkg_repos_next(struct pkg_repos *repos, struct pkg_repos_entry **re)
+
{
+
        assert(repos != NULL);
+

+
        if (*re == NULL)
+
                *re = STAILQ_FIRST(&repos->nodes);
+
        else
+
                *re = STAILQ_NEXT(*re, entries);
+

+
        if (*re == NULL)
+
                return (EPKG_END);
+

+
	 /* Check if we have switched to a repo */
+
	if (repos->switchable == 1) {
+
		if ((*re)->switched == 1)
+
			return (EPKG_OK);
+
		else
+
			return(pkg_repos_next(repos, re));
+
	}
+

+
	return (EPKG_OK);
+
}
+

+
int
+
pkg_repos_switch(struct pkg_repos *repos, const char *reponame)
+
{
+
	struct pkg_repos_entry *re = NULL;
+

+
	pkg_repos_switch_reset(repos);
+

+
	while (pkg_repos_next(repos, &re) == EPKG_OK)
+
		if (strcmp(reponame, pkg_repos_get_name(re)) == 0) {
+
			repos->switchable = 1;
+
			re->switched = 1;
+
			return (EPKG_OK);
+
		}
+

+
	return (EPKG_FATAL);
+
}
+

+
int
+
pkg_repos_switch_reset(struct pkg_repos *repos)
+
{
+
	struct pkg_repos_entry *re = NULL;
+

+
	repos->switchable = 0;
+

+
	while (pkg_repos_next(repos, &re) == EPKG_OK)
+
		re->switched = 0;
+

+
	return (EPKG_OK);
+
}
+

+
const char *
+
pkg_repos_get_name(struct pkg_repos_entry *re)
+
{
+
        assert(re != NULL);
+

+
        return (sbuf_get(re->name));
+
}
+

+
const char *
+
pkg_repos_get_url(struct pkg_repos_entry *re)
+
{
+
        assert(re != NULL);
+

+
        return (sbuf_get(re->url));
+
}
+

+
unsigned int
+
pkg_repos_get_line(struct pkg_repos_entry *re)
+
{
+
        assert(re != NULL);
+

+
        return(re->line);
+
}
+

+
int
+
pkg_repos_exists(struct pkg_repos *repos, const char *reponame)
+
{
+
	struct pkg_repos_entry *re = NULL;
+
	bool exists = false;
+

+
	while (pkg_repos_next(repos, &re) == EPKG_OK) {
+
		if (strcmp(pkg_repos_get_name(re), reponame) == 0) {
+
			exists = true;
+
			break;
+
		}
+
	}
+

+
	return (exists == true ? EPKG_OK : EPKG_FATAL);
+
}
+

+
void
+
pkg_repos_free(struct pkg_repos *repos)
+
{
+
        struct pkg_repos_entry *re1, *re2;
+

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

+
        re1 = STAILQ_FIRST(&repos->nodes);
+
        while (re1 != NULL) {
+
                re2 = STAILQ_NEXT(re1, entries);
+
                
+
		sbuf_free(re1->name);
+
		sbuf_free(re1->url);
+
                free(re1);
+

+
                re1 = re2;
+
        }
+

+
        free(repos);
+
}
+

static RSA *
load_rsa_private_key(char *rsa_key_path, pem_password_cb *password_cb)
{
modified libpkg/pkg_version.c
@@ -1,8 +1,10 @@
-
#include <err.h>
+
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
+

#include "pkg.h"
+
#include "pkg_event.h"

/*
 * split_version(pkgname, endname, epoch, revision) returns a pointer to
@@ -20,8 +22,10 @@ split_version(const char *pkgname, const char **endname, unsigned long *epoch, u
	const char *versionstr;
	const char *endversionstr;

-
	if (pkgname == NULL)
-
		errx(2, "%s: Passed NULL pkgname.", __func__);
+
	if (pkgname == NULL) {
+
		pkg_emit_error("%s: Passed NULL pkgname.", __func__);
+
		return (NULL);
+
	}

	/* Look for the last '-' the the pkgname */
	ch = strrchr(pkgname, '-');
@@ -122,8 +126,10 @@ get_component(const char *position, version_component *component)
	const char *pos = position;
	int hasstage = 0, haspatchlevel = 0;

-
	if (!pos)
-
		errx(2, "%s: Passed NULL position.", __func__);
+
	if (!pos) {
+
		pkg_emit_error("%s: Passed NULL position.", __func__);
+
		return (NULL);
+
	}

	/* handle version number */
	if (isdigit(*pos)) {
@@ -240,6 +246,8 @@ pkg_version_cmp(const char * const pkg1, const char * const pkg2)
	v1 = split_version(pkg1, &ve1, &e1, &r1);
	v2 = split_version(pkg2, &ve2, &e2, &r2);

+
	assert (v1 != NULL && v2 != NULL);
+

	/* Check epoch, port version, and port revision, in that order. */
	if (e1 != e2) {
		result = (e1 < e2 ? -1 : 1);
@@ -256,11 +264,13 @@ pkg_version_cmp(const char * const pkg1, const char * const pkg2)
			version_component vc2 = {0, 0, 0};
			if (v1 < ve1 && *v1 != '+') {
				v1 = get_component(v1, &vc1);
+
				assert (v1 != NULL);
			} else {
				block_v1 = 1;
			}
			if (v2 < ve2 && *v2 != '+') {
				v2 = get_component(v2, &vc2);
+
				assert (v2 != NULL);
			} else {
				block_v2 = 1;
			}
modified libpkg/pkgdb.c
@@ -4,6 +4,7 @@

#include <assert.h>
#include <errno.h>
+
#include <fcntl.h>
#include <regex.h>
#include <grp.h>
#include <pwd.h>
@@ -33,6 +34,7 @@ static void pkgdb_pkglt(sqlite3_context *, int, sqlite3_value **);
static void pkgdb_pkggt(sqlite3_context *, int, sqlite3_value **);
static int get_pragma(sqlite3 *, const char *, int64_t *);
static int pkgdb_upgrade(struct pkgdb *);
+
static int pkgdb_repos_new(struct pkgdb *, struct pkg_repos **);
static void populate_pkg(sqlite3_stmt *stmt, struct pkg *pkg);
static int create_temporary_pkgjobs(sqlite3 *);

@@ -54,6 +56,7 @@ static struct column_text_mapping {
	{ "prefix", pkg_set, PKG_PREFIX},
	{ "cksum", pkg_set, PKG_CKSUM},
	{ "repopath", pkg_set, PKG_REPOPATH},
+
	{ "dbname", pkg_set, PKG_REPONAME},
	{ "newversion", pkg_set, PKG_NEWVERSION},
	{ NULL, NULL, -1 }
};
@@ -117,7 +120,7 @@ populate_pkg(sqlite3_stmt *stmt, struct pkg *pkg) {
	for (icol = 0; icol < sqlite3_column_count(stmt); icol++) {
		colname = sqlite3_column_name(stmt, icol);
		switch (sqlite3_column_type(stmt, icol)) {
-
			case  SQLITE_TEXT:
+
			case SQLITE_TEXT:
				for (i = 0; columns_text[i].name != NULL; i++ ) {
					if (!strcmp(columns_text[i].name, colname)) {
						columns_text[i].set_text(pkg, columns_text[i].type, sqlite3_column_text(stmt, icol));
@@ -154,7 +157,6 @@ populate_pkg(sqlite3_stmt *stmt, struct pkg *pkg) {
	}
}

-

static void
pkgdb_regex(sqlite3_context *ctx, int argc, sqlite3_value **argv, int reg_type)
{
@@ -436,13 +438,64 @@ pkgdb_init(sqlite3 *sdb)
	return (sql_exec(sdb, sql));
}

+
static int
+
pkgdb_repos_new(struct pkgdb *db, struct pkg_repos **repos)
+
{
+
	sqlite3_stmt *stmt = NULL;
+
	struct pkgdb_it *it = NULL;
+
	struct pkg_repos_entry *re = NULL;
+
	const char *dbname = NULL;
+
	int ret;
+

+
	assert(db != NULL);
+

+
	pkg_repos_new(repos);
+

+
	if (sqlite3_prepare_v2(db->sqlite, "PRAGMA database_list;", -1, &stmt, NULL) != SQLITE_OK) {
+
		ERROR_SQLITE(db->sqlite);
+
		return (EPKG_FATAL);
+
	}
+
	
+
	it = pkgdb_it_new(db, stmt, PKG_REMOTE);
+

+
	while ((ret = sqlite3_step(it->stmt)) == SQLITE_ROW) {
+
		dbname = sqlite3_column_text(it->stmt, 1);
+

+
		/* skip the 'main' and 'temp' databases */
+
		if ((strcmp(dbname, "main") == 0) || (strcmp(dbname, "temp") == 0))
+
			continue;
+

+
		if ((re = calloc(1, sizeof(struct pkg_repos_entry))) == NULL) {
+
			pkg_emit_errno("malloc", "pkgdb_repos_new");
+
			return (EPKG_FATAL);
+
		}
+

+
		sbuf_set(&re->name, dbname);
+
		pkg_repos_add(*repos, re);
+
	}
+

+
	pkgdb_it_free(it);
+

+
	if (ret != SQLITE_DONE) {
+
		ERROR_SQLITE(db->sqlite);
+
		return (EPKG_FATAL);
+
	}
+

+
	return (EPKG_OK);
+
}
+

int
pkgdb_open(struct pkgdb **db_p, pkgdb_t type)
{
-
	struct pkgdb *db;
+
	struct pkgdb *db = NULL;
+
	struct pkg_repos *repos = NULL;
+
	struct pkg_repos_entry *re = NULL;
	char localpath[MAXPATHLEN + 1];
	char remotepath[MAXPATHLEN + 1];
-
	const char *dbdir;
+
	const char *dbdir = NULL;
+
	const char *repo_name = NULL;
+
	const char *multirepos_enabled = NULL;
+
	struct sbuf *sql = sbuf_new_auto();
	bool create = false;

	/*
@@ -518,20 +571,91 @@ pkgdb_open(struct pkgdb **db_p, pkgdb_t type)
	}

	if (type == PKGDB_REMOTE) {
-
		snprintf(remotepath, sizeof(remotepath), "%s/repo.sqlite", dbdir);
+
		multirepos_enabled = pkg_config("PKG_MULTIREPOS");

-
		if (access(remotepath, R_OK) != 0) {
-
			pkg_emit_errno("access", remotepath);
-
			pkgdb_close(db);
-
			return (EPKG_FATAL);
-
		}
+
		if (multirepos_enabled && (strcasecmp(multirepos_enabled, "yes") == 0)) {
+
			fprintf(stderr, "\t/!\\		   WARNING WARNING WARNING		/!\\\n");
+
			fprintf(stderr, "\t/!\\	     WORKING ON MULTIPLE REPOSITORIES		/!\\\n");
+
			fprintf(stderr, "\t/!\\  THIS FEATURE IS STILL CONSIDERED EXPERIMENTAL	/!\\\n");
+
			fprintf(stderr, "\t/!\\		     YOU HAVE BEEN WARNED		/!\\\n\n");

-
		if (sql_exec(db->sqlite, "ATTACH '%q' AS remote;", remotepath) != EPKG_OK) {
-
			pkgdb_close(db);
-
			return (EPKG_FATAL);
+
			if (pkg_repos_new(&repos) != EPKG_OK) {
+
				pkg_emit_error("cannot create multi repo object");
+
				pkgdb_close(db);
+
				return (EPKG_FATAL);
+
			}
+

+
			if (pkg_repos_load(repos) != EPKG_OK) {
+
				pkg_emit_error("cannot load repositories");
+
				pkgdb_close(db);
+
				pkg_repos_free(repos);
+
				return (EPKG_FATAL);
+
			}
+

+
			while (pkg_repos_next(repos, &re) == EPKG_OK) {
+
				repo_name = pkg_repos_get_name(re);
+

+
				snprintf(remotepath, sizeof(remotepath), "%s/%s.sqlite",
+
						dbdir, repo_name);
+

+
				if (access(remotepath, R_OK) != 0) {
+
					pkg_emit_errno("access", remotepath);
+
					sbuf_finish(sql);
+
					sbuf_delete(sql);
+
					pkgdb_close(db);
+
					pkg_repos_free(repos);
+
					return (EPKG_FATAL);
+
				}
+

+
				sbuf_printf(sql, "ATTACH '%s' AS '%s';", remotepath, repo_name);
+
			}
+

+
			sbuf_finish(sql);
+

+
			/* 
+
			 * Check if a default repo is defined.
+
			 */
+
			if (pkg_repos_exists(repos, "default")) {
+
				pkg_emit_error("no default repository defined");
+
				pkgdb_close(db);
+
				sbuf_delete(sql);
+
				pkg_repos_free(repos);
+
				return(EPKG_FATAL);
+
			}
+

+
			if (sql_exec(db->sqlite, sbuf_get(sql)) != EPKG_OK) {
+
				sbuf_finish(sql);
+
				sbuf_delete(sql);
+
				pkgdb_close(db);
+
				pkg_repos_free(repos);
+
				return (EPKG_FATAL);
+
			}
+

+
			pkg_repos_free(repos);
+
		} else {
+
			/*
+
			 * Working on a single remote repository
+
			 */
+

+
			snprintf(remotepath, sizeof(remotepath), "%s/repo.sqlite", dbdir);
+

+
			if (access(remotepath, R_OK) != 0) {
+
				pkg_emit_errno("access", remotepath);
+
				pkgdb_close(db);
+
				return (EPKG_FATAL);
+
			}
+

+
			sbuf_printf(sql, "ATTACH '%s' AS 'remote';", remotepath);
+
			sbuf_finish(sql);
+

+
			if (sql_exec(db->sqlite, sbuf_get(sql)) != EPKG_OK) {
+
				pkgdb_close(db);
+
				return (EPKG_FATAL);
+
			}
		}
	}

+
	sbuf_delete(sql);
	*db_p = db;
	return (EPKG_OK);
}
@@ -539,15 +663,51 @@ pkgdb_open(struct pkgdb **db_p, pkgdb_t type)
void
pkgdb_close(struct pkgdb *db)
{
+
	struct pkg_repos *repos = NULL;
+
	struct pkg_repos_entry *re = NULL;
+
	struct sbuf *sql = NULL;
+
	const char *multirepos_enabled = NULL;
+

	if (db == NULL)
		return;

	if (db->sqlite != NULL) {
-
		if (db->type == PKGDB_REMOTE)
-
			sql_exec(db->sqlite, "DETACH remote;");
+
		if (db->type == PKGDB_REMOTE) {
+
			multirepos_enabled = pkg_config("PKG_MULTIREPOS");
+

+
			if (multirepos_enabled && (strcasecmp(multirepos_enabled, "yes") == 0)) {
+
				/*
+
				 * Working on multiple remote repositories.
+
				 * Detach the remote repositories from the main database
+
				 */
+
				
+
				if (pkgdb_repos_new(db, &repos) != EPKG_OK) {
+
					pkg_emit_error("cannot get the attached databases");
+
					return;
+
				}
+

+
				sql = sbuf_new_auto();
+

+
				while (pkg_repos_next(repos, &re) == EPKG_OK) {
+
					sbuf_printf(sql, "DETACH '%s';", pkg_repos_get_name(re));
+
				}
+

+
				sbuf_finish(sql);
+
				sql_exec(db->sqlite, sbuf_get(sql));
+
				sbuf_delete(sql);
+
				pkg_repos_free(repos);
+
			} else {
+
				/*
+
				 * Working on a single remote repository.
+
				 * Detach it from the main database
+
				 */
+
				sql_exec(db->sqlite, "DETACH remote;");
+
			}
+
		}

		sqlite3_close(db->sqlite);
	}
+

	sqlite3_shutdown();
	free(db);
}
@@ -571,6 +731,7 @@ pkgdb_it_new(struct pkgdb *db, sqlite3_stmt *s, int type)
	return (it);
}

+

int
pkgdb_it_next(struct pkgdb_it *it, struct pkg **pkg_p, int flags)
{
@@ -792,22 +953,19 @@ int
pkgdb_load_deps(struct pkgdb *db, struct pkg *pkg)
{
	sqlite3_stmt *stmt = NULL;
-
	int ret;
-
	const char *sql = NULL;
+
	int ret = EPKG_OK;
+
	char sql[BUFSIZ];
+
	const char *basesql = "" 
+
			"SELECT d.name, d.origin, d.version "
+
			"FROM '%s'.deps AS d "
+
			"WHERE d.package_id = ?1;";

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

-
	if (pkg->type == PKG_REMOTE) {
-
		sql = ""
-
			"SELECT d.name, d.origin, d.version "
-
			"FROM remote.deps AS d "
-
			"WHERE d.package_id = ?1;";
-
	} else {
-
		sql = ""
-
			"SELECT d.name, d.origin, d.version "
-
			"FROM main.deps AS d "
-
			"WHERE d.package_id = ?1;";
-
	}
+
	if (pkg->type == PKG_REMOTE)
+
		snprintf(sql, sizeof(sql), basesql, pkg_get(pkg, PKG_REPONAME));
+
	else
+
		snprintf(sql, sizeof(sql), basesql, "main");

	if (pkg->flags & PKG_LOAD_DEPS)
		return (EPKG_OK);
@@ -957,25 +1115,22 @@ pkgdb_load_dirs(struct pkgdb *db, struct pkg *pkg)
int
pkgdb_load_license(struct pkgdb *db, struct pkg *pkg)
{
-
	const char *sql = NULL;
-

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

-
	if (pkg->type == PKG_REMOTE) {
-
		sql = ""
-
			"SELECT name "
-
			"FROM remote.pkg_licenses, remote.licenses AS l "
-
			"WHERE package_id = ?1 "
-
			"AND license_id = l.id "
-
			"ORDER by name DESC";
-
	} else {
-
		sql = ""
+
	char sql[BUFSIZ];
+
	const char *reponame = NULL;
+
	const char *basesql = "" 
			"SELECT name "
-
			"FROM main.pkg_licenses, main.licenses AS l "
+
			"FROM '%s'.pkg_licenses, '%s'.licenses AS l "
			"WHERE package_id = ?1 "
			"AND license_id = l.id "
			"ORDER by name DESC";
-
	}
+

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

+
	if (pkg->type == PKG_REMOTE) {
+
		reponame = pkg_get(pkg, PKG_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_LICENSES, pkg_addlicense, PKG_LICENSES));
}
@@ -983,25 +1138,22 @@ pkgdb_load_license(struct pkgdb *db, struct pkg *pkg)
int
pkgdb_load_category(struct pkgdb *db, struct pkg *pkg)
{
-
	const char *sql = NULL;
-

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

-
	if (pkg->type == PKG_REMOTE) {
-
		sql = ""
-
			"SELECT name "
-
			"FROM remote.pkg_categories, remote.categories AS c "
-
			"WHERE package_id = ?1 "
-
			"AND category_id = c.id "
-
			"ORDER by name DESC";
-
	} else {
-
		sql = ""
+
	char sql[BUFSIZ];
+
	const char *reponame = NULL;
+
	const char *basesql = ""
			"SELECT name "
-
			"FROM main.pkg_categories, main.categories AS c "
+
			"FROM '%s'.pkg_categories, '%s'.categories AS c "
			"WHERE package_id = ?1 "
			"AND category_id = c.id "
			"ORDER by name DESC";
-
	}
+

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

+
	if (pkg->type == PKG_REMOTE) {
+
		reponame = pkg_get(pkg, PKG_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_CATEGORIES, pkg_addcategory, PKG_CATEGORIES));
}
@@ -1119,26 +1271,23 @@ int
pkgdb_load_options(struct pkgdb *db, struct pkg *pkg)
{
	sqlite3_stmt *stmt = NULL;
-
	int ret;
-
	const char *sql = NULL;
-

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

-
	if (pkg->type == PKG_REMOTE) {
-
		sql = ""
-
		"SELECT option, value "
-
		"FROM remote.options "
-
		"WHERE package_id = ?1";
-
	} else {
-
		sql = ""
+
	int ret = EPKG_OK;
+
	char sql[BUFSIZ];
+
	const char *basesql = ""
		"SELECT option, value "
-
		"FROM main.options "
+
		"FROM '%s'.options "
		"WHERE package_id = ?1";
-
	}
+

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

	if (pkg->flags & PKG_LOAD_OPTIONS)
		return (EPKG_OK);

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

	if (sqlite3_prepare_v2(db->sqlite, sql, -1, &stmt, NULL) != SQLITE_OK) {
		ERROR_SQLITE(db->sqlite);
		return (EPKG_FATAL);
@@ -1838,23 +1987,45 @@ create_temporary_pkgjobs(sqlite3 *s)
			"comment TEXT, desc TEXT, message TEXT, "
			"arch TEXT, osversion TEXT, maintainer TEXT, "
			"www TEXT, prefix TEXT, flatsize INTEGER, newversion TEXT, "
-
			"newflatsize INTEGER, pkgsize INTEGER, cksum TEXT, repopath TEXT, automatic INTEGER);");
+
			"newflatsize INTEGER, pkgsize INTEGER, cksum TEXT, repopath TEXT, automatic INTEGER, "
+
			"dbname TEXT);");

	return (ret);
}

struct pkgdb_it *
-
pkgdb_query_installs(struct pkgdb *db, match_t match, int nbpkgs, char **pkgs)
+
pkgdb_query_installs(struct pkgdb *db, match_t match, int nbpkgs, char **pkgs, const char *repo)
{
-
	sqlite3_stmt *stmt = NULL;;
+
	struct pkg_repos *repos = NULL;
+
	sqlite3_stmt *stmt = NULL;
	int i = 0;
	struct sbuf *sql = sbuf_new_auto();
	const char *how = NULL;
+
	const char *reponame = NULL;
+
	const char *multirepos_enabled = NULL;

-
	const char finalsql[] = "select pkgid as id, origin, name, version, "
+
	const char finalsql[] = "SELECT pkgid AS id, origin, name, version, "
		"comment, desc, message, arch, osversion, maintainer, "
		"www, prefix, flatsize, newversion, newflatsize, pkgsize, "
-
		"cksum, repopath, automatic, (select count(*) from remote.deps as d where d.origin = pkgjobs.origin) as weight FROM pkgjobs order by weight DESC;";
+
		"cksum, repopath, automatic, (select count(*) FROM '%s'.deps as d WHERE d.origin = pkgjobs.origin) as weight, "
+
		"dbname FROM pkgjobs ORDER BY weight DESC;";
+
       
+
	const char main_sql[] = "INSERT OR IGNORE INTO pkgjobs (pkgid, origin, name, version, comment, desc, arch, "
+
			"osversion, maintainer, www, prefix, flatsize, pkgsize, "
+
			"cksum, repopath, automatic, dbname) "
+
			"SELECT id, origin, name, version, comment, desc, "
+
			"arch, osversion, maintainer, www, prefix, flatsize, pkgsize, "
+
			"cksum, path, 0, '%s' FROM '%s'.packages WHERE ";
+

+
	const char deps_sql[] = "INSERT OR IGNORE INTO pkgjobs (pkgid, origin, name, version, comment, desc, arch, "
+
				"osversion, maintainer, www, prefix, flatsize, pkgsize, "
+
				"cksum, repopath, automatic, dbname) "
+
				"SELECT DISTINCT r.id, r.origin, r.name, r.version, r.comment, r.desc, "
+
				"r.arch, r.osversion, r.maintainer, r.www, r.prefix, r.flatsize, r.pkgsize, "
+
				"r.cksum, r.path, 1, '%s' "
+
				"FROM '%s'.packages AS r where r.origin IN "
+
				"(SELECT d.origin FROM '%s'.deps AS d, pkgjobs AS j WHERE d.package_id = j.pkgid) "
+
				"AND (SELECT origin FROM main.packages WHERE origin=r.origin AND version=r.version) IS NULL;";

	assert(db != NULL);

@@ -1863,12 +2034,34 @@ pkgdb_query_installs(struct pkgdb *db, match_t match, int nbpkgs, char **pkgs)
		return (NULL);
	}

-
	sbuf_cat(sql, "INSERT OR IGNORE INTO pkgjobs (pkgid, origin, name, version, comment, desc, arch, "
-
			"osversion, maintainer, www, prefix, flatsize, pkgsize, "
-
			"cksum, repopath, automatic) "
-
			"SELECT id, origin, name, version, comment, desc, "
-
			"arch, osversion, maintainer, www, prefix, flatsize, pkgsize, "
-
			"cksum, path, 0 FROM remote.packages WHERE ");
+
	/* Working on multiple repositories */
+
	multirepos_enabled = pkg_config("PKG_MULTIREPOS");
+

+
	if (multirepos_enabled && (strcasecmp(multirepos_enabled, "yes") == 0)) {
+
		if (repo != NULL) {
+
			if (pkgdb_repos_new(db, &repos) != EPKG_OK) {
+
				pkg_emit_error("cannot get the attached databases");
+
				return (NULL);
+
			}
+

+
			if (pkg_repos_exists(repos, repo) != EPKG_OK) {
+
				pkg_emit_error("repository '%s' does not exists", repo);
+
				pkg_repos_free(repos);
+
				return (NULL);
+
			}
+

+
			reponame = repo;
+
			pkg_repos_free(repos);
+
		} else {
+
			/* default repository in multi-repos is 'default' */
+
			reponame = "default";
+
		}
+
	} else {
+
		/* default repository in single-repo is 'remote' */
+
		reponame = "remote";
+
	}
+

+
	sbuf_printf(sql, main_sql, reponame, reponame); 

	switch (match) {
		case MATCH_ALL:
@@ -1912,19 +2105,13 @@ pkgdb_query_installs(struct pkgdb *db, match_t match, int nbpkgs, char **pkgs)
	sql_exec(db->sqlite, "DELETE from pkgjobs where (select p.origin from main.packages as p where p.origin=pkgjobs.origin and version=pkgjobs.version) IS NOT NULL;");

	/* Append dependencies */
+
	sbuf_reset(sql);
+
	sbuf_printf(sql, deps_sql, reponame, reponame, reponame);
+

	do {
-
		sql_exec(db->sqlite, "INSERT OR IGNORE INTO pkgjobs (pkgid, origin, name, version, comment, desc, arch, "
-
				"osversion, maintainer, www, prefix, flatsize, pkgsize, "
-
				"cksum, repopath, automatic) "
-
				"SELECT DISTINCT r.id, r.origin, r.name, r.version, r.comment, r.desc, "
-
				"r.arch, r.osversion, r.maintainer, r.www, r.prefix, r.flatsize, r.pkgsize, "
-
				"r.cksum, r.path, 1 "
-
				"from remote.packages AS r where r.origin IN "
-
				"(SELECT d.origin from remote.deps AS d, pkgjobs as j WHERE d.package_id = j.pkgid) "
-
				"AND (SELECT p.origin from main.packages as p WHERE p.origin=r.origin AND version=r.version) IS NULL;");
+
		sql_exec(db->sqlite, sbuf_get(sql));
	} while (sqlite3_changes(db->sqlite) != 0);

-
	sbuf_delete(sql);

	/* Determine if there is an upgrade needed */
	sql_exec(db->sqlite, "INSERT OR REPLACE INTO pkgjobs (pkgid, origin, name, version, comment, desc, message, arch, "
@@ -1936,18 +2123,28 @@ pkgdb_query_installs(struct pkgdb *db, match_t match, int nbpkgs, char **pkgs)
			"FROM main.packages AS l, pkgjobs AS r WHERE l.origin = r.origin "
			"AND (PKGLT(l.version, r.version) OR (l.name != r.name))");

-
	if (sqlite3_prepare_v2(db->sqlite, finalsql, -1, &stmt, NULL) != SQLITE_OK) {
+
	sbuf_reset(sql);
+
	sbuf_printf(sql, finalsql, reponame);
+

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

+
	sbuf_finish(sql);
+
	sbuf_delete(sql);
+

	return (pkgdb_it_new(db, stmt, PKG_REMOTE));
}

struct pkgdb_it *
-
pkgdb_query_upgrades(struct pkgdb *db)
+
pkgdb_query_upgrades(struct pkgdb *db, const char *repo)
{
+
	struct pkg_repos *repos = NULL;
	sqlite3_stmt *stmt = NULL;
+
	struct sbuf *sql = sbuf_new_auto();
+
	const char *reponame = NULL;
+
	const char *multirepos_enabled = NULL;

	assert(db != NULL);

@@ -1956,58 +2153,107 @@ pkgdb_query_upgrades(struct pkgdb *db)
		return (NULL);
	}

-
	const char sql[] = "select pkgid as id, origin, name, version, "
+
	const char finalsql[] = "select pkgid as id, origin, name, version, "
		"comment, desc, message, arch, osversion, maintainer, "
		"www, prefix, flatsize, newversion, newflatsize, pkgsize, "
-
		"cksum, repopath, automatic, (select count(*) from remote.deps as d where d.origin = pkgjobs.origin) as weight FROM pkgjobs order by weight DESC;";
-

-
	create_temporary_pkgjobs(db->sqlite);
-

-
	sql_exec(db->sqlite,  "INSERT OR IGNORE INTO pkgjobs (pkgid, origin, name, version, comment, desc, arch, "
+
		"cksum, repopath, automatic, (select count(*) from '%s'.deps as d where d.origin = pkgjobs.origin) as weight, "
+
		"'%s' AS dbname FROM pkgjobs order by weight DESC;";
+
		
+
	const char pkgjobs_sql_1[] = "INSERT OR IGNORE INTO pkgjobs (pkgid, origin, name, version, comment, desc, arch, "
			"osversion, maintainer, www, prefix, flatsize, pkgsize, "
-
			"cksum, repopath, automatic) "
+
			"cksum, repopath, automatic, dbname) "
			"SELECT id, origin, name, version, comment, desc, "
			"arch, osversion, maintainer, www, prefix, flatsize, pkgsize, "
-
			"cksum, path, 0 FROM remote.packages WHERE origin IN (select origin from main.packages)");
-

-
	/* Remove packages already installed and in the latest version */
-
	sql_exec(db->sqlite, "DELETE from pkgjobs where (select p.origin from main.packages as p where p.origin=pkgjobs.origin and version=pkgjobs.version) IS NOT NULL;");
+
			"cksum, path, 0, '%s' FROM '%s'.packages WHERE origin IN (select origin from main.packages)";

-
	/* Append dependencies */
-
	do {
-
		sql_exec(db->sqlite, "INSERT OR IGNORE INTO pkgjobs (pkgid, origin, name, version, comment, desc, arch, "
+
	const char pkgjobs_sql_2[] = "INSERT OR IGNORE INTO pkgjobs (pkgid, origin, name, version, comment, desc, arch, "
				"osversion, maintainer, www, prefix, flatsize, pkgsize, "
-
				"cksum, repopath, automatic) "
+
				"cksum, repopath, automatic, dbname) "
				"SELECT DISTINCT r.id, r.origin, r.name, r.version, r.comment, r.desc, "
				"r.arch, r.osversion, r.maintainer, r.www, r.prefix, r.flatsize, r.pkgsize, "
-
				"r.cksum, r.path, 1 "
-
				"from remote.packages AS r where r.origin IN "
-
				"(SELECT d.origin from remote.deps AS d, pkgjobs as j WHERE d.package_id = j.pkgid) "
-
				"AND (SELECT p.origin from main.packages as p WHERE p.origin=r.origin AND version=r.version) IS NULL;");
-
	} while (sqlite3_changes(db->sqlite) != 0);
+
				"r.cksum, r.path, 1, '%s' AS dbname "
+
				"FROM '%s'.packages AS r where r.origin IN "
+
				"(SELECT d.origin from '%s'.deps AS d, pkgjobs as j WHERE d.package_id = j.pkgid) "
+
				"AND (SELECT p.origin from main.packages as p WHERE p.origin=r.origin AND version=r.version) IS NULL;";

-
	/* Determine if there is an upgrade needed */
-
	sql_exec(db->sqlite, "INSERT OR REPLACE INTO pkgjobs (pkgid, origin, name, version, comment, desc, message, arch, "
+
	const char pkgjobs_sql_3[] = "INSERT OR REPLACE INTO pkgjobs (pkgid, origin, name, version, comment, desc, message, arch, "
			"osversion, maintainer, www, prefix, flatsize, newversion, newflatsize, pkgsize, "
			"cksum, repopath, automatic) "
			"SELECT l.id, l.origin, l.name, l.version, l.comment, l.desc, l.message, l.arch, "
			"l.osversion, l.maintainer, l.www, l.prefix, l.flatsize, r.version AS newversion, "
			"r.flatsize AS newflatsize, r.pkgsize, r.cksum, r.repopath, r.automatic "
			"FROM main.packages AS l, pkgjobs AS r WHERE l.origin = r.origin "
-
			"AND (PKGLT(l.version, r.version) OR (l.name != r.name))");
+
			"AND (PKGLT(l.version, r.version) OR (l.name != r.name))";

-
	if (sqlite3_prepare_v2(db->sqlite, sql, -1, &stmt, NULL) != SQLITE_OK) {
+
	/* Working on multiple repositories */
+
	multirepos_enabled = pkg_config("PKG_MULTIREPOS");
+

+
	if (multirepos_enabled && (strcasecmp(multirepos_enabled, "yes") == 0)) {
+
		if (repo != NULL) {
+
			if (pkgdb_repos_new(db, &repos) != EPKG_OK) {
+
				pkg_emit_error("cannot get the attached databases");
+
				sbuf_delete(sql);
+
				return (NULL);
+
			}
+

+
			if (pkg_repos_exists(repos, repo) != EPKG_OK) {
+
				pkg_emit_error("repository '%s' does not exists", repo);
+
				pkg_repos_free(repos);
+
				sbuf_delete(sql);
+
				return (NULL);
+
			}
+

+
			reponame = repo;
+
			pkg_repos_free(repos);
+
		} else {
+
			/* default repository in multi-repos is 'default' */
+
			reponame = "default";
+
		}
+
	} else {
+
		/* default repository in single-repo is 'remote' */
+
		reponame = "remote";
+
	}
+

+
	create_temporary_pkgjobs(db->sqlite);
+

+
	sbuf_printf(sql, pkgjobs_sql_1, reponame, reponame);
+
	sql_exec(db->sqlite, sbuf_get(sql));
+

+
	/* Remove packages already installed and in the latest version */
+
	sql_exec(db->sqlite, "DELETE from pkgjobs where (select p.origin from main.packages as p where p.origin=pkgjobs.origin and version=pkgjobs.version) IS NOT NULL;");
+

+
	sbuf_reset(sql);
+
	sbuf_printf(sql, pkgjobs_sql_2, reponame, reponame, reponame);
+

+
	do {
+
		sql_exec(db->sqlite, sbuf_get(sql));
+
	} while (sqlite3_changes(db->sqlite) != 0);
+

+
	/* Determine if there is an upgrade needed */
+
	sql_exec(db->sqlite, pkgjobs_sql_3);
+

+
	sbuf_reset(sql);
+
	sbuf_printf(sql, finalsql, reponame, reponame);
+

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

+
	sbuf_finish(sql);
+
	sbuf_delete(sql);
+

	return (pkgdb_it_new(db, stmt, PKG_REMOTE));
}

struct pkgdb_it *
-
pkgdb_query_downgrades(struct pkgdb *db)
+
pkgdb_query_downgrades(struct pkgdb *db, const char *repo)
{
+
	struct pkg_repos *repos = NULL;
+
	struct sbuf *sql = sbuf_new_auto();
+
	const char *reponame = NULL;
	sqlite3_stmt *stmt = NULL;
+
	const char *multirepos_enabled = NULL;

	assert(db != NULL);

@@ -2016,17 +2262,47 @@ pkgdb_query_downgrades(struct pkgdb *db)
		return (NULL);
	}

-
	const char sql[] = ""
+
	const char finalsql[] = ""
		"SELECT l.id, l.origin AS origin, l.name AS name, l.version AS version, l.comment AS comment, l.desc AS desc, "
		"l.message AS message, l.arch AS arch, l.osversion AS osversion, l.maintainer AS maintainer, "
		"l.www AS www, l.prefix AS prefix, l.flatsize AS flatsize, r.version AS version, r.flatsize AS newflatsize, "
-
		"r.pkgsize AS pkgsize, r.path AS repopath "
+
		"r.pkgsize AS pkgsize, r.path AS repopath, '%s' AS dbname "
		"FROM main.packages AS l, "
-
		"remote.packages AS r "
+
		"'%s'.packages AS r "
		"WHERE l.origin = r.origin "
		"AND PKGGT(l.version, r.version)";

-
	if (sqlite3_prepare_v2(db->sqlite, sql, -1, &stmt, NULL) != SQLITE_OK) {
+
	/* Working on multiple repositories */
+
	multirepos_enabled = pkg_config("PKG_MULTIREPOS");
+

+
	if (multirepos_enabled && (strcasecmp(multirepos_enabled, "yes") == 0)) {
+
		if (repo != NULL) {
+
			if (pkgdb_repos_new(db, &repos) != EPKG_OK) {
+
				pkg_emit_error("cannot get the attached databases");
+
				return (NULL);
+
			}
+

+
			if (pkg_repos_exists(repos, repo) != EPKG_OK) {
+
				pkg_emit_error("repository '%s' does not exists", repo);
+
				pkg_repos_free(repos);
+
				return (NULL);
+
			}
+

+
			reponame = repo;
+
			pkg_repos_free(repos);
+
		} else {
+
			/* default repository in multi-repos is 'default' */
+
			reponame = "default";
+
		}
+
	} else {
+
		/* default repository in single-repo is 'remote' */
+
		reponame = "remote";
+
	}
+

+
	sbuf_printf(sql, finalsql, reponame, reponame);
+
	sbuf_finish(sql);
+

+
	if (sqlite3_prepare_v2(db->sqlite, sbuf_get(sql), -1, &stmt, NULL) != SQLITE_OK) {
		ERROR_SQLITE(db->sqlite);
		return (NULL);
	}
@@ -2156,25 +2432,11 @@ pkgdb_query_delete(struct pkgdb *db, match_t match, int nbpkgs, char **pkgs, int
	return (pkgdb_it_new(db, stmt, PKG_INSTALLED));
}

-
struct pkgdb_it *
-
pkgdb_rquery(struct pkgdb *db, const char *pattern, match_t match, pkgdb_field field)
+
static int
+
pkgdb_rquery_build_search_query(struct sbuf *sql, match_t match, unsigned int field)
{
-
	sqlite3_stmt *stmt = NULL;
-
	struct sbuf *sql = sbuf_new_auto();
-
	const char *what = NULL;
	const char *how = NULL;
-

-
	assert(db != NULL);
-
	assert(pattern != NULL && pattern[0] != '\0');
-

-
	if (db->type != PKGDB_REMOTE) {
-
		pkg_emit_error("remote database not attached (misuse)");
-
		return (NULL);
-
	}
-

-
	sbuf_cat(sql, "SELECT id, origin, name, version, comment, prefix, "
-
			"desc, arch, arch, osversion, maintainer, www, licenselogic, "
-
			"flatsize AS newflatsize, pkgsize, cksum, path AS repopath FROM remote.packages");
+
	const char *what = NULL;

	switch (match) {
		case MATCH_ALL:
@@ -2215,14 +2477,100 @@ pkgdb_rquery(struct pkgdb *db, const char *pattern, match_t match, pkgdb_field f
			break;
	}

-
	if (what != NULL && how != NULL) {
-
		sbuf_cat(sql, " WHERE ");
+
	if (what != NULL && how != NULL)
		sbuf_printf(sql, how, what);
+

+
	return (EPKG_OK);
+
}
+

+
struct pkgdb_it *
+
pkgdb_rquery(struct pkgdb *db, const char *pattern, match_t match, unsigned int field, const char *reponame)
+
{
+
	const char *dbname = NULL;
+
	sqlite3_stmt *stmt = NULL;
+
	struct sbuf *sql = NULL;
+
	struct pkg_repos *repos = NULL;
+
	struct pkg_repos_entry *re = NULL;
+
	const char *multirepos_enabled = NULL;
+
	const char *basesql = ""
+
				"SELECT id, origin, name, version, comment, "
+
					"prefix, desc, arch, osversion, maintainer, www, "
+
					"licenselogic, flatsize AS newflatsize, pkgsize, "
+
					"cksum, path AS repopath ";
+
	const char *multireposql = ""
+
				"SELECT id, origin, name, version, comment, "
+
					"prefix, desc, arch, osversion, maintainer, www, "
+
					"licenselogic, flatsize, pkgsize, "
+
					"cksum, path, '%s' AS dbname "
+
					"FROM '%s'.packages ";
+

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

+
	if (db->type != PKGDB_REMOTE) {
+
		pkg_emit_error("%s", "remote database not attached (misuse)");
+
		return (NULL);
+
	}
+

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

+
	multirepos_enabled = pkg_config("PKG_MULTIREPOS");
+
	    
+
	if (multirepos_enabled && (strcasecmp(multirepos_enabled, "yes") == 0)) {
+
		/*
+
		 * Working on multiple remote repositories
+
		 */
+

+
		/* add the dbname column to the SELECT */
+
		sbuf_cat(sql, ", dbname FROM ");
+

+
		if (pkgdb_repos_new(db, &repos) != EPKG_OK) {
+
			pkg_emit_error("pkgdb_rquery(): %s", "cannot get the attached databases");
+
			return (NULL);
+
		}
+

+
		if (reponame != NULL)
+
			if (pkg_repos_switch(repos, reponame) != EPKG_OK) {
+
				pkg_emit_error("pkgdb_rquery(): %s", "cannot switch to repository");
+
				return (NULL);
+
			}
+

+
		/* get the first repository entry */
+
		if (pkg_repos_next(repos, &re) == EPKG_OK) {
+
			dbname = pkg_repos_get_name(re);
+
			sbuf_cat(sql, "(");
+
			sbuf_printf(sql, multireposql, dbname, dbname);
+
		} else {
+
			/* there are no remote databases attached */
+
			sbuf_finish(sql);
+
			sbuf_delete(sql);
+
			pkg_repos_free(repos);
+
			return (NULL);
+
		}
+

+
		while (pkg_repos_next(repos, &re) == EPKG_OK) {
+
			dbname = pkg_repos_get_name(re);
+
			sbuf_cat(sql, " UNION ALL ");
+
			sbuf_printf(sql, multireposql, dbname, dbname);
+
		}
+

+
		/* close the UNIONs and build the search query */
+
		sbuf_cat(sql, ") WHERE ");
+
		pkgdb_rquery_build_search_query(sql, match, field);
+
		sbuf_finish(sql);
+
		pkg_repos_free(repos);
+
	} else {
+
		/* 
+
		 * Working on a single remote repository
+
		 */
+

+
		sbuf_cat(sql, ", 'remote' AS dbname FROM remote.packages WHERE ");
+
		pkgdb_rquery_build_search_query(sql, match, field);
+
		sbuf_cat(sql, ";");
+
		sbuf_finish(sql);
	}
-
	sbuf_cat(sql, ";");
-
	sbuf_finish(sql);

-
	if (sqlite3_prepare_v2(db->sqlite, sbuf_data(sql), -1, &stmt, NULL) != SQLITE_OK) {
+
	if (sqlite3_prepare_v2(db->sqlite, sbuf_get(sql), -1, &stmt, NULL) != SQLITE_OK) {
		ERROR_SQLITE(db->sqlite);
		return (NULL);
	}
modified pkg/clean.c
@@ -60,7 +60,7 @@ exec_clean(int argc, char **argv)
		}

		it = pkgdb_rquery(db, pkg_get(pkg, PKG_ORIGIN), MATCH_EXACT,
-
							FIELD_ORIGIN);
+
							FIELD_ORIGIN, NULL);

		if (it == NULL) {
			warnx("skipping %s", ent->fts_path);
modified pkg/install.c
@@ -17,7 +17,7 @@
void
usage_install(void)
{
-
	fprintf(stderr, "usage: pkg install [-ygxX] <pkg-name> <...>\n\n");
+
	fprintf(stderr, "usage: pkg install [-r reponame] [-ygxX] <pkg-name> <...>\n\n");
	fprintf(stderr, "For more information see 'pkg help install'.\n");
}

@@ -28,6 +28,7 @@ exec_install(int argc, char **argv)
	struct pkgdb_it *it = NULL;
	struct pkgdb *db = NULL;
	struct pkg_jobs *jobs = NULL;
+
	const char *reponame = NULL;
	int retcode = 1;
	int ch, yes = 0;
	int64_t dlsize = 0;
@@ -36,7 +37,7 @@ exec_install(int argc, char **argv)
	match_t match = MATCH_EXACT;
	const char *assume_yes = NULL;

-
	while ((ch = getopt(argc, argv, "ygxX")) != -1) {
+
	while ((ch = getopt(argc, argv, "ygxXr:")) != -1) {
		switch (ch) {
			case 'y':
				yes = 1;
@@ -50,6 +51,9 @@ exec_install(int argc, char **argv)
			case 'X':
				match = MATCH_EREGEX;
				break;
+
			case 'r':
+
				reponame = optarg;
+
				break;
			default:
				usage_install();
				return (EX_USAGE);
@@ -76,7 +80,7 @@ exec_install(int argc, char **argv)
		goto cleanup;
	}

-
	if ((it = pkgdb_query_installs(db, match, argc, argv)) == NULL)
+
	if ((it = pkgdb_query_installs(db, match, argc, argv, reponame)) == NULL)
		goto cleanup;

	while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC|PKG_LOAD_DEPS) == EPKG_OK) {
@@ -93,6 +97,7 @@ exec_install(int argc, char **argv)
	/* print a summary before applying the jobs */
	pkg = NULL;
	printf("The following packages will be installed:\n");
+

	while (pkg_jobs(jobs, &pkg) == EPKG_OK) {
		dlsize += pkg_new_pkgsize(pkg);
		if (pkg_get(pkg, PKG_NEWVERSION) != NULL) {
added pkg/pkg-repositories.5
@@ -0,0 +1,88 @@
+
.\"
+
.\" 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.1
+
.\" $FreeBSD$
+
.\"
+
.Dd Aug 23, 2011
+
.Dt PKG-REPOSITORIES 5
+
.Os
+
.Sh NAME
+
.Nm "repositories"
+
.Nd System-wide repositories file for
+
.Xr pkg 1
+
.Sh DESCRIPTION
+
This file contains the system-wide definitions
+
of remote repositories in
+
.Xr pkg 1
+
.Pp
+
The location of this file is
+
.Fa /etc/pkg/repositories
+
.Pp
+
Lines in the file beginning with a "#" or ";" are comments
+
and are ignored.
+
.Pp
+
A repository in the file is defined in the form of
+
.Fa name = url
+
.Pp
+
The following names of repositories cannot be used as
+
they are already reserved, or being used internally by
+
.Xr pkg 1
+
and thus cannot be re-defined - \fBmain\fP, \fBtemp\fP, \fBrepo\fP and \fBmemdb\fP
+
.Pp
+
When working on multiple remote repositories
+
.Xr pkg 1
+
will install a package and it's dependencies only from the 
+
first-matching repository.
+
.Pp
+
That is why you should always have a default repository, which is the first
+
defined repository. Your default repository should always be set to an
+
official
+
.Xr pkg 1
+
repository or a mirror of it.
+
.Pp
+
.Sh OPTIONS
+
The following options can be defined in
+
.Nm :
+
.Bl -tag -width F1
+
.El
+
.Sh WARNING
+
.Sh ENVRIOMENT
+
The following environment variables affect the execution of
+
.Xr pkg 1
+
.Bl -tag -width ".Ev PACKAGESITE"
+
.It Ev PACKAGESITE
+
Disables the use of multiple remote repository and switches to a
+
single repository use, which is the defined by the environment 
+
variable's value.
+
.El
+
.Sh FILES
+
.Sh SEE ALSO
+
.Xr pkg 1 ,
+
.Xr pkg-add 1 ,
+
.Xr pkg-autoremove 1 ,
+
.Xr pkg-search 1 ,
+
.Xr pkg-backup 1 ,
+
.Xr pkg-install 1 ,
+
.Xr pkg-delete 1 ,
+
.Xr pkg-info 1 ,
+
.Xr pkg-register 1 ,
+
.Xr pkg-repo 1 ,
+
.Xr pkg-update 1 ,
+
.Xr pkg-upgrade 1 ,
+
.Xr pkg-version 1 ,
+
.Xr pkg-which 1 ,
+
.Xr pkg.conf 5
+
.Sh AUTHORS AND CONTRIBUTORS
+
.Sh BUGS
modified pkg/pkg.conf.sample
@@ -8,4 +8,5 @@ PKG_CACHEDIR : /var/cache/pkg
PORTSDIR	    : /usr/ports
PUBKEY		    : /etc/ssl/pkg.conf
HANDLE_RC_SCRIPTS   : NO
+
PKG_MULTIREPOS	    : NO
ASSUME_ALWAYS_YES   : NO
modified pkg/repositories
@@ -1,7 +1,10 @@
# Repositories file for pkgng
# This file should be installed in /etc/pkg/repositories
# For more information on the format of this files
-
# please refer to pkg(1) man page
+
# please refer to the pkg-repositories(1) man page
+

+
# default repository (should be set to an official pkgng repository)
+
default		=	ftp://ftp.FreeBSD.org/pkgng/

# dnaeon's i386 repository
dnaeon-i386	=	http://unix-heaven.org/FreeBSD/pkgng/dnaeon-i386/
modified pkg/search.c
@@ -1,5 +1,6 @@
#include <stdio.h>
#include <stdbool.h>
+
#include <string.h>
#include <unistd.h>
#include <sysexits.h>

@@ -11,26 +12,27 @@
void
usage_search(void)
{
-
	fprintf(stderr, "usage: pkg search <pkg-name>\n");
-
	fprintf(stderr, "       pkg search [-fDsqop] <pkg-name>\n");
-
	fprintf(stderr, "       pkg search [-gxXcdfDsqop] <pattern>\n\n");
+
	fprintf(stderr, "usage: pkg search [-r reponame] <pkg-name>\n");
+
	fprintf(stderr, "       pkg search [-r reponame] [-fDsqop] <pkg-name>\n");
+
	fprintf(stderr, "       pkg search [-r reponame] [-gxXcdfDsqop] <pattern>\n\n");
	fprintf(stderr, "For more information see 'pkg help search'.\n");
}

int
exec_search(int argc, char **argv)
{
+
	const char *pattern = NULL;
+
	const char *reponame = NULL;
	int retcode = EPKG_OK, ch;
	int flags = PKG_LOAD_BASIC;
	unsigned int opt = 0;
	match_t match = MATCH_EXACT;
	pkgdb_field field = FIELD_NAME;
-
	const char *pattern = NULL;
	struct pkgdb *db = NULL;
	struct pkgdb_it *it = NULL;
	struct pkg *pkg = NULL;

-
	while ((ch = getopt(argc, argv, "gxXcdfDsqop")) != -1) {
+
	while ((ch = getopt(argc, argv, "gxXcdr:fDsqop")) != -1) {
		switch (ch) {
			case 'g':
				match = MATCH_GLOB;
@@ -47,6 +49,8 @@ exec_search(int argc, char **argv)
			case 'd':
				field = FIELD_DESC;
				break;
+
			case 'r':
+
				reponame = optarg;
			case 'f':
				opt |= INFO_FULL;
				flags |= PKG_LOAD_CATEGORIES|PKG_LOAD_LICENSES|PKG_LOAD_OPTIONS;
@@ -86,7 +90,7 @@ exec_search(int argc, char **argv)
	if (pkgdb_open(&db, PKGDB_REMOTE) != EPKG_OK)
		return (EX_IOERR);

-
	if ((it = pkgdb_rquery(db, pattern, match, field)) == NULL) {
+
	if ((it = pkgdb_rquery(db, pattern, match, field, reponame)) == NULL) {
		pkgdb_close(db);
		return (1);
	}
modified pkg/update.c
@@ -21,47 +21,22 @@
		ARCHIVE_EXTRACT_TIME  |ARCHIVE_EXTRACT_ACL | \
		ARCHIVE_EXTRACT_FFLAGS|ARCHIVE_EXTRACT_XATTR)

-
void
-
usage_update(void)
-
{
-
	fprintf(stderr, "usage: pkg update\n\n");
-
	fprintf(stderr, "For more information see 'pkg help update'.\n");
-
}
+
static int update_from_remote_repo(const char *name, const char *url);

-
int
-
exec_update(int argc, char **argv)
+
static int
+
update_from_remote_repo(const char *name, const char *url)
{
-
	char url[MAXPATHLEN + 1];
-
	const char *packagesite = NULL;
+
	struct archive *a = NULL;
+
	struct archive_entry *ae = NULL;
+
	char repofile[MAXPATHLEN];
+
	char repofile_unchecked[MAXPATHLEN];
	char *tmp = NULL;
-
	int retcode = EPKG_OK;
-
	struct archive *a;
-
	struct archive_entry *ae;
+
	const char *dbdir = NULL;
	unsigned char *sig = NULL;
	int siglen = 0;

-
	(void)argv;
-
	if (argc != 1) {
-
		usage_update();
-
		return (EX_USAGE);
-
	}
-

-
	if (geteuid() != 0) {
-
		warnx("updating the remote database can only be done as root");
-
		return (EX_NOPERM);
-
	}
-

-
	if ((packagesite = pkg_config("PACKAGESITE")) == NULL) {
-
		warnx("unable to determine PACKAGESITE");
-
		return (EPKG_FATAL);
-
	}
-

-
	if (packagesite[strlen(packagesite) - 1] == '/')
-
		snprintf(url, sizeof(url), "%srepo.txz", packagesite);
-
	else
-
		snprintf(url, sizeof(url), "%s/repo.txz", packagesite);
-

-
	tmp = mktemp(strdup("/tmp/repo.txz.XXXXXX"));
+
	tmp   = mktemp(strdup("/tmp/repo.txz.XXXXXX"));
+
	dbdir = pkg_config("PKG_DBDIR");

	if (pkg_fetch_file(url, tmp) != EPKG_OK) {
		unlink(tmp);
@@ -77,7 +52,9 @@ exec_update(int argc, char **argv)

	while (archive_read_next_header(a, &ae) == ARCHIVE_OK) {
		if (strcmp(archive_entry_pathname(ae), "repo.sqlite") == 0) {
-
			archive_entry_set_pathname(ae, "/var/db/pkg/repo.sqlite.unchecked");
+
			snprintf(repofile, sizeof(repofile), "%s/%s.sqlite", dbdir, name);
+
			snprintf(repofile_unchecked, sizeof(repofile_unchecked), "%s.unchecked", repofile);
+
			archive_entry_set_pathname(ae, repofile_unchecked);
			archive_read_extract(a, ae, EXTRACT_ARCHIVE_FLAGS);
		}
		if (strcmp(archive_entry_pathname(ae), "signature") == 0) {
@@ -88,20 +65,96 @@ exec_update(int argc, char **argv)
	}

	if (sig != NULL) {
-
		if (pkg_repo_verify( "/var/db/pkg/repo.sqlite.unchecked", sig, siglen - 1) != EPKG_OK) {
-
			fprintf(stderr, "Invalid signature removing\n");
-
			unlink("/var/db/pkg/repo.sqlite.unchecked");
+
		if (pkg_repo_verify(repofile_unchecked, sig, siglen - 1) != EPKG_OK) {
+
			warnx("Invalid signature, removing repository.\n");
+
			unlink(repofile_unchecked);
			free(sig);
			return (EPKG_FATAL);
		}
	}
-
	rename("/var/db/pkg/repo.sqlite.unchecked", "/var/db/pkg/repo.sqlite");

-
	if ( a != NULL)
+
	rename(repofile_unchecked, repofile);
+

+
	if (a != NULL)
		archive_read_finish(a);

	unlink(tmp);
	free(tmp);

+
	return (EPKG_OK);
+
}
+

+
void
+
usage_update(void)
+
{
+
	fprintf(stderr, "usage: pkg update\n\n");
+
	fprintf(stderr, "For more information see 'pkg help update'.\n");
+
}
+

+
int
+
exec_update(int argc, char **argv)
+
{
+
	char url[MAXPATHLEN];
+
	const char *packagesite = NULL;
+
	int retcode = EPKG_OK;
+
	struct pkg_repos *repos = NULL;
+
	struct pkg_repos_entry *re = NULL;
+
	const char *multirepos_enabled = NULL;
+

+
	(void)argv;
+
	if (argc != 1) {
+
		usage_update();
+
		return (EX_USAGE);
+
	}
+

+
	if (geteuid() != 0) {
+
		warnx("updating the remote database can only be done as root");
+
		return (EX_NOPERM);
+
	}
+

+
	/* 
+
	 * Fetch remote databases.
+
	 */
+
	multirepos_enabled = pkg_config("PKG_MULTIREPOS");
+

+
	if (multirepos_enabled && (strcasecmp(multirepos_enabled, "yes") != 0)) {
+
		/*
+
		 * Single remote database
+
		 */
+

+
		if ((packagesite = pkg_config("PACKAGESITE")) == NULL) {
+
			warnx("PACKAGESITE is not defined.");
+
			return (1);
+
		}
+

+
		if (packagesite[strlen(packagesite) - 1] == '/')
+
			snprintf(url, MAXPATHLEN, "%srepo.txz", packagesite);
+
		else
+
			snprintf(url, MAXPATHLEN, "%s/repo.txz", packagesite);
+

+
		retcode = update_from_remote_repo("repo", url);
+
	} else {
+
		if (pkg_repos_new(&repos) != EPKG_OK)
+
			return (1);
+

+
		if (pkg_repos_load(repos) != EPKG_OK) {
+
			pkg_repos_free(repos);
+
			return (1);
+
		}
+

+
		while (pkg_repos_next(repos, &re) == EPKG_OK) {
+
			packagesite = pkg_repos_get_url(re);
+

+
			if (packagesite[strlen(packagesite) - 1] == '/')
+
				snprintf(url, MAXPATHLEN, "%srepo.txz", packagesite);
+
			else
+
				snprintf(url, MAXPATHLEN, "%s/repo.txz", packagesite);
+

+
			retcode = update_from_remote_repo(pkg_repos_get_name(re), url);
+
		}
+

+
		pkg_repos_free(repos);
+
	}
+

	return (retcode);
}
modified pkg/upgrade.c
@@ -18,7 +18,7 @@
void
usage_upgrade(void)
{
-
	fprintf(stderr, "usage pkg upgrade [-y]\n");
+
	fprintf(stderr, "usage pkg upgrade [-r reponame] [-y]\n");
	fprintf(stderr, "For more information see 'pkg help upgrade'.\n");
}

@@ -29,6 +29,7 @@ exec_upgrade(int argc, char **argv)
	struct pkgdb_it *it = NULL;
	struct pkg *pkg = NULL;
	struct pkg_jobs *jobs = NULL;
+
	const char *reponame = NULL;
	int retcode = 1;
	int64_t oldsize = 0, newsize = 0;
	int64_t dlsize = 0;
@@ -41,11 +42,14 @@ exec_upgrade(int argc, char **argv)
		return (EX_NOPERM);
	}

-
	while ((ch = getopt(argc, argv, "y")) != -1) {
+
	while ((ch = getopt(argc, argv, "yr:")) != -1) {
		switch (ch) {
			case 'y':
				yes = 1;
				break;
+
			case 'r':
+
				reponame = optarg;
+
				break;
			default:
				break;
		}
@@ -66,7 +70,7 @@ exec_upgrade(int argc, char **argv)
		goto cleanup;
	}

-
	if ((it = pkgdb_query_upgrades(db)) == NULL) {
+
	if ((it = pkgdb_query_upgrades(db, reponame)) == NULL) {
		goto cleanup;
	}

modified pkg/utils.c
@@ -36,6 +36,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;
+
	const char *multirepos_enabled = NULL;
        char size[7];

        if (opt & INFO_FULL) {
@@ -44,6 +45,16 @@ print_info(struct pkg * const pkg, unsigned int opt)
                printf("%-15s: %s\n", "Origin", pkg_get(pkg, PKG_ORIGIN));
                printf("%-15s: %s\n", "Prefix", pkg_get(pkg, PKG_PREFIX));

+
		if (pkg_type(pkg) == PKG_REMOTE) {
+
			multirepos_enabled = pkg_config("PKG_MULTIREPOS");
+

+
			if (multirepos_enabled && (strcasecmp(multirepos_enabled, "yes") == 0)) {
+
				printf("%-15s: %s [%s]\n", "Repository",
+
						pkg_get(pkg, PKG_REPONAME),
+
						pkg_get(pkg, PKG_REPOURL));
+
			}
+
		}
+

                if (!pkg_list_is_empty(pkg, PKG_CATEGORIES)) {
                        printf("%-15s:", "Categories");
                        while (pkg_categories(pkg, &cat) == EPKG_OK)