summaryrefslogtreecommitdiffstats
path: root/src/pacman/pacman.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pacman/pacman.c')
-rw-r--r--src/pacman/pacman.c721
1 files changed, 473 insertions, 248 deletions
diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c
index 7fbffc36..984bd1b7 100644
--- a/src/pacman/pacman.c
+++ b/src/pacman/pacman.c
@@ -1,7 +1,7 @@
/*
* pacman.c
*
- * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org>
+ * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org>
* Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
*
* This program is free software; you can redistribute it and/or modify
@@ -26,8 +26,10 @@
#define PACKAGE_VERSION GIT_VERSION
#endif
+#include <ctype.h> /* isspace */
#include <stdlib.h> /* atoi */
#include <stdio.h>
+#include <ctype.h> /* isspace */
#include <limits.h>
#include <getopt.h>
#include <string.h>
@@ -55,16 +57,55 @@
#include "conf.h"
#include "package.h"
-pmdb_t *db_local;
/* list of targets specified on command line */
static alpm_list_t *pm_targets;
+/* Used to sort the options in --help */
+static int options_cmp(const void *p1, const void *p2)
+{
+ const char *s1 = p1;
+ const char *s2 = p2;
+
+ if(s1 == s2) return(0);
+ if(!s1) return(-1);
+ if(!s2) return(1);
+ /* First skip all spaces in both strings */
+ while(isspace((unsigned char)*s1)) {
+ s1++;
+ }
+ while(isspace((unsigned char)*s2)) {
+ s2++;
+ }
+ /* If we compare a long option (--abcd) and a short one (-a),
+ * the short one always wins */
+ if(*s1 == '-' && *s2 == '-') {
+ s1++;
+ s2++;
+ if(*s1 == '-' && *s2 == '-') {
+ /* two long -> strcmp */
+ s1++;
+ s2++;
+ } else if(*s2 == '-') {
+ /* s1 short, s2 long */
+ return(-1);
+ } else if(*s1 == '-') {
+ /* s1 long, s2 short */
+ return(1);
+ }
+ /* two short -> strcmp */
+ }
+
+ return(strcmp(s1, s2));
+}
+
/** Display usage/syntax for the specified operation.
* @param op the operation code requested
* @param myname basename(argv[0])
*/
static void usage(int op, const char * const myname)
{
+#define addlist(s) (list = alpm_list_add(list, s))
+ alpm_list_t *list = NULL, *i;
/* prefetch some strings for usage below, which moves a lot of calls
* out of gettext. */
char const * const str_opt = _("options");
@@ -89,85 +130,87 @@ static void usage(int op, const char * const myname)
if(op == PM_OP_REMOVE) {
printf("%s: %s {-R --remove} [%s] <%s>\n", str_usg, myname, str_opt, str_pkg);
printf("%s:\n", str_opt);
- printf(_(" -c, --cascade remove packages and all packages that depend on them\n"));
- printf(_(" -d, --nodeps skip dependency checks\n"));
- printf(_(" -k, --dbonly only remove database entries, do not remove files\n"));
- printf(_(" -n, --nosave remove configuration files as well\n"));
- printf(_(" -s, --recursive remove dependencies also (that won't break packages)\n"
+ addlist(_(" -c, --cascade remove packages and all packages that depend on them\n"));
+ addlist(_(" -n, --nosave remove configuration files as well\n"));
+ addlist(_(" -s, --recursive remove dependencies also (that won't break packages)\n"
" (-ss includes explicitly installed dependencies too)\n"));
- printf(_(" -u, --unneeded remove unneeded packages (that won't break packages)\n"));
- printf(_(" --print only print the targets instead of performing the operation\n"));
- printf(_(" --print-format <string>\n"
- " specify how the targets should be printed\n"));
+ addlist(_(" -u, --unneeded remove unneeded packages (that won't break packages)\n"));
} else if(op == PM_OP_UPGRADE) {
printf("%s: %s {-U --upgrade} [%s] <%s>\n", str_usg, myname, str_opt, str_file);
printf("%s:\n", str_opt);
- printf(_(" --asdeps install packages as non-explicitly installed\n"));
- printf(_(" --asexplicit install packages as explicitly installed\n"));
- printf(_(" -d, --nodeps skip dependency checks\n"));
- printf(_(" -f, --force force install, overwrite conflicting files\n"));
- printf(_(" -k, --dbonly add database entries, do not install or keep existing files\n"));
- printf(_(" --print only print the targets instead of performing the operation\n"));
- printf(_(" --print-format <string>\n"
- " specify how the targets should be printed\n"));
} else if(op == PM_OP_QUERY) {
printf("%s: %s {-Q --query} [%s] [%s]\n", str_usg, myname, str_opt, str_pkg);
printf("%s:\n", str_opt);
- printf(_(" -c, --changelog view the changelog of a package\n"));
- printf(_(" -d, --deps list packages installed as dependencies [filter]\n"));
- printf(_(" -e, --explicit list packages explicitly installed [filter]\n"));
- printf(_(" -g, --groups view all members of a package group\n"));
- printf(_(" -i, --info view package information (-ii for backup files)\n"));
- printf(_(" -k, --check check that the files owned by the package(s) are present\n"));
- printf(_(" -l, --list list the contents of the queried package\n"));
- printf(_(" -m, --foreign list installed packages not found in sync db(s) [filter]\n"));
- printf(_(" -o, --owns <file> query the package that owns <file>\n"));
- printf(_(" -p, --file <package> query a package file instead of the database\n"));
- printf(_(" -s, --search <regex> search locally-installed packages for matching strings\n"));
- printf(_(" -t, --unrequired list packages not required by any package [filter]\n"));
- printf(_(" -u, --upgrades list outdated packages [filter]\n"));
- printf(_(" -q, --quiet show less information for query and search\n"));
+ addlist(_(" -c, --changelog view the changelog of a package\n"));
+ addlist(_(" -d, --deps list packages installed as dependencies [filter]\n"));
+ addlist(_(" -e, --explicit list packages explicitly installed [filter]\n"));
+ addlist(_(" -g, --groups view all members of a package group\n"));
+ addlist(_(" -i, --info view package information (-ii for backup files)\n"));
+ addlist(_(" -k, --check check that the files owned by the package(s) are present\n"));
+ addlist(_(" -l, --list list the contents of the queried package\n"));
+ addlist(_(" -m, --foreign list installed packages not found in sync db(s) [filter]\n"));
+ addlist(_(" -o, --owns <file> query the package that owns <file>\n"));
+ addlist(_(" -p, --file <package> query a package file instead of the database\n"));
+ addlist(_(" -q, --quiet show less information for query and search\n"));
+ addlist(_(" -s, --search <regex> search locally-installed packages for matching strings\n"));
+ addlist(_(" -t, --unrequired list packages not required by any package [filter]\n"));
+ addlist(_(" -u, --upgrades list outdated packages [filter]\n"));
} else if(op == PM_OP_SYNC) {
printf("%s: %s {-S --sync} [%s] [%s]\n", str_usg, myname, str_opt, str_pkg);
printf("%s:\n", str_opt);
- printf(_(" --asdeps install packages as non-explicitly installed\n"));
- printf(_(" --asexplicit install packages as explicitly installed\n"));
- printf(_(" -c, --clean remove old packages from cache directory (-cc for all)\n"));
- printf(_(" -d, --nodeps skip dependency checks\n"));
- printf(_(" -f, --force force install, overwrite conflicting files\n"));
- printf(_(" -g, --groups view all members of a package group\n"));
- printf(_(" -i, --info view package information\n"));
- printf(_(" -l, --list <repo> view a list of packages in a repo\n"));
- printf(_(" -s, --search <regex> search remote repositories for matching strings\n"));
- printf(_(" -u, --sysupgrade upgrade installed packages (-uu allows downgrade)\n"));
- printf(_(" -w, --downloadonly download packages but do not install/upgrade anything\n"));
- printf(_(" -y, --refresh download fresh package databases from the server\n"));
- printf(_(" --needed don't reinstall up to date packages\n"));
- printf(_(" --ignore <pkg> ignore a package upgrade (can be used more than once)\n"));
- printf(_(" --ignoregroup <grp>\n"
- " ignore a group upgrade (can be used more than once)\n"));
- printf(_(" --print only print the targets instead of performing the operation\n"));
- printf(_(" --print-format <string>\n"
- " specify how the targets should be printed\n"));
- printf(_(" -q, --quiet show less information for query and search\n"));
+ addlist(_(" -c, --clean remove old packages from cache directory (-cc for all)\n"));
+ addlist(_(" -g, --groups view all members of a package group\n"));
+ addlist(_(" -i, --info view package information\n"));
+ addlist(_(" -l, --list <repo> view a list of packages in a repo\n"));
+ addlist(_(" -q, --quiet show less information for query and search\n"));
+ addlist(_(" -s, --search <regex> search remote repositories for matching strings\n"));
+ addlist(_(" -u, --sysupgrade upgrade installed packages (-uu allows downgrade)\n"));
+ addlist(_(" -w, --downloadonly download packages but do not install/upgrade anything\n"));
+ addlist(_(" -y, --refresh download fresh package databases from the server\n"));
+ addlist(_(" --needed don't reinstall up to date packages\n"));
} else if (op == PM_OP_DATABASE) {
printf("%s: %s {-D --database} <%s> <%s>\n", str_usg, myname, str_opt, str_pkg);
printf("%s:\n", str_opt);
- printf(_(" --asdeps mark packages as non-explicitly installed\n"));
- printf(_(" --asexplicit mark packages as explicitly installed\n"));
+ addlist(_(" --asdeps mark packages as non-explicitly installed\n"));
+ addlist(_(" --asexplicit mark packages as explicitly installed\n"));
+ }
+ switch(op) {
+ case PM_OP_SYNC:
+ case PM_OP_UPGRADE:
+ addlist(_(" -f, --force force install, overwrite conflicting files\n"));
+ addlist(_(" --asdeps install packages as non-explicitly installed\n"));
+ addlist(_(" --asexplicit install packages as explicitly installed\n"));
+ addlist(_(" --ignore <pkg> ignore a package upgrade (can be used more than once)\n"));
+ addlist(_(" --ignoregroup <grp>\n"
+ " ignore a group upgrade (can be used more than once)\n"));
+ /* pass through */
+ case PM_OP_REMOVE:
+ addlist(_(" -d, --nodeps skip dependency checks\n"));
+ addlist(_(" -k, --dbonly only modify database entries, not package files\n"));
+ addlist(_(" --noprogressbar do not show a progress bar when downloading files\n"));
+ addlist(_(" --noscriptlet do not execute the install scriptlet if one exists\n"));
+ addlist(_(" --print only print the targets instead of performing the operation\n"));
+ addlist(_(" --print-format <string>\n"
+ " specify how the targets should be printed\n"));
+ break;
}
- printf(_(" --config <path> set an alternate configuration file\n"));
- printf(_(" --logfile <path> set an alternate log file\n"));
- printf(_(" --noconfirm do not ask for any confirmation\n"));
- printf(_(" --noprogressbar do not show a progress bar when downloading files\n"));
- printf(_(" --noscriptlet do not execute the install scriptlet if one exists\n"));
- printf(_(" -v, --verbose be verbose\n"));
- printf(_(" --debug display debug messages\n"));
- printf(_(" -r, --root <path> set an alternate installation root\n"));
- printf(_(" -b, --dbpath <path> set an alternate database location\n"));
- printf(_(" --cachedir <dir> set an alternate package cache location\n"));
- printf(_(" --arch <arch> set an alternate architecture\n"));
+
+ addlist(_(" -b, --dbpath <path> set an alternate database location\n"));
+ addlist(_(" -r, --root <path> set an alternate installation root\n"));
+ addlist(_(" -v, --verbose be verbose\n"));
+ addlist(_(" --arch <arch> set an alternate architecture\n"));
+ addlist(_(" --cachedir <dir> set an alternate package cache location\n"));
+ addlist(_(" --config <path> set an alternate configuration file\n"));
+ addlist(_(" --debug display debug messages\n"));
+ addlist(_(" --logfile <path> set an alternate log file\n"));
+ addlist(_(" --noconfirm do not ask for any confirmation\n"));
+ }
+ list = alpm_list_msort(list, alpm_list_count(list), options_cmp);
+ for (i = list; i; i = alpm_list_next(i)) {
+ printf("%s", (char *)alpm_list_getdata(i));
}
+ alpm_list_free(list);
+#undef addlist
}
/** Output pacman version and copyright.
@@ -261,7 +304,7 @@ static ssize_t xwrite(int fd, const void *buf, size_t count)
* in a consistant state.
* @param signum the thrown signal
*/
-static RETSIGTYPE handler(int signum)
+static void handler(int signum)
{
int out = fileno(stdout);
int err = fileno(stderr);
@@ -352,6 +395,247 @@ static void setlibpaths(void)
#define check_optarg() if(!optarg) { return(1); }
+typedef void (*fn_add) (const char *s);
+
+static int parsearg_util_addlist(fn_add fn)
+{
+ alpm_list_t *list = NULL, *item = NULL; /* lists for splitting strings */
+
+ check_optarg();
+ list = strsplit(optarg, ',');
+ for(item = list; item; item = alpm_list_next(item)) {
+ fn((char *)alpm_list_getdata(item));
+ }
+ FREELIST(list);
+ return(0);
+}
+
+/** Helper function for parsing operation from command-line arguments.
+ * @param opt Keycode returned by getopt_long
+ * @param dryrun If nonzero, application state is NOT changed
+ * @return 0 if opt was handled, 1 if it was not handled
+ */
+static int parsearg_op(int opt, int dryrun)
+{
+ switch(opt) {
+ /* operations */
+ case 'D':
+ if(dryrun) break;
+ config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_DATABASE); break;
+ case 'Q':
+ if(dryrun) break;
+ config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_QUERY); break;
+ case 'R':
+ if(dryrun) break;
+ config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_REMOVE); break;
+ case 'S':
+ if(dryrun) break;
+ config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_SYNC); break;
+ case 'T':
+ if(dryrun) break;
+ config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_DEPTEST); break;
+ case 'U':
+ if(dryrun) break;
+ config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_UPGRADE); break;
+ case 'V':
+ if(dryrun) break;
+ config->version = 1; break;
+ case 'h':
+ if(dryrun) break;
+ config->help = 1; break;
+ default:
+ return(1);
+ }
+ return(0);
+}
+
+/** Helper functions for parsing command-line arguments.
+ * @param opt Keycode returned by getopt_long
+ * @return 0 on success, 1 on failure
+ */
+static int parsearg_global(int opt)
+{
+ switch(opt) {
+ case OP_ARCH: check_optarg(); setarch(optarg); break;
+ case OP_ASK:
+ check_optarg();
+ config->noask = 1;
+ config->ask = (unsigned int)atoi(optarg);
+ break;
+ case OP_CACHEDIR:
+ check_optarg();
+ if(alpm_option_add_cachedir(optarg) != 0) {
+ pm_printf(PM_LOG_ERROR, _("problem adding cachedir '%s' (%s)\n"),
+ optarg, alpm_strerrorlast());
+ return(1);
+ }
+ break;
+ case OP_CONFIG:
+ check_optarg();
+ if(config->configfile) {
+ free(config->configfile);
+ }
+ config->configfile = strndup(optarg, PATH_MAX);
+ break;
+ case OP_DEBUG:
+ /* debug levels are made more 'human readable' than using a raw logmask
+ * here, error and warning are set in config_new, though perhaps a
+ * --quiet option will remove these later */
+ if(optarg) {
+ unsigned short debug = (unsigned short)atoi(optarg);
+ switch(debug) {
+ case 2:
+ config->logmask |= PM_LOG_FUNCTION; /* fall through */
+ case 1:
+ config->logmask |= PM_LOG_DEBUG;
+ break;
+ default:
+ pm_printf(PM_LOG_ERROR, _("'%s' is not a valid debug level\n"),
+ optarg);
+ return(1);
+ }
+ } else {
+ config->logmask |= PM_LOG_DEBUG;
+ }
+ /* progress bars get wonky with debug on, shut them off */
+ config->noprogressbar = 1;
+ break;
+ case OP_LOGFILE:
+ check_optarg();
+ config->logfile = strndup(optarg, PATH_MAX);
+ break;
+ case OP_NOCONFIRM: config->noconfirm = 1; break;
+ case 'b':
+ check_optarg();
+ config->dbpath = strdup(optarg);
+ break;
+ case 'r': check_optarg(); config->rootdir = strdup(optarg); break;
+ case 'v': (config->verbose)++; break;
+ default: return(1);
+ }
+ return(0);
+}
+
+static int parsearg_database(int opt)
+{
+ switch(opt) {
+ case OP_ASDEPS: config->flags |= PM_TRANS_FLAG_ALLDEPS; break;
+ case OP_ASEXPLICIT: config->flags |= PM_TRANS_FLAG_ALLEXPLICIT; break;
+ default: return(1);
+ }
+ return(0);
+}
+
+static int parsearg_query(int opt)
+{
+ switch(opt) {
+ case 'c': config->op_q_changelog = 1; break;
+ case 'd': config->op_q_deps = 1; break;
+ case 'e': config->op_q_explicit = 1; break;
+ case 'g': (config->group)++; break;
+ case 'i': (config->op_q_info)++; break;
+ case 'k': config->op_q_check = 1; break;
+ case 'l': config->op_q_list = 1; break;
+ case 'm': config->op_q_foreign = 1; break;
+ case 'o': config->op_q_owns = 1; break;
+ case 'p': config->op_q_isfile = 1; break;
+ case 'q': config->quiet = 1; break;
+ case 's': config->op_q_search = 1; break;
+ case 't': config->op_q_unrequired = 1; break;
+ case 'u': config->op_q_upgrade = 1; break;
+ default: return(1);
+ }
+ return(0);
+}
+
+/* options common to -S -R -U */
+static int parsearg_trans(int opt)
+{
+ switch(opt) {
+ case 'd':
+ if(config->flags & PM_TRANS_FLAG_NODEPVERSION) {
+ config->flags |= PM_TRANS_FLAG_NODEPS;
+ } else {
+ config->flags |= PM_TRANS_FLAG_NODEPVERSION;
+ }
+ break;
+ case 'k': config->flags |= PM_TRANS_FLAG_DBONLY; break;
+ case OP_NOPROGRESSBAR: config->noprogressbar = 1; break;
+ case OP_NOSCRIPTLET: config->flags |= PM_TRANS_FLAG_NOSCRIPTLET; break;
+ case 'p': config->print = 1; break;
+ case OP_PRINTFORMAT:
+ check_optarg();
+ config->print_format = strdup(optarg);
+ break;
+ default: return(1);
+ }
+ return(0);
+}
+
+static int parsearg_remove(int opt)
+{
+ if (parsearg_trans(opt) == 0)
+ return(0);
+ switch(opt) {
+ case 'c': config->flags |= PM_TRANS_FLAG_CASCADE; break;
+ case 'n': config->flags |= PM_TRANS_FLAG_NOSAVE; break;
+ case 's':
+ if(config->flags & PM_TRANS_FLAG_RECURSE) {
+ config->flags |= PM_TRANS_FLAG_RECURSEALL;
+ } else {
+ config->flags |= PM_TRANS_FLAG_RECURSE;
+ }
+ break;
+ case 'u': config->flags |= PM_TRANS_FLAG_UNNEEDED; break;
+ default: return(1);
+ }
+ return(0);
+}
+
+/* options common to -S -U */
+static int parsearg_upgrade(int opt)
+{
+ if (parsearg_trans(opt) == 0)
+ return(0);
+ switch(opt) {
+ case 'f': config->flags |= PM_TRANS_FLAG_FORCE; break;
+ case OP_ASDEPS: config->flags |= PM_TRANS_FLAG_ALLDEPS; break;
+ case OP_ASEXPLICIT: config->flags |= PM_TRANS_FLAG_ALLEXPLICIT; break;
+ case OP_IGNORE:
+ parsearg_util_addlist(alpm_option_add_ignorepkg);
+ break;
+ case OP_IGNOREGROUP:
+ parsearg_util_addlist(alpm_option_add_ignoregrp);
+ break;
+ default: return(1);
+ }
+ return(0);
+}
+
+static int parsearg_sync(int opt)
+{
+ if (parsearg_upgrade(opt) == 0)
+ return(0);
+ switch(opt) {
+ case OP_NEEDED: config->flags |= PM_TRANS_FLAG_NEEDED; break;
+ case 'c': (config->op_s_clean)++; break;
+ case 'g': (config->group)++; break;
+ case 'i': (config->op_s_info)++; break;
+ case 'l': config->op_q_list = 1; break;
+ case 'q': config->quiet = 1; break;
+ case 's': config->op_s_search = 1; break;
+ case 'u': (config->op_s_upgrade)++; break;
+ case 'w':
+ config->op_s_downloadonly = 1;
+ config->flags |= PM_TRANS_FLAG_DOWNLOADONLY;
+ config->flags |= PM_TRANS_FLAG_NOCONFLICTS;
+ break;
+ case 'y': (config->op_s_sync)++; break;
+ default: return(1);
+ }
+ return(0);
+}
+
/** Parse command-line arguments for each operation.
* @param argc argc
* @param argv argv
@@ -361,6 +645,8 @@ static int parseargs(int argc, char *argv[])
{
int opt;
int option_index = 0;
+ int result;
+ const char *optstring = "DQRSTUVb:cdefghiklmnopqr:stuvwy";
static struct option opts[] =
{
{"database", no_argument, 0, 'D'},
@@ -418,175 +704,23 @@ static int parseargs(int argc, char *argv[])
{0, 0, 0, 0}
};
- while((opt = getopt_long(argc, argv, "DQRSTUVb:cdefghiklmnopqr:stuvwy", opts, &option_index))) {
- alpm_list_t *list = NULL, *item = NULL; /* lists for splitting strings */
-
+ /* parse operation */
+ while((opt = getopt_long(argc, argv, optstring, opts, &option_index))) {
if(opt < 0) {
break;
+ } else if(opt == 0) {
+ continue;
+ } else if(opt == '?') {
+ /* unknown option, getopt printed an error */
+ return(1);
}
- switch(opt) {
- case 0: break;
- case OP_NOCONFIRM: config->noconfirm = 1; break;
- case OP_CONFIG:
- check_optarg();
- if(config->configfile) {
- free(config->configfile);
- }
- config->configfile = strndup(optarg, PATH_MAX);
- break;
- case OP_IGNORE:
- check_optarg();
- list = strsplit(optarg, ',');
- for(item = list; item; item = alpm_list_next(item)) {
- alpm_option_add_ignorepkg((char *)alpm_list_getdata(item));
- }
- FREELIST(list);
- break;
- case OP_DEBUG:
- /* debug levels are made more 'human readable' than using a raw logmask
- * here, error and warning are set in config_new, though perhaps a
- * --quiet option will remove these later */
- if(optarg) {
- unsigned short debug = atoi(optarg);
- switch(debug) {
- case 2:
- config->logmask |= PM_LOG_FUNCTION; /* fall through */
- case 1:
- config->logmask |= PM_LOG_DEBUG;
- break;
- default:
- pm_printf(PM_LOG_ERROR, _("'%s' is not a valid debug level\n"),
- optarg);
- return(1);
- }
- } else {
- config->logmask |= PM_LOG_DEBUG;
- }
- /* progress bars get wonky with debug on, shut them off */
- config->noprogressbar = 1;
- break;
- case OP_NOPROGRESSBAR: config->noprogressbar = 1; break;
- case OP_NOSCRIPTLET: config->flags |= PM_TRANS_FLAG_NOSCRIPTLET; break;
- case OP_ASK:
- check_optarg();
- config->noask = 1;
- config->ask = atoi(optarg);
- break;
- case OP_CACHEDIR:
- check_optarg();
- if(alpm_option_add_cachedir(optarg) != 0) {
- pm_printf(PM_LOG_ERROR, _("problem adding cachedir '%s' (%s)\n"),
- optarg, alpm_strerrorlast());
- return(1);
- }
- break;
- case OP_ASDEPS:
- config->flags |= PM_TRANS_FLAG_ALLDEPS;
- break;
- case OP_LOGFILE:
- check_optarg();
- config->logfile = strndup(optarg, PATH_MAX);
- break;
- case OP_IGNOREGROUP:
- check_optarg();
- list = strsplit(optarg, ',');
- for(item = list; item; item = alpm_list_next(item)) {
- alpm_option_add_ignoregrp((char *)alpm_list_getdata(item));
- }
- FREELIST(list);
- break;
- case OP_NEEDED: config->flags |= PM_TRANS_FLAG_NEEDED; break;
- case OP_ASEXPLICIT:
- config->flags |= PM_TRANS_FLAG_ALLEXPLICIT;
- break;
- case OP_ARCH:
- check_optarg();
- setarch(optarg);
- break;
- case OP_PRINTFORMAT:
- check_optarg();
- config->print_format = strdup(optarg);
- break;
- case 'D': config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_DATABASE); break;
- case 'Q': config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_QUERY); break;
- case 'R': config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_REMOVE); break;
- case 'S': config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_SYNC); break;
- case 'T': config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_DEPTEST); break;
- case 'U': config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_UPGRADE); break;
- case 'V': config->version = 1; break;
- case 'b':
- check_optarg();
- config->dbpath = strdup(optarg);
- break;
- case 'c':
- (config->op_s_clean)++;
- config->flags |= PM_TRANS_FLAG_CASCADE;
- config->op_q_changelog = 1;
- break;
- case 'd':
- config->op_q_deps = 1;
- config->flags |= PM_TRANS_FLAG_NODEPS;
- break;
- case 'e':
- config->op_q_explicit = 1;
- break;
- case 'f': config->flags |= PM_TRANS_FLAG_FORCE; break;
- case 'g': (config->group)++; break;
- case 'h': config->help = 1; break;
- case 'i': (config->op_q_info)++; (config->op_s_info)++; break;
- case 'k':
- config->flags |= PM_TRANS_FLAG_DBONLY;
- config->op_q_check = 1;
- break;
- case 'l': config->op_q_list = 1; break;
- case 'm': config->op_q_foreign = 1; break;
- case 'n': config->flags |= PM_TRANS_FLAG_NOSAVE; break;
- case 'o': config->op_q_owns = 1; break;
- case 'p':
- config->op_q_isfile = 1;
- config->print = 1;
- break;
- case 'q':
- config->quiet = 1;
- break;
- case 'r':
- check_optarg();
- config->rootdir = strdup(optarg);
- break;
- case 's':
- config->op_s_search = 1;
- config->op_q_search = 1;
- if(config->flags & PM_TRANS_FLAG_RECURSE) {
- config->flags |= PM_TRANS_FLAG_RECURSEALL;
- } else {
- config->flags |= PM_TRANS_FLAG_RECURSE;
- }
- break;
- case 't':
- config->op_q_unrequired = 1;
- break;
- case 'u':
- (config->op_s_upgrade)++;
- config->op_q_upgrade = 1;
- config->flags |= PM_TRANS_FLAG_UNNEEDED;
- break;
- case 'v': (config->verbose)++; break;
- case 'w':
- config->op_s_downloadonly = 1;
- config->flags |= PM_TRANS_FLAG_DOWNLOADONLY;
- config->flags |= PM_TRANS_FLAG_NOCONFLICTS;
- break;
- case 'y': (config->op_s_sync)++; break;
- case '?': return(1);
- default: return(1);
- }
+ parsearg_op(opt, 0);
}
if(config->op == 0) {
pm_printf(PM_LOG_ERROR, _("only one operation may be used at a time\n"));
return(1);
}
-
if(config->help) {
usage(config->op, mbasename(argv[0]));
return(2);
@@ -596,6 +730,55 @@ static int parseargs(int argc, char *argv[])
return(2);
}
+ /* parse all other options */
+ optind = 1;
+ while((opt = getopt_long(argc, argv, optstring, opts, &option_index))) {
+ if(opt < 0) {
+ break;
+ } else if(opt == 0) {
+ continue;
+ } else if(opt == '?') {
+ /* this should have failed during first pass already */
+ return(1);
+ } else if(parsearg_op(opt, 1) == 0) { /* opt is an operation */
+ continue;
+ }
+
+ switch(config->op) {
+ case PM_OP_DATABASE:
+ result = parsearg_database(opt);
+ break;
+ case PM_OP_QUERY:
+ result = parsearg_query(opt);
+ break;
+ case PM_OP_REMOVE:
+ result = parsearg_remove(opt);
+ break;
+ case PM_OP_SYNC:
+ result = parsearg_sync(opt);
+ break;
+ case PM_OP_DEPTEST:
+ result = 1;
+ break;
+ case PM_OP_UPGRADE:
+ result = parsearg_upgrade(opt);
+ break;
+ default:
+ pm_printf(PM_LOG_ERROR, _("no operation specified (use -h for help)\n"));
+ return(1);
+ }
+ if (result == 0)
+ continue;
+
+ /* fall back to global options */
+ result = parsearg_global(opt);
+ if(result != 0) {
+ /* global option parsing failed, abort */
+ pm_printf(PM_LOG_ERROR, _("invalid option\n"));
+ return(result);
+ }
+ }
+
while(optind < argc) {
/* add the target to our target array */
pm_targets = alpm_list_add(pm_targets, strdup(argv[optind]));
@@ -615,6 +798,18 @@ static void option_add_syncfirst(const char *name) {
config->syncfirst = alpm_list_add(config->syncfirst, strdup(name));
}
+/* helper for being used with setrepeatingoption */
+static void option_add_cleanmethod(const char *value) {
+ if (strcmp(value, "KeepInstalled") == 0) {
+ config->cleanmethod |= PM_CLEAN_KEEPINST;
+ } else if (strcmp(value, "KeepCurrent") == 0) {
+ config->cleanmethod |= PM_CLEAN_KEEPCUR;
+ } else {
+ pm_printf(PM_LOG_ERROR, _("invalid value for 'CleanMethod' : '%s'\n"),
+ value);
+ }
+}
+
/** Add repeating options such as NoExtract, NoUpgrade, etc to libalpm
* settings. Refactored out of the parseconfig code since all of them did
* the exact same thing and duplicated code.
@@ -668,7 +863,7 @@ static char *get_tempfile(const char *path, const char *filename) {
}
/** External fetch callback */
-int download_with_xfercommand(const char *url, const char *localpath,
+static int download_with_xfercommand(const char *url, const char *localpath,
int force) {
int ret = 0;
int retval;
@@ -676,6 +871,7 @@ int download_with_xfercommand(const char *url, const char *localpath,
struct stat st;
char *parsedcmd,*tempcmd;
char cwd[PATH_MAX];
+ int restore_cwd = 0;
char *destfile, *tempfile, *filename;
if(!config->xfercommand) {
@@ -708,8 +904,14 @@ int download_with_xfercommand(const char *url, const char *localpath,
parsedcmd = strreplace(tempcmd, "%u", url);
free(tempcmd);
+ /* save the cwd so we can restore it later */
+ if(getcwd(cwd, PATH_MAX) == NULL) {
+ pm_printf(PM_LOG_ERROR, _("could not get current working directory\n"));
+ } else {
+ restore_cwd = 1;
+ }
+
/* cwd to the download directory */
- getcwd(cwd, PATH_MAX);
if(chdir(localpath)) {
pm_printf(PM_LOG_WARNING, _("could not chdir to download directory %s\n"), localpath);
ret = -1;
@@ -736,7 +938,11 @@ int download_with_xfercommand(const char *url, const char *localpath,
}
cleanup:
- chdir(cwd);
+ /* restore the old cwd if we have it */
+ if(restore_cwd && chdir(cwd) != 0) {
+ pm_printf(PM_LOG_ERROR, _("could not change directory to %s (%s)\n"), cwd, strerror(errno));
+ }
+
if(ret == -1) {
/* hack to let an user the time to cancel a download */
sleep(2);
@@ -767,6 +973,8 @@ static int _parse_options(char *key, char *value)
} else if(strcmp(key, "TotalDownload") == 0) {
config->totaldownload = 1;
pm_printf(PM_LOG_DEBUG, "config: totaldownload\n");
+ } else if(strcmp(key, "CheckSpace") == 0) {
+ alpm_option_set_checkspace(1);
} else {
pm_printf(PM_LOG_ERROR, _("directive '%s' without value not recognized\n"), key);
return(1);
@@ -818,15 +1026,7 @@ static int _parse_options(char *key, char *value)
alpm_option_set_fetchcb(download_with_xfercommand);
pm_printf(PM_LOG_DEBUG, "config: xfercommand: %s\n", value);
} else if (strcmp(key, "CleanMethod") == 0) {
- if (strcmp(value, "KeepInstalled") == 0) {
- config->cleanmethod = PM_CLEAN_KEEPINST;
- } else if (strcmp(value, "KeepCurrent") == 0) {
- config->cleanmethod = PM_CLEAN_KEEPCUR;
- } else {
- pm_printf(PM_LOG_ERROR, _("invalid value for 'CleanMethod' : '%s'\n"), value);
- return(1);
- }
- pm_printf(PM_LOG_DEBUG, "config: cleanmethod: %s\n", value);
+ setrepeatingoption(value, "CleanMethod", option_add_cleanmethod);
} else {
pm_printf(PM_LOG_ERROR, _("directive '%s' with a value not recognized\n"), key);
return(1);
@@ -989,7 +1189,7 @@ static int _parseconfig(const char *file, const char *givensection,
file, linenum, value);
break;
default:
- for(int gindex = 0; gindex < globbuf.gl_pathc; gindex++) {
+ for(size_t gindex = 0; gindex < globbuf.gl_pathc; gindex++) {
pm_printf(PM_LOG_DEBUG, "config file %s, line %d: including %s\n",
file, linenum, globbuf.gl_pathv[gindex]);
_parseconfig(globbuf.gl_pathv[gindex], section, db);
@@ -1161,6 +1361,39 @@ int main(int argc, char *argv[])
cleanup(ret);
}
+ /* we also support reading targets from stdin */
+ if(!isatty(fileno(stdin))) {
+ char line[PATH_MAX];
+ int i = 0;
+ while(i < PATH_MAX && (line[i] = (char)fgetc(stdin)) != EOF) {
+ if(isspace((unsigned char)line[i])) {
+ /* avoid adding zero length arg when multiple spaces separate args */
+ if(i > 0) {
+ line[i] = '\0';
+ pm_targets = alpm_list_add(pm_targets, strdup(line));
+ i = 0;
+ }
+ } else {
+ i++;
+ }
+ }
+ /* check for buffer overflow */
+ if (i >= PATH_MAX) {
+ pm_printf(PM_LOG_ERROR, _("buffer overflow detected in arg parsing\n"));
+ cleanup(EXIT_FAILURE);
+ }
+
+ /* end of stream -- check for data still in line buffer */
+ if(i > 0) {
+ line[i] = '\0';
+ pm_targets = alpm_list_add(pm_targets, strdup(line));
+ }
+ if (!freopen(ctermid(NULL), "r", stdin)) {
+ pm_printf(PM_LOG_ERROR, _("failed to reopen stdin for reading: (%s)\n"),
+ strerror(errno));
+ }
+ }
+
/* parse the config file */
ret = parseconfig(config->configfile);
if(ret != 0) {
@@ -1209,14 +1442,6 @@ int main(int argc, char *argv[])
list_display("Targets :", pm_targets);
}
- /* Opening local database */
- db_local = alpm_db_register_local();
- if(db_local == NULL) {
- pm_printf(PM_LOG_ERROR, _("could not register 'local' database (%s)\n"),
- alpm_strerrorlast());
- cleanup(EXIT_FAILURE);
- }
-
/* Log commandline */
if(needs_root()) {
cl_to_log(argc, argv);