summaryrefslogtreecommitdiffstats
path: root/src/depcon.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 00:45:20 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 00:45:20 +0000
commit9a08cbfcc1ef900a04580f35afe2a4592d7d6030 (patch)
tree004cc7027bca2f2c0bcb5806527c8e0c48df2d6e /src/depcon.c
parentInitial commit. (diff)
downloaddpkg-upstream.tar.xz
dpkg-upstream.zip
Adding upstream version 1.19.8.upstream/1.19.8upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--src/depcon.c699
1 files changed, 699 insertions, 0 deletions
diff --git a/src/depcon.c b/src/depcon.c
new file mode 100644
index 0000000..593edfb
--- /dev/null
+++ b/src/depcon.c
@@ -0,0 +1,699 @@
+/*
+ * dpkg - main program for package management
+ * depcon.c - dependency and conflict checking
+ *
+ * Copyright © 1994,1995 Ian Jackson <ijackson@chiark.greenend.org.uk>
+ * Copyright © 2006-2014 Guillem Jover <guillem@debian.org>
+ * Copyright © 2011 Linaro Limited
+ * Copyright © 2011 Raphaël Hertzog <hertzog@debian.org>
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <compat.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <dpkg/i18n.h>
+#include <dpkg/dpkg.h>
+#include <dpkg/dpkg-db.h>
+#include <dpkg/db-ctrl.h>
+#include <dpkg/db-fsys.h>
+
+#include "main.h"
+
+struct deppossi_pkg_iterator {
+ struct deppossi *possi;
+ struct pkginfo *pkg_next;
+ enum which_pkgbin which_pkgbin;
+};
+
+struct deppossi_pkg_iterator *
+deppossi_pkg_iter_new(struct deppossi *possi, enum which_pkgbin wpb)
+{
+ struct deppossi_pkg_iterator *iter;
+
+ iter = m_malloc(sizeof(*iter));
+ iter->possi = possi;
+ iter->pkg_next = &possi->ed->pkg;
+ iter->which_pkgbin = wpb;
+
+ return iter;
+}
+
+struct pkginfo *
+deppossi_pkg_iter_next(struct deppossi_pkg_iterator *iter)
+{
+ struct pkginfo *pkg_cur;
+ struct pkgbin *pkgbin;
+
+ while ((pkg_cur = iter->pkg_next)) {
+ iter->pkg_next = pkg_cur->arch_next;
+
+ switch (iter->which_pkgbin) {
+ case wpb_installed:
+ pkgbin = &pkg_cur->installed;
+ break;
+ case wpb_available:
+ pkgbin = &pkg_cur->available;
+ break;
+ case wpb_by_istobe:
+ if (pkg_cur->clientdata &&
+ pkg_cur->clientdata->istobe == PKG_ISTOBE_INSTALLNEW)
+ pkgbin = &pkg_cur->available;
+ else
+ pkgbin = &pkg_cur->installed;
+ break;
+ default:
+ internerr("unknown which_pkgbin %d", iter->which_pkgbin);
+ }
+
+ if (archsatisfied(pkgbin, iter->possi))
+ return pkg_cur;
+ }
+
+ return NULL;
+}
+
+void
+deppossi_pkg_iter_free(struct deppossi_pkg_iterator *iter)
+{
+ free(iter);
+}
+
+struct cyclesofarlink {
+ struct cyclesofarlink *prev;
+ struct pkginfo *pkg;
+ struct deppossi *possi;
+};
+
+static bool findbreakcyclerecursive(struct pkginfo *pkg,
+ struct cyclesofarlink *sofar);
+
+static bool
+foundcyclebroken(struct cyclesofarlink *thislink, struct cyclesofarlink *sofar,
+ struct pkginfo *dependedon, struct deppossi *possi)
+{
+ struct cyclesofarlink *sol;
+
+ if(!possi)
+ return false;
+
+ /* We're investigating the dependency ‘possi’ to see if it
+ * is part of a loop. To this end we look to see whether the
+ * depended-on package is already one of the packages whose
+ * dependencies we're searching. */
+ for (sol = sofar; sol && sol->pkg != dependedon; sol = sol->prev);
+
+ /* If not, we do a recursive search on it to see what we find. */
+ if (!sol)
+ return findbreakcyclerecursive(dependedon, thislink);
+
+ debug(dbg_depcon,"found cycle");
+ /* Right, we now break one of the links. We prefer to break
+ * a dependency of a package without a postinst script, as
+ * this is a null operation. If this is not possible we break
+ * the other link in the recursive calling tree which mentions
+ * this package (this being the first package involved in the
+ * cycle). It doesn't particularly matter which we pick, but if
+ * we break the earliest dependency we came across we may be
+ * able to do something straight away when findbreakcycle returns. */
+ sofar= thislink;
+ for (sol = sofar; !(sol != sofar && sol->pkg == dependedon); sol = sol->prev) {
+ if (!pkg_infodb_has_file(sol->pkg, &sol->pkg->installed, POSTINSTFILE))
+ break;
+ }
+
+ /* Now we have either a package with no postinst, or the other
+ * occurrence of the current package in the list. */
+ sol->possi->cyclebreak = true;
+
+ debug(dbg_depcon, "cycle broken at %s -> %s",
+ pkg_name(sol->possi->up->up, pnaw_always), sol->possi->ed->name);
+
+ return true;
+}
+
+/**
+ * Cycle breaking works recursively down the package dependency tree.
+ *
+ * ‘sofar’ is the list of packages we've descended down already - if we
+ * encounter any of its packages again in a dependency we have found a cycle.
+ */
+static bool
+findbreakcyclerecursive(struct pkginfo *pkg, struct cyclesofarlink *sofar)
+{
+ struct cyclesofarlink thislink, *sol;
+ struct dependency *dep;
+ struct deppossi *possi, *providelink;
+ struct pkginfo *provider, *pkg_pos;
+
+ if (pkg->clientdata->color == PKG_CYCLE_BLACK)
+ return false;
+ pkg->clientdata->color = PKG_CYCLE_GRAY;
+
+ if (debug_has_flag(dbg_depcondetail)) {
+ struct varbuf str_pkgs = VARBUF_INIT;
+
+ for (sol = sofar; sol; sol = sol->prev) {
+ varbuf_add_str(&str_pkgs, " <- ");
+ varbuf_add_pkgbin_name(&str_pkgs, sol->pkg, &sol->pkg->installed, pnaw_nonambig);
+ }
+ varbuf_end_str(&str_pkgs);
+ debug(dbg_depcondetail, "findbreakcyclerecursive %s %s",
+ pkg_name(pkg, pnaw_always), str_pkgs.buf);
+ varbuf_destroy(&str_pkgs);
+ }
+ thislink.pkg= pkg;
+ thislink.prev = sofar;
+ thislink.possi = NULL;
+ for (dep= pkg->installed.depends; dep; dep= dep->next) {
+ if (dep->type != dep_depends && dep->type != dep_predepends) continue;
+ for (possi= dep->list; possi; possi= possi->next) {
+ struct deppossi_pkg_iterator *possi_iter;
+
+ /* Don't find the same cycles again. */
+ if (possi->cyclebreak) continue;
+ thislink.possi= possi;
+
+ possi_iter = deppossi_pkg_iter_new(possi, wpb_installed);
+ while ((pkg_pos = deppossi_pkg_iter_next(possi_iter)))
+ if (foundcyclebroken(&thislink, sofar, pkg_pos, possi)) {
+ deppossi_pkg_iter_free(possi_iter);
+ return true;
+ }
+ deppossi_pkg_iter_free(possi_iter);
+
+ /* Right, now we try all the providers ... */
+ for (providelink = possi->ed->depended.installed;
+ providelink;
+ providelink = providelink->rev_next) {
+ if (providelink->up->type != dep_provides) continue;
+ provider= providelink->up->up;
+ if (provider->clientdata->istobe == PKG_ISTOBE_NORMAL)
+ continue;
+ /* We don't break things at ‘provides’ links, so ‘possi’ is
+ * still the one we use. */
+ if (foundcyclebroken(&thislink, sofar, provider, possi))
+ return true;
+ }
+ }
+ }
+ /* Nope, we didn't find a cycle to break. */
+ pkg->clientdata->color = PKG_CYCLE_BLACK;
+ return false;
+}
+
+bool
+findbreakcycle(struct pkginfo *pkg)
+{
+ struct pkg_hash_iter *iter;
+ struct pkginfo *tpkg;
+
+ /* Clear the visited flag of all packages before we traverse them. */
+ iter = pkg_hash_iter_new();
+ while ((tpkg = pkg_hash_iter_next_pkg(iter))) {
+ ensure_package_clientdata(tpkg);
+ tpkg->clientdata->color = PKG_CYCLE_WHITE;
+ }
+ pkg_hash_iter_free(iter);
+
+ return findbreakcyclerecursive(pkg, NULL);
+}
+
+void describedepcon(struct varbuf *addto, struct dependency *dep) {
+ const char *fmt;
+ struct varbuf depstr = VARBUF_INIT;
+
+ switch (dep->type) {
+ case dep_depends:
+ fmt = _("%s depends on %s");
+ break;
+ case dep_predepends:
+ fmt = _("%s pre-depends on %s");
+ break;
+ case dep_recommends:
+ fmt = _("%s recommends %s");
+ break;
+ case dep_suggests:
+ fmt = _("%s suggests %s");
+ break;
+ case dep_breaks:
+ fmt = _("%s breaks %s");
+ break;
+ case dep_conflicts:
+ fmt = _("%s conflicts with %s");
+ break;
+ case dep_enhances:
+ fmt = _("%s enhances %s");
+ break;
+ default:
+ internerr("unknown deptype '%d'", dep->type);
+ }
+
+ varbufdependency(&depstr, dep);
+ varbuf_end_str(&depstr);
+
+ varbuf_printf(addto, fmt, pkg_name(dep->up, pnaw_nonambig), depstr.buf);
+ varbuf_destroy(&depstr);
+}
+
+/*
+ * *whynot must already have been initialized; it need not be
+ * empty though - it will be reset before use.
+ *
+ * If depisok returns false for ‘not OK’ it will contain a description,
+ * newline-terminated BUT NOT NUL-TERMINATED, of the reason.
+ *
+ * If depisok returns true it will contain garbage.
+ * allowunconfigd should be non-zero during the ‘Pre-Depends’ checking
+ * before a package is unpacked, when it is sufficient for the package
+ * to be unpacked provided that both the unpacked and previously-configured
+ * versions are acceptable.
+ *
+ * On false return (‘not OK’), *canfixbyremove refers to a package which
+ * if removed (dep_conflicts) or deconfigured (dep_breaks) will fix
+ * the problem. Caller may pass NULL for canfixbyremove and need not
+ * initialize *canfixbyremove.
+ *
+ * On false return (‘not OK’), *canfixbytrigaw refers to a package which
+ * can fix the problem if all the packages listed in Triggers-Awaited have
+ * their triggers processed. Caller may pass NULL for canfixbytrigaw and
+ * need not initialize *canfixbytrigaw.
+ */
+bool
+depisok(struct dependency *dep, struct varbuf *whynot,
+ struct pkginfo **canfixbyremove, struct pkginfo **canfixbytrigaw,
+ bool allowunconfigd)
+{
+ struct deppossi *possi;
+ struct deppossi *provider;
+ struct pkginfo *pkg_pos;
+ int nconflicts;
+
+ /* Use this buffer so that when internationalisation comes along we
+ * don't have to rewrite the code completely, only redo the sprintf strings
+ * (assuming we have the fancy argument-number-specifiers).
+ * Allow 250x3 for package names, versions, &c, + 250 for ourselves. */
+ char linebuf[1024];
+
+ if (dep->type != dep_depends &&
+ dep->type != dep_predepends &&
+ dep->type != dep_breaks &&
+ dep->type != dep_conflicts &&
+ dep->type != dep_recommends &&
+ dep->type != dep_suggests &&
+ dep->type != dep_enhances)
+ internerr("unknown dependency type %d", dep->type);
+
+ if (canfixbyremove)
+ *canfixbyremove = NULL;
+ if (canfixbytrigaw)
+ *canfixbytrigaw = NULL;
+
+ /* The dependency is always OK if we're trying to remove the depend*ing*
+ * package. */
+ switch (dep->up->clientdata->istobe) {
+ case PKG_ISTOBE_REMOVE:
+ case PKG_ISTOBE_DECONFIGURE:
+ return true;
+ case PKG_ISTOBE_NORMAL:
+ /* Only installed packages can be made dependency problems. */
+ switch (dep->up->status) {
+ case PKG_STAT_INSTALLED:
+ case PKG_STAT_TRIGGERSPENDING:
+ case PKG_STAT_TRIGGERSAWAITED:
+ break;
+ case PKG_STAT_HALFCONFIGURED:
+ case PKG_STAT_UNPACKED:
+ case PKG_STAT_HALFINSTALLED:
+ if (dep->type == dep_predepends ||
+ dep->type == dep_conflicts ||
+ dep->type == dep_breaks)
+ break;
+ /* Fall through. */
+ case PKG_STAT_CONFIGFILES:
+ case PKG_STAT_NOTINSTALLED:
+ return true;
+ default:
+ internerr("unknown status depending '%d'", dep->up->status);
+ }
+ break;
+ case PKG_ISTOBE_INSTALLNEW:
+ case PKG_ISTOBE_PREINSTALL:
+ break;
+ default:
+ internerr("unknown istobe depending '%d'", dep->up->clientdata->istobe);
+ }
+
+ /* Describe the dependency, in case we have to moan about it. */
+ varbuf_reset(whynot);
+ varbuf_add_char(whynot, ' ');
+ describedepcon(whynot, dep);
+ varbuf_add_char(whynot, '\n');
+
+ /* TODO: Check dep_enhances as well. */
+ if (dep->type == dep_depends || dep->type == dep_predepends ||
+ dep->type == dep_recommends || dep->type == dep_suggests ) {
+ /* Go through the alternatives. As soon as we find one that
+ * we like, we return ‘true’ straight away. Otherwise, when we get to
+ * the end we'll have accumulated all the reasons in whynot and
+ * can return ‘false’. */
+
+ for (possi= dep->list; possi; possi= possi->next) {
+ struct deppossi_pkg_iterator *possi_iter;
+
+ possi_iter = deppossi_pkg_iter_new(possi, wpb_by_istobe);
+ while ((pkg_pos = deppossi_pkg_iter_next(possi_iter))) {
+ switch (pkg_pos->clientdata->istobe) {
+ case PKG_ISTOBE_REMOVE:
+ sprintf(linebuf, _(" %.250s is to be removed.\n"),
+ pkg_name(pkg_pos, pnaw_nonambig));
+ break;
+ case PKG_ISTOBE_DECONFIGURE:
+ sprintf(linebuf, _(" %.250s is to be deconfigured.\n"),
+ pkg_name(pkg_pos, pnaw_nonambig));
+ break;
+ case PKG_ISTOBE_INSTALLNEW:
+ if (versionsatisfied(&pkg_pos->available, possi)) {
+ deppossi_pkg_iter_free(possi_iter);
+ return true;
+ }
+ sprintf(linebuf, _(" %.250s is to be installed, but is version "
+ "%.250s.\n"),
+ pkgbin_name(pkg_pos, &pkg_pos->available, pnaw_nonambig),
+ versiondescribe(&pkg_pos->available.version, vdew_nonambig));
+ break;
+ case PKG_ISTOBE_NORMAL:
+ case PKG_ISTOBE_PREINSTALL:
+ switch (pkg_pos->status) {
+ case PKG_STAT_INSTALLED:
+ case PKG_STAT_TRIGGERSPENDING:
+ if (versionsatisfied(&pkg_pos->installed, possi)) {
+ deppossi_pkg_iter_free(possi_iter);
+ return true;
+ }
+ sprintf(linebuf, _(" %.250s is installed, but is version "
+ "%.250s.\n"),
+ pkg_name(pkg_pos, pnaw_nonambig),
+ versiondescribe(&pkg_pos->installed.version, vdew_nonambig));
+ break;
+ case PKG_STAT_NOTINSTALLED:
+ /* Don't say anything about this yet - it might be a virtual package.
+ * Later on, if nothing has put anything in linebuf, we know that it
+ * isn't and issue a diagnostic then. */
+ *linebuf = '\0';
+ break;
+ case PKG_STAT_TRIGGERSAWAITED:
+ if (canfixbytrigaw && versionsatisfied(&pkg_pos->installed, possi))
+ *canfixbytrigaw = pkg_pos;
+ /* Fall through. */
+ case PKG_STAT_UNPACKED:
+ case PKG_STAT_HALFCONFIGURED:
+ if (allowunconfigd) {
+ if (!dpkg_version_is_informative(&pkg_pos->configversion)) {
+ sprintf(linebuf, _(" %.250s is unpacked, but has never been "
+ "configured.\n"),
+ pkg_name(pkg_pos, pnaw_nonambig));
+ break;
+ } else if (!versionsatisfied(&pkg_pos->installed, possi)) {
+ sprintf(linebuf, _(" %.250s is unpacked, but is version "
+ "%.250s.\n"),
+ pkg_name(pkg_pos, pnaw_nonambig),
+ versiondescribe(&pkg_pos->installed.version,
+ vdew_nonambig));
+ break;
+ } else if (!dpkg_version_relate(&pkg_pos->configversion,
+ possi->verrel,
+ &possi->version)) {
+ sprintf(linebuf, _(" %.250s latest configured version is "
+ "%.250s.\n"),
+ pkg_name(pkg_pos, pnaw_nonambig),
+ versiondescribe(&pkg_pos->configversion, vdew_nonambig));
+ break;
+ } else {
+ deppossi_pkg_iter_free(possi_iter);
+ return true;
+ }
+ }
+ /* Fall through. */
+ default:
+ sprintf(linebuf, _(" %.250s is %s.\n"),
+ pkg_name(pkg_pos, pnaw_nonambig),
+ gettext(statusstrings[pkg_pos->status]));
+ break;
+ }
+ break;
+ default:
+ internerr("unknown istobe depended '%d'", pkg_pos->clientdata->istobe);
+ }
+ varbuf_add_str(whynot, linebuf);
+ }
+ deppossi_pkg_iter_free(possi_iter);
+
+ /* See if the package we're about to install Provides it. */
+ for (provider = possi->ed->depended.available;
+ provider;
+ provider = provider->rev_next) {
+ if (provider->up->type != dep_provides) continue;
+ if (!pkg_virtual_deppossi_satisfied(possi, provider))
+ continue;
+ if (provider->up->up->clientdata->istobe == PKG_ISTOBE_INSTALLNEW)
+ return true;
+ }
+
+ /* Now look at the packages already on the system. */
+ for (provider = possi->ed->depended.installed;
+ provider;
+ provider = provider->rev_next) {
+ if (provider->up->type != dep_provides) continue;
+ if (!pkg_virtual_deppossi_satisfied(possi, provider))
+ continue;
+
+ switch (provider->up->up->clientdata->istobe) {
+ case PKG_ISTOBE_INSTALLNEW:
+ /* Don't pay any attention to the Provides field of the
+ * currently-installed version of the package we're trying
+ * to install. We dealt with that by using the available
+ * information above. */
+ continue;
+ case PKG_ISTOBE_REMOVE:
+ sprintf(linebuf, _(" %.250s provides %.250s but is to be removed.\n"),
+ pkg_name(provider->up->up, pnaw_nonambig),
+ possi->ed->name);
+ break;
+ case PKG_ISTOBE_DECONFIGURE:
+ sprintf(linebuf, _(" %.250s provides %.250s but is to be deconfigured.\n"),
+ pkg_name(provider->up->up, pnaw_nonambig),
+ possi->ed->name);
+ break;
+ case PKG_ISTOBE_NORMAL:
+ case PKG_ISTOBE_PREINSTALL:
+ if (provider->up->up->status == PKG_STAT_INSTALLED ||
+ provider->up->up->status == PKG_STAT_TRIGGERSPENDING)
+ return true;
+ if (provider->up->up->status == PKG_STAT_TRIGGERSAWAITED)
+ *canfixbytrigaw = provider->up->up;
+ sprintf(linebuf, _(" %.250s provides %.250s but is %s.\n"),
+ pkg_name(provider->up->up, pnaw_nonambig),
+ possi->ed->name,
+ gettext(statusstrings[provider->up->up->status]));
+ break;
+ default:
+ internerr("unknown istobe provider '%d'",
+ provider->up->up->clientdata->istobe);
+ }
+ varbuf_add_str(whynot, linebuf);
+ }
+
+ if (!*linebuf) {
+ /* If the package wasn't installed at all, and we haven't said
+ * yet why this isn't satisfied, we should say so now. */
+ sprintf(linebuf, _(" %.250s is not installed.\n"), possi->ed->name);
+ varbuf_add_str(whynot, linebuf);
+ }
+ }
+
+ return false;
+ } else {
+ /* It's conflicts or breaks. There's only one main alternative,
+ * but we also have to consider Providers. We return ‘false’ as soon
+ * as we find something that matches the conflict, and only describe
+ * it then. If we get to the end without finding anything we return
+ * ‘true’. */
+
+ possi= dep->list;
+ nconflicts= 0;
+
+ if (possi->ed != possi->up->up->set) {
+ struct deppossi_pkg_iterator *possi_iter;
+
+ /* If the package conflicts with or breaks itself it must mean
+ * other packages which provide the same virtual name. We
+ * therefore don't look at the real package and go on to the
+ * virtual ones. */
+
+ possi_iter = deppossi_pkg_iter_new(possi, wpb_by_istobe);
+ while ((pkg_pos = deppossi_pkg_iter_next(possi_iter))) {
+ switch (pkg_pos->clientdata->istobe) {
+ case PKG_ISTOBE_REMOVE:
+ break;
+ case PKG_ISTOBE_INSTALLNEW:
+ if (!versionsatisfied(&pkg_pos->available, possi))
+ break;
+ sprintf(linebuf, _(" %.250s (version %.250s) is to be installed.\n"),
+ pkgbin_name(pkg_pos, &pkg_pos->available, pnaw_nonambig),
+ versiondescribe(&pkg_pos->available.version, vdew_nonambig));
+ varbuf_add_str(whynot, linebuf);
+ if (!canfixbyremove) {
+ deppossi_pkg_iter_free(possi_iter);
+ return false;
+ }
+ nconflicts++;
+ *canfixbyremove = pkg_pos;
+ break;
+ case PKG_ISTOBE_DECONFIGURE:
+ if (dep->type == dep_breaks)
+ break; /* Already deconfiguring this. */
+ /* Fall through. */
+ case PKG_ISTOBE_NORMAL:
+ case PKG_ISTOBE_PREINSTALL:
+ switch (pkg_pos->status) {
+ case PKG_STAT_NOTINSTALLED:
+ case PKG_STAT_CONFIGFILES:
+ break;
+ case PKG_STAT_HALFINSTALLED:
+ case PKG_STAT_UNPACKED:
+ case PKG_STAT_HALFCONFIGURED:
+ if (dep->type == dep_breaks)
+ break; /* No problem. */
+ /* Fall through. */
+ case PKG_STAT_INSTALLED:
+ case PKG_STAT_TRIGGERSPENDING:
+ case PKG_STAT_TRIGGERSAWAITED:
+ if (!versionsatisfied(&pkg_pos->installed, possi))
+ break;
+ sprintf(linebuf, _(" %.250s (version %.250s) is present and %s.\n"),
+ pkg_name(pkg_pos, pnaw_nonambig),
+ versiondescribe(&pkg_pos->installed.version, vdew_nonambig),
+ gettext(statusstrings[pkg_pos->status]));
+ varbuf_add_str(whynot, linebuf);
+ if (!canfixbyremove) {
+ deppossi_pkg_iter_free(possi_iter);
+ return false;
+ }
+ nconflicts++;
+ *canfixbyremove = pkg_pos;
+ }
+ break;
+ default:
+ internerr("unknown istobe conflict '%d'", pkg_pos->clientdata->istobe);
+ }
+ }
+ deppossi_pkg_iter_free(possi_iter);
+ }
+
+ /* See if the package we're about to install Provides it. */
+ for (provider = possi->ed->depended.available;
+ provider;
+ provider = provider->rev_next) {
+ if (provider->up->type != dep_provides) continue;
+ if (provider->up->up->clientdata->istobe != PKG_ISTOBE_INSTALLNEW)
+ continue;
+ if (provider->up->up->set == dep->up->set)
+ continue; /* Conflicts and provides the same. */
+ if (!pkg_virtual_deppossi_satisfied(possi, provider))
+ continue;
+ sprintf(linebuf, _(" %.250s provides %.250s and is to be installed.\n"),
+ pkgbin_name(provider->up->up, &provider->up->up->available,
+ pnaw_nonambig), possi->ed->name);
+ varbuf_add_str(whynot, linebuf);
+ /* We can't remove the one we're about to install: */
+ if (canfixbyremove)
+ *canfixbyremove = NULL;
+ return false;
+ }
+
+ /* Now look at the packages already on the system. */
+ for (provider = possi->ed->depended.installed;
+ provider;
+ provider = provider->rev_next) {
+ if (provider->up->type != dep_provides) continue;
+
+ if (provider->up->up->set == dep->up->set)
+ continue; /* Conflicts and provides the same. */
+
+ if (!pkg_virtual_deppossi_satisfied(possi, provider))
+ continue;
+
+ switch (provider->up->up->clientdata->istobe) {
+ case PKG_ISTOBE_INSTALLNEW:
+ /* Don't pay any attention to the Provides field of the
+ * currently-installed version of the package we're trying
+ * to install. We dealt with that package by using the
+ * available information above. */
+ continue;
+ case PKG_ISTOBE_REMOVE:
+ continue;
+ case PKG_ISTOBE_DECONFIGURE:
+ if (dep->type == dep_breaks)
+ continue; /* Already deconfiguring. */
+ /* Fall through. */
+ case PKG_ISTOBE_NORMAL:
+ case PKG_ISTOBE_PREINSTALL:
+ switch (provider->up->up->status) {
+ case PKG_STAT_NOTINSTALLED:
+ case PKG_STAT_CONFIGFILES:
+ continue;
+ case PKG_STAT_HALFINSTALLED:
+ case PKG_STAT_UNPACKED:
+ case PKG_STAT_HALFCONFIGURED:
+ if (dep->type == dep_breaks)
+ break; /* No problem. */
+ /* Fall through. */
+ case PKG_STAT_INSTALLED:
+ case PKG_STAT_TRIGGERSPENDING:
+ case PKG_STAT_TRIGGERSAWAITED:
+ sprintf(linebuf,
+ _(" %.250s provides %.250s and is present and %s.\n"),
+ pkg_name(provider->up->up, pnaw_nonambig), possi->ed->name,
+ gettext(statusstrings[provider->up->up->status]));
+ varbuf_add_str(whynot, linebuf);
+ if (!canfixbyremove)
+ return false;
+ nconflicts++;
+ *canfixbyremove= provider->up->up;
+ break;
+ }
+ break;
+ default:
+ internerr("unknown istobe conflict provider '%d'",
+ provider->up->up->clientdata->istobe);
+ }
+ }
+
+ if (!nconflicts)
+ return true;
+ if (nconflicts > 1)
+ *canfixbyremove = NULL;
+ return false;
+
+ } /* if (dependency) {...} else {...} */
+}