enquiry.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. /*
  2. * dpkg - main program for package management
  3. * enquiry.c - status enquiry and listing options
  4. *
  5. * Copyright © 1995,1996 Ian Jackson <ian@chiark.greenend.org.uk>
  6. * Copyright © 2006,2008-2012 Guillem Jover <guillem@debian.org>
  7. * Copyright © 2011 Linaro Limited
  8. * Copyright © 2011 Raphaël Hertzog <hertzog@debian.org>
  9. *
  10. * This is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation; either version 2 of the License, or
  13. * (at your option) any later version.
  14. *
  15. * This is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU General Public License
  21. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. */
  23. /* FIXME: per-package audit. */
  24. #include <config.h>
  25. #include <compat.h>
  26. #include <sys/types.h>
  27. #include <sys/ioctl.h>
  28. #include <sys/stat.h>
  29. #include <sys/termios.h>
  30. #include <assert.h>
  31. #include <string.h>
  32. #include <fcntl.h>
  33. #include <unistd.h>
  34. #include <stdbool.h>
  35. #include <stdlib.h>
  36. #include <stdio.h>
  37. #include <dpkg/i18n.h>
  38. #include <dpkg/dpkg.h>
  39. #include <dpkg/dpkg-db.h>
  40. #include <dpkg/arch.h>
  41. #include <dpkg/pkg-show.h>
  42. #include <dpkg/string.h>
  43. #include <dpkg/options.h>
  44. #include "filesdb.h"
  45. #include "infodb.h"
  46. #include "main.h"
  47. struct badstatinfo {
  48. bool (*yesno)(struct pkginfo *, const struct badstatinfo *bsi);
  49. union {
  50. int number;
  51. const char *string;
  52. } value;
  53. const char *explanation;
  54. };
  55. static bool
  56. bsyn_reinstreq(struct pkginfo *pkg, const struct badstatinfo *bsi)
  57. {
  58. return pkg->eflag & eflag_reinstreq;
  59. }
  60. static bool
  61. bsyn_status(struct pkginfo *pkg, const struct badstatinfo *bsi)
  62. {
  63. if (pkg->eflag & eflag_reinstreq)
  64. return false;
  65. return (int)pkg->status == bsi->value.number;
  66. }
  67. static bool
  68. bsyn_infofile(struct pkginfo *pkg, const struct badstatinfo *bsi)
  69. {
  70. if (pkg->status < stat_halfinstalled)
  71. return false;
  72. return !pkg_infodb_has_file(pkg, &pkg->installed, bsi->value.string);
  73. }
  74. static bool
  75. bsyn_arch(struct pkginfo *pkg, const struct badstatinfo *bsi)
  76. {
  77. if (pkg->status < stat_halfinstalled)
  78. return false;
  79. return pkg->installed.arch->type == (enum dpkg_arch_type)bsi->value.number;
  80. }
  81. static const struct badstatinfo badstatinfos[]= {
  82. {
  83. .yesno = bsyn_reinstreq,
  84. .value.number = 0,
  85. .explanation = N_(
  86. "The following packages are in a mess due to serious problems during\n"
  87. "installation. They must be reinstalled for them (and any packages\n"
  88. "that depend on them) to function properly:\n")
  89. }, {
  90. .yesno = bsyn_status,
  91. .value.number = stat_unpacked,
  92. .explanation = N_(
  93. "The following packages have been unpacked but not yet configured.\n"
  94. "They must be configured using dpkg --configure or the configure\n"
  95. "menu option in dselect for them to work:\n")
  96. }, {
  97. .yesno = bsyn_status,
  98. .value.number = stat_halfconfigured,
  99. .explanation = N_(
  100. "The following packages are only half configured, probably due to problems\n"
  101. "configuring them the first time. The configuration should be retried using\n"
  102. "dpkg --configure <package> or the configure menu option in dselect:\n")
  103. }, {
  104. .yesno = bsyn_status,
  105. .value.number = stat_halfinstalled,
  106. .explanation = N_(
  107. "The following packages are only half installed, due to problems during\n"
  108. "installation. The installation can probably be completed by retrying it;\n"
  109. "the packages can be removed using dselect or dpkg --remove:\n")
  110. }, {
  111. .yesno = bsyn_status,
  112. .value.number = stat_triggersawaited,
  113. .explanation = N_(
  114. "The following packages are awaiting processing of triggers that they\n"
  115. "have activated in other packages. This processing can be requested using\n"
  116. "dselect or dpkg --configure --pending (or dpkg --triggers-only):\n")
  117. }, {
  118. .yesno = bsyn_status,
  119. .value.number = stat_triggerspending,
  120. .explanation = N_(
  121. "The following packages have been triggered, but the trigger processing\n"
  122. "has not yet been done. Trigger processing can be requested using\n"
  123. "dselect or dpkg --configure --pending (or dpkg --triggers-only):\n")
  124. }, {
  125. .yesno = bsyn_infofile,
  126. .value.string = LISTFILE,
  127. .explanation = N_(
  128. "The following packages are missing the list control file in the\n"
  129. "database, they need to be reinstalled:\n")
  130. }, {
  131. .yesno = bsyn_infofile,
  132. .value.string = HASHFILE,
  133. .explanation = N_(
  134. "The following packages are missing the md5sums control file in the\n"
  135. "database, they need to be reinstalled:\n")
  136. }, {
  137. .yesno = bsyn_arch,
  138. .value.number = arch_none,
  139. .explanation = N_("The following packages do not have an architecture:\n")
  140. }, {
  141. .yesno = bsyn_arch,
  142. .value.number = arch_illegal,
  143. .explanation = N_("The following packages have an illegal architecture:\n")
  144. }, {
  145. .yesno = bsyn_arch,
  146. .value.number = arch_unknown,
  147. .explanation = N_(
  148. "The following packages have an unknown foreign architecture, which will\n"
  149. "cause dependency issues on front-ends. This can be fixed by registering\n"
  150. "the foreign architecture with dpkg --add-architecture:\n")
  151. }, {
  152. .yesno = NULL
  153. }
  154. };
  155. static void describebriefly(struct pkginfo *pkg) {
  156. int maxl, l;
  157. const char *pdesc;
  158. maxl= 57;
  159. l= strlen(pkg->set->name);
  160. if (l>20) maxl -= (l-20);
  161. pdesc = pkg_summary(pkg, &pkg->installed, &l);
  162. l = min(l, maxl);
  163. printf(" %-20s %.*s\n", pkg_name(pkg, pnaw_nonambig), l, pdesc);
  164. }
  165. int
  166. audit(const char *const *argv)
  167. {
  168. const struct badstatinfo *bsi;
  169. bool head_running = false;
  170. if (*argv)
  171. badusage(_("--%s takes no arguments"), cipaction->olong);
  172. modstatdb_open(msdbrw_readonly);
  173. for (bsi= badstatinfos; bsi->yesno; bsi++) {
  174. struct pkgiterator *it;
  175. struct pkginfo *pkg;
  176. bool head = false;
  177. it = pkg_db_iter_new();
  178. while ((pkg = pkg_db_iter_next_pkg(it))) {
  179. if (!bsi->yesno(pkg,bsi)) continue;
  180. if (!head_running) {
  181. if (modstatdb_is_locked())
  182. puts(_(
  183. "Another process has locked the database for writing, and might currently be\n"
  184. "modifying it, some of the following problems might just be due to that.\n"));
  185. head_running = true;
  186. }
  187. if (!head) {
  188. fputs(gettext(bsi->explanation),stdout);
  189. head = true;
  190. }
  191. describebriefly(pkg);
  192. }
  193. pkg_db_iter_free(it);
  194. if (head) putchar('\n');
  195. }
  196. m_output(stdout, _("<standard output>"));
  197. return 0;
  198. }
  199. struct sectionentry {
  200. struct sectionentry *next;
  201. const char *name;
  202. int count;
  203. };
  204. static bool
  205. yettobeunpacked(struct pkginfo *pkg, const char **thissect)
  206. {
  207. if (pkg->want != want_install)
  208. return false;
  209. switch (pkg->status) {
  210. case stat_unpacked: case stat_installed: case stat_halfconfigured:
  211. case stat_triggerspending:
  212. case stat_triggersawaited:
  213. return false;
  214. case stat_notinstalled: case stat_halfinstalled: case stat_configfiles:
  215. if (thissect)
  216. *thissect = str_is_set(pkg->section) ? pkg->section :
  217. C_("section", "<unknown>");
  218. return true;
  219. default:
  220. internerr("unknown package status '%d'", pkg->status);
  221. }
  222. return false;
  223. }
  224. int
  225. unpackchk(const char *const *argv)
  226. {
  227. int totalcount, sects;
  228. struct sectionentry *sectionentries, *se, **sep;
  229. struct pkgiterator *it;
  230. struct pkginfo *pkg;
  231. const char *thissect;
  232. char buf[20];
  233. int width;
  234. if (*argv)
  235. badusage(_("--%s takes no arguments"), cipaction->olong);
  236. modstatdb_open(msdbrw_readonly);
  237. totalcount= 0;
  238. sectionentries = NULL;
  239. sects= 0;
  240. it = pkg_db_iter_new();
  241. while ((pkg = pkg_db_iter_next_pkg(it))) {
  242. if (!yettobeunpacked(pkg, &thissect)) continue;
  243. for (se= sectionentries; se && strcasecmp(thissect,se->name); se= se->next);
  244. if (!se) {
  245. se= nfmalloc(sizeof(struct sectionentry));
  246. for (sep= &sectionentries;
  247. *sep && strcasecmp(thissect,(*sep)->name) > 0;
  248. sep= &(*sep)->next);
  249. se->name= thissect;
  250. se->count= 0;
  251. se->next= *sep;
  252. *sep= se;
  253. sects++;
  254. }
  255. se->count++; totalcount++;
  256. }
  257. pkg_db_iter_free(it);
  258. if (totalcount == 0)
  259. return 0;
  260. if (totalcount <= 12) {
  261. it = pkg_db_iter_new();
  262. while ((pkg = pkg_db_iter_next_pkg(it))) {
  263. if (!yettobeunpacked(pkg, NULL))
  264. continue;
  265. describebriefly(pkg);
  266. }
  267. pkg_db_iter_free(it);
  268. } else if (sects <= 12) {
  269. for (se= sectionentries; se; se= se->next) {
  270. sprintf(buf,"%d",se->count);
  271. printf(_(" %d in %s: "),se->count,se->name);
  272. width= 70-strlen(se->name)-strlen(buf);
  273. while (width > 59) { putchar(' '); width--; }
  274. it = pkg_db_iter_new();
  275. while ((pkg = pkg_db_iter_next_pkg(it))) {
  276. const char *pkgname;
  277. if (!yettobeunpacked(pkg,&thissect)) continue;
  278. if (strcasecmp(thissect,se->name)) continue;
  279. pkgname = pkg_name(pkg, pnaw_nonambig);
  280. width -= strlen(pkgname);
  281. width--;
  282. if (width < 4) { printf(" ..."); break; }
  283. printf(" %s", pkgname);
  284. }
  285. pkg_db_iter_free(it);
  286. putchar('\n');
  287. }
  288. } else {
  289. printf(P_(" %d package, from the following section:",
  290. " %d packages, from the following sections:", totalcount),
  291. totalcount);
  292. width= 0;
  293. for (se= sectionentries; se; se= se->next) {
  294. sprintf(buf,"%d",se->count);
  295. width -= (6 + strlen(se->name) + strlen(buf));
  296. if (width < 0) { putchar('\n'); width= 73 - strlen(se->name) - strlen(buf); }
  297. printf(" %s (%d)",se->name,se->count);
  298. }
  299. putchar('\n');
  300. }
  301. m_output(stdout, _("<standard output>"));
  302. return 0;
  303. }
  304. static int
  305. assert_version_support(const char *const *argv,
  306. struct dpkg_version *version,
  307. const char *feature_name)
  308. {
  309. struct pkginfo *pkg;
  310. if (*argv)
  311. badusage(_("--%s takes no arguments"), cipaction->olong);
  312. modstatdb_open(msdbrw_readonly);
  313. pkg = pkg_db_find_singleton("dpkg");
  314. switch (pkg->status) {
  315. case stat_installed:
  316. case stat_triggerspending:
  317. return 0;
  318. case stat_unpacked: case stat_halfconfigured: case stat_halfinstalled:
  319. case stat_triggersawaited:
  320. if (dpkg_version_relate(&pkg->configversion, dpkg_relation_ge, version))
  321. return 0;
  322. printf(_("Version of dpkg with working %s support not yet configured.\n"
  323. " Please use 'dpkg --configure dpkg', and then try again.\n"),
  324. feature_name);
  325. return 1;
  326. default:
  327. printf(_("dpkg not recorded as installed, cannot check for %s support!\n"),
  328. feature_name);
  329. return 1;
  330. }
  331. }
  332. int
  333. assertpredep(const char *const *argv)
  334. {
  335. struct dpkg_version version = { 0, "1.1.0", NULL };
  336. return assert_version_support(argv, &version, _("Pre-Depends field"));
  337. }
  338. int
  339. assertepoch(const char *const *argv)
  340. {
  341. struct dpkg_version version = { 0, "1.4.0.7", NULL };
  342. return assert_version_support(argv, &version, _("epoch"));
  343. }
  344. int
  345. assertlongfilenames(const char *const *argv)
  346. {
  347. struct dpkg_version version = { 0, "1.4.1.17", NULL };
  348. return assert_version_support(argv, &version, _("long filenames"));
  349. }
  350. int
  351. assertmulticonrep(const char *const *argv)
  352. {
  353. struct dpkg_version version = { 0, "1.4.1.19", NULL };
  354. return assert_version_support(argv, &version,
  355. _("multiple Conflicts and Replaces"));
  356. }
  357. int
  358. assertmultiarch(const char *const *argv)
  359. {
  360. struct dpkg_version version = { 0, "1.16.2", NULL };
  361. return assert_version_support(argv, &version, _("multi-arch"));
  362. }
  363. /**
  364. * Print a single package which:
  365. * (a) is the target of one or more relevant predependencies.
  366. * (b) has itself no unsatisfied pre-dependencies.
  367. *
  368. * If such a package is present output is the Packages file entry,
  369. * which can be massaged as appropriate.
  370. *
  371. * Exit status:
  372. * 0 = a package printed, OK
  373. * 1 = no suitable package available
  374. * 2 = error
  375. */
  376. int
  377. predeppackage(const char *const *argv)
  378. {
  379. static struct varbuf vb;
  380. struct pkgiterator *it;
  381. struct pkginfo *pkg = NULL, *startpkg, *trypkg;
  382. struct dependency *dep;
  383. struct deppossi *possi, *provider;
  384. if (*argv)
  385. badusage(_("--%s takes no arguments"), cipaction->olong);
  386. modstatdb_open(msdbrw_readonly | msdbrw_available_readonly);
  387. /* We use clientdata->istobe to detect loops. */
  388. clear_istobes();
  389. dep = NULL;
  390. it = pkg_db_iter_new();
  391. while (!dep && (pkg = pkg_db_iter_next_pkg(it))) {
  392. /* Ignore packages user doesn't want. */
  393. if (pkg->want != want_install)
  394. continue;
  395. /* Ignore packages not available. */
  396. if (!pkg->files)
  397. continue;
  398. pkg->clientdata->istobe= itb_preinstall;
  399. for (dep= pkg->available.depends; dep; dep= dep->next) {
  400. if (dep->type != dep_predepends) continue;
  401. if (depisok(dep, &vb, NULL, NULL, true))
  402. continue;
  403. /* This will leave dep non-NULL, and so exit the loop. */
  404. break;
  405. }
  406. pkg->clientdata->istobe= itb_normal;
  407. /* If dep is NULL we go and get the next package. */
  408. }
  409. pkg_db_iter_free(it);
  410. if (!dep)
  411. return 1; /* Not found. */
  412. assert(pkg);
  413. startpkg= pkg;
  414. pkg->clientdata->istobe= itb_preinstall;
  415. /* OK, we have found an unsatisfied predependency.
  416. * Now go and find the first thing we need to install, as a first step
  417. * towards satisfying it. */
  418. do {
  419. /* We search for a package which would satisfy dep, and put it in pkg. */
  420. for (possi = dep->list, pkg = NULL;
  421. !pkg && possi;
  422. possi=possi->next) {
  423. struct deppossi_pkg_iterator *possi_iter;
  424. possi_iter = deppossi_pkg_iter_new(possi, wpb_available);
  425. while (!pkg && (trypkg = deppossi_pkg_iter_next(possi_iter))) {
  426. if (trypkg->files && trypkg->clientdata->istobe == itb_normal &&
  427. versionsatisfied(&trypkg->available, possi)) {
  428. pkg = trypkg;
  429. break;
  430. }
  431. if (possi->verrel != dpkg_relation_none)
  432. continue;
  433. for (provider = possi->ed->depended.available;
  434. !pkg && provider;
  435. provider = provider->next) {
  436. if (provider->up->type != dep_provides)
  437. continue;
  438. trypkg = provider->up->up;
  439. if (!trypkg->files)
  440. continue;
  441. if (trypkg->clientdata->istobe == itb_normal) {
  442. pkg = trypkg;
  443. break;
  444. }
  445. }
  446. }
  447. deppossi_pkg_iter_free(possi_iter);
  448. }
  449. if (!pkg) {
  450. varbuf_reset(&vb);
  451. describedepcon(&vb,dep);
  452. varbuf_end_str(&vb);
  453. notice(_("cannot see how to satisfy pre-dependency:\n %s"), vb.buf);
  454. ohshit(_("cannot satisfy pre-dependencies for %.250s (wanted due to %.250s)"),
  455. pkgbin_name(dep->up, &dep->up->available, pnaw_nonambig),
  456. pkgbin_name(startpkg, &startpkg->available, pnaw_nonambig));
  457. }
  458. pkg->clientdata->istobe= itb_preinstall;
  459. for (dep= pkg->available.depends; dep; dep= dep->next) {
  460. if (dep->type != dep_predepends) continue;
  461. if (depisok(dep, &vb, NULL, NULL, true))
  462. continue;
  463. /* This will leave dep non-NULL, and so exit the loop. */
  464. break;
  465. }
  466. } while (dep);
  467. /* OK, we've found it - pkg has no unsatisfied pre-dependencies! */
  468. writerecord(stdout, _("<standard output>"), pkg, &pkg->available);
  469. m_output(stdout, _("<standard output>"));
  470. return 0;
  471. }
  472. int
  473. printarch(const char *const *argv)
  474. {
  475. if (*argv)
  476. badusage(_("--%s takes no arguments"), cipaction->olong);
  477. printf("%s\n", dpkg_arch_get(arch_native)->name);
  478. m_output(stdout, _("<standard output>"));
  479. return 0;
  480. }
  481. int
  482. printinstarch(const char *const *argv)
  483. {
  484. warning(_("obsolete option '--%s'; please use '--%s' instead"),
  485. "print-installation-architecture", "print-architecture");
  486. return printarch(argv);
  487. }
  488. int
  489. print_foreign_arches(const char *const *argv)
  490. {
  491. struct dpkg_arch *arch;
  492. if (*argv)
  493. badusage(_("--%s takes no arguments"), cipaction->olong);
  494. dpkg_arch_load_list();
  495. for (arch = dpkg_arch_get_list(); arch; arch = arch->next) {
  496. if (arch->type != arch_foreign)
  497. continue;
  498. printf("%s\n", arch->name);
  499. }
  500. m_output(stdout, _("<standard output>"));
  501. return 0;
  502. }
  503. int
  504. cmpversions(const char *const *argv)
  505. {
  506. struct relationinfo {
  507. const char *string;
  508. /* These values are exit status codes, so 0 = true, 1 = false. */
  509. int if_lesser, if_equal, if_greater;
  510. int if_none_a, if_none_both, if_none_b;
  511. };
  512. static const struct relationinfo relationinfos[]= {
  513. /* < = > !a!2!b */
  514. { "le", 0,0,1, 0,0,1 },
  515. { "lt", 0,1,1, 0,1,1 },
  516. { "eq", 1,0,1, 1,0,1 },
  517. { "ne", 0,1,0, 0,1,0 },
  518. { "ge", 1,0,0, 1,0,0 },
  519. { "gt", 1,1,0, 1,1,0 },
  520. /* These treat an empty version as later than any version. */
  521. { "le-nl", 0,0,1, 1,0,0 },
  522. { "lt-nl", 0,1,1, 1,1,0 },
  523. { "ge-nl", 1,0,0, 0,0,1 },
  524. { "gt-nl", 1,1,0, 0,1,1 },
  525. /* For compatibility with dpkg control file syntax. */
  526. { "<", 0,0,1, 0,0,1 },
  527. { "<=", 0,0,1, 0,0,1 },
  528. { "<<", 0,1,1, 0,1,1 },
  529. { "=", 1,0,1, 1,0,1 },
  530. { ">", 1,0,0, 1,0,0 },
  531. { ">=", 1,0,0, 1,0,0 },
  532. { ">>", 1,1,0, 1,1,0 },
  533. { NULL }
  534. };
  535. const struct relationinfo *rip;
  536. struct dpkg_version a, b;
  537. struct dpkg_error err;
  538. int r;
  539. if (!argv[0] || !argv[1] || !argv[2] || argv[3])
  540. badusage(_("--compare-versions takes three arguments:"
  541. " <version> <relation> <version>"));
  542. for (rip=relationinfos; rip->string && strcmp(rip->string,argv[1]); rip++);
  543. if (!rip->string) badusage(_("--compare-versions bad relation"));
  544. if (*argv[0] && strcmp(argv[0],"<unknown>")) {
  545. if (parseversion(&a, argv[0], &err) < 0) {
  546. if (err.type == DPKG_MSG_WARN)
  547. warning(_("version '%s' has bad syntax: %s"), argv[0], err.str);
  548. else
  549. ohshit(_("version '%s' has bad syntax: %s"), argv[0], err.str);
  550. dpkg_error_destroy(&err);
  551. }
  552. } else {
  553. dpkg_version_blank(&a);
  554. }
  555. if (*argv[2] && strcmp(argv[2],"<unknown>")) {
  556. if (parseversion(&b, argv[2], &err) < 0) {
  557. if (err.type == DPKG_MSG_WARN)
  558. warning(_("version '%s' has bad syntax: %s"), argv[2], err.str);
  559. else
  560. ohshit(_("version '%s' has bad syntax: %s"), argv[2], err.str);
  561. dpkg_error_destroy(&err);
  562. }
  563. } else {
  564. dpkg_version_blank(&b);
  565. }
  566. if (!dpkg_version_is_informative(&a)) {
  567. if (dpkg_version_is_informative(&b))
  568. return rip->if_none_a;
  569. else
  570. return rip->if_none_both;
  571. } else if (!dpkg_version_is_informative(&b)) {
  572. return rip->if_none_b;
  573. }
  574. r = dpkg_version_compare(&a, &b);
  575. debug(dbg_general,"cmpversions a=`%s' b=`%s' r=%d",
  576. versiondescribe(&a,vdew_always),
  577. versiondescribe(&b,vdew_always),
  578. r);
  579. if (r > 0)
  580. return rip->if_greater;
  581. else if (r < 0)
  582. return rip->if_lesser;
  583. else
  584. return rip->if_equal;
  585. }