Commit | Line | Data |
---|---|---|
1479465f GJ |
1 | /* |
2 | * dpkg-query - program for query the dpkg database | |
3 | * querycmd.c - status enquiry and listing options | |
4 | * | |
5 | * Copyright © 1995,1996 Ian Jackson <ijackson@chiark.greenend.org.uk> | |
6 | * Copyright © 2000,2001 Wichert Akkerman <wakkerma@debian.org> | |
7 | * Copyright © 2006-2015 Guillem Jover <guillem@debian.org> | |
8 | * Copyright © 2011 Linaro Limited | |
9 | * Copyright © 2011 Raphaël Hertzog <hertzog@debian.org> | |
10 | * | |
11 | * This is free software; you can redistribute it and/or modify | |
12 | * it under the terms of the GNU General Public License as published by | |
13 | * the Free Software Foundation; either version 2 of the License, or | |
14 | * (at your option) any later version. | |
15 | * | |
16 | * This is distributed in the hope that it will be useful, | |
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
19 | * GNU General Public License for more details. | |
20 | * | |
21 | * You should have received a copy of the GNU General Public License | |
22 | * along with this program. If not, see <https://www.gnu.org/licenses/>. | |
23 | */ | |
24 | ||
25 | #include <config.h> | |
26 | #include <compat.h> | |
27 | ||
28 | #include <sys/types.h> | |
29 | #include <sys/stat.h> | |
30 | #include <sys/ioctl.h> | |
31 | ||
32 | #if HAVE_LOCALE_H | |
33 | #include <locale.h> | |
34 | #endif | |
35 | #include <errno.h> | |
36 | #include <limits.h> | |
37 | #include <string.h> | |
38 | #include <fcntl.h> | |
39 | #include <dirent.h> | |
40 | #include <fnmatch.h> | |
41 | #include <termios.h> | |
42 | #include <unistd.h> | |
43 | #include <stdlib.h> | |
44 | #include <stdio.h> | |
45 | ||
46 | #include <dpkg/i18n.h> | |
47 | #include <dpkg/dpkg.h> | |
48 | #include <dpkg/dpkg-db.h> | |
49 | #include <dpkg/pkg-array.h> | |
50 | #include <dpkg/pkg-spec.h> | |
51 | #include <dpkg/pkg-format.h> | |
52 | #include <dpkg/pkg-show.h> | |
53 | #include <dpkg/string.h> | |
54 | #include <dpkg/path.h> | |
55 | #include <dpkg/file.h> | |
56 | #include <dpkg/options.h> | |
57 | ||
58 | #include "filesdb.h" | |
59 | #include "infodb.h" | |
60 | #include "main.h" | |
61 | ||
62 | static const char *showformat = "${binary:Package}\t${Version}\n"; | |
63 | ||
64 | static int opt_loadavail = 0; | |
65 | ||
66 | static int getwidth(void) { | |
67 | int fd; | |
68 | long res; | |
69 | struct winsize ws; | |
70 | const char *columns; | |
71 | char *endptr; | |
72 | ||
73 | columns = getenv("COLUMNS"); | |
74 | if (columns) { | |
75 | errno = 0; | |
76 | res = strtol(columns, &endptr, 10); | |
77 | if (errno == 0 && columns != endptr && *endptr == '\0' && | |
78 | res > 0 && res < INT_MAX) | |
79 | return res; | |
80 | } | |
81 | ||
82 | if (!isatty(1)) | |
83 | return -1; | |
84 | else { | |
85 | res = 80; | |
86 | ||
87 | fd = open("/dev/tty", O_RDONLY); | |
88 | if (fd != -1) { | |
89 | if (ioctl(fd, TIOCGWINSZ, &ws) == 0) | |
90 | res = ws.ws_col; | |
91 | close(fd); | |
92 | } | |
93 | ||
94 | return res; | |
95 | } | |
96 | } | |
97 | ||
98 | static int | |
99 | pkg_array_match_patterns(struct pkg_array *array, | |
100 | pkg_array_visitor_func *pkg_visitor, void *pkg_data, | |
101 | const char *const *argv) | |
102 | { | |
103 | int argc, i, ip, *found; | |
104 | int rc = 0; | |
105 | struct pkg_spec *ps; | |
106 | ||
107 | for (argc = 0; argv[argc]; argc++); | |
108 | found = m_calloc(argc, sizeof(int)); | |
109 | ||
110 | ps = m_malloc(sizeof(*ps) * argc); | |
111 | for (ip = 0; ip < argc; ip++) { | |
112 | pkg_spec_init(&ps[ip], PKG_SPEC_PATTERNS | PKG_SPEC_ARCH_WILDCARD); | |
113 | pkg_spec_parse(&ps[ip], argv[ip]); | |
114 | } | |
115 | ||
116 | for (i = 0; i < array->n_pkgs; i++) { | |
117 | struct pkginfo *pkg; | |
118 | bool pkg_found = false; | |
119 | ||
120 | pkg = array->pkgs[i]; | |
121 | for (ip = 0; ip < argc; ip++) { | |
122 | if (pkg_spec_match_pkg(&ps[ip], pkg, &pkg->installed)) { | |
123 | pkg_found = true; | |
124 | found[ip]++; | |
125 | } | |
126 | } | |
127 | if (!pkg_found) | |
128 | array->pkgs[i] = NULL; | |
129 | } | |
130 | ||
131 | pkg_array_foreach(array, pkg_visitor, pkg_data); | |
132 | ||
133 | for (ip = 0; ip < argc; ip++) { | |
134 | if (!found[ip]) { | |
135 | notice(_("no packages found matching %s"), argv[ip]); | |
136 | rc++; | |
137 | } | |
138 | pkg_spec_destroy(&ps[ip]); | |
139 | } | |
140 | ||
141 | free(ps); | |
142 | free(found); | |
143 | ||
144 | return rc; | |
145 | } | |
146 | ||
147 | struct list_format { | |
148 | bool head; | |
149 | int nw; | |
150 | int vw; | |
151 | int aw; | |
152 | int dw; | |
153 | }; | |
154 | ||
155 | static void | |
156 | list_format_init(struct list_format *fmt, struct pkg_array *array) | |
157 | { | |
158 | int w; | |
159 | ||
160 | if (fmt->nw != 0) | |
161 | return; | |
162 | ||
163 | w = getwidth(); | |
164 | if (w == -1) { | |
165 | int i; | |
166 | ||
167 | fmt->nw = 14; | |
168 | fmt->vw = 12; | |
169 | fmt->aw = 12; | |
170 | fmt->dw = 33; | |
171 | ||
172 | for (i = 0; i < array->n_pkgs; i++) { | |
173 | int plen, vlen, alen, dlen; | |
174 | ||
175 | if (array->pkgs[i] == NULL) | |
176 | continue; | |
177 | ||
178 | plen = str_width(pkg_name(array->pkgs[i], pnaw_nonambig)); | |
179 | vlen = str_width(versiondescribe(&array->pkgs[i]->installed.version, | |
180 | vdew_nonambig)); | |
181 | alen = str_width(dpkg_arch_describe(array->pkgs[i]->installed.arch)); | |
182 | pkgbin_summary(array->pkgs[i], &array->pkgs[i]->installed, &dlen); | |
183 | ||
184 | if (plen > fmt->nw) | |
185 | fmt->nw = plen; | |
186 | if (vlen > fmt->vw) | |
187 | fmt->vw = vlen; | |
188 | if (alen > fmt->aw) | |
189 | fmt->aw = alen; | |
190 | if (dlen > fmt->dw) | |
191 | fmt->dw = dlen; | |
192 | } | |
193 | } else { | |
194 | w -= 80; | |
195 | /* Let's not try to deal with terminals that are too small. */ | |
196 | if (w < 0) | |
197 | w = 0; | |
198 | /* Halve that so we can add it to both the name and description. */ | |
199 | w >>= 1; | |
200 | /* Name width. */ | |
201 | fmt->nw = (14 + (w / 2)); | |
202 | /* Version width. */ | |
203 | fmt->vw = (12 + (w / 4)); | |
204 | /* Architecture width. */ | |
205 | fmt->aw = (12 + (w / 4)); | |
206 | /* Description width. */ | |
207 | fmt->dw = (33 + w); | |
208 | } | |
209 | } | |
210 | ||
211 | static void | |
212 | list_format_print(struct list_format *fmt, | |
213 | int c_want, int c_status, int c_eflag, | |
214 | const char *name, const char *version, const char *arch, | |
215 | const char *desc, int desc_len) | |
216 | { | |
217 | struct str_crop_info ns, vs, as, ds; | |
218 | ||
219 | str_gen_crop(name, fmt->nw, &ns); | |
220 | str_gen_crop(version, fmt->vw, &vs); | |
221 | str_gen_crop(arch, fmt->aw, &as); | |
222 | str_gen_crop(desc, desc_len, &ds); | |
223 | ||
224 | printf("%c%c%c %-*.*s %-*.*s %-*.*s %.*s\n", c_want, c_status, c_eflag, | |
225 | ns.max_bytes, ns.str_bytes, name, | |
226 | vs.max_bytes, vs.str_bytes, version, | |
227 | as.max_bytes, as.str_bytes, arch, | |
228 | ds.str_bytes, desc); | |
229 | } | |
230 | ||
231 | static void | |
232 | list_format_print_header(struct list_format *fmt) | |
233 | { | |
234 | int l; | |
235 | ||
236 | if (fmt->head) | |
237 | return; | |
238 | ||
239 | /* TRANSLATORS: This is the header that appears on 'dpkg-query -l'. The | |
240 | * string should remain under 80 characters. The uppercase letters in | |
241 | * the state values denote the abbreviated letter that will appear on | |
242 | * the first three columns, which should ideally match the English one | |
243 | * (e.g. Remove → supRimeix), see dpkg-query(1) for further details. The | |
244 | * translated message can use additional lines if needed. */ | |
245 | fputs(_("\ | |
246 | Desired=Unknown/Install/Remove/Purge/Hold\n\ | |
247 | | Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend\n\ | |
248 | |/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)\n"), stdout); | |
249 | list_format_print(fmt, '|', '|', '/', _("Name"), _("Version"), | |
250 | _("Architecture"), _("Description"), fmt->dw); | |
251 | ||
252 | /* Status */ | |
253 | printf("+++-"); | |
254 | ||
255 | /* Package name. */ | |
256 | for (l = 0; l < fmt->nw; l++) | |
257 | printf("="); | |
258 | printf("-"); | |
259 | ||
260 | /* Version. */ | |
261 | for (l = 0; l < fmt->vw; l++) | |
262 | printf("="); | |
263 | printf("-"); | |
264 | ||
265 | /* Architecture. */ | |
266 | for (l = 0; l < fmt->aw; l++) | |
267 | printf("="); | |
268 | printf("-"); | |
269 | ||
270 | /* Description. */ | |
271 | for (l = 0; l < fmt->dw; l++) | |
272 | printf("="); | |
273 | printf("\n"); | |
274 | ||
275 | fmt->head = true; | |
276 | } | |
277 | ||
278 | static void | |
279 | pkg_array_list_item(struct pkg_array *array, struct pkginfo *pkg, void *pkg_data) | |
280 | { | |
281 | struct list_format *fmt = pkg_data; | |
282 | int l; | |
283 | const char *pdesc; | |
284 | ||
285 | list_format_init(fmt, array); | |
286 | list_format_print_header(fmt); | |
287 | ||
288 | pdesc = pkgbin_summary(pkg, &pkg->installed, &l); | |
289 | l = min(l, fmt->dw); | |
290 | ||
291 | list_format_print(fmt, | |
292 | pkg_abbrev_want(pkg), | |
293 | pkg_abbrev_status(pkg), | |
294 | pkg_abbrev_eflag(pkg), | |
295 | pkg_name(pkg, pnaw_nonambig), | |
296 | versiondescribe(&pkg->installed.version, vdew_nonambig), | |
297 | dpkg_arch_describe(pkg->installed.arch), | |
298 | pdesc, l); | |
299 | } | |
300 | ||
301 | static int | |
302 | listpackages(const char *const *argv) | |
303 | { | |
304 | struct pkg_array array; | |
305 | struct pkginfo *pkg; | |
306 | int i; | |
307 | int rc = 0; | |
308 | struct list_format fmt; | |
309 | ||
310 | if (!opt_loadavail) | |
311 | modstatdb_open(msdbrw_readonly); | |
312 | else | |
313 | modstatdb_open(msdbrw_readonly | msdbrw_available_readonly); | |
314 | ||
315 | pkg_array_init_from_db(&array); | |
316 | pkg_array_sort(&array, pkg_sorter_by_nonambig_name_arch); | |
317 | ||
318 | memset(&fmt, 0, sizeof(fmt)); | |
319 | ||
320 | if (!*argv) { | |
321 | for (i = 0; i < array.n_pkgs; i++) { | |
322 | pkg = array.pkgs[i]; | |
323 | if (pkg->status == PKG_STAT_NOTINSTALLED) | |
324 | array.pkgs[i] = NULL; | |
325 | } | |
326 | ||
327 | pkg_array_foreach(&array, pkg_array_list_item, &fmt); | |
328 | } else { | |
329 | rc = pkg_array_match_patterns(&array, pkg_array_list_item, &fmt, argv); | |
330 | } | |
331 | ||
332 | m_output(stdout, _("<standard output>")); | |
333 | m_output(stderr, _("<standard error>")); | |
334 | ||
335 | pkg_array_destroy(&array); | |
336 | modstatdb_shutdown(); | |
337 | ||
338 | return rc; | |
339 | } | |
340 | ||
341 | static int searchoutput(struct filenamenode *namenode) { | |
342 | struct filepackages_iterator *iter; | |
343 | struct pkginfo *pkg_owner; | |
344 | int found; | |
345 | ||
346 | if (namenode->divert) { | |
347 | const char *name_from = namenode->divert->camefrom ? | |
348 | namenode->divert->camefrom->name : namenode->name; | |
349 | const char *name_to = namenode->divert->useinstead ? | |
350 | namenode->divert->useinstead->name : namenode->name; | |
351 | ||
352 | if (namenode->divert->pkgset) { | |
353 | printf(_("diversion by %s from: %s\n"), | |
354 | namenode->divert->pkgset->name, name_from); | |
355 | printf(_("diversion by %s to: %s\n"), | |
356 | namenode->divert->pkgset->name, name_to); | |
357 | } else { | |
358 | printf(_("local diversion from: %s\n"), name_from); | |
359 | printf(_("local diversion to: %s\n"), name_to); | |
360 | } | |
361 | } | |
362 | found= 0; | |
363 | ||
364 | iter = filepackages_iter_new(namenode); | |
365 | while ((pkg_owner = filepackages_iter_next(iter))) { | |
366 | if (found) | |
367 | fputs(", ", stdout); | |
368 | fputs(pkg_name(pkg_owner, pnaw_nonambig), stdout); | |
369 | found++; | |
370 | } | |
371 | filepackages_iter_free(iter); | |
372 | ||
373 | if (found) printf(": %s\n",namenode->name); | |
374 | return found + (namenode->divert ? 1 : 0); | |
375 | } | |
376 | ||
377 | static int | |
378 | searchfiles(const char *const *argv) | |
379 | { | |
380 | struct filenamenode *namenode; | |
381 | struct fileiterator *iter; | |
382 | const char *thisarg; | |
383 | int found; | |
384 | int failures = 0; | |
385 | struct varbuf path = VARBUF_INIT; | |
386 | static struct varbuf vb; | |
387 | ||
388 | if (!*argv) | |
389 | badusage(_("--search needs at least one file name pattern argument")); | |
390 | ||
391 | modstatdb_open(msdbrw_readonly); | |
392 | ensure_allinstfiles_available_quiet(); | |
393 | ensure_diversions(); | |
394 | ||
395 | while ((thisarg = *argv++) != NULL) { | |
396 | found= 0; | |
397 | ||
398 | if (!strchr("*[?/",*thisarg)) { | |
399 | varbuf_reset(&vb); | |
400 | varbuf_add_char(&vb, '*'); | |
401 | varbuf_add_str(&vb, thisarg); | |
402 | varbuf_add_char(&vb, '*'); | |
403 | varbuf_end_str(&vb); | |
404 | thisarg= vb.buf; | |
405 | } | |
406 | if (!strpbrk(thisarg, "*[?\\")) { | |
407 | /* Trim trailing ‘/’ and ‘/.’ from the argument if it is not | |
408 | * a pattern, just a pathname. */ | |
409 | varbuf_reset(&path); | |
410 | varbuf_add_str(&path, thisarg); | |
411 | varbuf_end_str(&path); | |
412 | varbuf_trunc(&path, path_trim_slash_slashdot(path.buf)); | |
413 | ||
414 | namenode = findnamenode(path.buf, 0); | |
415 | found += searchoutput(namenode); | |
416 | } else { | |
417 | iter = files_db_iter_new(); | |
418 | while ((namenode = files_db_iter_next(iter)) != NULL) { | |
419 | if (fnmatch(thisarg,namenode->name,0)) continue; | |
420 | found+= searchoutput(namenode); | |
421 | } | |
422 | files_db_iter_free(iter); | |
423 | } | |
424 | if (!found) { | |
425 | notice(_("no path found matching pattern %s"), thisarg); | |
426 | failures++; | |
427 | m_output(stderr, _("<standard error>")); | |
428 | } else { | |
429 | m_output(stdout, _("<standard output>")); | |
430 | } | |
431 | } | |
432 | modstatdb_shutdown(); | |
433 | ||
434 | varbuf_destroy(&path); | |
435 | ||
436 | return failures; | |
437 | } | |
438 | ||
439 | static int | |
440 | enqperpackage(const char *const *argv) | |
441 | { | |
442 | const char *thisarg; | |
443 | struct fileinlist *file; | |
444 | struct pkginfo *pkg; | |
445 | struct filenamenode *namenode; | |
446 | int failures = 0; | |
447 | ||
448 | if (!*argv) | |
449 | badusage(_("--%s needs at least one package name argument"), cipaction->olong); | |
450 | ||
451 | if (cipaction->arg_int == act_printavail) | |
452 | modstatdb_open(msdbrw_readonly | msdbrw_available_readonly); | |
453 | else | |
454 | modstatdb_open(msdbrw_readonly); | |
455 | ||
456 | while ((thisarg = *argv++) != NULL) { | |
457 | pkg = dpkg_options_parse_pkgname(cipaction, thisarg); | |
458 | ||
459 | switch (cipaction->arg_int) { | |
460 | case act_status: | |
461 | if (pkg->status == PKG_STAT_NOTINSTALLED && | |
462 | pkg->priority == PKG_PRIO_UNKNOWN && | |
463 | str_is_unset(pkg->section) && | |
464 | !pkg->files && | |
465 | pkg->want == PKG_WANT_UNKNOWN && | |
466 | !pkg_is_informative(pkg, &pkg->installed)) { | |
467 | notice(_("package '%s' is not installed and no information is available"), | |
468 | pkg_name(pkg, pnaw_nonambig)); | |
469 | failures++; | |
470 | } else { | |
471 | writerecord(stdout, _("<standard output>"), pkg, &pkg->installed); | |
472 | } | |
473 | break; | |
474 | case act_printavail: | |
475 | if (!pkg_is_informative(pkg, &pkg->available)) { | |
476 | notice(_("package '%s' is not available"), | |
477 | pkgbin_name(pkg, &pkg->available, pnaw_nonambig)); | |
478 | failures++; | |
479 | } else { | |
480 | writerecord(stdout, _("<standard output>"), pkg, &pkg->available); | |
481 | } | |
482 | break; | |
483 | case act_listfiles: | |
484 | switch (pkg->status) { | |
485 | case PKG_STAT_NOTINSTALLED: | |
486 | notice(_("package '%s' is not installed"), | |
487 | pkg_name(pkg, pnaw_nonambig)); | |
488 | failures++; | |
489 | break; | |
490 | default: | |
491 | ensure_packagefiles_available(pkg); | |
492 | ensure_diversions(); | |
493 | file= pkg->clientdata->files; | |
494 | if (!file) { | |
495 | printf(_("Package '%s' does not contain any files (!)\n"), | |
496 | pkg_name(pkg, pnaw_nonambig)); | |
497 | } else { | |
498 | while (file) { | |
499 | namenode= file->namenode; | |
500 | puts(namenode->name); | |
501 | if (namenode->divert && !namenode->divert->camefrom) { | |
502 | if (!namenode->divert->pkgset) | |
503 | printf(_("locally diverted to: %s\n"), | |
504 | namenode->divert->useinstead->name); | |
505 | else if (pkg->set == namenode->divert->pkgset) | |
506 | printf(_("package diverts others to: %s\n"), | |
507 | namenode->divert->useinstead->name); | |
508 | else | |
509 | printf(_("diverted by %s to: %s\n"), | |
510 | namenode->divert->pkgset->name, | |
511 | namenode->divert->useinstead->name); | |
512 | } | |
513 | file= file->next; | |
514 | } | |
515 | } | |
516 | break; | |
517 | } | |
518 | break; | |
519 | default: | |
520 | internerr("unknown action '%d'", cipaction->arg_int); | |
521 | } | |
522 | ||
523 | if (*argv != NULL) | |
524 | putchar('\n'); | |
525 | ||
526 | m_output(stdout, _("<standard output>")); | |
527 | } | |
528 | ||
529 | if (failures) { | |
530 | fputs(_("Use dpkg --info (= dpkg-deb --info) to examine archive files,\n" | |
531 | "and dpkg --contents (= dpkg-deb --contents) to list their contents.\n"),stderr); | |
532 | m_output(stderr, _("<standard error>")); | |
533 | } | |
534 | modstatdb_shutdown(); | |
535 | ||
536 | return failures; | |
537 | } | |
538 | ||
539 | static void | |
540 | pkg_array_show_item(struct pkg_array *array, struct pkginfo *pkg, void *pkg_data) | |
541 | { | |
542 | struct pkg_format_node *fmt = pkg_data; | |
543 | ||
544 | pkg_format_show(fmt, pkg, &pkg->installed); | |
545 | } | |
546 | ||
547 | static int | |
548 | showpackages(const char *const *argv) | |
549 | { | |
550 | struct dpkg_error err; | |
551 | struct pkg_array array; | |
552 | struct pkginfo *pkg; | |
553 | struct pkg_format_node *fmt; | |
554 | int i; | |
555 | int rc = 0; | |
556 | ||
557 | fmt = pkg_format_parse(showformat, &err); | |
558 | if (!fmt) { | |
559 | notice(_("error in show format: %s"), err.str); | |
560 | dpkg_error_destroy(&err); | |
561 | rc++; | |
562 | return rc; | |
563 | } | |
564 | ||
565 | if (!opt_loadavail) | |
566 | modstatdb_open(msdbrw_readonly); | |
567 | else | |
568 | modstatdb_open(msdbrw_readonly | msdbrw_available_readonly); | |
569 | ||
570 | pkg_array_init_from_db(&array); | |
571 | pkg_array_sort(&array, pkg_sorter_by_nonambig_name_arch); | |
572 | ||
573 | if (!*argv) { | |
574 | for (i = 0; i < array.n_pkgs; i++) { | |
575 | pkg = array.pkgs[i]; | |
576 | if (pkg->status == PKG_STAT_NOTINSTALLED) | |
577 | continue; | |
578 | pkg_format_show(fmt, pkg, &pkg->installed); | |
579 | } | |
580 | } else { | |
581 | rc = pkg_array_match_patterns(&array, pkg_array_show_item, fmt, argv); | |
582 | } | |
583 | ||
584 | m_output(stdout, _("<standard output>")); | |
585 | m_output(stderr, _("<standard error>")); | |
586 | ||
587 | pkg_array_destroy(&array); | |
588 | pkg_format_free(fmt); | |
589 | modstatdb_shutdown(); | |
590 | ||
591 | return rc; | |
592 | } | |
593 | ||
594 | static bool | |
595 | pkg_infodb_is_internal(const char *filetype) | |
596 | { | |
597 | /* Do not expose internal database files. */ | |
598 | if (strcmp(filetype, LISTFILE) == 0 || | |
599 | strcmp(filetype, CONFFILESFILE) == 0) | |
600 | return true; | |
601 | ||
602 | if (strlen(filetype) > MAXCONTROLFILENAME) | |
603 | return true; | |
604 | ||
605 | return false; | |
606 | } | |
607 | ||
608 | static void | |
609 | pkg_infodb_check_filetype(const char *filetype) | |
610 | { | |
611 | const char *c; | |
612 | ||
613 | /* Validate control file name for sanity. */ | |
614 | for (c = "/."; *c; c++) | |
615 | if (strchr(filetype, *c)) | |
616 | badusage(_("control file contains %c"), *c); | |
617 | } | |
618 | ||
619 | static void | |
620 | pkg_infodb_print_filename(const char *filename, const char *filetype) | |
621 | { | |
622 | if (pkg_infodb_is_internal(filetype)) | |
623 | return; | |
624 | ||
625 | printf("%s\n", filename); | |
626 | } | |
627 | ||
628 | static void | |
629 | pkg_infodb_print_filetype(const char *filename, const char *filetype) | |
630 | { | |
631 | if (pkg_infodb_is_internal(filetype)) | |
632 | return; | |
633 | ||
634 | printf("%s\n", filetype); | |
635 | } | |
636 | ||
637 | static void | |
638 | control_path_file(struct pkginfo *pkg, const char *control_file) | |
639 | { | |
640 | const char *control_path; | |
641 | struct stat st; | |
642 | ||
643 | control_path = pkg_infodb_get_file(pkg, &pkg->installed, control_file); | |
644 | if (stat(control_path, &st) < 0) | |
645 | return; | |
646 | if (!S_ISREG(st.st_mode)) | |
647 | return; | |
648 | ||
649 | pkg_infodb_print_filename(control_path, control_file); | |
650 | } | |
651 | ||
652 | static int | |
653 | control_path(const char *const *argv) | |
654 | { | |
655 | struct pkginfo *pkg; | |
656 | const char *pkgname; | |
657 | const char *control_file; | |
658 | ||
659 | pkgname = *argv++; | |
660 | if (!pkgname) | |
661 | badusage(_("--%s needs at least one package name argument"), | |
662 | cipaction->olong); | |
663 | ||
664 | control_file = *argv++; | |
665 | if (control_file && *argv) | |
666 | badusage(_("--%s takes at most two arguments"), cipaction->olong); | |
667 | ||
668 | if (control_file) | |
669 | pkg_infodb_check_filetype(control_file); | |
670 | ||
671 | modstatdb_open(msdbrw_readonly); | |
672 | ||
673 | pkg = dpkg_options_parse_pkgname(cipaction, pkgname); | |
674 | if (pkg->status == PKG_STAT_NOTINSTALLED) | |
675 | ohshit(_("package '%s' is not installed"), | |
676 | pkg_name(pkg, pnaw_nonambig)); | |
677 | ||
678 | if (control_file) | |
679 | control_path_file(pkg, control_file); | |
680 | else | |
681 | pkg_infodb_foreach(pkg, &pkg->installed, pkg_infodb_print_filename); | |
682 | ||
683 | modstatdb_shutdown(); | |
684 | ||
685 | return 0; | |
686 | } | |
687 | ||
688 | static int | |
689 | control_list(const char *const *argv) | |
690 | { | |
691 | struct pkginfo *pkg; | |
692 | const char *pkgname; | |
693 | ||
694 | pkgname = *argv++; | |
695 | if (!pkgname || *argv) | |
696 | badusage(_("--%s takes one package name argument"), cipaction->olong); | |
697 | ||
698 | modstatdb_open(msdbrw_readonly); | |
699 | ||
700 | pkg = dpkg_options_parse_pkgname(cipaction, pkgname); | |
701 | if (pkg->status == PKG_STAT_NOTINSTALLED) | |
702 | ohshit(_("package '%s' is not installed"), pkg_name(pkg, pnaw_nonambig)); | |
703 | ||
704 | pkg_infodb_foreach(pkg, &pkg->installed, pkg_infodb_print_filetype); | |
705 | ||
706 | modstatdb_shutdown(); | |
707 | ||
708 | return 0; | |
709 | } | |
710 | ||
711 | static int | |
712 | control_show(const char *const *argv) | |
713 | { | |
714 | struct pkginfo *pkg; | |
715 | const char *pkgname; | |
716 | const char *filename; | |
717 | const char *control_file; | |
718 | ||
719 | pkgname = *argv++; | |
720 | if (!pkgname || !*argv) | |
721 | badusage(_("--%s takes exactly two arguments"), | |
722 | cipaction->olong); | |
723 | ||
724 | control_file = *argv++; | |
725 | if (!control_file || *argv) | |
726 | badusage(_("--%s takes exactly two arguments"), cipaction->olong); | |
727 | ||
728 | pkg_infodb_check_filetype(control_file); | |
729 | ||
730 | modstatdb_open(msdbrw_readonly); | |
731 | ||
732 | pkg = dpkg_options_parse_pkgname(cipaction, pkgname); | |
733 | if (pkg->status == PKG_STAT_NOTINSTALLED) | |
734 | ohshit(_("package '%s' is not installed"), pkg_name(pkg, pnaw_nonambig)); | |
735 | ||
736 | if (pkg_infodb_has_file(pkg, &pkg->installed, control_file)) | |
737 | filename = pkg_infodb_get_file(pkg, &pkg->installed, control_file); | |
738 | else | |
739 | ohshit(_("control file '%s' does not exist"), control_file); | |
740 | ||
741 | modstatdb_shutdown(); | |
742 | ||
743 | file_show(filename); | |
744 | ||
745 | return 0; | |
746 | } | |
747 | ||
748 | static void DPKG_ATTR_NORET | |
749 | printversion(const struct cmdinfo *ci, const char *value) | |
750 | { | |
751 | printf(_("Debian %s package management program query tool version %s.\n"), | |
752 | DPKGQUERY, PACKAGE_RELEASE); | |
753 | printf(_( | |
754 | "This is free software; see the GNU General Public License version 2 or\n" | |
755 | "later for copying conditions. There is NO warranty.\n")); | |
756 | ||
757 | m_output(stdout, _("<standard output>")); | |
758 | ||
759 | exit(0); | |
760 | } | |
761 | ||
762 | static void DPKG_ATTR_NORET | |
763 | usage(const struct cmdinfo *ci, const char *value) | |
764 | { | |
765 | printf(_( | |
766 | "Usage: %s [<option> ...] <command>\n" | |
767 | "\n"), DPKGQUERY); | |
768 | ||
769 | printf(_( | |
770 | "Commands:\n" | |
771 | " -s|--status <package> ... Display package status details.\n" | |
772 | " -p|--print-avail <package> ... Display available version details.\n" | |
773 | " -L|--listfiles <package> ... List files 'owned' by package(s).\n" | |
774 | " -l|--list [<pattern> ...] List packages concisely.\n" | |
775 | " -W|--show [<pattern> ...] Show information on package(s).\n" | |
776 | " -S|--search <pattern> ... Find package(s) owning file(s).\n" | |
777 | " --control-list <package> Print the package control file list.\n" | |
778 | " --control-show <package> <file>\n" | |
779 | " Show the package control file.\n" | |
780 | " -c|--control-path <package> [<file>]\n" | |
781 | " Print path for package control file.\n" | |
782 | "\n")); | |
783 | ||
784 | printf(_( | |
785 | " -?, --help Show this help message.\n" | |
786 | " --version Show the version.\n" | |
787 | "\n")); | |
788 | ||
789 | printf(_( | |
790 | "Options:\n" | |
791 | " --admindir=<directory> Use <directory> instead of %s.\n" | |
792 | " --load-avail Use available file on --show and --list.\n" | |
793 | " -f|--showformat=<format> Use alternative format for --show.\n" | |
794 | "\n"), ADMINDIR); | |
795 | ||
796 | printf(_( | |
797 | "Format syntax:\n" | |
798 | " A format is a string that will be output for each package. The format\n" | |
799 | " can include the standard escape sequences \\n (newline), \\r (carriage\n" | |
800 | " return) or \\\\ (plain backslash). Package information can be included\n" | |
801 | " by inserting variable references to package fields using the ${var[;width]}\n" | |
802 | " syntax. Fields will be right-aligned unless the width is negative in which\n" | |
803 | " case left alignment will be used.\n")); | |
804 | ||
805 | m_output(stdout, _("<standard output>")); | |
806 | ||
807 | exit(0); | |
808 | } | |
809 | ||
810 | static const char printforhelp[] = N_( | |
811 | "Use --help for help about querying packages."); | |
812 | ||
813 | static const char *admindir; | |
814 | ||
815 | /* This table has both the action entries in it and the normal options. | |
816 | * The action entries are made with the ACTION macro, as they all | |
817 | * have a very similar structure. */ | |
818 | static const struct cmdinfo cmdinfos[]= { | |
819 | ACTION( "listfiles", 'L', act_listfiles, enqperpackage ), | |
820 | ACTION( "status", 's', act_status, enqperpackage ), | |
821 | ACTION( "print-avail", 'p', act_printavail, enqperpackage ), | |
822 | ACTION( "list", 'l', act_listpackages, listpackages ), | |
823 | ACTION( "search", 'S', act_searchfiles, searchfiles ), | |
824 | ACTION( "show", 'W', act_listpackages, showpackages ), | |
825 | ACTION( "control-path", 'c', act_controlpath, control_path ), | |
826 | ACTION( "control-list", 0, act_controllist, control_list ), | |
827 | ACTION( "control-show", 0, act_controlshow, control_show ), | |
828 | ||
829 | { "admindir", 0, 1, NULL, &admindir, NULL }, | |
830 | { "load-avail", 0, 0, &opt_loadavail, NULL, NULL, 1 }, | |
831 | { "showformat", 'f', 1, NULL, &showformat, NULL }, | |
832 | { "help", '?', 0, NULL, NULL, usage }, | |
833 | { "version", 0, 0, NULL, NULL, printversion }, | |
834 | { NULL, 0, 0, NULL, NULL, NULL } | |
835 | }; | |
836 | ||
837 | int main(int argc, const char *const *argv) { | |
838 | int ret; | |
839 | ||
840 | dpkg_set_report_piped_mode(_IOFBF); | |
841 | dpkg_locales_init(PACKAGE); | |
842 | dpkg_program_init("dpkg-query"); | |
843 | dpkg_options_parse(&argv, cmdinfos, printforhelp); | |
844 | ||
845 | admindir = dpkg_db_set_dir(admindir); | |
846 | ||
847 | if (!cipaction) badusage(_("need an action option")); | |
848 | ||
849 | filesdbinit(); | |
850 | ||
851 | ret = cipaction->action(argv); | |
852 | ||
853 | dpkg_program_done(); | |
854 | ||
855 | return !!ret; | |
856 | } |