123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754 |
- /*
- * dpkg - main program for package management
- * enquiry.c - status enquiry and listing options
- *
- * Copyright © 1995,1996 Ian Jackson <ijackson@chiark.greenend.org.uk>
- * Copyright © 2006, 2008-2016 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 <assert.h>
- #include <string.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <stdbool.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <dpkg/i18n.h>
- #include <dpkg/dpkg.h>
- #include <dpkg/dpkg-db.h>
- #include <dpkg/arch.h>
- #include <dpkg/pkg-array.h>
- #include <dpkg/pkg-show.h>
- #include <dpkg/triglib.h>
- #include <dpkg/string.h>
- #include <dpkg/options.h>
- #include "filesdb.h"
- #include "infodb.h"
- #include "main.h"
- struct audit_problem {
- bool (*check)(struct pkginfo *, const struct audit_problem *problem);
- union {
- int number;
- const char *string;
- } value;
- const char *explanation;
- };
- static bool
- audit_reinstreq(struct pkginfo *pkg, const struct audit_problem *problem)
- {
- return pkg->eflag & PKG_EFLAG_REINSTREQ;
- }
- static bool
- audit_status(struct pkginfo *pkg, const struct audit_problem *problem)
- {
- if (pkg->eflag & PKG_EFLAG_REINSTREQ)
- return false;
- return (int)pkg->status == problem->value.number;
- }
- static bool
- audit_infofile(struct pkginfo *pkg, const struct audit_problem *problem)
- {
- if (pkg->status < PKG_STAT_HALFINSTALLED)
- return false;
- return !pkg_infodb_has_file(pkg, &pkg->installed, problem->value.string);
- }
- static bool
- audit_arch(struct pkginfo *pkg, const struct audit_problem *problem)
- {
- if (pkg->status < PKG_STAT_HALFINSTALLED)
- return false;
- return pkg->installed.arch->type == (enum dpkg_arch_type)problem->value.number;
- }
- static const struct audit_problem audit_problems[] = {
- {
- .check = audit_reinstreq,
- .value.number = 0,
- .explanation = N_(
- "The following packages are in a mess due to serious problems during\n"
- "installation. They must be reinstalled for them (and any packages\n"
- "that depend on them) to function properly:\n")
- }, {
- .check = audit_status,
- .value.number = PKG_STAT_UNPACKED,
- .explanation = N_(
- "The following packages have been unpacked but not yet configured.\n"
- "They must be configured using dpkg --configure or the configure\n"
- "menu option in dselect for them to work:\n")
- }, {
- .check = audit_status,
- .value.number = PKG_STAT_HALFCONFIGURED,
- .explanation = N_(
- "The following packages are only half configured, probably due to problems\n"
- "configuring them the first time. The configuration should be retried using\n"
- "dpkg --configure <package> or the configure menu option in dselect:\n")
- }, {
- .check = audit_status,
- .value.number = PKG_STAT_HALFINSTALLED,
- .explanation = N_(
- "The following packages are only half installed, due to problems during\n"
- "installation. The installation can probably be completed by retrying it;\n"
- "the packages can be removed using dselect or dpkg --remove:\n")
- }, {
- .check = audit_status,
- .value.number = PKG_STAT_TRIGGERSAWAITED,
- .explanation = N_(
- "The following packages are awaiting processing of triggers that they\n"
- "have activated in other packages. This processing can be requested using\n"
- "dselect or dpkg --configure --pending (or dpkg --triggers-only):\n")
- }, {
- .check = audit_status,
- .value.number = PKG_STAT_TRIGGERSPENDING,
- .explanation = N_(
- "The following packages have been triggered, but the trigger processing\n"
- "has not yet been done. Trigger processing can be requested using\n"
- "dselect or dpkg --configure --pending (or dpkg --triggers-only):\n")
- }, {
- .check = audit_infofile,
- .value.string = LISTFILE,
- .explanation = N_(
- "The following packages are missing the list control file in the\n"
- "database, they need to be reinstalled:\n")
- }, {
- .check = audit_infofile,
- .value.string = HASHFILE,
- .explanation = N_(
- "The following packages are missing the md5sums control file in the\n"
- "database, they need to be reinstalled:\n")
- }, {
- .check = audit_arch,
- .value.number = DPKG_ARCH_EMPTY,
- .explanation = N_("The following packages do not have an architecture:\n")
- }, {
- .check = audit_arch,
- .value.number = DPKG_ARCH_ILLEGAL,
- .explanation = N_("The following packages have an illegal architecture:\n")
- }, {
- .check = audit_arch,
- .value.number = DPKG_ARCH_UNKNOWN,
- .explanation = N_(
- "The following packages have an unknown foreign architecture, which will\n"
- "cause dependency issues on front-ends. This can be fixed by registering\n"
- "the foreign architecture with dpkg --add-architecture:\n")
- }, {
- .check = NULL
- }
- };
- static void describebriefly(struct pkginfo *pkg) {
- int maxl, l;
- const char *pdesc;
- maxl= 57;
- l= strlen(pkg->set->name);
- if (l>20) maxl -= (l-20);
- pdesc = pkgbin_summary(pkg, &pkg->installed, &l);
- l = min(l, maxl);
- printf(" %-20s %.*s\n", pkg_name(pkg, pnaw_nonambig), l, pdesc);
- }
- static struct pkginfo *
- pkg_array_mapper(const char *name)
- {
- struct pkginfo *pkg;
- pkg = dpkg_options_parse_pkgname(cipaction, name);
- if (pkg->status == PKG_STAT_NOTINSTALLED)
- notice(_("package '%s' is not installed"), pkg_name(pkg, pnaw_nonambig));
- return pkg;
- }
- int
- audit(const char *const *argv)
- {
- const struct audit_problem *problem;
- struct pkg_array array;
- bool head_running = false;
- int i;
- modstatdb_open(msdbrw_readonly);
- if (!*argv)
- pkg_array_init_from_db(&array);
- else
- pkg_array_init_from_names(&array, pkg_array_mapper, (const char **)argv);
- pkg_array_sort(&array, pkg_sorter_by_nonambig_name_arch);
- for (problem = audit_problems; problem->check; problem++) {
- bool head = false;
- for (i = 0; i < array.n_pkgs; i++) {
- struct pkginfo *pkg = array.pkgs[i];
- if (!problem->check(pkg, problem))
- continue;
- if (!head_running) {
- if (modstatdb_is_locked())
- puts(_(
- "Another process has locked the database for writing, and might currently be\n"
- "modifying it, some of the following problems might just be due to that.\n"));
- head_running = true;
- }
- if (!head) {
- fputs(gettext(problem->explanation), stdout);
- head = true;
- }
- describebriefly(pkg);
- }
- if (head) putchar('\n');
- }
- pkg_array_destroy(&array);
- m_output(stdout, _("<standard output>"));
- return 0;
- }
- struct sectionentry {
- struct sectionentry *next;
- const char *name;
- int count;
- };
- static bool
- yettobeunpacked(struct pkginfo *pkg, const char **thissect)
- {
- if (pkg->want != PKG_WANT_INSTALL)
- return false;
- switch (pkg->status) {
- case PKG_STAT_UNPACKED:
- case PKG_STAT_INSTALLED:
- case PKG_STAT_HALFCONFIGURED:
- case PKG_STAT_TRIGGERSPENDING:
- case PKG_STAT_TRIGGERSAWAITED:
- return false;
- case PKG_STAT_NOTINSTALLED:
- case PKG_STAT_HALFINSTALLED:
- case PKG_STAT_CONFIGFILES:
- if (thissect)
- *thissect = str_is_set(pkg->section) ? pkg->section :
- C_("section", "<unknown>");
- return true;
- default:
- internerr("unknown package status '%d'", pkg->status);
- }
- return false;
- }
- int
- unpackchk(const char *const *argv)
- {
- int totalcount, sects;
- struct sectionentry *sectionentries, *se, **sep;
- struct pkgiterator *iter;
- struct pkginfo *pkg;
- const char *thissect;
- char buf[20];
- int width;
- if (*argv)
- badusage(_("--%s takes no arguments"), cipaction->olong);
- modstatdb_open(msdbrw_readonly);
- totalcount= 0;
- sectionentries = NULL;
- sects= 0;
- iter = pkg_db_iter_new();
- while ((pkg = pkg_db_iter_next_pkg(iter))) {
- if (!yettobeunpacked(pkg, &thissect)) continue;
- for (se= sectionentries; se && strcasecmp(thissect,se->name); se= se->next);
- if (!se) {
- se= nfmalloc(sizeof(struct sectionentry));
- for (sep= §ionentries;
- *sep && strcasecmp(thissect,(*sep)->name) > 0;
- sep= &(*sep)->next);
- se->name= thissect;
- se->count= 0;
- se->next= *sep;
- *sep= se;
- sects++;
- }
- se->count++; totalcount++;
- }
- pkg_db_iter_free(iter);
- if (totalcount == 0)
- return 0;
- if (totalcount <= 12) {
- iter = pkg_db_iter_new();
- while ((pkg = pkg_db_iter_next_pkg(iter))) {
- if (!yettobeunpacked(pkg, NULL))
- continue;
- describebriefly(pkg);
- }
- pkg_db_iter_free(iter);
- } else if (sects <= 12) {
- for (se= sectionentries; se; se= se->next) {
- sprintf(buf,"%d",se->count);
- printf(_(" %d in %s: "),se->count,se->name);
- width= 70-strlen(se->name)-strlen(buf);
- while (width > 59) { putchar(' '); width--; }
- iter = pkg_db_iter_new();
- while ((pkg = pkg_db_iter_next_pkg(iter))) {
- const char *pkgname;
- if (!yettobeunpacked(pkg,&thissect)) continue;
- if (strcasecmp(thissect,se->name)) continue;
- pkgname = pkg_name(pkg, pnaw_nonambig);
- width -= strlen(pkgname);
- width--;
- if (width < 4) { printf(" ..."); break; }
- printf(" %s", pkgname);
- }
- pkg_db_iter_free(iter);
- putchar('\n');
- }
- } else {
- printf(P_(" %d package, from the following section:",
- " %d packages, from the following sections:", totalcount),
- totalcount);
- width= 0;
- for (se= sectionentries; se; se= se->next) {
- sprintf(buf,"%d",se->count);
- width -= (6 + strlen(se->name) + strlen(buf));
- if (width < 0) { putchar('\n'); width= 73 - strlen(se->name) - strlen(buf); }
- printf(" %s (%d)",se->name,se->count);
- }
- putchar('\n');
- }
- m_output(stdout, _("<standard output>"));
- return 0;
- }
- static int
- assert_version_support(const char *const *argv,
- struct dpkg_version *version,
- const char *feature_name)
- {
- struct pkginfo *pkg;
- if (*argv)
- badusage(_("--%s takes no arguments"), cipaction->olong);
- modstatdb_open(msdbrw_readonly);
- pkg = pkg_db_find_singleton("dpkg");
- switch (pkg->status) {
- case PKG_STAT_INSTALLED:
- case PKG_STAT_TRIGGERSPENDING:
- return 0;
- case PKG_STAT_UNPACKED:
- case PKG_STAT_HALFCONFIGURED:
- case PKG_STAT_HALFINSTALLED:
- case PKG_STAT_TRIGGERSAWAITED:
- if (dpkg_version_relate(&pkg->configversion, DPKG_RELATION_GE, version))
- return 0;
- printf(_("Version of dpkg with working %s support not yet configured.\n"
- " Please use 'dpkg --configure dpkg', and then try again.\n"),
- feature_name);
- return 1;
- default:
- printf(_("dpkg not recorded as installed, cannot check for %s support!\n"),
- feature_name);
- return 1;
- }
- }
- int
- assertpredep(const char *const *argv)
- {
- struct dpkg_version version = { 0, "1.1.0", NULL };
- return assert_version_support(argv, &version, _("Pre-Depends field"));
- }
- int
- assertepoch(const char *const *argv)
- {
- struct dpkg_version version = { 0, "1.4.0.7", NULL };
- return assert_version_support(argv, &version, _("epoch"));
- }
- int
- assertlongfilenames(const char *const *argv)
- {
- struct dpkg_version version = { 0, "1.4.1.17", NULL };
- return assert_version_support(argv, &version, _("long filenames"));
- }
- int
- assertmulticonrep(const char *const *argv)
- {
- struct dpkg_version version = { 0, "1.4.1.19", NULL };
- return assert_version_support(argv, &version,
- _("multiple Conflicts and Replaces"));
- }
- int
- assertmultiarch(const char *const *argv)
- {
- struct dpkg_version version = { 0, "1.16.2", NULL };
- return assert_version_support(argv, &version, _("multi-arch"));
- }
- int
- assertverprovides(const char *const *argv)
- {
- struct dpkg_version version = { 0, "1.17.11", NULL };
- return assert_version_support(argv, &version, _("versioned Provides"));
- }
- /**
- * Print a single package which:
- * (a) is the target of one or more relevant predependencies.
- * (b) has itself no unsatisfied pre-dependencies.
- *
- * If such a package is present output is the Packages file entry,
- * which can be massaged as appropriate.
- *
- * Exit status:
- * 0 = a package printed, OK
- * 1 = no suitable package available
- * 2 = error
- */
- int
- predeppackage(const char *const *argv)
- {
- static struct varbuf vb;
- struct pkgiterator *iter;
- struct pkginfo *pkg = NULL, *startpkg, *trypkg;
- struct dependency *dep;
- struct deppossi *possi, *provider;
- if (*argv)
- badusage(_("--%s takes no arguments"), cipaction->olong);
- modstatdb_open(msdbrw_readonly | msdbrw_available_readonly);
- /* We use clientdata->istobe to detect loops. */
- clear_istobes();
- dep = NULL;
- iter = pkg_db_iter_new();
- while (!dep && (pkg = pkg_db_iter_next_pkg(iter))) {
- /* Ignore packages user doesn't want. */
- if (pkg->want != PKG_WANT_INSTALL)
- continue;
- /* Ignore packages not available. */
- if (!pkg->files)
- continue;
- pkg->clientdata->istobe = PKG_ISTOBE_PREINSTALL;
- for (dep= pkg->available.depends; dep; dep= dep->next) {
- if (dep->type != dep_predepends) continue;
- if (depisok(dep, &vb, NULL, NULL, true))
- continue;
- /* This will leave dep non-NULL, and so exit the loop. */
- break;
- }
- pkg->clientdata->istobe = PKG_ISTOBE_NORMAL;
- /* If dep is NULL we go and get the next package. */
- }
- pkg_db_iter_free(iter);
- if (!dep)
- return 1; /* Not found. */
- assert(pkg);
- startpkg= pkg;
- pkg->clientdata->istobe = PKG_ISTOBE_PREINSTALL;
- /* OK, we have found an unsatisfied predependency.
- * Now go and find the first thing we need to install, as a first step
- * towards satisfying it. */
- do {
- /* We search for a package which would satisfy dep, and put it in pkg. */
- for (possi = dep->list, pkg = NULL;
- !pkg && possi;
- possi=possi->next) {
- struct deppossi_pkg_iterator *possi_iter;
- possi_iter = deppossi_pkg_iter_new(possi, wpb_available);
- while (!pkg && (trypkg = deppossi_pkg_iter_next(possi_iter))) {
- if (trypkg->files &&
- trypkg->clientdata->istobe == PKG_ISTOBE_NORMAL &&
- versionsatisfied(&trypkg->available, possi)) {
- pkg = trypkg;
- break;
- }
- for (provider = possi->ed->depended.available;
- !pkg && provider;
- provider = provider->next) {
- if (provider->up->type != dep_provides)
- continue;
- if (!pkg_virtual_deppossi_satisfied(possi, provider))
- continue;
- trypkg = provider->up->up;
- if (!trypkg->files)
- continue;
- if (trypkg->clientdata->istobe == PKG_ISTOBE_NORMAL) {
- pkg = trypkg;
- break;
- }
- }
- }
- deppossi_pkg_iter_free(possi_iter);
- }
- if (!pkg) {
- varbuf_reset(&vb);
- describedepcon(&vb,dep);
- varbuf_end_str(&vb);
- notice(_("cannot see how to satisfy pre-dependency:\n %s"), vb.buf);
- ohshit(_("cannot satisfy pre-dependencies for %.250s (wanted due to %.250s)"),
- pkgbin_name(dep->up, &dep->up->available, pnaw_nonambig),
- pkgbin_name(startpkg, &startpkg->available, pnaw_nonambig));
- }
- pkg->clientdata->istobe = PKG_ISTOBE_PREINSTALL;
- for (dep= pkg->available.depends; dep; dep= dep->next) {
- if (dep->type != dep_predepends) continue;
- if (depisok(dep, &vb, NULL, NULL, true))
- continue;
- /* This will leave dep non-NULL, and so exit the loop. */
- break;
- }
- } while (dep);
- /* OK, we've found it - pkg has no unsatisfied pre-dependencies! */
- writerecord(stdout, _("<standard output>"), pkg, &pkg->available);
- m_output(stdout, _("<standard output>"));
- return 0;
- }
- int
- printarch(const char *const *argv)
- {
- if (*argv)
- badusage(_("--%s takes no arguments"), cipaction->olong);
- printf("%s\n", dpkg_arch_get(DPKG_ARCH_NATIVE)->name);
- m_output(stdout, _("<standard output>"));
- return 0;
- }
- int
- print_foreign_arches(const char *const *argv)
- {
- struct dpkg_arch *arch;
- if (*argv)
- badusage(_("--%s takes no arguments"), cipaction->olong);
- dpkg_arch_load_list();
- for (arch = dpkg_arch_get_list(); arch; arch = arch->next) {
- if (arch->type != DPKG_ARCH_FOREIGN)
- continue;
- printf("%s\n", arch->name);
- }
- m_output(stdout, _("<standard output>"));
- return 0;
- }
- int
- validate_pkgname(const char *const *argv)
- {
- const char *emsg;
- if (!argv[0] || argv[1])
- badusage(_("--%s takes one <pkgname> argument"), cipaction->olong);
- emsg = pkg_name_is_illegal(argv[0]);
- if (emsg)
- ohshit(_("package name '%s' is invalid: %s"), argv[0], emsg);
- return 0;
- }
- int
- validate_trigname(const char *const *argv)
- {
- const char *emsg;
- if (!argv[0] || argv[1])
- badusage(_("--%s takes one <trigname> argument"), cipaction->olong);
- emsg = trig_name_is_illegal(argv[0]);
- if (emsg)
- ohshit(_("trigger name '%s' is invalid: %s"), argv[0], emsg);
- return 0;
- }
- int
- validate_archname(const char *const *argv)
- {
- const char *emsg;
- if (!argv[0] || argv[1])
- badusage(_("--%s takes one <archname> argument"), cipaction->olong);
- emsg = dpkg_arch_name_is_illegal(argv[0]);
- if (emsg)
- ohshit(_("architecture name '%s' is invalid: %s"), argv[0], emsg);
- return 0;
- }
- int
- validate_version(const char *const *argv)
- {
- struct dpkg_version version;
- struct dpkg_error err;
- if (!argv[0] || argv[1])
- badusage(_("--%s takes one <version> argument"), cipaction->olong);
- if (parseversion(&version, argv[0], &err) < 0) {
- dpkg_error_print(&err, _("version '%s' has bad syntax"), argv[0]);
- dpkg_error_destroy(&err);
- return 1;
- }
- return 0;
- }
- int
- cmpversions(const char *const *argv)
- {
- struct relationinfo {
- const char *string;
- /* These values are exit status codes, so 0 = true, 1 = false. */
- int if_lesser, if_equal, if_greater;
- int if_none_a, if_none_both, if_none_b;
- bool obsolete;
- };
- static const struct relationinfo relationinfos[]= {
- /* < = > !a!2!b */
- { "le", 0,0,1, 0,0,1 },
- { "lt", 0,1,1, 0,1,1 },
- { "eq", 1,0,1, 1,0,1 },
- { "ne", 0,1,0, 0,1,0 },
- { "ge", 1,0,0, 1,0,0 },
- { "gt", 1,1,0, 1,1,0 },
- /* These treat an empty version as later than any version. */
- { "le-nl", 0,0,1, 1,0,0 },
- { "lt-nl", 0,1,1, 1,1,0 },
- { "ge-nl", 1,0,0, 0,0,1 },
- { "gt-nl", 1,1,0, 0,1,1 },
- /* For compatibility with dpkg control file syntax. */
- { "<", 0,0,1, 0,0,1, .obsolete = true },
- { "<=", 0,0,1, 0,0,1 },
- { "<<", 0,1,1, 0,1,1 },
- { "=", 1,0,1, 1,0,1 },
- { ">", 1,0,0, 1,0,0, .obsolete = true },
- { ">=", 1,0,0, 1,0,0 },
- { ">>", 1,1,0, 1,1,0 },
- { NULL }
- };
- const struct relationinfo *rip;
- struct dpkg_version a, b;
- struct dpkg_error err;
- int rc;
- if (!argv[0] || !argv[1] || !argv[2] || argv[3])
- badusage(_("--compare-versions takes three arguments:"
- " <version> <relation> <version>"));
- for (rip=relationinfos; rip->string && strcmp(rip->string,argv[1]); rip++);
- if (!rip->string) badusage(_("--compare-versions bad relation"));
- if (rip->obsolete)
- warning(_("--%s used with obsolete relation operator '%s'"),
- cipaction->olong, rip->string);
- if (*argv[0] && strcmp(argv[0],"<unknown>")) {
- if (parseversion(&a, argv[0], &err) < 0) {
- dpkg_error_print(&err, _("version '%s' has bad syntax"), argv[0]);
- dpkg_error_destroy(&err);
- }
- } else {
- dpkg_version_blank(&a);
- }
- if (*argv[2] && strcmp(argv[2],"<unknown>")) {
- if (parseversion(&b, argv[2], &err) < 0) {
- dpkg_error_print(&err, _("version '%s' has bad syntax"), argv[2]);
- dpkg_error_destroy(&err);
- }
- } else {
- dpkg_version_blank(&b);
- }
- if (!dpkg_version_is_informative(&a)) {
- if (dpkg_version_is_informative(&b))
- return rip->if_none_a;
- else
- return rip->if_none_both;
- } else if (!dpkg_version_is_informative(&b)) {
- return rip->if_none_b;
- }
- rc = dpkg_version_compare(&a, &b);
- debug(dbg_general, "cmpversions a='%s' b='%s' r=%d",
- versiondescribe(&a,vdew_always),
- versiondescribe(&b,vdew_always),
- rc);
- if (rc > 0)
- return rip->if_greater;
- else if (rc < 0)
- return rip->if_lesser;
- else
- return rip->if_equal;
- }
|