diff options
Diffstat (limited to 'src/pacman/pacman.c')
-rw-r--r-- | src/pacman/pacman.c | 787 |
1 files changed, 559 insertions, 228 deletions
diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c index 5adafb14..77527531 100644 --- a/src/pacman/pacman.c +++ b/src/pacman/pacman.c @@ -1,8 +1,8 @@ /* * pacman.c - * - * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> - * + * + * Copyright (c) 2002-2007 by Judd Vinet <jvinet@zeroflux.org> + * * This program 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 @@ -15,127 +15,108 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, * USA. */ #include "config.h" -#include <stdlib.h> +#include <stdlib.h> /* atoi */ #include <stdio.h> #include <limits.h> #include <getopt.h> #include <string.h> #include <signal.h> -#include <sys/types.h> -#include <sys/stat.h> #include <unistd.h> -#include <libintl.h> -#include <locale.h> -#if defined(__APPLE__) -#include <malloc/malloc.h> -#elif defined(__OpenBSD__) || defined(__APPLE__) -#include <sys/malloc.h> -#elif defined(CYGWIN) -#include <libgen.h> /* basename */ -#else -#include <mcheck.h> /* debug */ +#include <sys/types.h> +#include <sys/utsname.h> /* uname */ +#include <locale.h> /* setlocale */ +#include <time.h> /* time_t */ +#if defined(PACMAN_DEBUG) && defined(HAVE_MCHECK_H) +#include <mcheck.h> /* debug tracing (mtrace) */ #endif -#include <time.h> +/* alpm */ #include <alpm.h> #include <alpm_list.h> /* pacman */ +#include "pacman.h" #include "util.h" -#include "log.h" -#include "downloadprog.h" +#include "callback.h" #include "conf.h" #include "package.h" -#include "add.h" -#include "remove.h" -#include "upgrade.h" -#include "query.h" -#include "sync.h" -#include "deptest.h" - -#if defined(__OpenBSD__) || defined(__APPLE__) -#define BSD -#endif - -/* Operations */ -enum { - PM_OP_MAIN = 1, - PM_OP_ADD, - PM_OP_REMOVE, - PM_OP_UPGRADE, - PM_OP_QUERY, - PM_OP_SYNC, - PM_OP_DEPTEST -}; - -config_t *config; pmdb_t *db_local; /* list of targets specified on command line */ static alpm_list_t *pm_targets; -/* Display usage/syntax for the specified operation. - * op: the operation code requested - * myname: basename(argv[0]) +/** Display usage/syntax for the specified operation. + * @param op the operation code requested + * @param myname basename(argv[0]) */ -static void usage(int op, char *myname) +static void usage(int op, const char * const myname) { + /* prefetch some strings for usage below, which moves a lot of calls + * out of gettext. */ + char const * const str_opt = _("options"); + char const * const str_file = _("file"); + char const * const str_pkg = _("package"); + char const * const str_usg = _("usage"); + char const * const str_opr = _("operation"); + if(op == PM_OP_MAIN) { - printf(_("usage: %s {-h --help}\n"), myname); - printf(_(" %s {-V --version}\n"), myname); - printf(_(" %s {-A --add} [options] <file>\n"), myname); - printf(_(" %s {-F --freshen} [options] <file>\n"), myname); - printf(_(" %s {-Q --query} [options] [package]\n"), myname); - printf(_(" %s {-R --remove} [options] <package>\n"), myname); - printf(_(" %s {-S --sync} [options] [package]\n"), myname); - printf(_(" %s {-U --upgrade} [options] <file>\n"), myname); + printf("%s: %s <%s> [...]\n", str_usg, myname, str_opr); + printf("%s:\n", str_opt); + printf(" %s {-h --help}\n", myname); + printf(" %s {-V --version}\n", myname); + printf(" %s {-A --add} [%s] <%s>\n", myname, str_opt, str_file); + printf(" %s {-Q --query} [%s] [%s]\n", myname, str_opt, str_pkg); + printf(" %s {-R --remove} [%s] <%s>\n", myname, str_opt, str_pkg); + printf(" %s {-S --sync} [%s] [%s]\n", myname, str_opt, str_pkg); + printf(" %s {-U --upgrade} [%s] <%s>\n", myname, str_opt, str_file); printf(_("\nuse '%s --help' with other options for more syntax\n"), myname); } else { if(op == PM_OP_ADD) { - printf(_("usage: %s {-A --add} [options] <file>\n"), myname); - printf(_("options:\n")); + printf("%s: %s {-A --add} [%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(_(" -d, --nodeps skip dependency checks\n")); printf(_(" -f, --force force install, overwrite conflicting files\n")); } else if(op == PM_OP_REMOVE) { - printf(_("usage: %s {-R --remove} [options] <package>\n"), myname); - printf(_("options:\n")); + 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 entry, 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")); } else if(op == PM_OP_UPGRADE) { - if(config->flags & PM_TRANS_FLAG_FRESHEN) { - printf(_("usage: %s {-F --freshen} [options] <file>\n"), myname); - } else { - printf(_("usage: %s {-U --upgrade} [options] <file>\n"), myname); - } - printf(_("options:\n")); + 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(_(" -d, --nodeps skip dependency checks\n")); printf(_(" -f, --force force install, overwrite conflicting files\n")); } else if(op == PM_OP_QUERY) { - printf(_("usage: %s {-Q --query} [options] [package]\n"), myname); - printf(_("options:\n")); + 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(_(" -e, --orphans list all packages installed as dependencies but no longer\n")); - printf(_(" required by any package\n")); + printf(_(" -d, --deps list all packages installed as dependencies\n")); + printf(_(" -e, --explicit list all packages explicitly installed\n")); printf(_(" -g, --groups view all members of a package group\n")); - printf(_(" -i, --info view package information\n")); + printf(_(" -i, --info view package information (-ii for backup files)\n")); printf(_(" -l, --list list the contents of the queried package\n")); printf(_(" -m, --foreign list installed packages not found in sync db(s)\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, --orphans list all packages not required by any package\n")); printf(_(" -u, --upgrades list all packages that can be upgraded\n")); + printf(_(" -q --quiet show less information for query and search\n")); } else if(op == PM_OP_SYNC) { - printf(_("usage: %s {-S --sync} [options] [package]\n"), myname); - printf(_("options:\n")); + 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(_(" -c, --clean remove old packages from cache directory (-cc for all)\n")); printf(_(" -d, --nodeps skip dependency checks\n")); printf(_(" -e, --dependsonly install dependencies only\n")); @@ -148,13 +129,17 @@ static void usage(int op, char *myname) printf(_(" -u, --sysupgrade upgrade all packages that are out of date\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 only install outdated or not yet installed 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(_(" -q --quiet show less information for query and search\n")); } 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(_(" --ask <number> pre-specify answers for questions (see manpage)\n")); printf(_(" --noprogressbar do not show a progress bar when downloading files\n")); - printf(_(" --noscriptlet do not execute the install scriptlet if there is any\n")); + printf(_(" --noscriptlet do not execute the install scriptlet if one exists\n")); printf(_(" -v, --verbose be verbose\n")); printf(_(" -r, --root <path> set an alternate installation root\n")); printf(_(" -b, --dbpath <path> set an alternate database location\n")); @@ -162,56 +147,148 @@ static void usage(int op, char *myname) } } -/* Version +/** Output pacman version and copyright. */ -static void version() +static void version(void) { printf("\n"); printf(" .--. Pacman v%s - libalpm v%s\n", PACKAGE_VERSION, LIB_VERSION); printf("/ _.-' .-. .-. .-. Copyright (C) 2002-2007 Judd Vinet <jvinet@zeroflux.org>\n"); printf("\\ '-. '-' '-' '-'\n"); - printf(" '--' \n"); - printf(_(" This program may be freely redistributed under\n")); - printf(_(" the terms of the GNU General Public License\n")); + printf(" '--'\n"); + printf(_(" This program may be freely redistributed under\n" + " the terms of the GNU General Public License\n")); printf("\n"); } +/** Sets up gettext localization. Safe to call multiple times. + */ +/* Inspired by the monotone function localize_monotone. */ +#if defined(ENABLE_NLS) +static void localize(void) +{ + static int init = 0; + if (!init) { + setlocale(LC_ALL, ""); + bindtextdomain(PACKAGE, LOCALEDIR); + textdomain(PACKAGE); + init = 1; + } +} +#endif + +/** Set user agent environment variable. + */ +static void setuseragent(void) +{ + const char *pacman = "Pacman/" PACKAGE_VERSION; + const char *libalpm = "libalpm/" LIB_VERSION; + char agent[101]; + struct utsname un; + + uname(&un); + snprintf(agent, 100, "%s (%s %s %s; %s) %s", pacman, un.sysname, + un.machine, un.release, setlocale(LC_MESSAGES, NULL), libalpm); + setenv("HTTP_USER_AGENT", agent, 0); +} + +/** Catches thrown signals. Performs necessary cleanup to ensure database is + * in a consistant state. + * @param signum the thrown signal + */ static void cleanup(int signum) { if(signum==SIGSEGV) { - fprintf(stderr, "Internal pacman error: Segmentation fault\n" - "Please submit a full bug report, with the given package if appropriate.\n"); + /* write a log message and write to stderr */ + pm_printf(PM_LOG_ERROR, "segmentation fault\n"); + pm_fprintf(stderr, PM_LOG_ERROR, "Internal pacman error: Segmentation fault.\n" + "Please submit a full bug report with --debug if appropriate.\n"); exit(signum); - } else if((signum == SIGINT) && (alpm_trans_release() == -1) - && (pm_errno == PM_ERR_TRANS_COMMITING)) { - return; - } - if(signum != 0) { - /* TODO why is this here? */ - fprintf(stderr, "\n"); + } else if((signum == SIGINT)) { + if(alpm_trans_interrupt() == 0) { + /* a transaction is being interrupted, don't exit pacman yet. */ + return; + } else { + /* no commiting transaction, we can release it now and then exit pacman */ + alpm_trans_release(); + } } /* free alpm library resources */ if(alpm_release() == -1) { - ERR(NL, "%s\n", alpm_strerror(pm_errno)); + pm_printf(PM_LOG_ERROR, alpm_strerrorlast()); } /* free memory */ FREELIST(pm_targets); - FREECONF(config); - - /* This fixes up any missing newlines (neednl) */ - MSG(NL, ""); + if(config) { + config_free(config); + config = NULL; + } exit(signum); } -/* Parse command-line arguments for each operation - * argc: argc - * argv: argv - * - * Returns: 0 on success, 1 on error +/** Sets all libalpm required paths in one go. Called after the command line and + * inital config file parsing. Once this is complete, we can see if any paths were + * defined. If a rootdir was defined and nothing else, we want all of our paths to + * live under the rootdir that was specified. Safe to call multiple times (will only + * do anything the first time). + */ +static void setlibpaths(void) +{ + static int init = 0; + if (!init) { + int ret = 0; + + pm_printf(PM_LOG_DEBUG, "setlibpaths() called\n"); + if(config->rootdir) { + char path[PATH_MAX]; + ret = alpm_option_set_root(config->rootdir); + if(ret != 0) { + pm_printf(PM_LOG_ERROR, _("problem setting rootdir '%s' (%s)\n"), + config->rootdir, alpm_strerrorlast()); + cleanup(ret); + } + if(!config->dbpath) { + snprintf(path, PATH_MAX, "%s%s", alpm_option_get_root(), DBPATH); + config->dbpath = strdup(path); + } + if(!config->logfile) { + snprintf(path, PATH_MAX, "%s%s", alpm_option_get_root(), LOGFILE); + ret = alpm_option_set_dbpath(path); + config->logfile = strdup(path); + } + } + if(config->dbpath) { + ret = alpm_option_set_dbpath(config->dbpath); + if(ret != 0) { + pm_printf(PM_LOG_ERROR, _("problem setting dbpath '%s' (%s)\n"), + config->dbpath, alpm_strerrorlast()); + cleanup(ret); + } + } + if(config->logfile) { + ret = alpm_option_set_logfile(config->logfile); + if(ret != 0) { + pm_printf(PM_LOG_ERROR, _("problem setting logfile '%s' (%s)\n"), + config->logfile, alpm_strerrorlast()); + cleanup(ret); + } + } + + /* add a default cachedir if one wasn't specified */ + if(alpm_option_get_cachedirs() == NULL) { + alpm_option_add_cachedir(CACHEDIR); + } + } +} + +/** Parse command-line arguments for each operation. + * @param argc argc + * @param argv argv + * @return 0 on success, 1 on error */ static int parseargs(int argc, char *argv[]) { @@ -220,7 +297,6 @@ static int parseargs(int argc, char *argv[]) static struct option opts[] = { {"add", no_argument, 0, 'A'}, - {"freshen", no_argument, 0, 'F'}, {"query", no_argument, 0, 'Q'}, {"remove", no_argument, 0, 'R'}, {"sync", no_argument, 0, 'S'}, @@ -232,22 +308,25 @@ static int parseargs(int argc, char *argv[]) {"changelog", no_argument, 0, 'c'}, {"clean", no_argument, 0, 'c'}, {"nodeps", no_argument, 0, 'd'}, + {"deps", no_argument, 0, 'd'}, {"dependsonly",no_argument, 0, 'e'}, - {"orphans", no_argument, 0, 'e'}, + {"explicit", no_argument, 0, 'e'}, {"force", no_argument, 0, 'f'}, {"groups", no_argument, 0, 'g'}, {"help", no_argument, 0, 'h'}, {"info", no_argument, 0, 'i'}, {"dbonly", no_argument, 0, 'k'}, {"list", no_argument, 0, 'l'}, - {"nosave", no_argument, 0, 'n'}, {"foreign", no_argument, 0, 'm'}, + {"nosave", no_argument, 0, 'n'}, {"owns", no_argument, 0, 'o'}, {"file", no_argument, 0, 'p'}, {"print-uris", no_argument, 0, 'p'}, + {"quiet", no_argument, 0, 'q'}, {"root", required_argument, 0, 'r'}, {"recursive", no_argument, 0, 's'}, {"search", no_argument, 0, 's'}, + {"orphans", no_argument, 0, 't'}, {"upgrades", no_argument, 0, 'u'}, {"sysupgrade", no_argument, 0, 'u'}, {"verbose", no_argument, 0, 'v'}, @@ -257,16 +336,19 @@ static int parseargs(int argc, char *argv[]) {"config", required_argument, 0, 1001}, {"ignore", required_argument, 0, 1002}, {"debug", optional_argument, 0, 1003}, - {"noprogressbar", no_argument, 0, 1004}, + {"noprogressbar", no_argument, 0, 1004}, {"noscriptlet", no_argument, 0, 1005}, - {"ask", required_argument, 0, 1006}, {"cachedir", required_argument, 0, 1007}, + {"asdeps", no_argument, 0, 1008}, + {"logfile", required_argument, 0, 1009}, + {"ignoregroup", required_argument, 0, 1010}, + {"needed", no_argument, 0, 1011}, {0, 0, 0, 0} }; - struct stat st; - unsigned short logmask; - while((opt = getopt_long(argc, argv, "ARUFQSTr:b:vkhscVfmnoldepiuwyg", opts, &option_index))) { + while((opt = getopt_long(argc, argv, "ARUFQSTr:b:vkhscVfmnoldepqituwygz", opts, &option_index))) { + alpm_list_t *list = NULL, *item = NULL; /* lists for splitting strings */ + if(opt < 0) { break; } @@ -277,51 +359,62 @@ static int parseargs(int argc, char *argv[]) if(config->configfile) { free(config->configfile); } - #if defined(__OpenBSD__) || defined(__APPLE__) - config->configfile = strdup(optarg); - #else config->configfile = strndup(optarg, PATH_MAX); - #endif break; - case 1002: alpm_option_add_ignorepkg(strdup(optarg)); break; + case 1002: + 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 1003: /* debug levels are made more 'human readable' than using a raw logmask - * here, we will ALWAYS set error and warning for now, though perhaps a + * here, error and warning are set in config_new, though perhaps a * --quiet option will remove these later */ - logmask = PM_LOG_ERROR | PM_LOG_WARNING; - if(optarg) { unsigned short debug = atoi(optarg); switch(debug) { - case 3: logmask |= PM_LOG_FUNCTION; /* fall through */ - case 2: logmask |= PM_LOG_DOWNLOAD; /*fall through */ - case 1: logmask |= PM_LOG_DEBUG; break; + case 2: + config->logmask |= PM_LOG_FUNCTION; /* fall through */ + case 1: + config->logmask |= PM_LOG_DEBUG; + break; default: - ERR(NL, _("'%s' is not a valid debug level"), optarg); + pm_printf(PM_LOG_ERROR, _("'%s' is not a valid debug level\n"), + optarg); return(1); } } else { - logmask |= PM_LOG_DEBUG; + config->logmask |= PM_LOG_DEBUG; } /* progress bars get wonky with debug on, shut them off */ config->noprogressbar = 1; - alpm_option_set_logmask(logmask); break; case 1004: config->noprogressbar = 1; break; case 1005: config->flags |= PM_TRANS_FLAG_NOSCRIPTLET; break; - case 1006: config->noask = 1; config->ask = atoi(optarg); break; case 1007: - if(stat(optarg, &st) == -1 || !S_ISDIR(st.st_mode)) { - ERR(NL, _("'%s' is not a valid cache directory\n"), optarg); + if(alpm_option_add_cachedir(optarg) != 0) { + pm_printf(PM_LOG_ERROR, _("problem adding cachedir '%s' (%s)\n"), + optarg, alpm_strerrorlast()); return(1); } - alpm_option_set_cachedir(optarg); break; - case 'A': config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_ADD); break; - case 'F': - config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_UPGRADE); - config->flags |= PM_TRANS_FLAG_FRESHEN; + case 1008: + config->flags |= PM_TRANS_FLAG_ALLDEPS; + break; + case 1009: + config->logfile = strdup(optarg); break; + case 1010: + 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 1011: config->flags |= PM_TRANS_FLAG_NEEDED; break; + case 'A': config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_ADD); 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; @@ -329,20 +422,19 @@ static int parseargs(int argc, char *argv[]) case 'U': config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_UPGRADE); break; case 'V': config->version = 1; break; case 'b': - if(stat(optarg, &st) == -1 || !S_ISDIR(st.st_mode)) { - ERR(NL, _("'%s' is not a valid db path\n"), optarg); - return(1); - } - alpm_option_set_dbpath(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->flags |= PM_TRANS_FLAG_NODEPS; break; + case 'd': + config->op_q_deps = 1; + config->flags |= PM_TRANS_FLAG_NODEPS; + break; case 'e': - config->op_q_orphans = 1; + config->op_q_explicit = 1; config->flags |= PM_TRANS_FLAG_DEPENDSONLY; break; case 'f': config->flags |= PM_TRANS_FLAG_FORCE; break; @@ -358,18 +450,20 @@ static int parseargs(int argc, char *argv[]) config->op_q_isfile = 1; config->flags |= PM_TRANS_FLAG_PRINTURIS; break; + case 'q': + config->quiet = 1; + break; case 'r': - if(stat(optarg, &st) == -1 || !S_ISDIR(st.st_mode)) { - ERR(NL, _("'%s' is not a valid root path\n"), optarg); - return(1); - } - alpm_option_set_root(optarg); + config->rootdir = strdup(optarg); break; case 's': config->op_s_search = 1; config->op_q_search = 1; config->flags |= PM_TRANS_FLAG_RECURSE; break; + case 't': + config->op_q_orphans = 1; + break; case 'u': config->op_s_upgrade = 1; config->op_q_upgrade = 1; @@ -387,12 +481,12 @@ static int parseargs(int argc, char *argv[]) } if(config->op == 0) { - ERR(NL, _("only one operation may be used at a time\n")); + pm_printf(PM_LOG_ERROR, _("only one operation may be used at a time\n")); return(1); } if(config->help) { - usage(config->op, basename(argv[0])); + usage(config->op, mbasename(argv[0])); return(2); } if(config->version) { @@ -409,143 +503,380 @@ static int parseargs(int argc, char *argv[]) return(0); } +/** 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. + * @param ptr a pointer to the start of the multiple options + * @param option the string (friendly) name of the option, used for messages + * @param optionfunc a function pointer to an alpm_option_add_* function + */ +static void setrepeatingoption(const char *ptr, const char *option, + void (*optionfunc)(const char*)) +{ + char *p = (char*)ptr; + char *q; + + while((q = strchr(p, ' '))) { + *q = '\0'; + (*optionfunc)(p); + pm_printf(PM_LOG_DEBUG, "config: %s: %s\n", option, p); + p = q; + p++; + } + (*optionfunc)(p); + pm_printf(PM_LOG_DEBUG, "config: %s: %s\n", option, p); +} + +/* The real parseconfig. Called with a null section argument by the publicly + * visible parseconfig so we can recall from within ourself on an include */ +static int _parseconfig(const char *file, const char *givensection, + pmdb_t * const givendb) +{ + FILE *fp = NULL; + char line[PATH_MAX+1]; + int linenum = 0; + char *ptr, *section = NULL; + pmdb_t *db = NULL; + + pm_printf(PM_LOG_DEBUG, "config: attempting to read file %s\n", file); + fp = fopen(file, "r"); + if(fp == NULL) { + pm_printf(PM_LOG_ERROR, _("config file %s could not be read.\n"), file); + return(1); + } + + /* if we are passed a section, use it as our starting point */ + if(givensection != NULL) { + section = strdup(givensection); + } + /* if we are passed a db, use it as our starting point */ + if(givendb != NULL) { + db = givendb; + } + + while(fgets(line, PATH_MAX, fp)) { + linenum++; + strtrim(line); + + /* ignore whole line and end of line comments */ + if(strlen(line) == 0 || line[0] == '#') { + continue; + } + if((ptr = strchr(line, '#'))) { + *ptr = '\0'; + } + + if(line[0] == '[' && line[strlen(line)-1] == ']') { + /* new config section, skip the '[' */ + ptr = line; + ptr++; + if(section) { + free(section); + } + section = strdup(ptr); + section[strlen(section)-1] = '\0'; + pm_printf(PM_LOG_DEBUG, "config: new section '%s'\n", section); + if(!strlen(section)) { + pm_printf(PM_LOG_ERROR, _("config file %s, line %d: bad section name.\n"), + file, linenum); + return(1); + } + /* if we are not looking at the options section, register a db and also + * ensure we have set all of our library paths as the library is too stupid + * at the moment to do lazy opening of the databases */ + if(strcmp(section, "options") != 0) { + setlibpaths(); + db = alpm_db_register_sync(section); + } + } else { + /* directive */ + char *key, *upperkey; + /* strsep modifies the 'line' string: 'key \0 ptr' */ + key = line; + ptr = line; + strsep(&ptr, "="); + strtrim(key); + strtrim(ptr); + + if(key == NULL) { + pm_printf(PM_LOG_ERROR, _("config file %s, line %d: syntax error in config file- missing key.\n"), + file, linenum); + return(1); + } + /* For each directive, compare to the uppercase and camelcase string. + * This prevents issues with certain locales where characters don't + * follow the toupper() rules we may expect, e.g. tr_TR where i != I. + */ + upperkey = strtoupper(strdup(key)); + if(section == NULL && (strcmp(key, "Include") == 0 || strcmp(upperkey, "INCLUDE") == 0)) { + pm_printf(PM_LOG_ERROR, _("config file %s, line %d: 'Include' directive must belong to a section.\n"), + file, linenum); + return(1); + } + if(ptr == NULL && strcmp(section, "options") == 0) { + /* directives without settings, all in [options] */ + if(strcmp(key, "NoPassiveFTP") == 0 || strcmp(upperkey, "NOPASSIVEFTP") == 0) { + alpm_option_set_nopassiveftp(1); + pm_printf(PM_LOG_DEBUG, "config: nopassiveftp\n"); + } else if(strcmp(key, "UseSyslog") == 0 || strcmp(upperkey, "USESYSLOG") == 0) { + alpm_option_set_usesyslog(1); + pm_printf(PM_LOG_DEBUG, "config: usesyslog\n"); + } else if(strcmp(key, "ILoveCandy") == 0 || strcmp(upperkey, "ILOVECANDY") == 0) { + config->chomp = 1; + pm_printf(PM_LOG_DEBUG, "config: chomp\n"); + } else if(strcmp(key, "UseColor") == 0 || strcmp(upperkey, "USECOLOR") == 0) { + config->usecolor = 1; + pm_printf(PM_LOG_DEBUG, "config: usecolor\n"); + } else if(strcmp(key, "ShowSize") == 0 || strcmp(upperkey, "SHOWSIZE") == 0) { + config->showsize = 1; + pm_printf(PM_LOG_DEBUG, "config: showsize\n"); + } else if(strcmp(key, "UseDelta") == 0 || strcmp(upperkey, "USEDELTA") == 0) { + alpm_option_set_usedelta(1); + pm_printf(PM_LOG_DEBUG, "config: usedelta\n"); + } else if(strcmp(key, "TotalDownload") == 0 || strcmp(upperkey, "TOTALDOWNLOAD") == 0) { + config->totaldownload = 1; + pm_printf(PM_LOG_DEBUG, "config: totaldownload\n"); + } else { + pm_printf(PM_LOG_ERROR, _("config file %s, line %d: directive '%s' not recognized.\n"), + file, linenum, key); + return(1); + } + } else { + /* directives with settings */ + if(strcmp(key, "Include") == 0 || strcmp(upperkey, "INCLUDE") == 0) { + int ret; + pm_printf(PM_LOG_DEBUG, "config: including %s\n", ptr); + ret = _parseconfig(ptr, section, db); + if(ret != 0) { + return(ret); + } + } else if(strcmp(section, "options") == 0) { + if(strcmp(key, "NoUpgrade") == 0 + || strcmp(upperkey, "NOUPGRADE") == 0) { + setrepeatingoption(ptr, "NoUpgrade", alpm_option_add_noupgrade); + } else if(strcmp(key, "NoExtract") == 0 + || strcmp(upperkey, "NOEXTRACT") == 0) { + setrepeatingoption(ptr, "NoExtract", alpm_option_add_noextract); + } else if(strcmp(key, "IgnorePkg") == 0 + || strcmp(upperkey, "IGNOREPKG") == 0) { + setrepeatingoption(ptr, "IgnorePkg", alpm_option_add_ignorepkg); + } else if(strcmp(key, "IgnoreGroup") == 0 + || strcmp(upperkey, "IGNOREGROUP") == 0) { + setrepeatingoption(ptr, "IgnoreGroup", alpm_option_add_ignoregrp); + } else if(strcmp(key, "HoldPkg") == 0 + || strcmp(upperkey, "HOLDPKG") == 0) { + setrepeatingoption(ptr, "HoldPkg", alpm_option_add_holdpkg); + } else if(strcmp(key, "DBPath") == 0 || strcmp(upperkey, "DBPATH") == 0) { + /* don't overwrite a path specified on the command line */ + if(!config->dbpath) { + config->dbpath = strdup(ptr); + pm_printf(PM_LOG_DEBUG, "config: dbpath: %s\n", ptr); + } + } else if(strcmp(key, "CacheDir") == 0 || strcmp(upperkey, "CACHEDIR") == 0) { + if(alpm_option_add_cachedir(ptr) != 0) { + pm_printf(PM_LOG_ERROR, _("problem adding cachedir '%s' (%s)\n"), + ptr, alpm_strerrorlast()); + return(1); + } + pm_printf(PM_LOG_DEBUG, "config: cachedir: %s\n", ptr); + } else if(strcmp(key, "RootDir") == 0 || strcmp(upperkey, "ROOTDIR") == 0) { + /* don't overwrite a path specified on the command line */ + if(!config->rootdir) { + config->rootdir = strdup(ptr); + pm_printf(PM_LOG_DEBUG, "config: rootdir: %s\n", ptr); + } + } else if (strcmp(key, "LogFile") == 0 || strcmp(upperkey, "LOGFILE") == 0) { + if(!config->logfile) { + config->logfile = strdup(ptr); + pm_printf(PM_LOG_DEBUG, "config: logfile: %s\n", ptr); + } + } else if (strcmp(key, "XferCommand") == 0 || strcmp(upperkey, "XFERCOMMAND") == 0) { + alpm_option_set_xfercommand(ptr); + pm_printf(PM_LOG_DEBUG, "config: xfercommand: %s\n", ptr); + } else if (strcmp(key, "UpgradeDelay") == 0 || strcmp(upperkey, "UPGRADEDELAY") == 0) { + /* The config value is in days, we use seconds */ + time_t ud = atol(ptr) * 60 * 60 *24; + alpm_option_set_upgradedelay(ud); + pm_printf(PM_LOG_DEBUG, "config: upgradedelay: %d\n", (int)ud); + } else { + pm_printf(PM_LOG_ERROR, _("config file %s, line %d: directive '%s' not recognized.\n"), + file, linenum, key); + return(1); + } + } else if(strcmp(key, "Server") == 0 || strcmp(upperkey, "SERVER") == 0) { + /* let's attempt a replacement for the current repo */ + char *server = strreplace(ptr, "$repo", section); + + if(alpm_db_setserver(db, server) != 0) { + /* pm_errno is set by alpm_db_setserver */ + return(1); + } + + free(server); + } else { + pm_printf(PM_LOG_ERROR, _("config file %s, line %d: directive '%s' not recognized.\n"), + file, linenum, key); + return(1); + } + } + free(upperkey); + } + } + fclose(fp); + if(section){ + free(section); + } + + /* call setlibpaths here to ensure we have called it at least once */ + setlibpaths(); + pm_printf(PM_LOG_DEBUG, "config: finished parsing %s\n", file); + return(0); +} + +/** Parse a configuration file. + * @param file path to the config file. + * @return 0 on success, non-zero on error + */ +int parseconfig(const char *file) +{ + /* call the real parseconfig function with a null section & db argument */ + return(_parseconfig(file, NULL, NULL)); +} + +/** Main function. + * @param argc argc + * @param argv argv + * @return A return code indicating success, failure, etc. + */ int main(int argc, char *argv[]) { int ret = 0; - char *lang = NULL; -#ifndef CYGWIN - uid_t myuid; +#if defined(HAVE_GETEUID) + /* geteuid undefined in CYGWIN */ + uid_t myuid = geteuid(); #endif -#if defined(PACMAN_DEBUG) && !defined(CYGWIN) && !defined(BSD) +#if defined(PACMAN_DEBUG) && defined(HAVE_MCHECK_H) /*setenv("MALLOC_TRACE","pacman.mtrace", 0);*/ mtrace(); #endif + /* set signal handlers */ signal(SIGINT, cleanup); signal(SIGTERM, cleanup); signal(SIGSEGV, cleanup); /* i18n init */ - lang = setlocale(LC_ALL, ""); - /* if setlocale returns null, the locale was invalid- override it */ - if (lang == NULL) { - lang = "C"; - setlocale(LC_ALL, "C"); - setenv("LC_ALL", lang, 1); - MSG(NL, _("warning: current locale is invalid; using default \"C\" locale")); - } +#if defined(ENABLE_NLS) + localize(); +#endif - /* workaround for tr_TR */ - if(lang && !strcmp(lang, "tr_TR")) { - setlocale(LC_CTYPE, "C"); - } - bindtextdomain("pacman", "/usr/share/locale"); - textdomain("pacman"); + /* set user agent for downloading */ + setuseragent(); /* init config data */ config = config_new(); - config->op = PM_OP_MAIN; + /* disable progressbar if the output is redirected */ if(!isatty(1)) { config->noprogressbar = 1; } - /* initialize pm library */ + /* initialize library */ if(alpm_initialize() == -1) { - ERR(NL, _("failed to initialize alpm library (%s)\n"), alpm_strerror(pm_errno)); - cleanup(1); + pm_printf(PM_LOG_ERROR, _("failed to initialize alpm library (%s)\n"), + alpm_strerrorlast()); + cleanup(EXIT_FAILURE); } + /* Setup logging as soon as possible, to print out maximum debugging info */ + alpm_option_set_logcb(cb_log); + alpm_option_set_dlcb(cb_dl_progress); + /* define paths to reasonable defaults */ + alpm_option_set_root(ROOTDIR); + alpm_option_set_dbpath(DBPATH); + alpm_option_set_logfile(LOGFILE); + + /* Priority of options: + * 1. command line + * 2. config file + * 3. compiled-in defaults + * However, we have to parse the command line first because a config file + * location can be specified here, so we need to make sure we prefer these + * options over the config file coming second. + */ + /* parse the command line */ ret = parseargs(argc, argv); if(ret != 0) { - config_free(config); - exit(ret); + cleanup(ret); } -#ifndef CYGWIN - /* see if we're root or not */ - myuid = geteuid(); -#ifndef FAKEROOT - if(!myuid && getenv("FAKEROOTKEY")) { - /* fakeroot doesn't count, we're non-root */ - myuid = 99; + /* parse the config file */ + ret = parseconfig(config->configfile); + if(ret != 0) { + cleanup(ret); } -#endif +#if defined(HAVE_GETEUID) /* check if we have sufficient permission for the requested operation */ - if(myuid > 0) { - if(config->op != PM_OP_MAIN && config->op != PM_OP_QUERY && config->op != PM_OP_DEPTEST) { - if((config->op == PM_OP_SYNC && !config->op_s_sync && - (config->op_s_search || config->group || config->op_q_list || config->op_q_info - || config->flags & PM_TRANS_FLAG_PRINTURIS)) - || (config->op == PM_OP_DEPTEST && config->op_d_resolve) - || (strcmp(alpm_option_get_root(), PM_ROOT) != 0)) { - /* special case: PM_OP_SYNC can be used w/ config->op_s_search by any user */ - /* special case: ignore root user check if -r is specified, fall back on - * normal FS checking */ - } else { - ERR(NL, _("you cannot perform this operation unless you are root.\n")); - config_free(config); - exit(EXIT_FAILURE); - } - } + if(myuid > 0 && needs_transaction()) { + pm_printf(PM_LOG_ERROR, _("you cannot perform this operation unless you are root.\n")); + cleanup(EXIT_FAILURE); } #endif - /* Setup logging as soon as possible, to print out maximum debugging info */ - alpm_option_set_logcb(cb_log); - - if(config->configfile == NULL) { - config->configfile = strdup(PACCONF); - } - - if(alpm_parse_config(config->configfile, NULL, "") != 0) { - ERR(NL, _("failed to parse config (%s)\n"), alpm_strerror(pm_errno)); - cleanup(1); - } - - /* set library parameters */ - alpm_option_set_dlcb(log_progress); - if(config->verbose > 0) { - printf("Root : %s\n", alpm_option_get_root()); - printf("DBPath : %s\n", alpm_option_get_dbpath()); - printf("CacheDir : %s\n", alpm_option_get_cachedir()); - list_display(_("Targets :"), pm_targets); + alpm_list_t *i; + printf("Root : %s\n", alpm_option_get_root()); + printf("Conf File : %s\n", config->configfile); + printf("DB Path : %s\n", alpm_option_get_dbpath()); + printf("Cache Dirs: "); + for(i = alpm_option_get_cachedirs(); i; i = alpm_list_next(i)) { + printf("%s ", (char*)alpm_list_getdata(i)); + } + printf("\n"); + printf("Lock File : %s\n", alpm_option_get_lockfile()); + printf("Log File : %s\n", alpm_option_get_logfile()); + list_display("Targets :", pm_targets); } /* Opening local database */ - db_local = alpm_db_register("local"); + db_local = alpm_db_register_local(); if(db_local == NULL) { - ERR(NL, _("could not register 'local' database (%s)\n"), alpm_strerror(pm_errno)); - cleanup(1); - } - - if(alpm_list_count(pm_targets) == 0 && !(config->op == PM_OP_QUERY || (config->op == PM_OP_SYNC - && (config->op_s_sync || config->op_s_upgrade || config->op_s_clean || config->group - || config->op_q_list)))) { - ERR(NL, _("no targets specified (use -h for help)\n")); - cleanup(1); + pm_printf(PM_LOG_ERROR, _("could not register 'local' database (%s)\n"), + alpm_strerrorlast()); + cleanup(EXIT_FAILURE); } /* start the requested operation */ switch(config->op) { - case PM_OP_ADD: ret = pacman_add(pm_targets); break; - case PM_OP_REMOVE: ret = pacman_remove(pm_targets); break; - case PM_OP_UPGRADE: ret = pacman_upgrade(pm_targets); break; - case PM_OP_QUERY: ret = pacman_query(pm_targets); break; - case PM_OP_SYNC: ret = pacman_sync(pm_targets); break; - case PM_OP_DEPTEST: ret = pacman_deptest(pm_targets); break; + case PM_OP_ADD: + ret = pacman_add(pm_targets); + break; + case PM_OP_REMOVE: + ret = pacman_remove(pm_targets); + break; + case PM_OP_UPGRADE: + ret = pacman_upgrade(pm_targets); + break; + case PM_OP_QUERY: + ret = pacman_query(pm_targets); + break; + case PM_OP_SYNC: + ret = pacman_sync(pm_targets); + break; + case PM_OP_DEPTEST: + ret = pacman_deptest(pm_targets); + break; default: - ERR(NL, _("no operation specified (use -h for help)\n")); - ret = 1; + pm_printf(PM_LOG_ERROR, _("no operation specified (use -h for help)\n")); + ret = EXIT_FAILURE; } cleanup(ret); /* not reached */ - return(0); + return(EXIT_SUCCESS); } /* vim: set ts=2 sw=2 noet: */ |