diff options
Diffstat (limited to '')
-rw-r--r-- | src/querycmd.c | 867 |
1 files changed, 867 insertions, 0 deletions
diff --git a/src/querycmd.c b/src/querycmd.c new file mode 100644 index 0000000..6ee555e --- /dev/null +++ b/src/querycmd.c @@ -0,0 +1,867 @@ +/* + * dpkg-query - program for query the dpkg database + * querycmd.c - status enquiry and listing options + * + * Copyright © 1995,1996 Ian Jackson <ijackson@chiark.greenend.org.uk> + * Copyright © 2000,2001 Wichert Akkerman <wakkerma@debian.org> + * Copyright © 2006-2015 Guillem Jover <guillem@debian.org> + * Copyright © 2011 Linaro Limited + * Copyright © 2011 Raphaël Hertzog <hertzog@debian.org> + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <compat.h> + +#include <sys/types.h> +#include <sys/stat.h> + +#if HAVE_LOCALE_H +#include <locale.h> +#endif +#include <errno.h> +#include <limits.h> +#include <string.h> +#include <fcntl.h> +#include <dirent.h> +#include <fnmatch.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + +#include <dpkg/i18n.h> +#include <dpkg/dpkg.h> +#include <dpkg/dpkg-db.h> +#include <dpkg/pkg-array.h> +#include <dpkg/pkg-spec.h> +#include <dpkg/pkg-format.h> +#include <dpkg/pkg-show.h> +#include <dpkg/string.h> +#include <dpkg/path.h> +#include <dpkg/file.h> +#include <dpkg/pager.h> +#include <dpkg/options.h> +#include <dpkg/db-ctrl.h> +#include <dpkg/db-fsys.h> + +#include "main.h" + +static const char *showformat = "${binary:Package}\t${Version}\n"; + +static int opt_loadavail = 0; + +static int +pkg_array_match_patterns(struct pkg_array *array, + pkg_array_visitor_func *pkg_visitor, void *pkg_data, + const char *const *argv) +{ + int argc, i, ip, *found; + int rc = 0; + struct pkg_spec *ps; + + for (argc = 0; argv[argc]; argc++); + found = m_calloc(argc, sizeof(int)); + + ps = m_malloc(sizeof(*ps) * argc); + for (ip = 0; ip < argc; ip++) { + pkg_spec_init(&ps[ip], PKG_SPEC_PATTERNS | PKG_SPEC_ARCH_WILDCARD); + pkg_spec_parse(&ps[ip], argv[ip]); + } + + for (i = 0; i < array->n_pkgs; i++) { + struct pkginfo *pkg; + bool pkg_found = false; + + pkg = array->pkgs[i]; + for (ip = 0; ip < argc; ip++) { + if (pkg_spec_match_pkg(&ps[ip], pkg, &pkg->installed)) { + pkg_found = true; + found[ip]++; + } + } + if (!pkg_found) + array->pkgs[i] = NULL; + } + + pkg_array_foreach(array, pkg_visitor, pkg_data); + + for (ip = 0; ip < argc; ip++) { + if (!found[ip]) { + notice(_("no packages found matching %s"), argv[ip]); + rc++; + } + pkg_spec_destroy(&ps[ip]); + } + + free(ps); + free(found); + + return rc; +} + +struct list_format { + bool head; + int nw; + int vw; + int aw; + int dw; +}; + +static void +list_format_init(struct list_format *fmt, struct pkg_array *array) +{ + int i; + + if (fmt->nw != 0) + return; + + fmt->nw = 14; + fmt->vw = 12; + fmt->aw = 12; + fmt->dw = 33; + + for (i = 0; i < array->n_pkgs; i++) { + int plen, vlen, alen, dlen; + + if (array->pkgs[i] == NULL) + continue; + + plen = str_width(pkg_name(array->pkgs[i], pnaw_nonambig)); + vlen = str_width(versiondescribe(&array->pkgs[i]->installed.version, + vdew_nonambig)); + alen = str_width(dpkg_arch_describe(array->pkgs[i]->installed.arch)); + pkgbin_synopsis(array->pkgs[i], &array->pkgs[i]->installed, &dlen); + + if (plen > fmt->nw) + fmt->nw = plen; + if (vlen > fmt->vw) + fmt->vw = vlen; + if (alen > fmt->aw) + fmt->aw = alen; + if (dlen > fmt->dw) + fmt->dw = dlen; + } +} + +static void +list_format_print(struct list_format *fmt, + int c_want, int c_status, int c_eflag, + const char *name, const char *version, const char *arch, + const char *desc, int desc_len) +{ + struct str_crop_info ns, vs, as, ds; + + str_gen_crop(name, fmt->nw, &ns); + str_gen_crop(version, fmt->vw, &vs); + str_gen_crop(arch, fmt->aw, &as); + str_gen_crop(desc, desc_len, &ds); + + printf("%c%c%c %-*.*s %-*.*s %-*.*s %.*s\n", c_want, c_status, c_eflag, + ns.max_bytes, ns.str_bytes, name, + vs.max_bytes, vs.str_bytes, version, + as.max_bytes, as.str_bytes, arch, + ds.str_bytes, desc); +} + +static void +list_format_print_header(struct list_format *fmt) +{ + int l; + + if (fmt->head) + return; + + /* TRANSLATORS: This is the header that appears on 'dpkg-query -l'. The + * string should remain under 80 characters. The uppercase letters in + * the state values denote the abbreviated letter that will appear on + * the first three columns, which should ideally match the English one + * (e.g. Remove → supRimeix), see dpkg-query(1) for further details. The + * translated message can use additional lines if needed. */ + fputs(_("\ +Desired=Unknown/Install/Remove/Purge/Hold\n\ +| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend\n\ +|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)\n"), stdout); + list_format_print(fmt, '|', '|', '/', _("Name"), _("Version"), + _("Architecture"), _("Description"), fmt->dw); + + /* Status */ + printf("+++-"); + + /* Package name. */ + for (l = 0; l < fmt->nw; l++) + printf("="); + printf("-"); + + /* Version. */ + for (l = 0; l < fmt->vw; l++) + printf("="); + printf("-"); + + /* Architecture. */ + for (l = 0; l < fmt->aw; l++) + printf("="); + printf("-"); + + /* Description. */ + for (l = 0; l < fmt->dw; l++) + printf("="); + printf("\n"); + + fmt->head = true; +} + +static void +pkg_array_list_item(struct pkg_array *array, struct pkginfo *pkg, void *pkg_data) +{ + struct list_format *fmt = pkg_data; + int l; + const char *pdesc; + + list_format_init(fmt, array); + list_format_print_header(fmt); + + pdesc = pkgbin_synopsis(pkg, &pkg->installed, &l); + l = min(l, fmt->dw); + + list_format_print(fmt, + pkg_abbrev_want(pkg), + pkg_abbrev_status(pkg), + pkg_abbrev_eflag(pkg), + pkg_name(pkg, pnaw_nonambig), + versiondescribe(&pkg->installed.version, vdew_nonambig), + dpkg_arch_describe(pkg->installed.arch), + pdesc, l); +} + +static int +listpackages(const char *const *argv) +{ + struct pkg_array array; + struct pkginfo *pkg; + int i; + int rc = 0; + struct list_format fmt; + struct pager *pager; + + if (!opt_loadavail) + modstatdb_open(msdbrw_readonly); + else + modstatdb_open(msdbrw_readonly | msdbrw_available_readonly); + + pkg_array_init_from_hash(&array); + pkg_array_sort(&array, pkg_sorter_by_nonambig_name_arch); + + memset(&fmt, 0, sizeof(fmt)); + + pager = pager_spawn(_("showing package list on pager")); + + if (!*argv) { + for (i = 0; i < array.n_pkgs; i++) { + pkg = array.pkgs[i]; + if (pkg->status == PKG_STAT_NOTINSTALLED) + array.pkgs[i] = NULL; + } + + pkg_array_foreach(&array, pkg_array_list_item, &fmt); + } else { + rc = pkg_array_match_patterns(&array, pkg_array_list_item, &fmt, argv); + } + + m_output(stdout, _("<standard output>")); + m_output(stderr, _("<standard error>")); + + pager_reap(pager); + + pkg_array_destroy(&array); + modstatdb_shutdown(); + + return rc; +} + +static int +searchoutput(struct fsys_namenode *namenode) +{ + struct fsys_node_pkgs_iter *iter; + struct pkginfo *pkg_owner; + int found; + + if (namenode->divert) { + const char *name_from = namenode->divert->camefrom ? + namenode->divert->camefrom->name : namenode->name; + const char *name_to = namenode->divert->useinstead ? + namenode->divert->useinstead->name : namenode->name; + + if (namenode->divert->pkgset) { + printf(_("diversion by %s from: %s\n"), + namenode->divert->pkgset->name, name_from); + printf(_("diversion by %s to: %s\n"), + namenode->divert->pkgset->name, name_to); + } else { + printf(_("local diversion from: %s\n"), name_from); + printf(_("local diversion to: %s\n"), name_to); + } + } + found= 0; + + iter = fsys_node_pkgs_iter_new(namenode); + while ((pkg_owner = fsys_node_pkgs_iter_next(iter))) { + if (found) + fputs(", ", stdout); + fputs(pkg_name(pkg_owner, pnaw_nonambig), stdout); + found++; + } + fsys_node_pkgs_iter_free(iter); + + if (found) printf(": %s\n",namenode->name); + return found + (namenode->divert ? 1 : 0); +} + +static int +searchfiles(const char *const *argv) +{ + struct fsys_namenode *namenode; + struct fsys_hash_iter *iter; + const char *thisarg; + int found; + int failures = 0; + struct varbuf path = VARBUF_INIT; + static struct varbuf vb; + + if (!*argv) + badusage(_("--search needs at least one file name pattern argument")); + + modstatdb_open(msdbrw_readonly); + ensure_allinstfiles_available_quiet(); + ensure_diversions(); + + while ((thisarg = *argv++) != NULL) { + found= 0; + + if (!strchr("*[?/",*thisarg)) { + varbuf_reset(&vb); + varbuf_add_char(&vb, '*'); + varbuf_add_str(&vb, thisarg); + varbuf_add_char(&vb, '*'); + varbuf_end_str(&vb); + thisarg= vb.buf; + } + if (!strpbrk(thisarg, "*[?\\")) { + /* Trim trailing ‘/’ and ‘/.’ from the argument if it is not + * a pattern, just a pathname. */ + varbuf_reset(&path); + varbuf_add_str(&path, thisarg); + varbuf_end_str(&path); + varbuf_trunc(&path, path_trim_slash_slashdot(path.buf)); + + namenode = fsys_hash_find_node(path.buf, 0); + found += searchoutput(namenode); + } else { + iter = fsys_hash_iter_new(); + while ((namenode = fsys_hash_iter_next(iter)) != NULL) { + if (fnmatch(thisarg,namenode->name,0)) continue; + found+= searchoutput(namenode); + } + fsys_hash_iter_free(iter); + } + if (!found) { + notice(_("no path found matching pattern %s"), thisarg); + failures++; + m_output(stderr, _("<standard error>")); + } else { + m_output(stdout, _("<standard output>")); + } + } + modstatdb_shutdown(); + + varbuf_destroy(&path); + + return failures; +} + +static int +print_status(const char *const *argv) +{ + const char *thisarg; + struct pkginfo *pkg; + int failures = 0; + + modstatdb_open(msdbrw_readonly); + + if (!*argv) { + writedb_records(stdout, _("<standard output>"), 0); + } else { + while ((thisarg = *argv++) != NULL) { + pkg = dpkg_options_parse_pkgname(cipaction, thisarg); + + if (pkg->status == PKG_STAT_NOTINSTALLED && + pkg->priority == PKG_PRIO_UNKNOWN && + str_is_unset(pkg->section) && + !pkg->archives && + pkg->want == PKG_WANT_UNKNOWN && + !pkg_is_informative(pkg, &pkg->installed)) { + notice(_("package '%s' is not installed and no information is available"), + pkg_name(pkg, pnaw_nonambig)); + failures++; + } else { + writerecord(stdout, _("<standard output>"), pkg, &pkg->installed); + } + + if (*argv != NULL) + putchar('\n'); + } + } + + m_output(stdout, _("<standard output>")); + if (failures) { + fputs(_("Use dpkg --info (= dpkg-deb --info) to examine archive files.\n"), + stderr); + m_output(stderr, _("<standard error>")); + } + + modstatdb_shutdown(); + + return failures; +} + +static int +print_avail(const char *const *argv) +{ + const char *thisarg; + struct pkginfo *pkg; + int failures = 0; + + modstatdb_open(msdbrw_readonly | msdbrw_available_readonly); + + if (!*argv) { + writedb_records(stdout, _("<standard output>"), wdb_dump_available); + } else { + while ((thisarg = *argv++) != NULL) { + pkg = dpkg_options_parse_pkgname(cipaction, thisarg); + + if (!pkg_is_informative(pkg, &pkg->available)) { + notice(_("package '%s' is not available"), + pkgbin_name(pkg, &pkg->available, pnaw_nonambig)); + failures++; + } else { + writerecord(stdout, _("<standard output>"), pkg, &pkg->available); + } + + if (*argv != NULL) + putchar('\n'); + } + } + + m_output(stdout, _("<standard output>")); + if (failures) + m_output(stderr, _("<standard error>")); + + modstatdb_shutdown(); + + return failures; +} + +static int +list_files(const char *const *argv) +{ + const char *thisarg; + struct fsys_namenode_list *file; + struct pkginfo *pkg; + struct fsys_namenode *namenode; + int failures = 0; + + if (!*argv) + badusage(_("--%s needs at least one package name argument"), cipaction->olong); + + modstatdb_open(msdbrw_readonly); + + while ((thisarg = *argv++) != NULL) { + pkg = dpkg_options_parse_pkgname(cipaction, thisarg); + + switch (pkg->status) { + case PKG_STAT_NOTINSTALLED: + notice(_("package '%s' is not installed"), + pkg_name(pkg, pnaw_nonambig)); + failures++; + break; + default: + ensure_packagefiles_available(pkg); + ensure_diversions(); + file = pkg->files; + if (!file) { + printf(_("Package '%s' does not contain any files (!)\n"), + pkg_name(pkg, pnaw_nonambig)); + } else { + while (file) { + namenode = file->namenode; + puts(namenode->name); + if (namenode->divert && !namenode->divert->camefrom) { + if (!namenode->divert->pkgset) + printf(_("locally diverted to: %s\n"), + namenode->divert->useinstead->name); + else if (pkg->set == namenode->divert->pkgset) + printf(_("package diverts others to: %s\n"), + namenode->divert->useinstead->name); + else + printf(_("diverted by %s to: %s\n"), + namenode->divert->pkgset->name, + namenode->divert->useinstead->name); + } + file = file->next; + } + } + break; + } + + if (*argv != NULL) + putchar('\n'); + } + + m_output(stdout, _("<standard output>")); + if (failures) { + fputs(_("Use dpkg --contents (= dpkg-deb --contents) to list archive files contents.\n"), + stderr); + m_output(stderr, _("<standard error>")); + } + + modstatdb_shutdown(); + + return failures; +} + +static void +pkg_array_show_item(struct pkg_array *array, struct pkginfo *pkg, void *pkg_data) +{ + struct pkg_format_node *fmt = pkg_data; + + pkg_format_show(fmt, pkg, &pkg->installed); +} + +static int +showpackages(const char *const *argv) +{ + struct dpkg_error err; + struct pkg_array array; + struct pkginfo *pkg; + struct pkg_format_node *fmt; + int i; + int rc = 0; + + fmt = pkg_format_parse(showformat, &err); + if (!fmt) { + notice(_("error in show format: %s"), err.str); + dpkg_error_destroy(&err); + rc++; + return rc; + } + + if (!opt_loadavail) + modstatdb_open(msdbrw_readonly); + else + modstatdb_open(msdbrw_readonly | msdbrw_available_readonly); + + pkg_array_init_from_hash(&array); + pkg_array_sort(&array, pkg_sorter_by_nonambig_name_arch); + + if (!*argv) { + for (i = 0; i < array.n_pkgs; i++) { + pkg = array.pkgs[i]; + if (pkg->status == PKG_STAT_NOTINSTALLED) + continue; + pkg_format_show(fmt, pkg, &pkg->installed); + } + } else { + rc = pkg_array_match_patterns(&array, pkg_array_show_item, fmt, argv); + } + + m_output(stdout, _("<standard output>")); + m_output(stderr, _("<standard error>")); + + pkg_array_destroy(&array); + pkg_format_free(fmt); + modstatdb_shutdown(); + + return rc; +} + +static bool +pkg_infodb_is_internal(const char *filetype) +{ + /* Do not expose internal database files. */ + if (strcmp(filetype, LISTFILE) == 0 || + strcmp(filetype, CONFFILESFILE) == 0) + return true; + + if (strlen(filetype) > MAXCONTROLFILENAME) + return true; + + return false; +} + +static void +pkg_infodb_check_filetype(const char *filetype) +{ + const char *c; + + /* Validate control file name for sanity. */ + for (c = "/."; *c; c++) + if (strchr(filetype, *c)) + badusage(_("control file contains %c"), *c); +} + +static void +pkg_infodb_print_filename(const char *filename, const char *filetype) +{ + if (pkg_infodb_is_internal(filetype)) + return; + + printf("%s\n", filename); +} + +static void +pkg_infodb_print_filetype(const char *filename, const char *filetype) +{ + if (pkg_infodb_is_internal(filetype)) + return; + + printf("%s\n", filetype); +} + +static void +control_path_file(struct pkginfo *pkg, const char *control_file) +{ + const char *control_pathname; + struct stat st; + + control_pathname = pkg_infodb_get_file(pkg, &pkg->installed, control_file); + if (stat(control_pathname, &st) < 0) + return; + if (!S_ISREG(st.st_mode)) + return; + + pkg_infodb_print_filename(control_pathname, control_file); +} + +static int +control_path(const char *const *argv) +{ + struct pkginfo *pkg; + const char *pkgname; + const char *control_file; + + pkgname = *argv++; + if (!pkgname) + badusage(_("--%s needs at least one package name argument"), + cipaction->olong); + + control_file = *argv++; + if (control_file && *argv) + badusage(_("--%s takes at most two arguments"), cipaction->olong); + + if (control_file) + pkg_infodb_check_filetype(control_file); + + modstatdb_open(msdbrw_readonly); + + pkg = dpkg_options_parse_pkgname(cipaction, pkgname); + if (pkg->status == PKG_STAT_NOTINSTALLED) + ohshit(_("package '%s' is not installed"), + pkg_name(pkg, pnaw_nonambig)); + + if (control_file) + control_path_file(pkg, control_file); + else + pkg_infodb_foreach(pkg, &pkg->installed, pkg_infodb_print_filename); + + modstatdb_shutdown(); + + return 0; +} + +static int +control_list(const char *const *argv) +{ + struct pkginfo *pkg; + const char *pkgname; + + pkgname = *argv++; + if (!pkgname || *argv) + badusage(_("--%s takes one package name argument"), cipaction->olong); + + modstatdb_open(msdbrw_readonly); + + pkg = dpkg_options_parse_pkgname(cipaction, pkgname); + if (pkg->status == PKG_STAT_NOTINSTALLED) + ohshit(_("package '%s' is not installed"), pkg_name(pkg, pnaw_nonambig)); + + pkg_infodb_foreach(pkg, &pkg->installed, pkg_infodb_print_filetype); + + modstatdb_shutdown(); + + return 0; +} + +static int +control_show(const char *const *argv) +{ + struct pkginfo *pkg; + const char *pkgname; + const char *filename; + const char *control_file; + + pkgname = *argv++; + if (!pkgname || !*argv) + badusage(_("--%s takes exactly two arguments"), + cipaction->olong); + + control_file = *argv++; + if (!control_file || *argv) + badusage(_("--%s takes exactly two arguments"), cipaction->olong); + + pkg_infodb_check_filetype(control_file); + + modstatdb_open(msdbrw_readonly); + + pkg = dpkg_options_parse_pkgname(cipaction, pkgname); + if (pkg->status == PKG_STAT_NOTINSTALLED) + ohshit(_("package '%s' is not installed"), pkg_name(pkg, pnaw_nonambig)); + + if (pkg_infodb_has_file(pkg, &pkg->installed, control_file)) + filename = pkg_infodb_get_file(pkg, &pkg->installed, control_file); + else + ohshit(_("control file '%s' does not exist"), control_file); + + modstatdb_shutdown(); + + file_show(filename); + + return 0; +} + +static void +set_no_pager(const struct cmdinfo *ci, const char *value) +{ + pager_enable(false); +} + +static void DPKG_ATTR_NORET +printversion(const struct cmdinfo *ci, const char *value) +{ + printf(_("Debian %s package management program query tool version %s.\n"), + DPKGQUERY, PACKAGE_RELEASE); + printf(_( +"This is free software; see the GNU General Public License version 2 or\n" +"later for copying conditions. There is NO warranty.\n")); + + m_output(stdout, _("<standard output>")); + + exit(0); +} + +static void DPKG_ATTR_NORET +usage(const struct cmdinfo *ci, const char *value) +{ + printf(_( +"Usage: %s [<option> ...] <command>\n" +"\n"), DPKGQUERY); + + printf(_( +"Commands:\n" +" -s, --status [<package>...] Display package status details.\n" +" -p, --print-avail [<package>...] Display available version details.\n" +" -L, --listfiles <package>... List files 'owned' by package(s).\n" +" -l, --list [<pattern>...] List packages concisely.\n" +" -W, --show [<pattern>...] Show information on package(s).\n" +" -S, --search <pattern>... Find package(s) owning file(s).\n" +" --control-list <package> Print the package control file list.\n" +" --control-show <package> <file>\n" +" Show the package control file.\n" +" -c, --control-path <package> [<file>]\n" +" Print path for package control file.\n" +"\n")); + + printf(_( +" -?, --help Show this help message.\n" +" --version Show the version.\n" +"\n")); + + printf(_( +"Options:\n" +" --admindir=<directory> Use <directory> instead of %s.\n" +" --load-avail Use available file on --show and --list.\n" +" -f|--showformat=<format> Use alternative format for --show.\n" +"\n"), ADMINDIR); + + printf(_( +"Format syntax:\n" +" A format is a string that will be output for each package. The format\n" +" can include the standard escape sequences \\n (newline), \\r (carriage\n" +" return) or \\\\ (plain backslash). Package information can be included\n" +" by inserting variable references to package fields using the ${var[;width]}\n" +" syntax. Fields will be right-aligned unless the width is negative in which\n" +" case left alignment will be used.\n")); + + m_output(stdout, _("<standard output>")); + + exit(0); +} + +static const char printforhelp[] = N_( +"Use --help for help about querying packages."); + +static const char *admindir; + +/* This table has both the action entries in it and the normal options. + * The action entries are made with the ACTION macro, as they all + * have a very similar structure. */ +static const struct cmdinfo cmdinfos[]= { + ACTION( "listfiles", 'L', act_listfiles, list_files ), + ACTION( "status", 's', act_status, print_status ), + ACTION( "print-avail", 'p', act_printavail, print_avail ), + ACTION( "list", 'l', act_listpackages, listpackages ), + ACTION( "search", 'S', act_searchfiles, searchfiles ), + ACTION( "show", 'W', act_listpackages, showpackages ), + ACTION( "control-path", 'c', act_controlpath, control_path ), + ACTION( "control-list", 0, act_controllist, control_list ), + ACTION( "control-show", 0, act_controlshow, control_show ), + + { "admindir", 0, 1, NULL, &admindir, NULL }, + { "load-avail", 0, 0, &opt_loadavail, NULL, NULL, 1 }, + { "showformat", 'f', 1, NULL, &showformat, NULL }, + { "no-pager", 0, 0, NULL, NULL, set_no_pager }, + { "help", '?', 0, NULL, NULL, usage }, + { "version", 0, 0, NULL, NULL, printversion }, + { NULL, 0, 0, NULL, NULL, NULL } +}; + +int main(int argc, const char *const *argv) { + int ret; + + dpkg_set_report_piped_mode(_IOFBF); + dpkg_locales_init(PACKAGE); + dpkg_program_init("dpkg-query"); + dpkg_options_parse(&argv, cmdinfos, printforhelp); + + admindir = dpkg_db_set_dir(admindir); + + if (!cipaction) badusage(_("need an action option")); + + fsys_hash_init(); + + ret = cipaction->action(argv); + + dpkg_program_done(); + + return !!ret; +} |