blob: 22bf8190d7913b389e867b0fdbce1a85aec07f1b [file] [log] [blame]
/*
* dpkg - main program for package management
* main.c - main program
*
* Copyright © 1994,1995 Ian Jackson <ian@chiark.greenend.org.uk>
* Copyright © 2006-2010 Guillem Jover <guillem@debian.org>
* Copyright © 2010 Canonical Ltd.
* written by Martin Pitt <martin.pitt@canonical.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <compat.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <errno.h>
#include <limits.h>
#if HAVE_LOCALE_H
#include <locale.h>
#endif
#include <ctype.h>
#include <string.h>
#include <fcntl.h>
#include <dirent.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <dpkg/macros.h>
#include <dpkg/i18n.h>
#include <dpkg/dpkg.h>
#include <dpkg/dpkg-db.h>
#include <dpkg/subproc.h>
#include <dpkg/command.h>
#include <dpkg/options.h>
#include "main.h"
#include "filesdb.h"
#include "filters.h"
static void DPKG_ATTR_NORET
printversion(const struct cmdinfo *ci, const char *value)
{
printf(_("Debian `%s' package management program version %s.\n"),
DPKG, DPKG_VERSION_ARCH);
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);
}
/*
* FIXME: Options that need fixing:
* dpkg --yet-to-unpack
* dpkg --command-fd
*/
static void DPKG_ATTR_NORET
usage(const struct cmdinfo *ci, const char *value)
{
printf(_(
"Usage: %s [<option> ...] <command>\n"
"\n"), DPKG);
printf(_(
"Commands:\n"
" -i|--install <.deb file name> ... | -R|--recursive <directory> ...\n"
" --unpack <.deb file name> ... | -R|--recursive <directory> ...\n"
" -A|--record-avail <.deb file name> ... | -R|--recursive <directory> ...\n"
" --configure <package> ... | -a|--pending\n"
" --triggers-only <package> ... | -a|--pending\n"
" -r|--remove <package> ... | -a|--pending\n"
" -P|--purge <package> ... | -a|--pending\n"
" --get-selections [<pattern> ...] Get list of selections to stdout.\n"
" --set-selections Set package selections from stdin.\n"
" --clear-selections Deselect every non-essential package.\n"
" --update-avail <Packages-file> Replace available packages info.\n"
" --merge-avail <Packages-file> Merge with info from file.\n"
" --clear-avail Erase existing available info.\n"
" --forget-old-unavail Forget uninstalled unavailable pkgs.\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"
" -S|--search <pattern> ... Find package(s) owning file(s).\n"
" -C|--audit Check for broken package(s).\n"
" --print-architecture Print dpkg architecture.\n"
" --compare-versions <a> <op> <b> Compare version numbers - see below.\n"
" --force-help Show help on forcing.\n"
" -Dh|--debug=help Show help on debugging.\n"
"\n"));
printf(_(
" -h|--help Show this help message.\n"
" --version Show the version.\n"
"\n"));
printf(_(
"Use dpkg -b|--build|-c|--contents|-e|--control|-I|--info|-f|--field|\n"
" -x|--extract|-X|--vextract|--fsys-tarfile on archives (type %s --help).\n"
"\n"), BACKEND);
printf(_(
"For internal use: dpkg --assert-support-predepends | --predep-package |\n"
" --assert-working-epoch | --assert-long-filenames | --assert-multi-conrep.\n"
"\n"));
printf(_(
"Options:\n"
" --admindir=<directory> Use <directory> instead of %s.\n"
" --root=<directory> Install on a different root directory.\n"
" --instdir=<directory> Change installation dir without changing admin dir.\n"
" --path-exclude=<pattern> Do not install paths which match a shell pattern.\n"
" --path-include=<pattern> Re-include a pattern after a previous exclusion.\n"
" -O|--selected-only Skip packages not selected for install/upgrade.\n"
" -E|--skip-same-version Skip packages whose same version is installed.\n"
" -G|--refuse-downgrade Skip packages with earlier version than installed.\n"
" -B|--auto-deconfigure Install even if it would break some other package.\n"
" --[no-]triggers Skip or force consequential trigger processing.\n"
" --no-debsig Do not try to verify package signatures.\n"
" --no-act|--dry-run|--simulate\n"
" Just say what we would do - don't do it.\n"
" -D|--debug=<octal> Enable debugging (see -Dhelp or --debug=help).\n"
" --status-fd <n> Send status change updates to file descriptor <n>.\n"
" --log=<filename> Log status changes and actions to <filename>.\n"
" --ignore-depends=<package>,...\n"
" Ignore dependencies involving <package>.\n"
" --force-... Override problems (see --force-help).\n"
" --no-force-...|--refuse-...\n"
" Stop when problems encountered.\n"
" --abort-after <n> Abort after encountering <n> errors.\n"
"\n"), ADMINDIR);
printf(_(
"Comparison operators for --compare-versions are:\n"
" lt le eq ne ge gt (treat empty version as earlier than any version);\n"
" lt-nl le-nl ge-nl gt-nl (treat empty version as later than any version);\n"
" < << <= = >= >> > (only for compatibility with control file syntax).\n"
"\n"));
printf(_(
"Use `dselect' or `aptitude' for user-friendly package management.\n"));
m_output(stdout, _("<standard output>"));
exit(0);
}
const char native_arch[] = ARCHITECTURE;
static const char printforhelp[] = N_(
"Type dpkg --help for help about installing and deinstalling packages [*];\n"
"Use `dselect' or `aptitude' for user-friendly package management;\n"
"Type dpkg -Dhelp for a list of dpkg debug flag values;\n"
"Type dpkg --force-help for a list of forcing options;\n"
"Type dpkg-deb --help for help about manipulating *.deb files;\n"
"\n"
"Options marked [*] produce a lot of output - pipe it through `less' or `more' !");
int f_pending=0, f_recursive=0, f_alsoselect=1, f_skipsame=0, f_noact=0;
int f_autodeconf=0, f_nodebsig=0;
int f_triggers = 0;
int fc_downgrade=1, fc_configureany=0, fc_hold=0, fc_removereinstreq=0, fc_overwrite=0;
int fc_removeessential=0, fc_conflicts=0, fc_depends=0, fc_dependsversion=0;
int fc_breaks=0, fc_badpath=0, fc_overwritediverted=0, fc_architecture=0;
int fc_nonroot=0, fc_overwritedir=0, fc_conff_new=0, fc_conff_miss=0;
int fc_conff_old=0, fc_conff_def=0;
int fc_conff_ask = 0;
int fc_unsafe_io = 0;
int fc_badverify = 0;
int fc_badversion = 0;
int errabort = 50;
static const char *admindir = ADMINDIR;
const char *instdir= "";
struct pkg_list *ignoredependss = NULL;
static const char *
forcetype_str(char type)
{
switch (type) {
case '\0':
case ' ':
return " ";
case '*':
return "[*]";
case '!':
return "[!]";
default:
internerr("unknown force type %c", type);
}
}
static const struct forceinfo {
const char *name;
int *opt;
char type;
const char *desc;
} forceinfos[]= {
{ "all", NULL,
'!', N_("Set all force options")},
{ "downgrade", &fc_downgrade,
'*', N_("Replace a package with a lower version") },
{ "configure-any", &fc_configureany,
' ', N_("Configure any package which may help this one") },
{ "hold", &fc_hold,
' ', N_("Process incidental packages even when on hold") },
{ "not-root", &fc_nonroot,
' ', N_("Try to (de)install things even when not root") },
{ "bad-path", &fc_badpath,
' ', N_("PATH is missing important programs, problems likely") },
{ "bad-verify", &fc_badverify,
' ', N_("Install a package even if it fails authenticity check") },
{ "bad-version", &fc_badversion,
' ', N_("Process even packages with wrong versions") },
{ "overwrite", &fc_overwrite,
' ', N_("Overwrite a file from one package with another") },
{ "overwrite-diverted", &fc_overwritediverted,
' ', N_("Overwrite a diverted file with an undiverted version") },
{ "overwrite-dir", &fc_overwritedir,
'!', N_("Overwrite one package's directory with another's file") },
{ "unsafe-io", &fc_unsafe_io,
'!', N_("Do not perform safe I/O operations when unpacking") },
{ "confnew", &fc_conff_new,
'!', N_("Always use the new config files, don't prompt") },
{ "confold", &fc_conff_old,
'!', N_("Always use the old config files, don't prompt") },
/* XXX: Handle automatically line wrappings. */
{ "confdef", &fc_conff_def,
'!', N_("Use the default option for new config files if one\n"
" is available, don't prompt. If no default can be found,\n"
" you will be prompted unless one of the confold or\n"
" confnew options is also given") },
{ "confmiss", &fc_conff_miss,
'!', N_("Always install missing config files") },
{ "confask", &fc_conff_ask,
'!', N_("Offer to replace config files with no new versions") },
{ "architecture", &fc_architecture,
'!', N_("Process even packages with wrong or no architecture") },
{ "breaks", &fc_breaks,
'!', N_("Install even if it would break another package") },
{ "conflicts", &fc_conflicts,
'!', N_("Allow installation of conflicting packages") },
{ "depends", &fc_depends,
'!', N_("Turn all dependency problems into warnings") },
{ "depends-version", &fc_dependsversion,
'!', N_("Turn dependency version problems into warnings") },
{ "remove-reinstreq", &fc_removereinstreq,
'!', N_("Remove packages which require installation") },
{ "remove-essential", &fc_removeessential,
'!', N_("Remove an essential package") },
{ NULL }
};
#define DBG_DEF(n, d) \
{ .flag = dbg_##n, .name = #n, .desc = d }
static const struct debuginfo {
int flag;
const char *name;
const char *desc;
} debuginfos[] = {
DBG_DEF(general, N_("Generally helpful progress information")),
DBG_DEF(scripts, N_("Invocation and status of maintainer scripts")),
DBG_DEF(eachfile, N_("Output for each file processed")),
DBG_DEF(eachfiledetail, N_("Lots of output for each file processed")),
DBG_DEF(conff, N_("Output for each configuration file")),
DBG_DEF(conffdetail, N_("Lots of output for each configuration file")),
DBG_DEF(depcon, N_("Dependencies and conflicts")),
DBG_DEF(depcondetail, N_("Lots of dependencies/conflicts output")),
DBG_DEF(triggers, N_("Trigger activation and processing")),
DBG_DEF(triggersdetail, N_("Lots of output regarding triggers")),
DBG_DEF(triggersstupid, N_("Silly amounts of output regarding triggers")),
DBG_DEF(veryverbose, N_("Lots of drivel about eg the dpkg/info directory")),
DBG_DEF(stupidlyverbose, N_("Insane amounts of drivel")),
{ 0, NULL, NULL }
};
static void setdebug(const struct cmdinfo *cpi, const char *value) {
char *endp;
unsigned long mask;
const struct debuginfo *dip;
if (*value == 'h') {
printf(_(
"%s debugging option, --debug=<octal> or -D<octal>:\n"
"\n"
" Number Ref. in source Description\n"), DPKG);
for (dip = debuginfos; dip->name; dip++)
printf(" %6o %-16s %s\n", dip->flag, dip->name, gettext(dip->desc));
printf(_("\n"
"Debugging options can be mixed using bitwise-or.\n"
"Note that the meanings and values are subject to change.\n"));
m_output(stdout, _("<standard output>"));
exit(0);
}
mask = strtoul(value, &endp, 8);
if (value == endp || *endp) badusage(_("--debug requires an octal argument"));
debug_set_mask(mask);
}
static void
setfilter(const struct cmdinfo *cip, const char *value)
{
filter_add(value, cip->arg_int);
}
static void setroot(const struct cmdinfo *cip, const char *value) {
char *p;
instdir= value;
m_asprintf(&p, "%s%s", value, ADMINDIR);
admindir= p;
}
static void ignoredepends(const struct cmdinfo *cip, const char *value) {
char *copy, *p;
const char *pnerr;
copy= m_malloc(strlen(value)+2);
strcpy(copy,value);
copy[strlen(value) + 1] = '\0';
for (p=copy; *p; p++) {
if (*p != ',') continue;
*p++ = '\0';
if (!*p || *p==',' || p==copy+1)
badusage(_("null package name in --ignore-depends comma-separated list `%.250s'"),
value);
}
p= copy;
while (*p) {
pnerr = pkg_name_is_illegal(p, NULL);
if (pnerr) ohshite(_("--ignore-depends requires a legal package name. "
"`%.250s' is not; %s"), p, pnerr);
pkg_list_prepend(&ignoredependss, pkg_db_find(p));
p+= strlen(p)+1;
}
free(copy);
}
static void setinteger(const struct cmdinfo *cip, const char *value) {
unsigned long v;
char *ep;
v= strtoul(value,&ep,0);
if (value == ep || *ep || v > INT_MAX)
badusage(_("invalid integer for --%s: `%.250s'"),cip->olong,value);
*cip->iassignto= v;
}
static void setpipe(const struct cmdinfo *cip, const char *value) {
unsigned long v;
char *ep;
v= strtoul(value,&ep,0);
if (value == ep || *ep || v > INT_MAX)
badusage(_("invalid integer for --%s: `%.250s'"),cip->olong,value);
statusfd_add(v);
}
static bool
is_invoke_action(enum action action)
{
switch (action) {
case act_unpack:
case act_configure:
case act_install:
case act_triggers:
case act_remove:
case act_purge:
return true;
default:
return false;
}
}
struct invoke_hook *pre_invoke_hooks = NULL;
struct invoke_hook **pre_invoke_hooks_tail = &pre_invoke_hooks;
struct invoke_hook *post_invoke_hooks = NULL;
struct invoke_hook **post_invoke_hooks_tail = &post_invoke_hooks;
struct invoke_hook *status_loggers = NULL;
struct invoke_hook **status_loggers_tail = &status_loggers;
static void
set_invoke_hook(const struct cmdinfo *cip, const char *value)
{
struct invoke_hook ***hook_tail = cip->arg_ptr;
struct invoke_hook *hook_new;
hook_new = nfmalloc(sizeof(struct invoke_hook));
hook_new->command = nfstrsave(value);
hook_new->next = NULL;
/* Add the new hook at the tail of the list to preserve the order. */
**hook_tail = hook_new;
*hook_tail = &hook_new->next;
}
static void
run_invoke_hooks(const char *action, struct invoke_hook *hook_head)
{
struct invoke_hook *hook;
setenv("DPKG_HOOK_ACTION", action, 1);
for (hook = hook_head; hook; hook = hook->next) {
int status;
/* XXX: As an optimization, use exec instead if no shell metachar are
* used “!$=&|\\`'"^~;<>{}[]()?*#”. */
status = system(hook->command);
if (status != 0)
ohshit(_("error executing hook '%s', exit code %d"), hook->command,
status);
}
unsetenv("DPKG_HOOK_ACTION");
}
static void
run_status_loggers(struct invoke_hook *hook_head)
{
struct invoke_hook *hook;
for (hook = hook_head; hook; hook = hook->next) {
pid_t pid;
int p[2];
m_pipe(p);
pid = subproc_fork();
if (pid == 0) {
/* Setup stdin and stdout. */
m_dup2(p[0], 0);
close(1);
close(p[0]);
close(p[1]);
command_shell(hook->command, _("status logger"));
}
close(p[0]);
statusfd_add(p[1]);
}
}
static void setforce(const struct cmdinfo *cip, const char *value) {
const char *comma;
size_t l;
const struct forceinfo *fip;
if (!strcmp(value,"help")) {
printf(_(
"%s forcing options - control behaviour when problems found:\n"
" warn but continue: --force-<thing>,<thing>,...\n"
" stop with error: --refuse-<thing>,<thing>,... | --no-force-<thing>,...\n"
" Forcing things:\n"), DPKG);
for (fip = forceinfos; fip->name; fip++)
printf(" %s %-18s %s\n", forcetype_str(fip->type), fip->name,
gettext(fip->desc));
printf(_(
"\n"
"WARNING - use of options marked [!] can seriously damage your installation.\n"
"Forcing options marked [*] are enabled by default.\n"));
m_output(stdout, _("<standard output>"));
exit(0);
}
for (;;) {
comma= strchr(value,',');
l = comma ? (size_t)(comma - value) : strlen(value);
for (fip=forceinfos; fip->name; fip++)
if (!strncmp(fip->name,value,l) && strlen(fip->name)==l) break;
if (!fip->name) {
badusage(_("unknown force/refuse option `%.*s'"),
(int)min(l, 250), value);
} else if (strcmp(fip->name, "all") == 0) {
for (fip = forceinfos; fip->name; fip++)
if (fip->opt)
*fip->opt = cip->arg_int;
} else if (fip->opt) {
*fip->opt = cip->arg_int;
} else {
warning(_("obsolete force/refuse option '%s'\n"), fip->name);
}
if (!comma) break;
value= ++comma;
}
}
int execbackend(const char *const *argv) DPKG_ATTR_NORET;
int commandfd(const char *const *argv);
/* 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[]= {
#define ACTIONBACKEND(longopt, shortopt, backend) \
{ longopt, shortopt, 0, NULL, NULL, setaction, 0, (void *)backend, execbackend }
ACTION( "install", 'i', act_install, archivefiles ),
ACTION( "unpack", 0, act_unpack, archivefiles ),
ACTION( "record-avail", 'A', act_avail, archivefiles ),
ACTION( "configure", 0, act_configure, packages ),
ACTION( "remove", 'r', act_remove, packages ),
ACTION( "purge", 'P', act_purge, packages ),
ACTION( "triggers-only", 0, act_triggers, packages ),
ACTIONBACKEND( "listfiles", 'L', DPKGQUERY),
ACTIONBACKEND( "status", 's', DPKGQUERY),
ACTION( "get-selections", 0, act_getselections, getselections ),
ACTION( "set-selections", 0, act_setselections, setselections ),
ACTION( "clear-selections", 0, act_clearselections, clearselections ),
ACTIONBACKEND( "print-avail", 'p', DPKGQUERY),
ACTION( "update-avail", 0, act_avreplace, updateavailable ),
ACTION( "merge-avail", 0, act_avmerge, updateavailable ),
ACTION( "clear-avail", 0, act_avclear, updateavailable ),
ACTION( "forget-old-unavail", 0, act_forgetold, forgetold ),
ACTION( "audit", 'C', act_audit, audit ),
ACTION( "yet-to-unpack", 0, act_unpackchk, unpackchk ),
ACTIONBACKEND( "list", 'l', DPKGQUERY),
ACTIONBACKEND( "search", 'S', DPKGQUERY),
ACTION( "assert-support-predepends", 0, act_assertpredep, assertpredep ),
ACTION( "assert-working-epoch", 0, act_assertepoch, assertepoch ),
ACTION( "assert-long-filenames", 0, act_assertlongfilenames, assertlongfilenames ),
ACTION( "assert-multi-conrep", 0, act_assertmulticonrep, assertmulticonrep ),
ACTION( "print-architecture", 0, act_printarch, printarch ),
ACTION( "print-installation-architecture", 0, act_printinstarch, printinstarch ),
ACTION( "predep-package", 0, act_predeppackage, predeppackage ),
ACTION( "compare-versions", 0, act_cmpversions, cmpversions ),
/*
ACTION( "command-fd", 'c', act_commandfd, commandfd ),
*/
{ "pre-invoke", 0, 1, NULL, NULL, set_invoke_hook, 0, &pre_invoke_hooks_tail },
{ "post-invoke", 0, 1, NULL, NULL, set_invoke_hook, 0, &post_invoke_hooks_tail },
{ "path-exclude", 0, 1, NULL, NULL, setfilter, 0 },
{ "path-include", 0, 1, NULL, NULL, setfilter, 1 },
{ "status-logger", 0, 1, NULL, NULL, set_invoke_hook, 0, &status_loggers_tail },
{ "status-fd", 0, 1, NULL, NULL, setpipe, 0 },
{ "log", 0, 1, NULL, &log_file, NULL, 0 },
{ "pending", 'a', 0, &f_pending, NULL, NULL, 1 },
{ "recursive", 'R', 0, &f_recursive, NULL, NULL, 1 },
{ "no-act", 0, 0, &f_noact, NULL, NULL, 1 },
{ "dry-run", 0, 0, &f_noact, NULL, NULL, 1 },
{ "simulate", 0, 0, &f_noact, NULL, NULL, 1 },
{ "no-debsig", 0, 0, &f_nodebsig, NULL, NULL, 1 },
/* Alias ('G') for --refuse. */
{ NULL, 'G', 0, &fc_downgrade, NULL, NULL, 0 },
{ "selected-only", 'O', 0, &f_alsoselect, NULL, NULL, 0 },
{ "triggers", 0, 0, &f_triggers, NULL, NULL, 1 },
{ "no-triggers", 0, 0, &f_triggers, NULL, NULL, -1 },
/* FIXME: Remove ('N') sometime. */
{ "no-also-select", 'N', 0, &f_alsoselect, NULL, NULL, 0 },
{ "skip-same-version", 'E', 0, &f_skipsame, NULL, NULL, 1 },
{ "auto-deconfigure", 'B', 0, &f_autodeconf, NULL, NULL, 1 },
{ "root", 0, 1, NULL, NULL, setroot, 0 },
{ "abort-after", 0, 1, &errabort, NULL, setinteger, 0 },
{ "admindir", 0, 1, NULL, &admindir, NULL, 0 },
{ "instdir", 0, 1, NULL, &instdir, NULL, 0 },
{ "ignore-depends", 0, 1, NULL, NULL, ignoredepends, 0 },
{ "force", 0, 2, NULL, NULL, setforce, 1 },
{ "refuse", 0, 2, NULL, NULL, setforce, 0 },
{ "no-force", 0, 2, NULL, NULL, setforce, 0 },
{ "debug", 'D', 1, NULL, NULL, setdebug, 0 },
{ "help", 'h', 0, NULL, NULL, usage, 0 },
{ "version", 0, 0, NULL, NULL, printversion, 0 },
ACTIONBACKEND( "build", 'b', BACKEND),
ACTIONBACKEND( "contents", 'c', BACKEND),
ACTIONBACKEND( "control", 'e', BACKEND),
ACTIONBACKEND( "info", 'I', BACKEND),
ACTIONBACKEND( "field", 'f', BACKEND),
ACTIONBACKEND( "extract", 'x', BACKEND),
ACTIONBACKEND( "vextract", 'X', BACKEND),
ACTIONBACKEND( "fsys-tarfile", 0, BACKEND),
{ NULL, 0, 0, NULL, NULL, NULL, 0 }
};
int
execbackend(const char *const *argv)
{
struct command cmd;
char *arg;
command_init(&cmd, cipaction->arg_ptr, NULL);
command_add_arg(&cmd, cipaction->arg_ptr);
m_asprintf(&arg, "--%s", cipaction->olong);
command_add_arg(&cmd, arg);
/* Exlicitely separate arguments from options as any user-supplied
* separator got stripped by the option parser */
command_add_arg(&cmd, "--");
command_add_argl(&cmd, (const char **)argv);
command_exec(&cmd);
}
int
commandfd(const char *const *argv)
{
struct varbuf linevb = VARBUF_INIT;
const char * pipein;
const char **newargs = NULL;
char *ptr, *endptr;
FILE *in;
unsigned long infd;
int ret = 0;
int c, lno, i;
bool skipchar;
pipein = *argv++;
if (pipein == NULL)
badusage(_("--command-fd takes one argument, not zero"));
if (*argv)
badusage(_("--command-fd only takes one argument"));
errno = 0;
infd = strtoul(pipein, &endptr, 10);
if (pipein == endptr || *endptr || infd > INT_MAX)
ohshite(_("invalid integer for --%s: `%.250s'"), "command-fd", pipein);
if ((in= fdopen(infd, "r")) == NULL)
ohshite(_("couldn't open `%i' for stream"), (int) infd);
for (;;) {
bool mode = false;
int argc= 1;
lno= 0;
push_error_context();
do { c= getc(in); if (c == '\n') lno++; } while (c != EOF && isspace(c));
if (c == EOF) break;
if (c == '#') {
do { c= getc(in); if (c == '\n') lno++; } while (c != EOF && c != '\n');
continue;
}
varbuf_reset(&linevb);
do {
varbuf_add_char(&linevb, c);
c= getc(in);
if (c == '\n') lno++;
/* This isn't fully accurate, but overestimating can't hurt. */
if (isspace(c))
argc++;
} while (c != EOF && c != '\n');
if (c == EOF) ohshit(_("unexpected eof before end of line %d"),lno);
if (!argc) continue;
varbuf_end_str(&linevb);
newargs = m_realloc(newargs, sizeof(const char *) * (argc + 1));
argc= 1;
ptr= linevb.buf;
endptr = ptr + linevb.used + 1;
skipchar = false;
while(ptr < endptr) {
if (skipchar) {
skipchar = false;
} else if (*ptr == '\\') {
memmove(ptr, (ptr+1), (linevb.used-(linevb.buf - ptr)-1));
endptr--;
skipchar = true;
continue;
} else if (isspace(*ptr)) {
if (mode == true) {
*ptr = '\0';
mode = false;
}
} else {
if (mode == false) {
newargs[argc]= ptr;
argc++;
mode = true;
}
}
ptr++;
}
*ptr = '\0';
newargs[argc++] = NULL;
/* We strdup() each argument, but never free it, because the
* error messages contain references back to these strings.
* Freeing them, and reusing the memory, would make those
* error messages confusing, to say the least. */
for(i=1;i<argc;i++)
if (newargs[i])
newargs[i] = m_strdup(newargs[i]);
setaction(NULL, NULL);
myopt((const char *const **)&newargs, cmdinfos, printforhelp);
if (!cipaction) badusage(_("need an action option"));
ret |= cipaction->action(newargs);
pop_error_context(ehflag_normaltidy);
}
return ret;
}
int main(int argc, const char *const *argv) {
int ret;
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
dpkg_set_progname("dpkg");
standard_startup();
loadcfgfile(DPKG, cmdinfos);
myopt(&argv, cmdinfos, printforhelp);
if (!cipaction) badusage(_("need an action option"));
admindir = dpkg_db_set_dir(admindir);
/* Always set environment, to avoid possible security risks. */
if (setenv("DPKG_ADMINDIR", admindir, 1) < 0)
ohshite(_("unable to setenv for subprocesses"));
if (!f_triggers)
f_triggers = (cipaction->arg_int == act_triggers && *argv) ? -1 : 1;
setvbuf(stdout, NULL, _IONBF, 0);
if (is_invoke_action(cipaction->arg_int)) {
run_invoke_hooks(cipaction->olong, pre_invoke_hooks);
run_status_loggers(status_loggers);
}
filesdbinit();
ret = cipaction->action(argv);
if (is_invoke_action(cipaction->arg_int))
run_invoke_hooks(cipaction->olong, post_invoke_hooks);
standard_shutdown();
return reportbroken_retexitstatus(ret);
}