diff options
Diffstat (limited to 'lib/libalpm/deps.c')
-rw-r--r-- | lib/libalpm/deps.c | 228 |
1 files changed, 149 insertions, 79 deletions
diff --git a/lib/libalpm/deps.c b/lib/libalpm/deps.c index 26f9b16d..dca8877e 100644 --- a/lib/libalpm/deps.c +++ b/lib/libalpm/deps.c @@ -1,7 +1,7 @@ /* * deps.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * Copyright (c) 2005 by Aurelien Foret <orelien@chez.com> * Copyright (c) 2005, 2006 by Miklos Vajna <vmiklos@frugalware.org> @@ -34,7 +34,6 @@ #include "graph.h" #include "package.h" #include "db.h" -#include "cache.h" #include "handle.h" void _alpm_dep_free(pmdepend_t *dep) @@ -196,36 +195,25 @@ pmpkg_t *_alpm_find_dep_satisfier(alpm_list_t *pkgs, pmdepend_t *dep) for(i = pkgs; i; i = alpm_list_next(i)) { pmpkg_t *pkg = i->data; - if(alpm_depcmp(pkg, dep)) { + if(_alpm_depcmp_tolerant(pkg, dep)) { return(pkg); } } return(NULL); } -/** Checks dependencies and returns missing ones in a list. - * Dependencies can include versions with depmod operators. - * @param db pointer to the local package database - * @param targets an alpm_list_t* of dependencies strings to satisfy - * @return an alpm_list_t* of missing dependencies strings +/** Find a package satisfying a specified dependency. + * The dependency can include versions with depmod operators. + * @param pkgs an alpm_list_t* of pmpkg_t where the satisfier will be searched + * @param depstring package or provision name, versioned or not + * @return a pmpkg_t* satisfying depstring */ -alpm_list_t SYMEXPORT *alpm_deptest(pmdb_t *db, alpm_list_t *targets) +pmpkg_t SYMEXPORT *alpm_find_satisfier(alpm_list_t *pkgs, const char *depstring) { - alpm_list_t *i, *ret = NULL; - - for(i = targets; i; i = alpm_list_next(i)) { - pmdepend_t *dep; - char *target; - - target = alpm_list_getdata(i); - dep = _alpm_splitdep(target); - - if(!_alpm_find_dep_satisfier(_alpm_db_get_pkgcache(db), dep)) { - ret = alpm_list_add(ret, target); - } - _alpm_dep_free(dep); - } - return(ret); + pmdepend_t *dep = _alpm_splitdep(depstring); + pmpkg_t *pkg = _alpm_find_dep_satisfier(pkgs, dep); + _alpm_dep_free(dep); + return(pkg); } /** Checks dependencies and returns missing ones in a list. @@ -248,8 +236,8 @@ alpm_list_t SYMEXPORT *alpm_checkdeps(alpm_list_t *pkglist, int reversedeps, targets = alpm_list_join(alpm_list_copy(remove), alpm_list_copy(upgrade)); for(i = pkglist; i; i = i->next) { - void *pkg = i->data; - if(alpm_list_find(targets, pkg, _alpm_pkg_cmp)) { + pmpkg_t *pkg = i->data; + if(_alpm_pkg_find(targets, pkg->name)) { modified = alpm_list_add(modified, pkg); } else { dblist = alpm_list_add(dblist, pkg); @@ -331,89 +319,116 @@ static int dep_vercmp(const char *version1, pmdepmod_t mod, return(equal); } -int SYMEXPORT alpm_depcmp(pmpkg_t *pkg, pmdepend_t *dep) +/* nodepversion: skip version checking */ +static int _depcmp(pmpkg_t *pkg, pmdepend_t *dep, int nodepversion) { alpm_list_t *i; - - ALPM_LOG_FUNC; - - const char *pkgname = alpm_pkg_get_name(pkg); - const char *pkgversion = alpm_pkg_get_version(pkg); int satisfy = 0; + int depmod; + + if(nodepversion) { + depmod = PM_DEP_MOD_ANY; + } else { + depmod = dep->mod; + } /* check (pkg->name, pkg->version) */ - satisfy = (strcmp(pkgname, dep->name) == 0 - && dep_vercmp(pkgversion, dep->mod, dep->version)); + if(pkg->name_hash && dep->name_hash + && pkg->name_hash != dep->name_hash) { + /* skip more expensive checks */ + } else { + satisfy = (strcmp(pkg->name, dep->name) == 0 + && dep_vercmp(pkg->version, depmod, dep->version)); + if(satisfy) { + return(satisfy); + } + } /* check provisions, format : "name=version" */ for(i = alpm_pkg_get_provides(pkg); i && !satisfy; i = i->next) { - char *provname = strdup(i->data); - char *provver = strchr(provname, '='); + const char *provision = i->data; + const char *provver = strchr(provision, '='); if(provver == NULL) { /* no provision version */ - satisfy = (dep->mod == PM_DEP_MOD_ANY - && strcmp(provname, dep->name) == 0); + satisfy = (depmod == PM_DEP_MOD_ANY + && strcmp(provision, dep->name) == 0); } else { - *provver = '\0'; + /* This is a bit tricker than the old code for performance reasons. To + * prevent the need to copy and duplicate strings, strncmp only the name + * portion if they are the same length, since there is a version and + * operator in play here. Cast is to silence sign conversion warning; + * we know provver >= provision if we are here. */ + size_t namelen = (size_t)(provver - provision); provver += 1; - satisfy = (strcmp(provname, dep->name) == 0 - && dep_vercmp(provver, dep->mod, dep->version)); + satisfy = (strlen(dep->name) == namelen + && strncmp(provision, dep->name, namelen) == 0 + && dep_vercmp(provver, depmod, dep->version)); } - free(provname); } return(satisfy); } +/* tolerant : respects NODEPVERSION flag */ +int _alpm_depcmp_tolerant(pmpkg_t *pkg, pmdepend_t *dep) +{ + int nodepversion = 0; + int flags = alpm_trans_get_flags(); + + if (flags != -1) { + nodepversion = flags & PM_TRANS_FLAG_NODEPVERSION; + } + + return(_depcmp(pkg, dep, nodepversion)); +} + +/* strict : ignores NODEPVERSION flag */ +int _alpm_depcmp(pmpkg_t *pkg, pmdepend_t *dep) +{ + return(_depcmp(pkg, dep, 0)); +} + pmdepend_t *_alpm_splitdep(const char *depstring) { pmdepend_t *depend; - char *ptr = NULL; - char *newstr = NULL; + const char *ptr, *version = NULL; if(depstring == NULL) { return(NULL); } - STRDUP(newstr, depstring, RET_ERR(PM_ERR_MEMORY, NULL)); CALLOC(depend, 1, sizeof(pmdepend_t), RET_ERR(PM_ERR_MEMORY, NULL)); /* Find a version comparator if one exists. If it does, set the type and * increment the ptr accordingly so we can copy the right strings. */ - if((ptr = strstr(newstr, ">="))) { + if((ptr = strstr(depstring, ">="))) { depend->mod = PM_DEP_MOD_GE; - *ptr = '\0'; - ptr += 2; - } else if((ptr = strstr(newstr, "<="))) { + version = ptr + 2; + } else if((ptr = strstr(depstring, "<="))) { depend->mod = PM_DEP_MOD_LE; - *ptr = '\0'; - ptr += 2; - } else if((ptr = strstr(newstr, "="))) { /* Note: we must do =,<,> checks after <=, >= checks */ + version = ptr + 2; + } else if((ptr = strstr(depstring, "="))) { + /* Note: we must do =,<,> checks after <=, >= checks */ depend->mod = PM_DEP_MOD_EQ; - *ptr = '\0'; - ptr += 1; - } else if((ptr = strstr(newstr, "<"))) { + version = ptr + 1; + } else if((ptr = strstr(depstring, "<"))) { depend->mod = PM_DEP_MOD_LT; - *ptr = '\0'; - ptr += 1; - } else if((ptr = strstr(newstr, ">"))) { + version = ptr + 1; + } else if((ptr = strstr(depstring, ">"))) { depend->mod = PM_DEP_MOD_GT; - *ptr = '\0'; - ptr += 1; + version = ptr + 1; } else { - /* no version specified - copy the name and return it */ + /* no version specified, leave version and ptr NULL */ depend->mod = PM_DEP_MOD_ANY; - STRDUP(depend->name, newstr, RET_ERR(PM_ERR_MEMORY, NULL)); - depend->version = NULL; - free(newstr); - return(depend); } - /* if we get here, we have a version comparator, copy the right parts - * to the right places */ - STRDUP(depend->name, newstr, RET_ERR(PM_ERR_MEMORY, NULL)); - STRDUP(depend->version, ptr, RET_ERR(PM_ERR_MEMORY, NULL)); - free(newstr); + /* copy the right parts to the right places */ + STRNDUP(depend->name, depstring, ptr - depstring, + RET_ERR(PM_ERR_MEMORY, NULL)); + depend->name_hash = _alpm_hash_sdbm(depend->name); + if(version) { + STRDUP(depend->version, version, RET_ERR(PM_ERR_MEMORY, NULL)); + } return(depend); } @@ -424,6 +439,7 @@ pmdepend_t *_alpm_dep_dup(const pmdepend_t *dep) CALLOC(newdep, 1, sizeof(pmdepend_t), RET_ERR(PM_ERR_MEMORY, NULL)); STRDUP(newdep->name, dep->name, RET_ERR(PM_ERR_MEMORY, NULL)); + newdep->name_hash = dep->name_hash; STRDUP(newdep->version, dep->version, RET_ERR(PM_ERR_MEMORY, NULL)); newdep->mod = dep->mod; @@ -459,7 +475,7 @@ static int can_remove_package(pmdb_t *db, pmpkg_t *pkg, alpm_list_t *targets, * if checkdeps detected it would break something */ /* see if other packages need it */ - for(i = _alpm_db_get_pkgcache(db); i; i = i->next) { + for(i = _alpm_db_get_pkgcache_list(db); i; i = i->next) { pmpkg_t *lpkg = i->data; if(_alpm_dep_edge(lpkg, pkg) && !_alpm_pkg_find(targets, lpkg->name)) { return(0); @@ -492,7 +508,7 @@ void _alpm_recursedeps(pmdb_t *db, alpm_list_t *targs, int include_explicit) for(i = targs; i; i = i->next) { pmpkg_t *pkg = i->data; - for(j = _alpm_db_get_pkgcache(db); j; j = j->next) { + for(j = _alpm_db_get_pkgcache_list(db); j; j = j->next) { pmpkg_t *deppkg = j->data; if(_alpm_dep_edge(pkg, deppkg) && can_remove_package(db, deppkg, targs, include_explicit)) { @@ -505,6 +521,28 @@ void _alpm_recursedeps(pmdb_t *db, alpm_list_t *targs, int include_explicit) } } +/** Find a package satisfying a specified dependency. + * First look for a literal, going through each db one by one. Then look for + * providers. The first satisfier found is returned. + * The dependency can include versions with depmod operators. + * @param dbs an alpm_list_t* of pmdb_t where the satisfier will be searched + * @param depstring package or provision name, versioned or not + * @return a pmpkg_t* satisfying depstring + */ +pmpkg_t SYMEXPORT *alpm_find_dbs_satisfier(alpm_list_t *dbs, const char *depstring) +{ + pmdepend_t *dep; + pmpkg_t *pkg; + + ASSERT(dbs, return(NULL)); + + dep = _alpm_splitdep(depstring); + ASSERT(dep, return(NULL)); + pkg = _alpm_resolvedep(dep, dbs, NULL, 1); + _alpm_dep_free(dep); + return(pkg); +} + /** * helper function for resolvedeps: search for dep satisfier in dbs * @@ -522,10 +560,14 @@ pmpkg_t *_alpm_resolvedep(pmdepend_t *dep, alpm_list_t *dbs, { alpm_list_t *i, *j; int ignored = 0; + + alpm_list_t *providers = NULL; + int count; + /* 1. literals */ for(i = dbs; i; i = i->next) { pmpkg_t *pkg = _alpm_db_get_pkgfromcache(i->data, dep->name); - if(pkg && alpm_depcmp(pkg, dep) && !_alpm_pkg_find(excluding, pkg->name)) { + if(pkg && _alpm_depcmp_tolerant(pkg, dep) && !_alpm_pkg_find(excluding, pkg->name)) { if(_alpm_pkg_should_ignore(pkg)) { int install = 0; if (prompt) { @@ -544,9 +586,9 @@ pmpkg_t *_alpm_resolvedep(pmdepend_t *dep, alpm_list_t *dbs, } /* 2. satisfiers (skip literals here) */ for(i = dbs; i; i = i->next) { - for(j = _alpm_db_get_pkgcache(i->data); j; j = j->next) { + for(j = _alpm_db_get_pkgcache_list(i->data); j; j = j->next) { pmpkg_t *pkg = j->data; - if(alpm_depcmp(pkg, dep) && strcmp(pkg->name, dep->name) && + if(_alpm_depcmp_tolerant(pkg, dep) && strcmp(pkg->name, dep->name) != 0 && !_alpm_pkg_find(excluding, pkg->name)) { if(_alpm_pkg_should_ignore(pkg)) { int install = 0; @@ -561,12 +603,40 @@ pmpkg_t *_alpm_resolvedep(pmdepend_t *dep, alpm_list_t *dbs, continue; } } - _alpm_log(PM_LOG_WARNING, _("provider package was selected (%s provides %s)\n"), - pkg->name, dep->name); - return(pkg); + _alpm_log(PM_LOG_DEBUG, "provider found (%s provides %s)\n", + pkg->name, dep->name); + providers = alpm_list_add(providers, pkg); + /* keep looking for other providers in the all dbs */ } } } + + /* first check if one provider is already installed locally */ + for(i = providers; i; i = i->next) { + pmpkg_t *pkg = i->data; + if (_alpm_pkghash_find(_alpm_db_get_pkgcache(handle->db_local), pkg->name)) { + alpm_list_free(providers); + return(pkg); + } + } + count = alpm_list_count(providers); + if (count >= 1) { + /* default to first provider if there is no QUESTION callback */ + int index = 0; + if(count > 1) { + /* if there is more than one provider, we ask the user */ + QUESTION(handle->trans, PM_TRANS_CONV_SELECT_PROVIDER, + providers, dep, NULL, &index); + } + if(index >= 0 && index < count) { + pmpkg_t *pkg = alpm_list_getdata(alpm_list_nth(providers, index)); + alpm_list_free(providers); + return(pkg); + } + alpm_list_free(providers); + providers = NULL; + } + if(ignored) { /* resolvedeps will override these */ pm_errno = PM_ERR_PKG_IGNORED; } else { @@ -672,7 +742,7 @@ int _alpm_dep_edge(pmpkg_t *pkg1, pmpkg_t *pkg2) { alpm_list_t *i; for(i = alpm_pkg_get_depends(pkg1); i; i = i->next) { - if(alpm_depcmp(pkg2, i->data)) { + if(_alpm_depcmp_tolerant(pkg2, i->data)) { return(1); } } |