Issue a warning if the user for whatever reason tries to remove
an optdepend of an installed package.

Signed-off-by: Benedikt Morbach <[email protected]>
---

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


Reply via email to