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