1 /*
2  * dpkg - main program for package management
3  * enquiry.c - status enquiry and listing options
4  *
5  * Copyright © 1995,1996 Ian Jackson <ijackson@chiark.greenend.org.uk>
6  * Copyright © 2006, 2008-2016 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 <https://www.gnu.org/licenses/>.
22  */
23 
24 #include <config.h>
25 #include <compat.h>
26 
27 #include <sys/types.h>
28 
29 #include <string.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <stdbool.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 
36 #include <dpkg/i18n.h>
37 #include <dpkg/dpkg.h>
38 #include <dpkg/dpkg-db.h>
39 #include <dpkg/arch.h>
40 #include <dpkg/pkg-array.h>
41 #include <dpkg/pkg-show.h>
42 #include <dpkg/triglib.h>
43 #include <dpkg/string.h>
44 #include <dpkg/options.h>
45 #include <dpkg/db-ctrl.h>
46 #include <dpkg/db-fsys.h>
47 
48 #include "main.h"
49 
50 struct audit_problem {
51   bool (*check)(struct pkginfo *, const struct audit_problem *problem);
52   union {
53     int number;
54     const char *string;
55   } value;
56   const char *explanation;
57 };
58 
59 static bool
audit_reinstreq(struct pkginfo * pkg,const struct audit_problem * problem)60 audit_reinstreq(struct pkginfo *pkg, const struct audit_problem *problem)
61 {
62   return pkg->eflag & PKG_EFLAG_REINSTREQ;
63 }
64 
65 static bool
audit_status(struct pkginfo * pkg,const struct audit_problem * problem)66 audit_status(struct pkginfo *pkg, const struct audit_problem *problem)
67 {
68   if (pkg->eflag & PKG_EFLAG_REINSTREQ)
69     return false;
70   return (int)pkg->status == problem->value.number;
71 }
72 
73 static bool
audit_infofile(struct pkginfo * pkg,const struct audit_problem * problem)74 audit_infofile(struct pkginfo *pkg, const struct audit_problem *problem)
75 {
76   if (pkg->status < PKG_STAT_HALFINSTALLED)
77     return false;
78   return !pkg_infodb_has_file(pkg, &pkg->installed, problem->value.string);
79 }
80 
81 static bool
audit_arch(struct pkginfo * pkg,const struct audit_problem * problem)82 audit_arch(struct pkginfo *pkg, const struct audit_problem *problem)
83 {
84   if (pkg->status < PKG_STAT_HALFINSTALLED)
85     return false;
86   return pkg->installed.arch->type == (enum dpkg_arch_type)problem->value.number;
87 }
88 
89 static const struct audit_problem audit_problems[] = {
90   {
91     .check = audit_reinstreq,
92     .value.number = 0,
93     .explanation = N_(
94     "The following packages are in a mess due to serious problems during\n"
95     "installation.  They must be reinstalled for them (and any packages\n"
96     "that depend on them) to function properly:\n")
97   }, {
98     .check = audit_status,
99     .value.number = PKG_STAT_UNPACKED,
100     .explanation = N_(
101     "The following packages have been unpacked but not yet configured.\n"
102     "They must be configured using dpkg --configure or the configure\n"
103     "menu option in dselect for them to work:\n")
104   }, {
105     .check = audit_status,
106     .value.number = PKG_STAT_HALFCONFIGURED,
107     .explanation = N_(
108     "The following packages are only half configured, probably due to problems\n"
109     "configuring them the first time.  The configuration should be retried using\n"
110     "dpkg --configure <package> or the configure menu option in dselect:\n")
111   }, {
112     .check = audit_status,
113     .value.number = PKG_STAT_HALFINSTALLED,
114     .explanation = N_(
115     "The following packages are only half installed, due to problems during\n"
116     "installation.  The installation can probably be completed by retrying it;\n"
117     "the packages can be removed using dselect or dpkg --remove:\n")
118   }, {
119     .check = audit_status,
120     .value.number = PKG_STAT_TRIGGERSAWAITED,
121     .explanation = N_(
122     "The following packages are awaiting processing of triggers that they\n"
123     "have activated in other packages.  This processing can be requested using\n"
124     "dselect or dpkg --configure --pending (or dpkg --triggers-only):\n")
125   }, {
126     .check = audit_status,
127     .value.number = PKG_STAT_TRIGGERSPENDING,
128     .explanation = N_(
129     "The following packages have been triggered, but the trigger processing\n"
130     "has not yet been done.  Trigger processing can be requested using\n"
131     "dselect or dpkg --configure --pending (or dpkg --triggers-only):\n")
132   }, {
133     .check = audit_infofile,
134     .value.string = LISTFILE,
135     .explanation = N_(
136     "The following packages are missing the list control file in the\n"
137     "database, they need to be reinstalled:\n")
138   }, {
139     .check = audit_infofile,
140     .value.string = HASHFILE,
141     .explanation = N_(
142     "The following packages are missing the md5sums control file in the\n"
143     "database, they need to be reinstalled:\n")
144   }, {
145     .check = audit_arch,
146     .value.number = DPKG_ARCH_EMPTY,
147     .explanation = N_("The following packages do not have an architecture:\n")
148   }, {
149     .check = audit_arch,
150     .value.number = DPKG_ARCH_ILLEGAL,
151     .explanation = N_("The following packages have an illegal architecture:\n")
152   }, {
153     .check = audit_arch,
154     .value.number = DPKG_ARCH_UNKNOWN,
155     .explanation = N_(
156     "The following packages have an unknown foreign architecture, which will\n"
157     "cause dependency issues on front-ends. This can be fixed by registering\n"
158     "the foreign architecture with dpkg --add-architecture:\n")
159   }, {
160     .check = NULL
161   }
162 };
163 
describebriefly(struct pkginfo * pkg)164 static void describebriefly(struct pkginfo *pkg) {
165   int maxl, l;
166   const char *pdesc;
167 
168   maxl= 57;
169   l= strlen(pkg->set->name);
170   if (l>20) maxl -= (l-20);
171 
172   pdesc = pkgbin_synopsis(pkg, &pkg->installed, &l);
173   l = min(l, maxl);
174 
175   printf(" %-20s %.*s\n", pkg_name(pkg, pnaw_nonambig), l, pdesc);
176 }
177 
178 static struct pkginfo *
pkg_array_mapper(const char * name)179 pkg_array_mapper(const char *name)
180 {
181   struct pkginfo *pkg;
182 
183   pkg = dpkg_options_parse_pkgname(cipaction, name);
184   if (pkg->status == PKG_STAT_NOTINSTALLED)
185     notice(_("package '%s' is not installed"), pkg_name(pkg, pnaw_nonambig));
186 
187   return pkg;
188 }
189 
190 int
audit(const char * const * argv)191 audit(const char *const *argv)
192 {
193   const struct audit_problem *problem;
194   struct pkg_array array;
195   bool head_running = false;
196   int i;
197 
198   modstatdb_open(msdbrw_readonly);
199 
200   if (!*argv)
201     pkg_array_init_from_hash(&array);
202   else
203     pkg_array_init_from_names(&array, pkg_array_mapper, (const char **)argv);
204 
205   pkg_array_sort(&array, pkg_sorter_by_nonambig_name_arch);
206 
207   for (problem = audit_problems; problem->check; problem++) {
208     bool head = false;
209 
210     for (i = 0; i < array.n_pkgs; i++) {
211       struct pkginfo *pkg = array.pkgs[i];
212 
213       if (!problem->check(pkg, problem))
214         continue;
215       if (!head_running) {
216         if (modstatdb_is_locked())
217           puts(_(
218 "Another process has locked the database for writing, and might currently be\n"
219 "modifying it, some of the following problems might just be due to that.\n"));
220         head_running = true;
221       }
222       if (!head) {
223         fputs(gettext(problem->explanation), stdout);
224         head = true;
225       }
226       describebriefly(pkg);
227     }
228 
229     if (head) putchar('\n');
230   }
231 
232   pkg_array_destroy(&array);
233 
234   m_output(stdout, _("<standard output>"));
235 
236   return 0;
237 }
238 
239 struct sectionentry {
240   struct sectionentry *next;
241   const char *name;
242   int count;
243 };
244 
245 static bool
yettobeunpacked(struct pkginfo * pkg,const char ** thissect)246 yettobeunpacked(struct pkginfo *pkg, const char **thissect)
247 {
248   if (pkg->want != PKG_WANT_INSTALL)
249     return false;
250 
251   switch (pkg->status) {
252   case PKG_STAT_UNPACKED:
253   case PKG_STAT_INSTALLED:
254   case PKG_STAT_HALFCONFIGURED:
255   case PKG_STAT_TRIGGERSPENDING:
256   case PKG_STAT_TRIGGERSAWAITED:
257     return false;
258   case PKG_STAT_NOTINSTALLED:
259   case PKG_STAT_HALFINSTALLED:
260   case PKG_STAT_CONFIGFILES:
261     if (thissect)
262       *thissect = str_is_set(pkg->section) ? pkg->section :
263                                              C_("section", "<unknown>");
264     return true;
265   default:
266     internerr("unknown package status '%d'", pkg->status);
267   }
268   return false;
269 }
270 
271 int
unpackchk(const char * const * argv)272 unpackchk(const char *const *argv)
273 {
274   int totalcount, sects;
275   struct sectionentry *sectionentries, *se, **sep;
276   struct pkg_hash_iter *iter;
277   struct pkginfo *pkg;
278   const char *thissect;
279   char buf[20];
280   int width;
281 
282   if (*argv)
283     badusage(_("--%s takes no arguments"), cipaction->olong);
284 
285   modstatdb_open(msdbrw_readonly);
286 
287   totalcount= 0;
288   sectionentries = NULL;
289   sects= 0;
290   iter = pkg_hash_iter_new();
291   while ((pkg = pkg_hash_iter_next_pkg(iter))) {
292     if (!yettobeunpacked(pkg, &thissect)) continue;
293     for (se= sectionentries; se && strcasecmp(thissect,se->name); se= se->next);
294     if (!se) {
295       se = nfmalloc(sizeof(*se));
296       for (sep= &sectionentries;
297            *sep && strcasecmp(thissect,(*sep)->name) > 0;
298            sep= &(*sep)->next);
299       se->name= thissect;
300       se->count= 0;
301       se->next= *sep;
302       *sep= se;
303       sects++;
304     }
305     se->count++; totalcount++;
306   }
307   pkg_hash_iter_free(iter);
308 
309   if (totalcount == 0)
310     return 0;
311 
312   if (totalcount <= 12) {
313     iter = pkg_hash_iter_new();
314     while ((pkg = pkg_hash_iter_next_pkg(iter))) {
315       if (!yettobeunpacked(pkg, NULL))
316         continue;
317       describebriefly(pkg);
318     }
319     pkg_hash_iter_free(iter);
320   } else if (sects <= 12) {
321     for (se= sectionentries; se; se= se->next) {
322       sprintf(buf,"%d",se->count);
323       printf(_(" %d in %s: "),se->count,se->name);
324       width= 70-strlen(se->name)-strlen(buf);
325       while (width > 59) { putchar(' '); width--; }
326       iter = pkg_hash_iter_new();
327       while ((pkg = pkg_hash_iter_next_pkg(iter))) {
328         const char *pkgname;
329 
330         if (!yettobeunpacked(pkg,&thissect)) continue;
331         if (strcasecmp(thissect,se->name)) continue;
332         pkgname = pkg_name(pkg, pnaw_nonambig);
333         width -= strlen(pkgname);
334         width--;
335         if (width < 4) { printf(" ..."); break; }
336         printf(" %s", pkgname);
337       }
338       pkg_hash_iter_free(iter);
339       putchar('\n');
340     }
341   } else {
342     printf(P_(" %d package, from the following section:",
343               " %d packages, from the following sections:", totalcount),
344            totalcount);
345     width= 0;
346     for (se= sectionentries; se; se= se->next) {
347       sprintf(buf,"%d",se->count);
348       width -= (6 + strlen(se->name) + strlen(buf));
349       if (width < 0) { putchar('\n'); width= 73 - strlen(se->name) - strlen(buf); }
350       printf("   %s (%d)",se->name,se->count);
351     }
352     putchar('\n');
353   }
354 
355   m_output(stdout, _("<standard output>"));
356 
357   return 0;
358 }
359 
360 static int
assert_version_support(const char * const * argv,struct dpkg_version * version,const char * feature_name)361 assert_version_support(const char *const *argv,
362                        struct dpkg_version *version,
363                        const char *feature_name)
364 {
365   struct pkginfo *pkg;
366 
367   if (*argv)
368     badusage(_("--%s takes no arguments"), cipaction->olong);
369 
370   modstatdb_open(msdbrw_readonly);
371 
372   pkg = pkg_hash_find_singleton("dpkg");
373   switch (pkg->status) {
374   case PKG_STAT_INSTALLED:
375   case PKG_STAT_TRIGGERSPENDING:
376     return 0;
377   case PKG_STAT_UNPACKED:
378   case PKG_STAT_HALFCONFIGURED:
379   case PKG_STAT_HALFINSTALLED:
380   case PKG_STAT_TRIGGERSAWAITED:
381     if (dpkg_version_relate(&pkg->configversion, DPKG_RELATION_GE, version))
382       return 0;
383     printf(_("Version of dpkg with working %s support not yet configured.\n"
384              " Please use 'dpkg --configure dpkg', and then try again.\n"),
385            feature_name);
386     return 1;
387   default:
388     printf(_("dpkg not recorded as installed, cannot check for %s support!\n"),
389            feature_name);
390     return 1;
391   }
392 }
393 
394 int
assertpredep(const char * const * argv)395 assertpredep(const char *const *argv)
396 {
397   struct dpkg_version version = { 0, "1.1.0", NULL };
398 
399   return assert_version_support(argv, &version, _("Pre-Depends field"));
400 }
401 
402 int
assertepoch(const char * const * argv)403 assertepoch(const char *const *argv)
404 {
405   struct dpkg_version version = { 0, "1.4.0.7", NULL };
406 
407   return assert_version_support(argv, &version, _("epoch"));
408 }
409 
410 int
assertlongfilenames(const char * const * argv)411 assertlongfilenames(const char *const *argv)
412 {
413   struct dpkg_version version = { 0, "1.4.1.17", NULL };
414 
415   return assert_version_support(argv, &version, _("long filenames"));
416 }
417 
418 int
assertmulticonrep(const char * const * argv)419 assertmulticonrep(const char *const *argv)
420 {
421   struct dpkg_version version = { 0, "1.4.1.19", NULL };
422 
423   return assert_version_support(argv, &version,
424                                 _("multiple Conflicts and Replaces"));
425 }
426 
427 int
assertmultiarch(const char * const * argv)428 assertmultiarch(const char *const *argv)
429 {
430   struct dpkg_version version = { 0, "1.16.2", NULL };
431 
432   return assert_version_support(argv, &version, _("multi-arch"));
433 }
434 
435 int
assertverprovides(const char * const * argv)436 assertverprovides(const char *const *argv)
437 {
438   struct dpkg_version version = { 0, "1.17.11", NULL };
439 
440   return assert_version_support(argv, &version, _("versioned Provides"));
441 }
442 
443 /**
444  * Print a single package which:
445  *  (a) is the target of one or more relevant predependencies.
446  *  (b) has itself no unsatisfied pre-dependencies.
447  *
448  * If such a package is present output is the Packages file entry,
449  * which can be massaged as appropriate.
450  *
451  * Exit status:
452  *  0 = a package printed, OK
453  *  1 = no suitable package available
454  *  2 = error
455  */
456 int
predeppackage(const char * const * argv)457 predeppackage(const char *const *argv)
458 {
459   static struct varbuf vb;
460 
461   struct pkg_hash_iter *iter;
462   struct pkginfo *pkg = NULL, *startpkg, *trypkg;
463   struct dependency *dep;
464   struct deppossi *possi, *provider;
465 
466   if (*argv)
467     badusage(_("--%s takes no arguments"), cipaction->olong);
468 
469   modstatdb_open(msdbrw_readonly | msdbrw_available_readonly);
470   /* We use clientdata->istobe to detect loops. */
471   clear_istobes();
472 
473   dep = NULL;
474   iter = pkg_hash_iter_new();
475   while (!dep && (pkg = pkg_hash_iter_next_pkg(iter))) {
476     /* Ignore packages user doesn't want. */
477     if (pkg->want != PKG_WANT_INSTALL)
478       continue;
479     /* Ignore packages not available. */
480     if (!pkg->archives)
481       continue;
482     pkg->clientdata->istobe = PKG_ISTOBE_PREINSTALL;
483     for (dep= pkg->available.depends; dep; dep= dep->next) {
484       if (dep->type != dep_predepends) continue;
485       if (depisok(dep, &vb, NULL, NULL, true))
486         continue;
487       /* This will leave dep non-NULL, and so exit the loop. */
488       break;
489     }
490     pkg->clientdata->istobe = PKG_ISTOBE_NORMAL;
491     /* If dep is NULL we go and get the next package. */
492   }
493   pkg_hash_iter_free(iter);
494 
495   if (!dep)
496     return 1; /* Not found. */
497   if (pkg == NULL)
498     internerr("unexpected unfound package");
499 
500   startpkg= pkg;
501   pkg->clientdata->istobe = PKG_ISTOBE_PREINSTALL;
502 
503   /* OK, we have found an unsatisfied predependency.
504    * Now go and find the first thing we need to install, as a first step
505    * towards satisfying it. */
506   do {
507     /* We search for a package which would satisfy dep, and put it in pkg. */
508     for (possi = dep->list, pkg = NULL;
509          !pkg && possi;
510          possi=possi->next) {
511       struct deppossi_pkg_iterator *possi_iter;
512 
513       possi_iter = deppossi_pkg_iter_new(possi, wpb_available);
514       while (!pkg && (trypkg = deppossi_pkg_iter_next(possi_iter))) {
515         if (trypkg->archives &&
516             trypkg->clientdata->istobe == PKG_ISTOBE_NORMAL &&
517             versionsatisfied(&trypkg->available, possi)) {
518           pkg = trypkg;
519           break;
520         }
521         for (provider = possi->ed->depended.available;
522              !pkg && provider;
523              provider = provider->next) {
524           if (provider->up->type != dep_provides)
525             continue;
526           if (!pkg_virtual_deppossi_satisfied(possi, provider))
527             continue;
528           trypkg = provider->up->up;
529           if (!trypkg->archives)
530             continue;
531           if (trypkg->clientdata->istobe == PKG_ISTOBE_NORMAL) {
532             pkg = trypkg;
533             break;
534           }
535         }
536       }
537       deppossi_pkg_iter_free(possi_iter);
538     }
539     if (!pkg) {
540       varbuf_reset(&vb);
541       describedepcon(&vb,dep);
542       varbuf_end_str(&vb);
543       notice(_("cannot see how to satisfy pre-dependency:\n %s"), vb.buf);
544       ohshit(_("cannot satisfy pre-dependencies for %.250s (wanted due to %.250s)"),
545              pkgbin_name(dep->up, &dep->up->available, pnaw_nonambig),
546              pkgbin_name(startpkg, &startpkg->available, pnaw_nonambig));
547     }
548     pkg->clientdata->istobe = PKG_ISTOBE_PREINSTALL;
549     for (dep= pkg->available.depends; dep; dep= dep->next) {
550       if (dep->type != dep_predepends) continue;
551       if (depisok(dep, &vb, NULL, NULL, true))
552         continue;
553       /* This will leave dep non-NULL, and so exit the loop. */
554       break;
555     }
556   } while (dep);
557 
558   /* OK, we've found it - pkg has no unsatisfied pre-dependencies! */
559   writerecord(stdout, _("<standard output>"), pkg, &pkg->available);
560 
561   m_output(stdout, _("<standard output>"));
562 
563   return 0;
564 }
565 
566 int
printarch(const char * const * argv)567 printarch(const char *const *argv)
568 {
569   if (*argv)
570     badusage(_("--%s takes no arguments"), cipaction->olong);
571 
572   printf("%s\n", dpkg_arch_get(DPKG_ARCH_NATIVE)->name);
573 
574   m_output(stdout, _("<standard output>"));
575 
576   return 0;
577 }
578 
579 int
print_foreign_arches(const char * const * argv)580 print_foreign_arches(const char *const *argv)
581 {
582   struct dpkg_arch *arch;
583 
584   if (*argv)
585     badusage(_("--%s takes no arguments"), cipaction->olong);
586 
587   dpkg_arch_load_list();
588 
589   for (arch = dpkg_arch_get_list(); arch; arch = arch->next) {
590     if (arch->type != DPKG_ARCH_FOREIGN)
591       continue;
592 
593     printf("%s\n", arch->name);
594   }
595 
596   m_output(stdout, _("<standard output>"));
597 
598   return 0;
599 }
600 
601 int
validate_pkgname(const char * const * argv)602 validate_pkgname(const char *const *argv)
603 {
604   const char *emsg;
605 
606   if (!argv[0] || argv[1])
607     badusage(_("--%s takes one <pkgname> argument"), cipaction->olong);
608 
609   emsg = pkg_name_is_illegal(argv[0]);
610   if (emsg)
611     ohshit(_("package name '%s' is invalid: %s"), argv[0], emsg);
612 
613   return 0;
614 }
615 
616 int
validate_trigname(const char * const * argv)617 validate_trigname(const char *const *argv)
618 {
619   const char *emsg;
620 
621   if (!argv[0] || argv[1])
622     badusage(_("--%s takes one <trigname> argument"), cipaction->olong);
623 
624   emsg = trig_name_is_illegal(argv[0]);
625   if (emsg)
626     ohshit(_("trigger name '%s' is invalid: %s"), argv[0], emsg);
627 
628   return 0;
629 }
630 
631 int
validate_archname(const char * const * argv)632 validate_archname(const char *const *argv)
633 {
634   const char *emsg;
635 
636   if (!argv[0] || argv[1])
637     badusage(_("--%s takes one <archname> argument"), cipaction->olong);
638 
639   emsg = dpkg_arch_name_is_illegal(argv[0]);
640   if (emsg)
641     ohshit(_("architecture name '%s' is invalid: %s"), argv[0], emsg);
642 
643   return 0;
644 }
645 
646 int
validate_version(const char * const * argv)647 validate_version(const char *const *argv)
648 {
649   struct dpkg_version version;
650   struct dpkg_error err;
651 
652   if (!argv[0] || argv[1])
653     badusage(_("--%s takes one <version> argument"), cipaction->olong);
654 
655   if (parseversion(&version, argv[0], &err) < 0) {
656     dpkg_error_print(&err, _("version '%s' has bad syntax"), argv[0]);
657     dpkg_error_destroy(&err);
658 
659     return 1;
660   }
661 
662   return 0;
663 }
664 
665 int
cmpversions(const char * const * argv)666 cmpversions(const char *const *argv)
667 {
668   struct relationinfo {
669     const char *string;
670     /* These values are exit status codes, so 0 = true, 1 = false. */
671     int if_lesser, if_equal, if_greater;
672     int if_none_a, if_none_both, if_none_b;
673     bool obsolete;
674   };
675 
676   static const struct relationinfo relationinfos[]= {
677     /*             < = > !a!2!b  */
678     { "le",        0,0,1, 0,0,1  },
679     { "lt",        0,1,1, 0,1,1  },
680     { "eq",        1,0,1, 1,0,1  },
681     { "ne",        0,1,0, 0,1,0  },
682     { "ge",        1,0,0, 1,0,0  },
683     { "gt",        1,1,0, 1,1,0  },
684 
685     /* These treat an empty version as later than any version. */
686     { "le-nl",     0,0,1, 1,0,0  },
687     { "lt-nl",     0,1,1, 1,1,0  },
688     { "ge-nl",     1,0,0, 0,0,1  },
689     { "gt-nl",     1,1,0, 0,1,1  },
690 
691     /* For compatibility with dpkg control file syntax. */
692     { "<",         0,0,1, 0,0,1, .obsolete = true },
693     { "<=",        0,0,1, 0,0,1  },
694     { "<<",        0,1,1, 0,1,1  },
695     { "=",         1,0,1, 1,0,1  },
696     { ">",         1,0,0, 1,0,0, .obsolete = true },
697     { ">=",        1,0,0, 1,0,0  },
698     { ">>",        1,1,0, 1,1,0  },
699     { NULL                       }
700   };
701 
702   const struct relationinfo *rip;
703   struct dpkg_version a, b;
704   struct dpkg_error err;
705   int rc;
706 
707   if (!argv[0] || !argv[1] || !argv[2] || argv[3])
708     badusage(_("--compare-versions takes three arguments:"
709              " <version> <relation> <version>"));
710 
711   for (rip=relationinfos; rip->string && strcmp(rip->string,argv[1]); rip++);
712 
713   if (!rip->string) badusage(_("--compare-versions bad relation"));
714 
715   if (rip->obsolete)
716     warning(_("--%s used with obsolete relation operator '%s'"),
717             cipaction->olong, rip->string);
718 
719   if (*argv[0] && strcmp(argv[0],"<unknown>")) {
720     if (parseversion(&a, argv[0], &err) < 0) {
721       dpkg_error_print(&err, _("version '%s' has bad syntax"), argv[0]);
722       dpkg_error_destroy(&err);
723     }
724   } else {
725     dpkg_version_blank(&a);
726   }
727   if (*argv[2] && strcmp(argv[2],"<unknown>")) {
728     if (parseversion(&b, argv[2], &err) < 0) {
729       dpkg_error_print(&err, _("version '%s' has bad syntax"), argv[2]);
730       dpkg_error_destroy(&err);
731     }
732   } else {
733     dpkg_version_blank(&b);
734   }
735   if (!dpkg_version_is_informative(&a)) {
736     if (dpkg_version_is_informative(&b))
737       return rip->if_none_a;
738     else
739       return rip->if_none_both;
740   } else if (!dpkg_version_is_informative(&b)) {
741     return rip->if_none_b;
742   }
743   rc = dpkg_version_compare(&a, &b);
744   debug(dbg_general, "cmpversions a='%s' b='%s' r=%d",
745         versiondescribe(&a,vdew_always),
746         versiondescribe(&b,vdew_always),
747         rc);
748   if (rc > 0)
749     return rip->if_greater;
750   else if (rc < 0)
751     return rip->if_lesser;
752   else
753     return rip->if_equal;
754 }
755