[pacman-dev] [PATCH 00/15] Full optdep support with all the bells and whistles
Hi there! This should implement everything that's listed on this page: https://wiki.archlinux.org/index.php/User:Allan/Pacman_OptDepends I know that this is probably 4.1 material and will most likely need quite a bit of polish, but I'd like to gather some comments now before the bit-rot creeps in. If you like, you can also get the code at https://github.com/moben/pacman on the optdep branch. Please take a look and comment. --- Benedikt Benedikt Morbach (15): Split optdep into alpm_depend_t and description Hook new optdepend structures up Only display uninstalled optdepends during install/upgrade Show optdep install status in package info optdepends are not orphans unless --optdeps is specified Make package info show optional requirements Make recursive removal consider optdepends Show list of optrequires with -Qtdn Add config option for showing all optdeps again Warn on optdep removal Add flag to recurse through optdepends Show status on optdepends that are part of transaction Add option to install all optdepends by default Add option to ask which optdeps to install Make both HandleOptdeps settings work together doc/pacman.8.txt | 11 +++ doc/pacman.conf.5.txt | 14 ++++ etc/pacman.conf.in | 3 + lib/libalpm/add.c | 4 +- lib/libalpm/alpm.h | 25 ++++++- lib/libalpm/be_local.c | 12 +++- lib/libalpm/be_package.c | 5 +- lib/libalpm/be_sync.c | 8 ++- lib/libalpm/deps.c | 127 ++++++++++++++++++++++++++++++---- lib/libalpm/deps.h | 5 +- lib/libalpm/error.c | 2 + lib/libalpm/package.c | 40 ++++++++--- lib/libalpm/remove.c | 22 +++++-- lib/libalpm/sync.c | 2 +- lib/libalpm/trans.c | 10 ++- src/pacman/conf.c | 28 ++++++++ src/pacman/conf.h | 10 +++ src/pacman/package.c | 24 +++++-- src/pacman/pacman.c | 7 ++ src/pacman/query.c | 27 ++++++- src/pacman/remove.c | 35 ++++++++-- src/pacman/sync.c | 125 ++++++++++++++++++++++++++++++++-- src/pacman/upgrade.c | 2 +- src/pacman/util.c | 172 +++++++++++++++++++++++++++++++++++++++------ src/pacman/util.h | 5 +- src/util/pactree.c | 2 +- src/util/testdb.c | 2 +- 27 files changed, 635 insertions(+), 94 deletions(-) -- 1.7.6.1
This is done as a preparation to better handle optdepends. This commit should not change pacman's behaviour, as it simply adds new functions and data structures and doesn't yet hook them up anywhere. Signed-off-by: Benedikt Morbach <benedikt.morbach@googlemail.com> --- lib/libalpm/alpm.h | 12 +++++++++ lib/libalpm/deps.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/libalpm/deps.h | 3 ++ 3 files changed, 83 insertions(+), 0 deletions(-) diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h index cd124e5..4587148 100644 --- a/lib/libalpm/alpm.h +++ b/lib/libalpm/alpm.h @@ -147,6 +147,12 @@ typedef struct _alpm_depend_t { alpm_depmod_t mod; } alpm_depend_t; +/** Optional dependency */ +typedef struct _alpm_optdepend_t { + alpm_depend_t *depend; + char *description; +} alpm_optdepend_t; + /** Missing dependency */ typedef struct _alpm_depmissing_t { char *target; @@ -1090,6 +1096,12 @@ alpm_list_t *alpm_checkconflicts(alpm_handle_t *handle, alpm_list_t *pkglist); */ char *alpm_dep_compute_string(const alpm_depend_t *dep); +/** Returns a newly allocated string representing the optional dependency information. + * @param dep a optional dependency info structure + * @return a formatted string, e.g. "sqlite: for Database support" + */ +char *alpm_optdep_compute_string(const alpm_optdepend_t *optdep); + /** @} */ /** @} */ diff --git a/lib/libalpm/deps.c b/lib/libalpm/deps.c index 0da20c1..b6aba20 100644 --- a/lib/libalpm/deps.c +++ b/lib/libalpm/deps.c @@ -44,6 +44,13 @@ void _alpm_dep_free(alpm_depend_t *dep) FREE(dep); } +void _alpm_optdep_free(alpm_optdepend_t *optdep) +{ + _alpm_dep_free(optdep->depend); + FREE(optdep->description); + FREE(optdep); +} + static alpm_depmissing_t *depmiss_new(const char *target, alpm_depend_t *dep, const char *causingpkg) { @@ -473,6 +480,44 @@ alpm_depend_t *_alpm_dep_dup(const alpm_depend_t *dep) return newdep; } +alpm_optdepend_t *_alpm_splitoptdep(const char *optstring) +{ + alpm_optdepend_t *optdep; + char *depstring; + const char *ptr; + + ASSERT(optstring != NULL, return NULL); + + CALLOC(optdep, 1, sizeof(alpm_optdepend_t), return NULL); + + /* Note the extra space in ": " to avoid matching the epoch */ + if((ptr = strstr(optstring, ": ")) == NULL) { + ptr = optstring + strlen(optstring); + } + + STRNDUP(depstring, optstring, ptr - optstring, return NULL); + optdep->depend = _alpm_splitdep(depstring); + FREE(depstring); + + if(*ptr != '\0') { + STRDUP(optdep->description, ptr + 2, return NULL); + optdep->description = _alpm_strtrim(optdep->description); + } + + return optdep; +} + +alpm_optdepend_t *_alpm_optdep_dup(const alpm_optdepend_t *optdep) +{ + alpm_optdepend_t *newdep; + CALLOC(newdep, 1, sizeof(alpm_optdepend_t), return NULL); + + newdep->depend = _alpm_dep_dup(optdep->depend); + STRDUP(newdep->description, optdep->description, return NULL); + + return newdep; +} + /* These parameters are messy. We check if this package, given a list of * targets and a db is safe to remove. We do NOT remove it if it is in the * target list, or if if the package was explictly installed and @@ -868,4 +913,27 @@ char SYMEXPORT *alpm_dep_compute_string(const alpm_depend_t *dep) return str; } + +/** Reverse of splitoptdep; make a optdep string from a alpm_optdepend_t struct. + * The string must be freed! + * @param optdep the optdepend to turn into a string + * @return a string-formatted optional dependency with description + */ +char SYMEXPORT *alpm_optdep_compute_string(const alpm_optdepend_t *optdep) +{ + ASSERT(optdep != NULL, return NULL); + + char *depstring = alpm_dep_compute_string(optdep->depend); + + if(optdep->description != NULL) { + char *str; + size_t len = strlen(depstring) + strlen(optdep->description) + 3; + MALLOC(str, len, return NULL); + snprintf(str, len, "%s: %s", depstring, optdep->description); + free(depstring); + return str; + } + + return depstring; +} /* vim: set ts=2 sw=2 noet: */ diff --git a/lib/libalpm/deps.h b/lib/libalpm/deps.h index ce25bda..69b65df 100644 --- a/lib/libalpm/deps.h +++ b/lib/libalpm/deps.h @@ -28,7 +28,9 @@ #include "alpm.h" void _alpm_dep_free(alpm_depend_t *dep); +void _alpm_optdep_free(alpm_optdepend_t *optdep); alpm_depend_t *_alpm_dep_dup(const alpm_depend_t *dep); +alpm_optdepend_t *_alpm_optdep_dup(const alpm_optdepend_t *optdep); void _alpm_depmiss_free(alpm_depmissing_t *miss); alpm_list_t *_alpm_sortbydeps(alpm_handle_t *handle, alpm_list_t *targets, int reverse); int _alpm_recursedeps(alpm_db_t *db, alpm_list_t *targs, int include_explicit); @@ -36,6 +38,7 @@ int _alpm_resolvedeps(alpm_handle_t *handle, alpm_list_t *localpkgs, alpm_pkg_t alpm_list_t *preferred, alpm_list_t **packages, alpm_list_t *remove, alpm_list_t **data); alpm_depend_t *_alpm_splitdep(const char *depstring); +alpm_optdepend_t *_alpm_splitoptdep(const char *optstring); int _alpm_depcmp_literal(alpm_pkg_t *pkg, alpm_depend_t *dep); int _alpm_depcmp(alpm_pkg_t *pkg, alpm_depend_t *dep); -- 1.7.6.1
No new behaviour introduced, everything should work exactly as before. Signed-off-by: Benedikt Morbach <benedikt.morbach@googlemail.com> --- lib/libalpm/be_local.c | 12 ++++++- lib/libalpm/be_package.c | 5 ++- lib/libalpm/be_sync.c | 8 ++++- lib/libalpm/package.c | 7 +++- src/pacman/package.c | 18 ++++++++++- src/pacman/util.c | 73 ++++++++++++++++++++++++++++++++++++++------- 6 files changed, 102 insertions(+), 21 deletions(-) diff --git a/lib/libalpm/be_local.c b/lib/libalpm/be_local.c index 80711df..ad425b6 100644 --- a/lib/libalpm/be_local.c +++ b/lib/libalpm/be_local.c @@ -512,6 +512,12 @@ static char *get_pkgpath(alpm_db_t *db, alpm_pkg_t *info) f = alpm_list_add(f, _alpm_splitdep(line)); \ } while(1) /* note the while(1) and not (0) */ +#define READ_AND_SPLITOPTDEP(f) do { \ + if(fgets(line, sizeof(line), fp) == NULL && !feof(fp)) goto error; \ + if(_alpm_strip_newline(line) == 0) break; \ + f = alpm_list_add(f, _alpm_splitoptdep(line)); \ +} while(1) /* note the while(1) and not (0) */ + static int local_db_read(alpm_pkg_t *info, alpm_dbinfrq_t inforeq) { FILE *fp = NULL; @@ -607,7 +613,7 @@ static int local_db_read(alpm_pkg_t *info, alpm_dbinfrq_t inforeq) } else if(strcmp(line, "%DEPENDS%") == 0) { READ_AND_SPLITDEP(info->depends); } else if(strcmp(line, "%OPTDEPENDS%") == 0) { - READ_AND_STORE_ALL(info->optdepends); + READ_AND_SPLITOPTDEP(info->optdepends); } else if(strcmp(line, "%CONFLICTS%") == 0) { READ_AND_SPLITDEP(info->conflicts); } else if(strcmp(line, "%PROVIDES%") == 0) { @@ -818,7 +824,9 @@ int _alpm_local_db_write(alpm_db_t *db, alpm_pkg_t *info, alpm_dbinfrq_t inforeq if(info->optdepends) { fputs("%OPTDEPENDS%\n", fp); for(lp = info->optdepends; lp; lp = lp->next) { - fprintf(fp, "%s\n", (char *)lp->data); + char *optstring = alpm_optdep_compute_string(lp->data); + fprintf(fp, "%s\n", optstring); + free(optstring); } fprintf(fp, "\n"); } diff --git a/lib/libalpm/be_package.c b/lib/libalpm/be_package.c index 8b035ca..1364d08 100644 --- a/lib/libalpm/be_package.c +++ b/lib/libalpm/be_package.c @@ -35,7 +35,7 @@ #include "log.h" #include "handle.h" #include "package.h" -#include "deps.h" /* _alpm_splitdep */ +#include "deps.h" /** * Open a package changelog for reading. Similar to fopen in functionality, @@ -192,7 +192,8 @@ static int parse_descfile(alpm_handle_t *handle, struct archive *a, alpm_pkg_t * alpm_depend_t *dep = _alpm_splitdep(ptr); newpkg->depends = alpm_list_add(newpkg->depends, dep); } else if(strcmp(key, "optdepend") == 0) { - newpkg->optdepends = alpm_list_add(newpkg->optdepends, strdup(ptr)); + alpm_optdepend_t *optdep = _alpm_splitoptdep(ptr); + newpkg->optdepends = alpm_list_add(newpkg->optdepends, optdep); } else if(strcmp(key, "conflict") == 0) { alpm_depend_t *conflict = _alpm_splitdep(ptr); newpkg->conflicts = alpm_list_add(newpkg->conflicts, conflict); diff --git a/lib/libalpm/be_sync.c b/lib/libalpm/be_sync.c index 7eb2539..9f6797d 100644 --- a/lib/libalpm/be_sync.c +++ b/lib/libalpm/be_sync.c @@ -491,6 +491,12 @@ static int sync_db_populate(alpm_db_t *db) f = alpm_list_add(f, _alpm_splitdep(line)); \ } while(1) /* note the while(1) and not (0) */ +#define READ_AND_SPLITOPTDEP(f) do { \ + if(_alpm_archive_fgets(archive, &buf) != ARCHIVE_OK) goto error; \ + if(_alpm_strip_newline(buf.line) == 0) break; \ + f = alpm_list_add(f, _alpm_splitoptdep(line)); \ +} while(1) /* note the while(1) and not (0) */ + static int sync_db_read(alpm_db_t *db, struct archive *archive, struct archive_entry *entry, alpm_pkg_t **likely_pkg) { @@ -577,7 +583,7 @@ static int sync_db_read(alpm_db_t *db, struct archive *archive, } else if(strcmp(line, "%DEPENDS%") == 0) { READ_AND_SPLITDEP(pkg->depends); } else if(strcmp(line, "%OPTDEPENDS%") == 0) { - READ_AND_STORE_ALL(pkg->optdepends); + READ_AND_SPLITOPTDEP(pkg->optdepends); } else if(strcmp(line, "%CONFLICTS%") == 0) { READ_AND_SPLITDEP(pkg->conflicts); } else if(strcmp(line, "%PROVIDES%") == 0) { diff --git a/lib/libalpm/package.c b/lib/libalpm/package.c index 3045857..c1fbcca 100644 --- a/lib/libalpm/package.c +++ b/lib/libalpm/package.c @@ -538,7 +538,9 @@ int _alpm_pkg_dup(alpm_pkg_t *pkg, alpm_pkg_t **new_ptr) for(i = pkg->depends; i; i = i->next) { newpkg->depends = alpm_list_add(newpkg->depends, _alpm_dep_dup(i->data)); } - newpkg->optdepends = alpm_list_strdup(pkg->optdepends); + for(i = pkg->optdepends; i; i = i->next) { + newpkg->optdepends = alpm_list_add(newpkg->optdepends, _alpm_optdep_dup(i->data)); + } for(i = pkg->conflicts; i; i = i->next) { newpkg->conflicts = alpm_list_add(newpkg->conflicts, _alpm_dep_dup(i->data)); } @@ -600,7 +602,8 @@ void _alpm_pkg_free(alpm_pkg_t *pkg) alpm_list_free(pkg->backup); alpm_list_free_inner(pkg->depends, (alpm_list_fn_free)_alpm_dep_free); alpm_list_free(pkg->depends); - FREELIST(pkg->optdepends); + alpm_list_free_inner(pkg->optdepends, (alpm_list_fn_free)_alpm_optdep_free); + alpm_list_free(pkg->optdepends); alpm_list_free_inner(pkg->conflicts, (alpm_list_fn_free)_alpm_dep_free); alpm_list_free(pkg->conflicts); alpm_list_free_inner(pkg->provides, (alpm_list_fn_free)_alpm_dep_free); diff --git a/src/pacman/package.c b/src/pacman/package.c index 6a312f9..4f8a180 100644 --- a/src/pacman/package.c +++ b/src/pacman/package.c @@ -39,7 +39,6 @@ /** Turn a depends list into a text list. * @param deps a list with items of type alpm_depend_t - * @return a string list, must be freed */ static void deplist_display(const char *title, alpm_list_t *deps) @@ -53,6 +52,21 @@ static void deplist_display(const char *title, FREELIST(text); } +/** Turn a optdepends list into a text list. + * @param optdeps a list with items of type alpm_optdepend_t + */ +static void optdeplist_display(const char *title, + alpm_list_t *optdeps) +{ + alpm_list_t *i, *text = NULL; + for(i = optdeps; i; i = alpm_list_next(i)) { + alpm_optdepend_t *optdep = alpm_list_getdata(i); + text = alpm_list_add(text, alpm_optdep_compute_string(optdep)); + } + list_display_linebreak(title, text); + FREELIST(text); +} + /** * Display the details of a package. * Extra information entails 'required by' info for sync packages and backup @@ -112,7 +126,7 @@ void dump_pkg_full(alpm_pkg_t *pkg, int extra) list_display(_("Groups :"), alpm_pkg_get_groups(pkg)); deplist_display(_("Provides :"), alpm_pkg_get_provides(pkg)); deplist_display(_("Depends On :"), alpm_pkg_get_depends(pkg)); - list_display_linebreak(_("Optional Deps :"), alpm_pkg_get_optdepends(pkg)); + optdeplist_display(_("Optional Deps :"), alpm_pkg_get_optdepends(pkg)); if(extra || from == PKG_FROM_LOCALDB) { list_display(_("Required By :"), requiredby); } diff --git a/src/pacman/util.c b/src/pacman/util.c index 594186f..e8dd326 100644 --- a/src/pacman/util.c +++ b/src/pacman/util.c @@ -996,32 +996,81 @@ void print_packages(const alpm_list_t *packages) } } -/* Helper function for comparing strings using the - * alpm "compare func" signature */ -int str_cmp(const void *s1, const void *s2) +/* Helper function for comparing optdepends using the + * alpm "compare func" signature. */ +static int opt_cmp(const void *o1, const void *o2) { - return strcmp(s1, s2); + const alpm_optdepend_t *od1 = o1; + const alpm_optdepend_t *od2 = o2; + int ret; + + ret = strcmp(od1->depend->name, od2->depend->name); + if(ret == 0) { + ret = od1->depend->mod - od2->depend->mod; + } + if(ret == 0 && od1->depend->version != od2->depend->version) { + if(od1->depend->version && od2->depend->version) { + ret = strcmp(od1->depend->version, od2->depend->version); + } else if(!od1->depend->version && od2->depend->version) { + return -1; + } else if(od1->depend->version && !od2->depend->version) { + return 1; + } + } + if(ret == 0 && od1->description != od2->description) { + if(od1->description && od2->description) { + ret = strcmp(od1->description, od2->description); + } else if(!od1->description && od2->description) { + return -1; + } else if(od1->description && !od2->description) { + return 1; + } + } + + return ret; } void display_new_optdepends(alpm_pkg_t *oldpkg, alpm_pkg_t *newpkg) { - alpm_list_t *old = alpm_pkg_get_optdepends(oldpkg); - alpm_list_t *new = alpm_pkg_get_optdepends(newpkg); - alpm_list_t *optdeps = alpm_list_diff(new,old,str_cmp); - if(optdeps) { + alpm_list_t *i, *old, *new, *optdeps, *optstrings = NULL; + + old = alpm_pkg_get_optdepends(oldpkg); + new = alpm_pkg_get_optdepends(newpkg); + optdeps = alpm_list_diff(new, old, opt_cmp); + + /* turn optdepends list into a text list */ + for(i = optdeps; i; i = alpm_list_next(i)) { + alpm_optdepend_t *optdep = alpm_list_getdata(i); + optstrings = alpm_list_add(optstrings, alpm_optdep_compute_string(optdep)); + } + + if(optstrings) { printf(_("New optional dependencies for %s\n"), alpm_pkg_get_name(newpkg)); - list_display_linebreak(" ", optdeps); + list_display_linebreak(" ", optstrings); } + alpm_list_free(optdeps); + FREELIST(optstrings); } void display_optdepends(alpm_pkg_t *pkg) { - alpm_list_t *optdeps = alpm_pkg_get_optdepends(pkg); - if(optdeps) { + alpm_list_t *i, *optdeps, *optstrings = NULL; + + optdeps = alpm_pkg_get_optdepends(pkg); + + /* turn optdepends list into a text list */ + for(i = optdeps; i; i = alpm_list_next(i)) { + alpm_optdepend_t *optdep = alpm_list_getdata(i); + optstrings = alpm_list_add(optstrings, alpm_optdep_compute_string(optdep)); + } + + if(optstrings) { printf(_("Optional dependencies for %s\n"), alpm_pkg_get_name(pkg)); - list_display_linebreak(" ", optdeps); + list_display_linebreak(" ", optstrings); } + + FREELIST(optstrings); } static void display_repo_list(const char *dbname, alpm_list_t *list) -- 1.7.6.1
Signed-off-by: Benedikt Morbach <benedikt.morbach@googlemail.com> --- src/pacman/util.c | 42 ++++++++++++++++++++++++++++-------------- 1 files changed, 28 insertions(+), 14 deletions(-) diff --git a/src/pacman/util.c b/src/pacman/util.c index e8dd326..27121d3 100644 --- a/src/pacman/util.c +++ b/src/pacman/util.c @@ -1030,19 +1030,39 @@ static int opt_cmp(const void *o1, const void *o2) return ret; } +/** Creates a newly-allocated list of optdepend strings from a list of optdepends. + * The list must be freed! + * @param optlist an alpm_list_t of optdepends to turn into a strings + * @return an alpm_list_t of optdepend formatted strings with description + */ +alpm_list_t *optdep_string_list(const alpm_list_t *optlist) +{ + alpm_list_t *optstrings = NULL; + alpm_optdepend_t *optdep; + alpm_db_t *db_local; + + db_local = alpm_option_get_localdb(config->handle); + + /* turn optdepends list into a text list */ + for( ; optlist; optlist = alpm_list_next(optlist)) { + optdep = alpm_list_getdata(optlist); + if(alpm_db_get_pkg(db_local, optdep->depend->name) == NULL) { + optstrings = alpm_list_add(optstrings, alpm_optdep_compute_string(optdep)); + } + } + + return optstrings; +} + void display_new_optdepends(alpm_pkg_t *oldpkg, alpm_pkg_t *newpkg) { - alpm_list_t *i, *old, *new, *optdeps, *optstrings = NULL; + alpm_list_t *old, *new, *optdeps, *optstrings; old = alpm_pkg_get_optdepends(oldpkg); new = alpm_pkg_get_optdepends(newpkg); optdeps = alpm_list_diff(new, old, opt_cmp); - /* turn optdepends list into a text list */ - for(i = optdeps; i; i = alpm_list_next(i)) { - alpm_optdepend_t *optdep = alpm_list_getdata(i); - optstrings = alpm_list_add(optstrings, alpm_optdep_compute_string(optdep)); - } + optstrings = optdep_string_list(optdeps); if(optstrings) { printf(_("New optional dependencies for %s\n"), alpm_pkg_get_name(newpkg)); @@ -1055,15 +1075,9 @@ void display_new_optdepends(alpm_pkg_t *oldpkg, alpm_pkg_t *newpkg) void display_optdepends(alpm_pkg_t *pkg) { - alpm_list_t *i, *optdeps, *optstrings = NULL; + alpm_list_t *optstrings; - optdeps = alpm_pkg_get_optdepends(pkg); - - /* turn optdepends list into a text list */ - for(i = optdeps; i; i = alpm_list_next(i)) { - alpm_optdepend_t *optdep = alpm_list_getdata(i); - optstrings = alpm_list_add(optstrings, alpm_optdep_compute_string(optdep)); - } + optstrings = optdep_string_list(alpm_pkg_get_optdepends(pkg)); if(optstrings) { printf(_("Optional dependencies for %s\n"), alpm_pkg_get_name(pkg)); -- 1.7.6.1
Signed-off-by: Benedikt Morbach <benedikt.morbach@googlemail.com> --- src/pacman/package.c | 7 ++----- src/pacman/util.c | 20 +++++++++++++++----- src/pacman/util.h | 1 + 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/pacman/package.c b/src/pacman/package.c index 4f8a180..7e17f0c 100644 --- a/src/pacman/package.c +++ b/src/pacman/package.c @@ -58,11 +58,8 @@ static void deplist_display(const char *title, static void optdeplist_display(const char *title, alpm_list_t *optdeps) { - alpm_list_t *i, *text = NULL; - for(i = optdeps; i; i = alpm_list_next(i)) { - alpm_optdepend_t *optdep = alpm_list_getdata(i); - text = alpm_list_add(text, alpm_optdep_compute_string(optdep)); - } + alpm_list_t *text = NULL; + text = optdep_string_list(optdeps, 1); list_display_linebreak(title, text); FREELIST(text); } diff --git a/src/pacman/util.c b/src/pacman/util.c index 27121d3..1c534e3 100644 --- a/src/pacman/util.c +++ b/src/pacman/util.c @@ -1033,9 +1033,10 @@ static int opt_cmp(const void *o1, const void *o2) /** Creates a newly-allocated list of optdepend strings from a list of optdepends. * The list must be freed! * @param optlist an alpm_list_t of optdepends to turn into a strings + * @param include_installed if false, installed packages are excluded from the list. * @return an alpm_list_t of optdepend formatted strings with description */ -alpm_list_t *optdep_string_list(const alpm_list_t *optlist) +alpm_list_t *optdep_string_list(const alpm_list_t *optlist, int include_installed) { alpm_list_t *optstrings = NULL; alpm_optdepend_t *optdep; @@ -1046,11 +1047,20 @@ alpm_list_t *optdep_string_list(const alpm_list_t *optlist) /* turn optdepends list into a text list */ for( ; optlist; optlist = alpm_list_next(optlist)) { optdep = alpm_list_getdata(optlist); - if(alpm_db_get_pkg(db_local, optdep->depend->name) == NULL) { + if(include_installed && alpm_db_get_pkg(db_local, optdep->depend->name)) { + const char * const installed = _(" [installed]"); + char *str, *tmp; + tmp = alpm_optdep_compute_string(optdep); + if((str = realloc(tmp, strlen(tmp) + strlen(installed) + 1)) != NULL) { + strcpy(str + strlen(str), installed); + optstrings = alpm_list_add(optstrings, str); + } else { + free(tmp); + } + } else { optstrings = alpm_list_add(optstrings, alpm_optdep_compute_string(optdep)); } } - return optstrings; } @@ -1062,7 +1072,7 @@ void display_new_optdepends(alpm_pkg_t *oldpkg, alpm_pkg_t *newpkg) new = alpm_pkg_get_optdepends(newpkg); optdeps = alpm_list_diff(new, old, opt_cmp); - optstrings = optdep_string_list(optdeps); + optstrings = optdep_string_list(optdeps, 0); if(optstrings) { printf(_("New optional dependencies for %s\n"), alpm_pkg_get_name(newpkg)); @@ -1077,7 +1087,7 @@ void display_optdepends(alpm_pkg_t *pkg) { alpm_list_t *optstrings; - optstrings = optdep_string_list(alpm_pkg_get_optdepends(pkg)); + optstrings = optdep_string_list(alpm_pkg_get_optdepends(pkg), 0); if(optstrings) { printf(_("Optional dependencies for %s\n"), alpm_pkg_get_name(pkg)); diff --git a/src/pacman/util.h b/src/pacman/util.h index 6a26b08..01d2211 100644 --- a/src/pacman/util.h +++ b/src/pacman/util.h @@ -63,6 +63,7 @@ void display_targets(const alpm_list_t *pkgs, int install); int str_cmp(const void *s1, const void *s2); void display_new_optdepends(alpm_pkg_t *oldpkg, alpm_pkg_t *newpkg); void display_optdepends(alpm_pkg_t *pkg); +alpm_list_t *optdep_string_list(const alpm_list_t *optdeps, int include_installed); void print_packages(const alpm_list_t *packages); void select_display(const alpm_list_t *pkglist); int select_question(int count); -- 1.7.6.1
Signed-off-by: Benedikt Morbach <benedikt.morbach@googlemail.com> --- doc/pacman.8.txt | 6 ++++++ lib/libalpm/alpm.h | 2 +- lib/libalpm/package.c | 33 +++++++++++++++++++++++---------- src/pacman/conf.h | 1 + src/pacman/package.c | 2 +- src/pacman/pacman.c | 3 +++ src/pacman/query.c | 16 +++++++++++++--- src/util/pactree.c | 2 +- 8 files changed, 49 insertions(+), 16 deletions(-) diff --git a/doc/pacman.8.txt b/doc/pacman.8.txt index 5985381..45386c7 100644 --- a/doc/pacman.8.txt +++ b/doc/pacman.8.txt @@ -280,6 +280,12 @@ Query Options[[QO]] database(s). Typically these are packages that were downloaded manually and installed with '\--upgrade'. +*-n, \--optdeps*:: + When using the '-t' option with this flag, do not treat installed + optional dependencies as if they were normal dependencies. This option + can be used to list packages which were installed as dependencies but are + only optionally used by other packages. + *-o, \--owns* <file>:: Search for packages that own the specified file(s). The path can be relative or absolute and one or more files can be specified. diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h index 4587148..f1e7fd6 100644 --- a/lib/libalpm/alpm.h +++ b/lib/libalpm/alpm.h @@ -700,7 +700,7 @@ int alpm_pkg_vercmp(const char *a, const char *b); * @param pkg a package * @return the list of packages requiring pkg */ -alpm_list_t *alpm_pkg_compute_requiredby(alpm_pkg_t *pkg); +alpm_list_t *alpm_pkg_compute_requiredby(alpm_pkg_t *pkg, const int find_optdeps); /** @name Package Property Accessors * Any pointer returned by these functions points to internal structures diff --git a/lib/libalpm/package.c b/lib/libalpm/package.c index c1fbcca..8c0a678 100644 --- a/lib/libalpm/package.c +++ b/lib/libalpm/package.c @@ -389,7 +389,8 @@ int SYMEXPORT alpm_pkg_has_scriptlet(alpm_pkg_t *pkg) return pkg->ops->has_scriptlet(pkg); } -static void find_requiredby(alpm_pkg_t *pkg, alpm_db_t *db, alpm_list_t **reqs) +static void find_requiredby(alpm_pkg_t *pkg, alpm_db_t *db, + alpm_list_t **reqs, const int find_optdeps) { const alpm_list_t *i; pkg->handle->pm_errno = 0; @@ -397,11 +398,23 @@ static void find_requiredby(alpm_pkg_t *pkg, alpm_db_t *db, alpm_list_t **reqs) for(i = _alpm_db_get_pkgcache(db); i; i = i->next) { alpm_pkg_t *cachepkg = i->data; alpm_list_t *j; - for(j = alpm_pkg_get_depends(cachepkg); j; j = j->next) { - if(_alpm_depcmp(pkg, j->data)) { - const char *cachepkgname = cachepkg->name; - if(alpm_list_find_str(*reqs, cachepkgname) == NULL) { - *reqs = alpm_list_add(*reqs, strdup(cachepkgname)); + if(find_optdeps) { + for(j = alpm_pkg_get_optdepends(cachepkg); j; j = j->next) { + alpm_optdepend_t *optdep = j->data; + if(_alpm_depcmp(pkg, optdep->depend)) { + const char *cachepkgname = cachepkg->name; + if(alpm_list_find_str(*reqs, cachepkgname) == NULL) { + *reqs = alpm_list_add(*reqs, strdup(cachepkgname)); + } + } + } + } else { + for(j = alpm_pkg_get_depends(cachepkg); j; j = j->next) { + if(_alpm_depcmp(pkg, j->data)) { + const char *cachepkgname = cachepkg->name; + if(alpm_list_find_str(*reqs, cachepkgname) == NULL) { + *reqs = alpm_list_add(*reqs, strdup(cachepkgname)); + } } } } @@ -409,7 +422,7 @@ static void find_requiredby(alpm_pkg_t *pkg, alpm_db_t *db, alpm_list_t **reqs) } /** Compute the packages requiring a given package. */ -alpm_list_t SYMEXPORT *alpm_pkg_compute_requiredby(alpm_pkg_t *pkg) +alpm_list_t SYMEXPORT *alpm_pkg_compute_requiredby(alpm_pkg_t *pkg, const int find_optdeps) { const alpm_list_t *i; alpm_list_t *reqs = NULL; @@ -420,17 +433,17 @@ alpm_list_t SYMEXPORT *alpm_pkg_compute_requiredby(alpm_pkg_t *pkg) if(pkg->origin == PKG_FROM_FILE) { /* The sane option; search locally for things that require this. */ - find_requiredby(pkg, pkg->handle->db_local, &reqs); + find_requiredby(pkg, pkg->handle->db_local, &reqs, find_optdeps); } else { /* We have a DB package. if it is a local package, then we should * only search the local DB; else search all known sync databases. */ db = pkg->origin_data.db; if(db->status & DB_STATUS_LOCAL) { - find_requiredby(pkg, db, &reqs); + find_requiredby(pkg, db, &reqs, find_optdeps); } else { for(i = pkg->handle->dbs_sync; i; i = i->next) { db = i->data; - find_requiredby(pkg, db, &reqs); + find_requiredby(pkg, db, &reqs, find_optdeps); } reqs = alpm_list_msort(reqs, alpm_list_count(reqs), _alpm_str_cmp); } diff --git a/src/pacman/conf.h b/src/pacman/conf.h index 396cde5..552afad 100644 --- a/src/pacman/conf.h +++ b/src/pacman/conf.h @@ -54,6 +54,7 @@ typedef struct __config_t { unsigned short op_q_unrequired; unsigned short op_q_deps; unsigned short op_q_explicit; + unsigned short op_q_optdeps; unsigned short op_q_owns; unsigned short op_q_search; unsigned short op_q_changelog; diff --git a/src/pacman/package.c b/src/pacman/package.c index 7e17f0c..b8b34c3 100644 --- a/src/pacman/package.c +++ b/src/pacman/package.c @@ -108,7 +108,7 @@ void dump_pkg_full(alpm_pkg_t *pkg, int extra) if(extra || from == PKG_FROM_LOCALDB) { /* compute this here so we don't get a pause in the middle of output */ - requiredby = alpm_pkg_compute_requiredby(pkg); + requiredby = alpm_pkg_compute_requiredby(pkg, 0); } /* actual output */ diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c index d285a05..3a19619 100644 --- a/src/pacman/pacman.c +++ b/src/pacman/pacman.c @@ -148,6 +148,7 @@ static void usage(int op, const char * const myname) addlist(_(" -k, --check check that the files owned by the package(s) are present\n")); addlist(_(" -l, --list list the contents of the queried package\n")); addlist(_(" -m, --foreign list installed packages not found in sync db(s) [filter]\n")); + addlist(_(" -n, --optdeps don't consider installed optdepends to be required\n")); addlist(_(" -o, --owns <file> query the package that owns <file>\n")); addlist(_(" -p, --file <package> query a package file instead of the database\n")); addlist(_(" -q, --quiet show less information for query and search\n")); @@ -461,6 +462,7 @@ static int parsearg_query(int opt) case 'c': config->op_q_changelog = 1; break; case 'd': config->op_q_deps = 1; break; case 'e': config->op_q_explicit = 1; break; + case 'n': config->op_q_optdeps = 1; break; case 'g': (config->group)++; break; case 'i': (config->op_q_info)++; break; case 'k': config->op_q_check = 1; break; @@ -604,6 +606,7 @@ static int parseargs(int argc, char *argv[]) {"list", no_argument, 0, 'l'}, {"foreign", no_argument, 0, 'm'}, {"nosave", no_argument, 0, 'n'}, + {"optdeps", no_argument, 0, 'n'}, {"owns", no_argument, 0, 'o'}, {"file", no_argument, 0, 'p'}, {"print", no_argument, 0, 'p'}, diff --git a/src/pacman/query.c b/src/pacman/query.c index 1098b85..8523e23 100644 --- a/src/pacman/query.c +++ b/src/pacman/query.c @@ -363,9 +363,15 @@ static int is_foreign(alpm_pkg_t *pkg) return 0; } -static int is_unrequired(alpm_pkg_t *pkg) +static int is_unrequired(alpm_pkg_t *pkg, const int find_optdeps) { - alpm_list_t *requiredby = alpm_pkg_compute_requiredby(pkg); + alpm_list_t *requiredby; + if(find_optdeps) { + requiredby = alpm_pkg_compute_requiredby(pkg, 1); + } else { + requiredby = alpm_pkg_compute_requiredby(pkg, 0); + } + if(requiredby == NULL) { return 1; } @@ -390,7 +396,11 @@ static int filter(alpm_pkg_t *pkg) return 0; } /* check if this pkg is unrequired */ - if(config->op_q_unrequired && !is_unrequired(pkg)) { + if(config->op_q_unrequired && !is_unrequired(pkg, 0)) { + return 0; + } + /* check if this pkg is optionally required */ + if(config->op_q_unrequired && !config->op_q_optdeps && !is_unrequired(pkg, 1)) { return 0; } /* check if this pkg is outdated */ diff --git a/src/util/pactree.c b/src/util/pactree.c index b883291..2dd543b 100644 --- a/src/util/pactree.c +++ b/src/util/pactree.c @@ -347,7 +347,7 @@ static void walk_reverse_deps(alpm_list_t *dblist, alpm_pkg_t *pkg, int depth) } walked = alpm_list_add(walked, (void *)alpm_pkg_get_name(pkg)); - required_by = alpm_pkg_compute_requiredby(pkg); + required_by = alpm_pkg_compute_requiredby(pkg, 0); for(i = required_by; i; i = alpm_list_next(i)) { const char *pkgname = alpm_list_getdata(i); -- 1.7.6.1
Signed-off-by: Benedikt Morbach <benedikt.morbach@googlemail.com> --- src/pacman/package.c | 9 ++++++--- 1 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/pacman/package.c b/src/pacman/package.c index b8b34c3..8578d69 100644 --- a/src/pacman/package.c +++ b/src/pacman/package.c @@ -66,8 +66,8 @@ static void optdeplist_display(const char *title, /** * Display the details of a package. - * Extra information entails 'required by' info for sync packages and backup - * files info for local packages. + * Extra information entails 'required by' and 'optrequired by' info + * for sync packages and backup files info for local packages. * @param pkg package to display information for * @param from the type of package we are dealing with * @param extra should we show extra information @@ -80,6 +80,7 @@ void dump_pkg_full(alpm_pkg_t *pkg, int extra) const char *label; double size; alpm_list_t *requiredby = NULL; + alpm_list_t *optrequiredby = NULL; alpm_pkgfrom_t from; from = alpm_pkg_get_origin(pkg); @@ -108,7 +109,8 @@ void dump_pkg_full(alpm_pkg_t *pkg, int extra) if(extra || from == PKG_FROM_LOCALDB) { /* compute this here so we don't get a pause in the middle of output */ - requiredby = alpm_pkg_compute_requiredby(pkg, 0); + requiredby = alpm_pkg_compute_requiredby(pkg, 0); + optrequiredby = alpm_pkg_compute_requiredby(pkg, 1); } /* actual output */ @@ -126,6 +128,7 @@ void dump_pkg_full(alpm_pkg_t *pkg, int extra) optdeplist_display(_("Optional Deps :"), alpm_pkg_get_optdepends(pkg)); if(extra || from == PKG_FROM_LOCALDB) { list_display(_("Required By :"), requiredby); + list_display(_("Optional For :"), optrequiredby); } deplist_display(_("Conflicts With :"), alpm_pkg_get_conflicts(pkg)); deplist_display(_("Replaces :"), alpm_pkg_get_replaces(pkg)); -- 1.7.6.1
Signed-off-by: Benedikt Morbach <benedikt.morbach@googlemail.com> --- lib/libalpm/deps.c | 18 +++++++++++++++++- 1 files changed, 17 insertions(+), 1 deletions(-) diff --git a/lib/libalpm/deps.c b/lib/libalpm/deps.c index b6aba20..4382d6c 100644 --- a/lib/libalpm/deps.c +++ b/lib/libalpm/deps.c @@ -85,6 +85,19 @@ static int _alpm_dep_edge(alpm_pkg_t *pkg1, alpm_pkg_t *pkg2) return 0; } +/* Does pkg1 optdepend on pkg2, ie. does pkg2 satisfy a optional dependency of pkg1? */ +static int _alpm_optdep_edge(alpm_pkg_t *pkg1, alpm_pkg_t *pkg2) +{ + alpm_list_t *i; + for(i = alpm_pkg_get_optdepends(pkg1); i; i = i->next) { + alpm_optdepend_t *optdep = i->data; + if(_alpm_depcmp(pkg2, optdep->depend)) { + return 1; + } + } + return 0; +} + /* Convert a list of alpm_pkg_t * to a graph structure, * with a edge for each dependency. * Returns a list of vertices (one vertex = one package) @@ -552,6 +565,9 @@ static int can_remove_package(alpm_db_t *db, alpm_pkg_t *pkg, if(_alpm_dep_edge(lpkg, pkg) && !_alpm_pkg_find(targets, lpkg->name)) { return 0; } + if(_alpm_optdep_edge(lpkg, pkg) && !_alpm_pkg_find(targets, lpkg->name)) { + return 0; + } } /* it's ok to remove */ @@ -581,7 +597,7 @@ int _alpm_recursedeps(alpm_db_t *db, alpm_list_t *targs, int include_explicit) alpm_pkg_t *pkg = i->data; for(j = _alpm_db_get_pkgcache(db); j; j = j->next) { alpm_pkg_t *deppkg = j->data; - if(_alpm_dep_edge(pkg, deppkg) + if((_alpm_dep_edge(pkg, deppkg) || _alpm_optdep_edge(pkg, deppkg)) && can_remove_package(db, deppkg, targs, include_explicit)) { alpm_pkg_t *copy; _alpm_log(db->handle, ALPM_LOG_DEBUG, "adding '%s' to the targets\n", -- 1.7.6.1
Signed-off-by: Benedikt Morbach <benedikt.morbach@googlemail.com> --- src/pacman/query.c | 11 ++++++++++- src/pacman/util.c | 23 ++++++++++++++++++----- src/pacman/util.h | 1 + 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/pacman/query.c b/src/pacman/query.c index 8523e23..757afde 100644 --- a/src/pacman/query.c +++ b/src/pacman/query.c @@ -488,7 +488,16 @@ static int display(alpm_pkg_t *pkg) if(!config->op_q_info && !config->op_q_list && !config->op_q_changelog && !config->op_q_check) { if(!config->quiet) { - printf("%s %s\n", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg)); + printf("%s %s", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg)); + + alpm_list_t *optrequires; + if(config->op_q_unrequired && config->op_q_optdeps && + (optrequires = alpm_pkg_compute_requiredby(pkg, 1)) != NULL) { + list_display_extra(_(" (optdepend for:"), optrequires, ", ", ")"); + FREELIST(optrequires); + } else { + printf("\n"); + } } else { printf("%s\n", alpm_pkg_get_name(pkg)); } diff --git a/src/pacman/util.c b/src/pacman/util.c index 1c534e3..5318142 100644 --- a/src/pacman/util.c +++ b/src/pacman/util.c @@ -600,16 +600,24 @@ int table_display(const char *title, const alpm_list_t *header, void list_display(const char *title, const alpm_list_t *list) { + list_display_extra(title, list, NULL, NULL); +} + +void list_display_extra(const char *title, const alpm_list_t *list, + const char *delim, const char *after) +{ const alpm_list_t *i; int len = 0; + delim = delim ? delim : " "; + if(title) { len = string_length(title) + 1; printf("%s ", title); } if(!list) { - printf("%s\n", _("None")); + printf("%s", _("None")); } else { const int maxcols = getcols(); int cols = len; @@ -628,15 +636,20 @@ void list_display(const char *title, const alpm_list_t *list) printf(" "); } } else if(cols != len) { - /* 2 spaces are added if this is not the first element on a line. */ - printf(" "); - cols += 2; + /* delimiter is added if this is not the first element on a line. */ + printf("%s", delim); + cols += strlen(delim); } printf("%s", str); cols += s; } - printf("\n"); } + + if(after) { + printf("%s", after); + } + + printf("\n"); } void list_display_linebreak(const char *title, const alpm_list_t *list) diff --git a/src/pacman/util.h b/src/pacman/util.h index 01d2211..0eac77a 100644 --- a/src/pacman/util.h +++ b/src/pacman/util.h @@ -57,6 +57,7 @@ void string_display(const char *title, const char *string); double humanize_size(off_t bytes, const char target_unit, const char **label); int table_display(const char *title, const alpm_list_t *header, const alpm_list_t *rows); void list_display(const char *title, const alpm_list_t *list); +void list_display_extra(const char *title, const alpm_list_t *list, const char *delim, const char *after); void list_display_linebreak(const char *title, const alpm_list_t *list); void signature_display(const char *title, alpm_siglist_t *siglist); void display_targets(const alpm_list_t *pkgs, int install); -- 1.7.6.1
Signed-off-by: Benedikt Morbach <benedikt.morbach@googlemail.com> --- doc/pacman.conf.5.txt | 5 +++++ etc/pacman.conf.in | 1 + src/pacman/conf.c | 3 +++ src/pacman/conf.h | 1 + src/pacman/util.c | 4 ++-- 5 files changed, 12 insertions(+), 2 deletions(-) diff --git a/doc/pacman.conf.5.txt b/doc/pacman.conf.5.txt index e793e78..ad7fb40 100644 --- a/doc/pacman.conf.5.txt +++ b/doc/pacman.conf.5.txt @@ -178,6 +178,11 @@ Options Performs an approximate check for adequate available disk space before installing packages. +*ShowAllOptdeps*:: + When installing a package, show installed optional dependencies too. + Default behavior is to only show uninstalled ones. + If turned on, installed optional dependencies will be marked with '[installed]' + *VerbosePkgLists*:: Displays name, version and size of target packages formatted as a table for upgrade, sync and remove operations. diff --git a/etc/pacman.conf.in b/etc/pacman.conf.in index b9595ef..73445ac 100644 --- a/etc/pacman.conf.in +++ b/etc/pacman.conf.in @@ -35,6 +35,7 @@ Architecture = auto #TotalDownload #CheckSpace #VerbosePkgLists +#ShowAllOptdeps # PGP signature checking #SigLevel = Optional diff --git a/src/pacman/conf.c b/src/pacman/conf.c index 6587be9..f2982e3 100644 --- a/src/pacman/conf.c +++ b/src/pacman/conf.c @@ -376,6 +376,9 @@ static int _parse_options(const char *key, char *value, } else if(strcmp(key, "TotalDownload") == 0) { config->totaldownload = 1; pm_printf(ALPM_LOG_DEBUG, "config: totaldownload\n"); + } else if(strcmp(key, "ShowAllOptdeps") == 0) { + config->showalloptdeps = 1; + pm_printf(ALPM_LOG_DEBUG, "config: showalloptdeps\n"); } else if(strcmp(key, "CheckSpace") == 0) { config->checkspace = 1; } else { diff --git a/src/pacman/conf.h b/src/pacman/conf.h index 552afad..23008ea 100644 --- a/src/pacman/conf.h +++ b/src/pacman/conf.h @@ -33,6 +33,7 @@ typedef struct __config_t { unsigned short logmask; unsigned short print; unsigned short checkspace; + unsigned short showalloptdeps; unsigned short usesyslog; unsigned short usedelta; char *arch; diff --git a/src/pacman/util.c b/src/pacman/util.c index 5318142..afe6840 100644 --- a/src/pacman/util.c +++ b/src/pacman/util.c @@ -1085,7 +1085,7 @@ void display_new_optdepends(alpm_pkg_t *oldpkg, alpm_pkg_t *newpkg) new = alpm_pkg_get_optdepends(newpkg); optdeps = alpm_list_diff(new, old, opt_cmp); - optstrings = optdep_string_list(optdeps, 0); + optstrings = optdep_string_list(optdeps, config->showalloptdeps); if(optstrings) { printf(_("New optional dependencies for %s\n"), alpm_pkg_get_name(newpkg)); @@ -1100,7 +1100,7 @@ void display_optdepends(alpm_pkg_t *pkg) { alpm_list_t *optstrings; - optstrings = optdep_string_list(alpm_pkg_get_optdepends(pkg), 0); + optstrings = optdep_string_list(alpm_pkg_get_optdepends(pkg), config->showalloptdeps); if(optstrings) { printf(_("Optional dependencies for %s\n"), alpm_pkg_get_name(pkg)); -- 1.7.6.1
Issue a warning if the user for whatever reason tries to remove an optdepend of an installed package. Signed-off-by: Benedikt Morbach <benedikt.morbach@googlemail.com> --- I'm not entirely satisfied with this, but I think the logic belongs to libalpm and the output to pacman. Your mileage might vary, so comments welcome. Especially if you know a much cleaner way to do this. lib/libalpm/alpm.h | 5 ++++- lib/libalpm/deps.c | 35 ++++++++++++++++++++++++++--------- lib/libalpm/error.c | 2 ++ lib/libalpm/remove.c | 16 +++++++++++++--- lib/libalpm/sync.c | 2 +- lib/libalpm/trans.c | 10 ++++++++-- src/pacman/remove.c | 35 ++++++++++++++++++++++++++++------- src/util/testdb.c | 2 +- 8 files changed, 83 insertions(+), 24 deletions(-) diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h index f1e7fd6..1093d5e 100644 --- a/lib/libalpm/alpm.h +++ b/lib/libalpm/alpm.h @@ -159,6 +159,8 @@ typedef struct _alpm_depmissing_t { alpm_depend_t *depend; /* this is used in case of remove dependency error only */ char *causingpkg; + /* this is used for optdepends only */ + char *description; } alpm_depmissing_t; /** Conflict */ @@ -1083,7 +1085,7 @@ int alpm_remove_pkg(alpm_handle_t *handle, alpm_pkg_t *pkg); */ alpm_list_t *alpm_checkdeps(alpm_handle_t *handle, alpm_list_t *pkglist, - alpm_list_t *remove, alpm_list_t *upgrade, int reversedeps); + alpm_list_t *remove, alpm_list_t *upgrade, int reversedeps, int consider_optdeps); alpm_pkg_t *alpm_find_satisfier(alpm_list_t *pkgs, const char *depstring); alpm_pkg_t *alpm_find_dbs_satisfier(alpm_handle_t *handle, alpm_list_t *dbs, const char *depstring); @@ -1171,6 +1173,7 @@ enum _alpm_errno_t { ALPM_ERR_DLT_PATCHFAILED, /* Dependencies */ ALPM_ERR_UNSATISFIED_DEPS, + ALPM_ERR_UNSATISFIED_OPTDEPS, ALPM_ERR_CONFLICTING_DEPS, ALPM_ERR_FILE_CONFLICTS, /* Misc */ diff --git a/lib/libalpm/deps.c b/lib/libalpm/deps.c index 4382d6c..407e279 100644 --- a/lib/libalpm/deps.c +++ b/lib/libalpm/deps.c @@ -52,7 +52,7 @@ void _alpm_optdep_free(alpm_optdepend_t *optdep) } static alpm_depmissing_t *depmiss_new(const char *target, alpm_depend_t *dep, - const char *causingpkg) + const char *causingpkg, const char *description) { alpm_depmissing_t *miss; @@ -61,6 +61,7 @@ static alpm_depmissing_t *depmiss_new(const char *target, alpm_depend_t *dep, STRDUP(miss->target, target, return NULL); miss->depend = _alpm_dep_dup(dep); STRDUP(miss->causingpkg, causingpkg, return NULL); + STRDUP(miss->description, description, return NULL); return miss; } @@ -70,6 +71,7 @@ void _alpm_depmiss_free(alpm_depmissing_t *miss) _alpm_dep_free(miss->depend); FREE(miss->target); FREE(miss->causingpkg); + FREE(miss->description); FREE(miss); } @@ -283,11 +285,12 @@ alpm_pkg_t SYMEXPORT *alpm_find_satisfier(alpm_list_t *pkgs, const char *depstri * @param remove an alpm_list_t* of packages to be removed * @param upgrade an alpm_list_t* of packages to be upgraded (remove-then-upgrade) * @param reversedeps handles the backward dependencies + * @param consider_optdeps handles optional dependencies instead of normal ones * @return an alpm_list_t* of alpm_depmissing_t pointers. */ alpm_list_t SYMEXPORT *alpm_checkdeps(alpm_handle_t *handle, alpm_list_t *pkglist, alpm_list_t *remove, alpm_list_t *upgrade, - int reversedeps) + int reversedeps, int consider_optdeps) { alpm_list_t *i, *j; alpm_list_t *dblist = NULL, *modified = NULL; @@ -326,7 +329,7 @@ alpm_list_t SYMEXPORT *alpm_checkdeps(alpm_handle_t *handle, _alpm_log(handle, ALPM_LOG_DEBUG, "checkdeps: missing dependency '%s' for package '%s'\n", missdepstring, tp->name); free(missdepstring); - miss = depmiss_new(tp->name, depend, NULL); + miss = depmiss_new(tp->name, depend, NULL, NULL); baddeps = alpm_list_add(baddeps, miss); } release_filtered_depend(depend, nodepversion); @@ -338,8 +341,17 @@ alpm_list_t SYMEXPORT *alpm_checkdeps(alpm_handle_t *handle, * the packages listed in the requiredby field. */ for(i = dblist; i; i = i->next) { alpm_pkg_t *lp = i->data; - for(j = alpm_pkg_get_depends(lp); j; j = j->next) { - alpm_depend_t *depend = j->data; + j = consider_optdeps ? alpm_pkg_get_optdepends(lp) : alpm_pkg_get_depends(lp); + for(; j; j = j->next) { + alpm_depend_t *depend; + const char *description = NULL; + if(consider_optdeps) { + alpm_optdepend_t *optdep = j->data; + depend = optdep->depend; + description = optdep->description; + } else { + depend = j->data; + } depend = filtered_depend(depend, nodepversion); alpm_pkg_t *causingpkg = find_dep_satisfier(modified, depend); /* we won't break this depend, if it is already broken, we ignore it */ @@ -350,10 +362,15 @@ alpm_list_t SYMEXPORT *alpm_checkdeps(alpm_handle_t *handle, !find_dep_satisfier(dblist, depend)) { alpm_depmissing_t *miss; char *missdepstring = alpm_dep_compute_string(depend); - _alpm_log(handle, ALPM_LOG_DEBUG, "checkdeps: transaction would break '%s' dependency of '%s'\n", - missdepstring, lp->name); + if(consider_optdeps) { + _alpm_log(handle, ALPM_LOG_DEBUG, "checkdeps: transaction would break '%s' optional dependency of '%s'\n", + missdepstring, lp->name); + } else { + _alpm_log(handle, ALPM_LOG_DEBUG, "checkdeps: transaction would break '%s' dependency of '%s'\n", + missdepstring, lp->name); + } free(missdepstring); - miss = depmiss_new(lp->name, depend, causingpkg->name); + miss = depmiss_new(lp->name, depend, causingpkg->name, description); baddeps = alpm_list_add(baddeps, miss); } release_filtered_depend(depend, nodepversion); @@ -797,7 +814,7 @@ int _alpm_resolvedeps(alpm_handle_t *handle, alpm_list_t *localpkgs, for(i = alpm_list_last(*packages); i; i = i->next) { alpm_pkg_t *tpkg = i->data; targ = alpm_list_add(NULL, tpkg); - deps = alpm_checkdeps(handle, localpkgs, remove, targ, 0); + deps = alpm_checkdeps(handle, localpkgs, remove, targ, 0, 0); alpm_list_free(targ); for(j = deps; j; j = j->next) { diff --git a/lib/libalpm/error.c b/lib/libalpm/error.c index b3f5681..4e17263 100644 --- a/lib/libalpm/error.c +++ b/lib/libalpm/error.c @@ -136,6 +136,8 @@ const char SYMEXPORT *alpm_strerror(enum _alpm_errno_t err) /* Dependencies */ case ALPM_ERR_UNSATISFIED_DEPS: return _("could not satisfy dependencies"); + case ALPM_ERR_UNSATISFIED_OPTDEPS: + return _("could not satisfy optional dependencies"); case ALPM_ERR_CONFLICTING_DEPS: return _("conflicting dependencies"); case ALPM_ERR_FILE_CONFLICTS: diff --git a/lib/libalpm/remove.c b/lib/libalpm/remove.c index 8021702..3293713 100644 --- a/lib/libalpm/remove.c +++ b/lib/libalpm/remove.c @@ -102,7 +102,7 @@ static int remove_prepare_cascade(alpm_handle_t *handle, alpm_list_t *lp) alpm_list_free_inner(lp, (alpm_list_fn_free)_alpm_depmiss_free); alpm_list_free(lp); lp = alpm_checkdeps(handle, _alpm_db_get_pkgcache(handle->db_local), - trans->remove, NULL, 1); + trans->remove, NULL, 1, 0); } return 0; } @@ -133,7 +133,7 @@ static void remove_prepare_keep_needed(alpm_handle_t *handle, alpm_list_t *lp) alpm_list_free_inner(lp, (alpm_list_fn_free)_alpm_depmiss_free); alpm_list_free(lp); lp = alpm_checkdeps(handle, _alpm_db_get_pkgcache(handle->db_local), - trans->remove, NULL, 1); + trans->remove, NULL, 1, 0); } } @@ -164,7 +164,7 @@ int _alpm_remove_prepare(alpm_handle_t *handle, alpm_list_t **data) EVENT(handle, ALPM_EVENT_CHECKDEPS_START, NULL, NULL); _alpm_log(handle, ALPM_LOG_DEBUG, "looking for unsatisfied dependencies\n"); - lp = alpm_checkdeps(handle, _alpm_db_get_pkgcache(db), trans->remove, NULL, 1); + lp = alpm_checkdeps(handle, _alpm_db_get_pkgcache(db), trans->remove, NULL, 1, 0); if(lp != NULL) { if(trans->flags & ALPM_TRANS_FLAG_CASCADE) { @@ -206,6 +206,16 @@ int _alpm_remove_prepare(alpm_handle_t *handle, alpm_list_t **data) if(!(trans->flags & ALPM_TRANS_FLAG_NODEPS)) { EVENT(handle, ALPM_EVENT_CHECKDEPS_DONE, NULL, NULL); + lp = alpm_checkdeps(handle, _alpm_db_get_pkgcache(db), trans->remove, NULL, 1, 1); + if(lp != NULL) { + if(data) { + *data = lp; + } else { + alpm_list_free_inner(lp, (alpm_list_fn_free)_alpm_depmiss_free); + alpm_list_free(lp); + } + RET_ERR(handle, ALPM_ERR_UNSATISFIED_OPTDEPS, -1); + } } return 0; diff --git a/lib/libalpm/sync.c b/lib/libalpm/sync.c index e2562c0..ad9eb08 100644 --- a/lib/libalpm/sync.c +++ b/lib/libalpm/sync.c @@ -566,7 +566,7 @@ int _alpm_sync_prepare(alpm_handle_t *handle, alpm_list_t **data) if(!(trans->flags & ALPM_TRANS_FLAG_NODEPS)) { _alpm_log(handle, ALPM_LOG_DEBUG, "checking dependencies\n"); deps = alpm_checkdeps(handle, _alpm_db_get_pkgcache(handle->db_local), - trans->remove, trans->add, 1); + trans->remove, trans->add, 1, 0); if(deps) { handle->pm_errno = ALPM_ERR_UNSATISFIED_DEPS; ret = -1; diff --git a/lib/libalpm/trans.c b/lib/libalpm/trans.c index 47b9c98..9513858 100644 --- a/lib/libalpm/trans.c +++ b/lib/libalpm/trans.c @@ -101,6 +101,7 @@ static alpm_list_t *check_arch(alpm_handle_t *handle, alpm_list_t *pkgs) int SYMEXPORT alpm_trans_prepare(alpm_handle_t *handle, alpm_list_t **data) { alpm_trans_t *trans; + int retval = 0; /* Sanity checks */ CHECK_HANDLE(handle, return -1); @@ -127,7 +128,12 @@ int SYMEXPORT alpm_trans_prepare(alpm_handle_t *handle, alpm_list_t **data) if(trans->add == NULL) { if(_alpm_remove_prepare(handle, data) == -1) { /* pm_errno is set by _alpm_remove_prepare() */ - return -1; + /* UNSATISFIED_OPTDEPS is not really an error. */ + if(alpm_errno(handle) == ALPM_ERR_UNSATISFIED_OPTDEPS) { + retval = -1; + } else { + return -1; + } } } else { if(_alpm_sync_prepare(handle, data) == -1) { @@ -138,7 +144,7 @@ int SYMEXPORT alpm_trans_prepare(alpm_handle_t *handle, alpm_list_t **data) trans->state = STATE_PREPARED; - return 0; + return retval; } /** Commit a transaction. */ diff --git a/src/pacman/remove.c b/src/pacman/remove.c index f0ac04e..9f48789 100644 --- a/src/pacman/remove.c +++ b/src/pacman/remove.c @@ -103,29 +103,50 @@ int pacman_remove(alpm_list_t *targets) /* Step 2: prepare the transaction based on its type, targets and flags */ if(alpm_trans_prepare(config->handle, &data) == -1) { enum _alpm_errno_t err = alpm_errno(config->handle); - pm_fprintf(stderr, ALPM_LOG_ERROR, _("failed to prepare transaction (%s)\n"), - alpm_strerror(err)); + switch(err) { case ALPM_ERR_PKG_INVALID_ARCH: + pm_fprintf(stderr, ALPM_LOG_ERROR, _("failed to prepare transaction (%s)\n"), + alpm_strerror(err)); for(i = data; i; i = alpm_list_next(i)) { char *pkg = alpm_list_getdata(i); printf(_(":: package %s does not have a valid architecture\n"), pkg); } - break; + FREELIST(data); + retval = 1; + goto cleanup; case ALPM_ERR_UNSATISFIED_DEPS: + pm_fprintf(stderr, ALPM_LOG_ERROR, _("failed to prepare transaction (%s)\n"), + alpm_strerror(err)); for(i = data; i; i = alpm_list_next(i)) { alpm_depmissing_t *miss = alpm_list_getdata(i); char *depstring = alpm_dep_compute_string(miss->depend); printf(_(":: %s: requires %s\n"), miss->target, depstring); free(depstring); } + FREELIST(data); + retval = 1; + goto cleanup; + case ALPM_ERR_UNSATISFIED_OPTDEPS: + for(i = data; i; i = alpm_list_next(i)) { + alpm_depmissing_t *miss = alpm_list_getdata(i); + char *depstring = alpm_dep_compute_string(miss->depend); + if(miss->description) { + printf(_(":: %s: optionally requires %s (%s)\n"), miss->target, depstring, miss->description); + } else { + printf(_(":: %s: optionally requires %s\n"), miss->target, depstring); + } + free(depstring); + } + FREELIST(data); break; default: - break; + pm_fprintf(stderr, ALPM_LOG_ERROR, _("failed to prepare transaction (%s)\n"), + alpm_strerror(err)); + FREELIST(data); + retval = 1; + goto cleanup; } - FREELIST(data); - retval = 1; - goto cleanup; } /* Search for holdpkg in target list */ diff --git a/src/util/testdb.c b/src/util/testdb.c index d85687a..7b69ffb 100644 --- a/src/util/testdb.c +++ b/src/util/testdb.c @@ -99,7 +99,7 @@ static int checkdeps(alpm_list_t *pkglist) alpm_list_t *data, *i; int ret = 0; /* check dependencies */ - data = alpm_checkdeps(handle, pkglist, NULL, pkglist, 0); + data = alpm_checkdeps(handle, pkglist, NULL, pkglist, 0, 0); for(i = data; i; i = alpm_list_next(i)) { alpm_depmissing_t *miss = alpm_list_getdata(i); char *depstring = alpm_dep_compute_string(miss->depend); -- 1.7.6.1
Signed-off-by: Benedikt Morbach <benedikt.morbach@googlemail.com> --- doc/pacman.8.txt | 5 +++++ lib/libalpm/alpm.h | 4 +++- lib/libalpm/deps.c | 8 ++++---- lib/libalpm/deps.h | 2 +- lib/libalpm/remove.c | 6 ++++-- src/pacman/pacman.c | 4 ++++ 6 files changed, 21 insertions(+), 8 deletions(-) diff --git a/doc/pacman.8.txt b/doc/pacman.8.txt index 45386c7..4d84f7d 100644 --- a/doc/pacman.8.txt +++ b/doc/pacman.8.txt @@ -340,6 +340,11 @@ Remove Options[[RO]] to a backwards '\--sync' operation, and helps keep a clean system without orphans. If you want to omit condition (B), pass this option twice. +*-o, \--recursive-optdeps*:: + When recursively removing packages, also remove optional dependencies that + might still be in use by other packages. You will be warned which packages + might still optionally use the removed ones. + *-u, \--unneeded*:: Removes targets that are not required by any other packages. This is mostly useful when removing a group without using the '-c' option, diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h index 1093d5e..868a98b 100644 --- a/lib/libalpm/alpm.h +++ b/lib/libalpm/alpm.h @@ -993,7 +993,9 @@ typedef enum _alpm_transflag_t { /** Remove also explicitly installed unneeded deps (use with ALPM_TRANS_FLAG_RECURSE). */ ALPM_TRANS_FLAG_RECURSEALL = (1 << 16), /** Do not lock the database during the operation. */ - ALPM_TRANS_FLAG_NOLOCK = (1 << 17) + ALPM_TRANS_FLAG_NOLOCK = (1 << 17), + /** Recurse through optdepends, even if needed by other packages. */ + ALPM_TRANS_FLAG_RECURSE_OPTDEPS = (1 << 18) } alpm_transflag_t; /** Returns the bitfield of flags for the current transaction. diff --git a/lib/libalpm/deps.c b/lib/libalpm/deps.c index 407e279..54a0d3a 100644 --- a/lib/libalpm/deps.c +++ b/lib/libalpm/deps.c @@ -553,7 +553,7 @@ alpm_optdepend_t *_alpm_optdep_dup(const alpm_optdepend_t *optdep) * target list, or if if the package was explictly installed and * include_explicit == 0 */ static int can_remove_package(alpm_db_t *db, alpm_pkg_t *pkg, - alpm_list_t *targets, int include_explicit) + alpm_list_t *targets, int include_explicit, int include_optdeps) { alpm_list_t *i; @@ -582,7 +582,7 @@ static int can_remove_package(alpm_db_t *db, alpm_pkg_t *pkg, if(_alpm_dep_edge(lpkg, pkg) && !_alpm_pkg_find(targets, lpkg->name)) { return 0; } - if(_alpm_optdep_edge(lpkg, pkg) && !_alpm_pkg_find(targets, lpkg->name)) { + if(!include_optdeps && _alpm_optdep_edge(lpkg, pkg) && !_alpm_pkg_find(targets, lpkg->name)) { return 0; } } @@ -602,7 +602,7 @@ static int can_remove_package(alpm_db_t *db, alpm_pkg_t *pkg, * @param include_explicit if 0, explicitly installed packages are not included * @return 0 on success, -1 on errors */ -int _alpm_recursedeps(alpm_db_t *db, alpm_list_t *targs, int include_explicit) +int _alpm_recursedeps(alpm_db_t *db, alpm_list_t *targs, int include_explicit, int include_optdeps) { alpm_list_t *i, *j; @@ -615,7 +615,7 @@ int _alpm_recursedeps(alpm_db_t *db, alpm_list_t *targs, int include_explicit) for(j = _alpm_db_get_pkgcache(db); j; j = j->next) { alpm_pkg_t *deppkg = j->data; if((_alpm_dep_edge(pkg, deppkg) || _alpm_optdep_edge(pkg, deppkg)) - && can_remove_package(db, deppkg, targs, include_explicit)) { + && can_remove_package(db, deppkg, targs, include_explicit, include_optdeps)) { alpm_pkg_t *copy; _alpm_log(db->handle, ALPM_LOG_DEBUG, "adding '%s' to the targets\n", deppkg->name); diff --git a/lib/libalpm/deps.h b/lib/libalpm/deps.h index 69b65df..13e92b8 100644 --- a/lib/libalpm/deps.h +++ b/lib/libalpm/deps.h @@ -33,7 +33,7 @@ alpm_depend_t *_alpm_dep_dup(const alpm_depend_t *dep); alpm_optdepend_t *_alpm_optdep_dup(const alpm_optdepend_t *optdep); void _alpm_depmiss_free(alpm_depmissing_t *miss); alpm_list_t *_alpm_sortbydeps(alpm_handle_t *handle, alpm_list_t *targets, int reverse); -int _alpm_recursedeps(alpm_db_t *db, alpm_list_t *targs, int include_explicit); +int _alpm_recursedeps(alpm_db_t *db, alpm_list_t *targs, int include_explicit, int include_optdeps); int _alpm_resolvedeps(alpm_handle_t *handle, alpm_list_t *localpkgs, alpm_pkg_t *pkg, alpm_list_t *preferred, alpm_list_t **packages, alpm_list_t *remove, alpm_list_t **data); diff --git a/lib/libalpm/remove.c b/lib/libalpm/remove.c index 3293713..9e65767 100644 --- a/lib/libalpm/remove.c +++ b/lib/libalpm/remove.c @@ -155,7 +155,8 @@ int _alpm_remove_prepare(alpm_handle_t *handle, alpm_list_t **data) && !(trans->flags & ALPM_TRANS_FLAG_CASCADE)) { _alpm_log(handle, ALPM_LOG_DEBUG, "finding removable dependencies\n"); if(_alpm_recursedeps(db, trans->remove, - trans->flags & ALPM_TRANS_FLAG_RECURSEALL)) { + trans->flags & ALPM_TRANS_FLAG_RECURSEALL, + trans->flags & ALPM_TRANS_FLAG_RECURSE_OPTDEPS)) { return -1; } } @@ -199,7 +200,8 @@ int _alpm_remove_prepare(alpm_handle_t *handle, alpm_list_t **data) && (trans->flags & ALPM_TRANS_FLAG_RECURSE)) { _alpm_log(handle, ALPM_LOG_DEBUG, "finding removable dependencies\n"); if(_alpm_recursedeps(db, trans->remove, - trans->flags & ALPM_TRANS_FLAG_RECURSEALL)) { + trans->flags & ALPM_TRANS_FLAG_RECURSEALL, + trans->flags & ALPM_TRANS_FLAG_RECURSE_OPTDEPS)) { return -1; } } diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c index 3a19619..02504c4 100644 --- a/src/pacman/pacman.c +++ b/src/pacman/pacman.c @@ -131,6 +131,8 @@ static void usage(int op, const char * const myname) addlist(_(" -n, --nosave remove configuration files\n")); addlist(_(" -s, --recursive remove unnecessary dependencies\n" " (-ss includes explicitly installed dependencies)\n")); + addlist(_(" -o, remove optional dependencies,\n" + " --recurse-optdeps even if they might be used by other packages\n")); addlist(_(" -u, --unneeded remove unneeded packages\n")); } else if(op == PM_OP_UPGRADE) { printf("%s: %s {-U --upgrade} [%s] <%s>\n", str_usg, myname, str_opt, str_file); @@ -510,6 +512,7 @@ static int parsearg_remove(int opt) switch(opt) { case 'c': config->flags |= ALPM_TRANS_FLAG_CASCADE; break; case 'n': config->flags |= ALPM_TRANS_FLAG_NOSAVE; break; + case 'o': config->flags |= ALPM_TRANS_FLAG_RECURSE_OPTDEPS; break; case 's': case OP_RECURSIVE: /* 's' is the legacy flag here, but since recursive is used in -S without @@ -608,6 +611,7 @@ static int parseargs(int argc, char *argv[]) {"nosave", no_argument, 0, 'n'}, {"optdeps", no_argument, 0, 'n'}, {"owns", no_argument, 0, 'o'}, + {"recurse-optdeps", no_argument, 0, 'o'}, {"file", no_argument, 0, 'p'}, {"print", no_argument, 0, 'p'}, {"quiet", no_argument, 0, 'q'}, -- 1.7.6.1
Signed-off-by: Benedikt Morbach <benedikt.morbach@googlemail.com> --- src/pacman/util.c | 40 ++++++++++++++++++++++++++-------------- 1 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/pacman/util.c b/src/pacman/util.c index afe6840..1f54f65 100644 --- a/src/pacman/util.c +++ b/src/pacman/util.c @@ -1053,26 +1053,38 @@ alpm_list_t *optdep_string_list(const alpm_list_t *optlist, int include_installe { alpm_list_t *optstrings = NULL; alpm_optdepend_t *optdep; - alpm_db_t *db_local; + alpm_db_t *db_local = alpm_option_get_localdb(config->handle); - db_local = alpm_option_get_localdb(config->handle); + /* calculate these outside the loop. */ + alpm_list_t *pkgcache = alpm_db_get_pkgcache(db_local); + alpm_list_t *remove = alpm_trans_get_remove(config->handle); + alpm_list_t *add = alpm_trans_get_add(config->handle); - /* turn optdepends list into a text list */ + /* turn optdepends list into a text list. */ for( ; optlist; optlist = alpm_list_next(optlist)) { optdep = alpm_list_getdata(optlist); - if(include_installed && alpm_db_get_pkg(db_local, optdep->depend->name)) { - const char * const installed = _(" [installed]"); - char *str, *tmp; - tmp = alpm_optdep_compute_string(optdep); - if((str = realloc(tmp, strlen(tmp) + strlen(installed) + 1)) != NULL) { - strcpy(str + strlen(str), installed); - optstrings = alpm_list_add(optstrings, str); - } else { - free(tmp); - } + char *depstr = alpm_dep_compute_string(optdep->depend); + char *tmp, *str = alpm_optdep_compute_string(optdep); + const char *state = NULL; + + if(alpm_find_satisfier(pkgcache, depstr) && alpm_find_satisfier(remove, depstr) == NULL) { + state = include_installed ? _(" [installed]") : NULL; + } else if(alpm_find_satisfier(add, depstr)) { + state = include_installed ? _(" [installing]") : NULL; + } else if(alpm_find_satisfier(remove, depstr)) { + state = _(" [removing]"); } else { - optstrings = alpm_list_add(optstrings, alpm_optdep_compute_string(optdep)); + optstrings = alpm_list_add(optstrings, str); } + + if(state) { + if((tmp = realloc(str, strlen(str) + strlen(state) + 1)) != NULL) { + strcpy(tmp + strlen(tmp), state); + str = tmp; + } /* if realloc fails, we only loose the state information, which is nonfatal. */ + optstrings = alpm_list_add(optstrings, str); + } + free(depstr); } return optstrings; } -- 1.7.6.1
Signed-off-by: Benedikt Morbach <benedikt.morbach@googlemail.com> --- doc/pacman.conf.5.txt | 4 +++ etc/pacman.conf.in | 2 + lib/libalpm/add.c | 4 +- lib/libalpm/alpm.h | 2 +- src/pacman/conf.c | 23 ++++++++++++++++++++ src/pacman/conf.h | 7 ++++++ src/pacman/sync.c | 55 ++++++++++++++++++++++++++++++++++++++++++++---- src/pacman/upgrade.c | 2 +- 8 files changed, 90 insertions(+), 9 deletions(-) diff --git a/doc/pacman.conf.5.txt b/doc/pacman.conf.5.txt index ad7fb40..5354358 100644 --- a/doc/pacman.conf.5.txt +++ b/doc/pacman.conf.5.txt @@ -156,6 +156,10 @@ Options packages are only cleaned if not installed locally and not present in any known sync database. +*HandleOptdeps =* Install:: + If set to `Install`, the optional dependencies of all targets are + automatically installed. + *SigLevel =* ...:: Set the default signature verification level. For more information, see <<SC,Package and Database Signature Checking>> below. diff --git a/etc/pacman.conf.in b/etc/pacman.conf.in index 73445ac..cfd96e1 100644 --- a/etc/pacman.conf.in +++ b/etc/pacman.conf.in @@ -37,6 +37,8 @@ Architecture = auto #VerbosePkgLists #ShowAllOptdeps +#HandleOptdeps = Install + # PGP signature checking #SigLevel = Optional diff --git a/lib/libalpm/add.c b/lib/libalpm/add.c index b526145..ce5439e 100644 --- a/lib/libalpm/add.c +++ b/lib/libalpm/add.c @@ -50,7 +50,7 @@ #include "handle.h" /** Add a package to the transaction. */ -int SYMEXPORT alpm_add_pkg(alpm_handle_t *handle, alpm_pkg_t *pkg) +int SYMEXPORT alpm_add_pkg(alpm_handle_t *handle, alpm_pkg_t *pkg, alpm_pkgreason_t reason) { const char *pkgname, *pkgver; alpm_trans_t *trans; @@ -98,7 +98,7 @@ int SYMEXPORT alpm_add_pkg(alpm_handle_t *handle, alpm_pkg_t *pkg) } /* add the package to the transaction */ - pkg->reason = ALPM_PKG_REASON_EXPLICIT; + pkg->reason = reason; _alpm_log(handle, ALPM_LOG_DEBUG, "adding package %s-%s to the transaction add list\n", pkgname, pkgver); trans->add = alpm_list_add(trans->add, pkg); diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h index 868a98b..9e174ac 100644 --- a/lib/libalpm/alpm.h +++ b/lib/libalpm/alpm.h @@ -1069,7 +1069,7 @@ int alpm_sync_sysupgrade(alpm_handle_t *handle, int enable_downgrade); * @param pkg the package to add * @return 0 on success, -1 on error (pm_errno is set accordingly) */ -int alpm_add_pkg(alpm_handle_t *handle, alpm_pkg_t *pkg); +int alpm_add_pkg(alpm_handle_t *handle, alpm_pkg_t *pkg, alpm_pkgreason_t reason); /** Add a package removal action to the transaction. * @param handle the context handle diff --git a/src/pacman/conf.c b/src/pacman/conf.c index f2982e3..e88e916 100644 --- a/src/pacman/conf.c +++ b/src/pacman/conf.c @@ -333,6 +333,21 @@ static int process_cleanmethods(alpm_list_t *values) { return 0; } +static int process_handleoptdeps(alpm_list_t *actions) { + alpm_list_t *i; + for(i = actions; i; i = alpm_list_next(i)) { + const char *action = i->data; + if(strcmp(action, "Install") == 0) { + config->handleoptdeps |= PM_OPTDEPS_INSTALL; + } else { + pm_printf(ALPM_LOG_ERROR, _("invalid action for 'HandleOptdeps' : '%s'\n"), + action); + return 1; + } + } + return 0; +} + /** Add repeating options such as NoExtract, NoUpgrade, etc to libalpm * settings. Refactored out of the parseconfig code since all of them did * the exact same thing and duplicated code. @@ -439,6 +454,14 @@ static int _parse_options(const char *key, char *value, return 1; } FREELIST(methods); + } else if(strcmp(key, "HandleOptdeps") == 0) { + alpm_list_t *actions = NULL; + setrepeatingoption(value, "HandleOptdeps", &actions); + if(process_handleoptdeps(actions)) { + FREELIST(actions); + return 1; + } + FREELIST(actions); } else if(strcmp(key, "SigLevel") == 0) { alpm_list_t *values = NULL; setrepeatingoption(value, "SigLevel", &values); diff --git a/src/pacman/conf.h b/src/pacman/conf.h index 23008ea..384f379 100644 --- a/src/pacman/conf.h +++ b/src/pacman/conf.h @@ -85,6 +85,8 @@ typedef struct __config_t { unsigned short totaldownload; /* select -Sc behavior */ unsigned short cleanmethod; + /* wether and how to handle optdeps */ + unsigned short handleoptdeps; alpm_list_t *holdpkg; alpm_list_t *syncfirst; alpm_list_t *ignorepkg; @@ -136,6 +138,11 @@ enum { PM_CLEAN_KEEPCUR = (1 << 1) }; +/* optdepends handling */ +enum { + PM_OPTDEPS_INSTALL = 1 +}; + /* global config variable */ extern config_t *config; diff --git a/src/pacman/sync.c b/src/pacman/sync.c index 15eea95..1bb09e9 100644 --- a/src/pacman/sync.c +++ b/src/pacman/sync.c @@ -591,9 +591,14 @@ static alpm_db_t *get_db(const char *dbname) return NULL; } -static int process_pkg(alpm_pkg_t *pkg) +static int process_pkg(alpm_pkg_t *pkg, int as_dep) { - int ret = alpm_add_pkg(config->handle, pkg); + int ret; + if(as_dep) { + ret = alpm_add_pkg(config->handle, pkg, ALPM_PKG_REASON_DEPEND); + } else { + ret = alpm_add_pkg(config->handle, pkg, ALPM_PKG_REASON_EXPLICIT); + } if(ret == -1) { enum _alpm_errno_t err = alpm_errno(config->handle); @@ -611,6 +616,34 @@ static int process_pkg(alpm_pkg_t *pkg) return 0; } +static int process_optdeps(alpm_list_t *dblist, alpm_pkg_t *pkg) +{ + if(config->handleoptdeps & PM_OPTDEPS_INSTALL) { + alpm_list_t *optdeps = alpm_pkg_get_optdepends(pkg); + alpm_list_t *i; + + for(i = optdeps; i; i = alpm_list_next(i)) { + alpm_optdepend_t *optdep = alpm_list_getdata(i); + char *depstring = alpm_dep_compute_string(optdep->depend); + alpm_pkg_t *pkg = alpm_find_dbs_satisfier(config->handle, dblist, depstring); + free(depstring); + + if(alpm_errno(config->handle) == ALPM_ERR_PKG_IGNORED) { + pm_printf(ALPM_LOG_WARNING, _("skipping optdepend: %s\n"), optdep->depend->name); + continue; + } + + if(pkg) { + if(process_pkg(pkg, 1) == 1) { + return 1; + } + } + } + } + + return 0; +} + static int process_group(alpm_list_t *dbs, const char *group) { int ret = 0; @@ -644,7 +677,11 @@ static int process_group(alpm_list_t *dbs, const char *group) continue; alpm_pkg_t *pkg = alpm_list_getdata(i); - if(process_pkg(pkg) == 1) { + if(process_pkg(pkg, 0) == 1) { + ret = 1; + free(array); + goto cleanup; + } else if(process_optdeps(dbs, pkg) == 1) { ret = 1; free(array); goto cleanup; @@ -655,7 +692,10 @@ static int process_group(alpm_list_t *dbs, const char *group) for(i = pkgs; i; i = alpm_list_next(i)) { alpm_pkg_t *pkg = alpm_list_getdata(i); - if(process_pkg(pkg) == 1) { + if(process_pkg(pkg, 0) == 1) { + ret = 1; + goto cleanup; + } else if(process_optdeps(dbs, pkg) == 1) { ret = 1; goto cleanup; } @@ -669,6 +709,7 @@ cleanup: static int process_targname(alpm_list_t *dblist, const char *targname) { alpm_pkg_t *pkg = alpm_find_dbs_satisfier(config->handle, dblist, targname); + int retval = 0; /* #FS#23342 - skip ignored packages when user says no */ if(alpm_errno(config->handle) == ALPM_ERR_PKG_IGNORED) { @@ -677,7 +718,11 @@ static int process_targname(alpm_list_t *dblist, const char *targname) } if(pkg) { - return process_pkg(pkg); + retval = process_pkg(pkg, 0); + if(retval == 0) { + retval = process_optdeps(dblist, pkg); + } + return retval; } /* fallback on group */ return process_group(dblist, targname); diff --git a/src/pacman/upgrade.c b/src/pacman/upgrade.c index dcd0d66..6dba50b 100644 --- a/src/pacman/upgrade.c +++ b/src/pacman/upgrade.c @@ -82,7 +82,7 @@ int pacman_upgrade(alpm_list_t *targets) trans_release(); return 1; } - if(alpm_add_pkg(config->handle, pkg) == -1) { + if(alpm_add_pkg(config->handle, pkg, ALPM_PKG_REASON_EXPLICIT) == -1) { pm_fprintf(stderr, ALPM_LOG_ERROR, "'%s': %s\n", targ, alpm_strerror(alpm_errno(config->handle))); alpm_pkg_free(pkg); -- 1.7.6.1
Signed-off-by: Benedikt Morbach <benedikt.morbach@googlemail.com> --- doc/pacman.conf.5.txt | 5 +++- etc/pacman.conf.in | 2 +- src/pacman/conf.c | 2 + src/pacman/conf.h | 3 +- src/pacman/sync.c | 70 ++++++++++++++++++++++++++++++++++++++++++++++++- src/pacman/util.c | 59 ++++++++++++++++++++++++++++------------ src/pacman/util.h | 1 + 7 files changed, 120 insertions(+), 22 deletions(-) diff --git a/doc/pacman.conf.5.txt b/doc/pacman.conf.5.txt index 5354358..2bfe757 100644 --- a/doc/pacman.conf.5.txt +++ b/doc/pacman.conf.5.txt @@ -156,9 +156,12 @@ Options packages are only cleaned if not installed locally and not present in any known sync database. -*HandleOptdeps =* Install:: +*HandleOptdeps =* Install &| Ask:: If set to `Install`, the optional dependencies of all targets are automatically installed. + If set to `Ask`, display a prompt for each target, where the user can select + which of the optional dependencies of that target will be installed. + Also, the optional dependencies of the targets won't be shown during installation. *SigLevel =* ...:: Set the default signature verification level. For more information, see diff --git a/etc/pacman.conf.in b/etc/pacman.conf.in index cfd96e1..06f4846 100644 --- a/etc/pacman.conf.in +++ b/etc/pacman.conf.in @@ -37,7 +37,7 @@ Architecture = auto #VerbosePkgLists #ShowAllOptdeps -#HandleOptdeps = Install +#HandleOptdeps = Install Ask # PGP signature checking #SigLevel = Optional diff --git a/src/pacman/conf.c b/src/pacman/conf.c index e88e916..35de34a 100644 --- a/src/pacman/conf.c +++ b/src/pacman/conf.c @@ -339,6 +339,8 @@ static int process_handleoptdeps(alpm_list_t *actions) { const char *action = i->data; if(strcmp(action, "Install") == 0) { config->handleoptdeps |= PM_OPTDEPS_INSTALL; + } else if(strcmp(action, "Ask") == 0) { + config->handleoptdeps |= PM_OPTDEPS_ASK; } else { pm_printf(ALPM_LOG_ERROR, _("invalid action for 'HandleOptdeps' : '%s'\n"), action); diff --git a/src/pacman/conf.h b/src/pacman/conf.h index 384f379..6948abd 100644 --- a/src/pacman/conf.h +++ b/src/pacman/conf.h @@ -140,7 +140,8 @@ enum { /* optdepends handling */ enum { - PM_OPTDEPS_INSTALL = 1 + PM_OPTDEPS_INSTALL = 1, + PM_OPTDEPS_ASK = (1 << 1) }; /* global config variable */ diff --git a/src/pacman/sync.c b/src/pacman/sync.c index 1bb09e9..f073b59 100644 --- a/src/pacman/sync.c +++ b/src/pacman/sync.c @@ -618,7 +618,75 @@ static int process_pkg(alpm_pkg_t *pkg, int as_dep) static int process_optdeps(alpm_list_t *dblist, alpm_pkg_t *pkg) { - if(config->handleoptdeps & PM_OPTDEPS_INSTALL) { + if(config->print == 0 && (config->handleoptdeps & PM_OPTDEPS_ASK)) { + alpm_list_t *optdeps = alpm_pkg_get_optdepends(pkg); + int retval = 0; + + if(optdeps) { + alpm_list_t *i, *depstrings, *optstrings; + depstrings = optstrings = NULL; + + if(config->showalloptdeps == 0) { + alpm_db_t *db_local = alpm_option_get_localdb(config->handle); + alpm_list_t *pkgcache = alpm_db_get_pkgcache(db_local); + + for(i = optdeps; i; i = alpm_list_next(i)) { + alpm_optdepend_t *optdep = alpm_list_getdata(i); + char *depstring = alpm_dep_compute_string(optdep->depend); + if(alpm_find_satisfier(pkgcache, depstring) == NULL) { + depstrings = alpm_list_add(depstrings, depstring); + optstrings = alpm_list_add(optstrings, alpm_optdep_compute_string(optdep)); + } else { + free(depstring); + } + } + } else { + for(i = optdeps; i; i = alpm_list_next(i)) { + alpm_optdepend_t *optdep = alpm_list_getdata(i); + depstrings = alpm_list_add(depstrings, alpm_dep_compute_string(optdep->depend)); + optstrings = alpm_list_add(optstrings, alpm_optdep_compute_string(optdep)); + } + } + + int count = alpm_list_count(optstrings); + + if(count) { + printf(_(":: %s has %d optional dependencies:\n"), alpm_pkg_get_name(pkg), count); + select_optdep_display(optstrings); /* use config->showalloptdeps ! */ + + char *array = malloc(count); + if(!array) { + retval = 1; + goto cleanup; + } + + if(multiselect_question(array, count)) { + retval = 1; + goto cleanup; + } + + int n = 0; + for(i = depstrings; i; i = alpm_list_next(i)) { + if(array[n++] == 0) { continue; } + + char *depstring = alpm_list_getdata(i); + alpm_pkg_t *pkg = alpm_find_dbs_satisfier(config->handle, dblist, depstring); + + if(process_pkg(pkg, 1) == 1) { + retval = 1; + goto cleanup; + } + } + + cleanup: + free(array); + } + + FREELIST(depstrings); + FREELIST(optstrings); + return retval; + } + } else if(config->handleoptdeps & PM_OPTDEPS_INSTALL) { alpm_list_t *optdeps = alpm_pkg_get_optdepends(pkg); alpm_list_t *i; diff --git a/src/pacman/util.c b/src/pacman/util.c index 1f54f65..72a78d9 100644 --- a/src/pacman/util.c +++ b/src/pacman/util.c @@ -1091,35 +1091,43 @@ alpm_list_t *optdep_string_list(const alpm_list_t *optlist, int include_installe void display_new_optdepends(alpm_pkg_t *oldpkg, alpm_pkg_t *newpkg) { - alpm_list_t *old, *new, *optdeps, *optstrings; + /* don't show optdepends if we already asked for them. '*/ + if(!((config->handleoptdeps & PM_OPTDEPS_ASK) && + alpm_find_satisfier(alpm_trans_get_add(config->handle), alpm_pkg_get_name(newpkg)))) { + alpm_list_t *old, *new, *optdeps, *optstrings; - old = alpm_pkg_get_optdepends(oldpkg); - new = alpm_pkg_get_optdepends(newpkg); - optdeps = alpm_list_diff(new, old, opt_cmp); + old = alpm_pkg_get_optdepends(oldpkg); + new = alpm_pkg_get_optdepends(newpkg); + optdeps = alpm_list_diff(new, old, opt_cmp); - optstrings = optdep_string_list(optdeps, config->showalloptdeps); + optstrings = optdep_string_list(optdeps, config->showalloptdeps); - if(optstrings) { - printf(_("New optional dependencies for %s\n"), alpm_pkg_get_name(newpkg)); - list_display_linebreak(" ", optstrings); + if(optstrings) { + printf(_("New optional dependencies for %s\n"), alpm_pkg_get_name(newpkg)); + list_display_linebreak(" ", optstrings); + } + + alpm_list_free(optdeps); + FREELIST(optstrings); } - - alpm_list_free(optdeps); - FREELIST(optstrings); } void display_optdepends(alpm_pkg_t *pkg) { - alpm_list_t *optstrings; + /* don't show optdepends if we already asked for them. '*/ + if(!((config->handleoptdeps & PM_OPTDEPS_ASK) && + alpm_find_satisfier(alpm_trans_get_add(config->handle), alpm_pkg_get_name(pkg)))) { + alpm_list_t *optstrings; - optstrings = optdep_string_list(alpm_pkg_get_optdepends(pkg), config->showalloptdeps); + optstrings = optdep_string_list(alpm_pkg_get_optdepends(pkg), config->showalloptdeps); - if(optstrings) { - printf(_("Optional dependencies for %s\n"), alpm_pkg_get_name(pkg)); - list_display_linebreak(" ", optstrings); + if(optstrings) { + printf(_("Optional dependencies for %s\n"), alpm_pkg_get_name(pkg)); + list_display_linebreak(" ", optstrings); + } + + FREELIST(optstrings); } - - FREELIST(optstrings); } static void display_repo_list(const char *dbname, alpm_list_t *list) @@ -1159,6 +1167,21 @@ void select_display(const alpm_list_t *pkglist) FREELIST(list); } +void select_optdep_display(alpm_list_t *optstrings) +{ + const alpm_list_t *i; + int nth = 1; + char number[8]; /* 2 padding left, 2 number, 1 ')', 2 padding right, 1 '\0' */ + + for(i = optstrings; i; i = i->next, nth++) { + char *str = alpm_list_getdata(i); + if(str) { + snprintf(number, 8, " %2d) ", nth); + string_display(number, str); + } + } +} + static int parseindex(char *s, int *val, int min, int max) { char *endptr = NULL; diff --git a/src/pacman/util.h b/src/pacman/util.h index 0eac77a..2e35e84 100644 --- a/src/pacman/util.h +++ b/src/pacman/util.h @@ -67,6 +67,7 @@ void display_optdepends(alpm_pkg_t *pkg); alpm_list_t *optdep_string_list(const alpm_list_t *optdeps, int include_installed); void print_packages(const alpm_list_t *packages); void select_display(const alpm_list_t *pkglist); +void select_optdep_display(alpm_list_t *optstrings); int select_question(int count); int multiselect_question(char *array, int count); int yesno(char *fmt, ...); -- 1.7.6.1
Signed-off-by: Benedikt Morbach <benedikt.morbach@googlemail.com> --- doc/pacman.conf.5.txt | 2 ++ src/pacman/sync.c | 4 ++-- src/pacman/util.c | 11 ++++++++--- src/pacman/util.h | 2 +- 4 files changed, 13 insertions(+), 6 deletions(-) diff --git a/doc/pacman.conf.5.txt b/doc/pacman.conf.5.txt index 2bfe757..7735a1f 100644 --- a/doc/pacman.conf.5.txt +++ b/doc/pacman.conf.5.txt @@ -162,6 +162,8 @@ Options If set to `Ask`, display a prompt for each target, where the user can select which of the optional dependencies of that target will be installed. Also, the optional dependencies of the targets won't be shown during installation. + The prompt selects none of the optional dependencies by default. + If both values are specified, all optional dependencies are selected by default. *SigLevel =* ...:: Set the default signature verification level. For more information, see diff --git a/src/pacman/sync.c b/src/pacman/sync.c index f073b59..8f2f49f 100644 --- a/src/pacman/sync.c +++ b/src/pacman/sync.c @@ -660,7 +660,7 @@ static int process_optdeps(alpm_list_t *dblist, alpm_pkg_t *pkg) goto cleanup; } - if(multiselect_question(array, count)) { + if(multiselect_question(array, count, config->handleoptdeps & PM_OPTDEPS_INSTALL)) { retval = 1; goto cleanup; } @@ -734,7 +734,7 @@ static int process_group(alpm_list_t *dbs, const char *group) ret = 1; goto cleanup; } - if(multiselect_question(array, count)) { + if(multiselect_question(array, count, 1)) { ret = 1; free(array); goto cleanup; diff --git a/src/pacman/util.c b/src/pacman/util.c index 72a78d9..1a8a39d 100644 --- a/src/pacman/util.c +++ b/src/pacman/util.c @@ -1255,11 +1255,12 @@ static int multiselect_parse(char *array, int count, char *response) return 0; } -int multiselect_question(char *array, int count) +int multiselect_question(char *array, int count, int default_state) { char *response, *lastchar; FILE *stream; size_t response_len = 64; + default_state = default_state ? 1 : 0; if(config->noconfirm) { stream = stdout; @@ -1277,10 +1278,14 @@ int multiselect_question(char *array, int count) *lastchar = 1; while(1) { - memset(array, 1, count); + memset(array, default_state, count); fprintf(stream, "\n"); - fprintf(stream, _("Enter a selection (default=all)")); + if(default_state) { + fprintf(stream, _("Enter a selection (default=all)")); + } else { + fprintf(stream, _("Enter a selection (default=none)")); + } fprintf(stream, ": "); fflush(stream); diff --git a/src/pacman/util.h b/src/pacman/util.h index 2e35e84..b505dba 100644 --- a/src/pacman/util.h +++ b/src/pacman/util.h @@ -69,7 +69,7 @@ void print_packages(const alpm_list_t *packages); void select_display(const alpm_list_t *pkglist); void select_optdep_display(alpm_list_t *optstrings); int select_question(int count); -int multiselect_question(char *array, int count); +int multiselect_question(char *array, int count, int default_state); int yesno(char *fmt, ...); int noyes(char *fmt, ...); int pm_printf(alpm_loglevel_t level, const char *format, ...) __attribute__((format(printf,2,3))); -- 1.7.6.1
participants (1)
-
Benedikt Morbach