libalpm
Arch Linux Package Manager Library
pacman.c
Go to the documentation of this file.
00001 /*
00002  *  pacman.c
00003  *
00004  *  Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org>
00005  *  Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
00006  *
00007  *  This program is free software; you can redistribute it and/or modify
00008  *  it under the terms of the GNU General Public License as published by
00009  *  the Free Software Foundation; either version 2 of the License, or
00010  *  (at your option) any later version.
00011  *
00012  *  This program is distributed in the hope that it will be useful,
00013  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *  GNU General Public License for more details.
00016  *
00017  *  You should have received a copy of the GNU General Public License
00018  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00019  */
00020 
00021 /* special handling of package version for GIT */
00022 #if defined(GIT_VERSION)
00023 #undef PACKAGE_VERSION
00024 #define PACKAGE_VERSION GIT_VERSION
00025 #endif
00026 
00027 #include <ctype.h> /* isspace */
00028 #include <stdlib.h> /* atoi */
00029 #include <stdio.h>
00030 #include <ctype.h> /* isspace */
00031 #include <limits.h>
00032 #include <getopt.h>
00033 #include <string.h>
00034 #include <signal.h>
00035 #include <unistd.h>
00036 #include <sys/types.h>
00037 #include <sys/utsname.h> /* uname */
00038 #include <locale.h> /* setlocale */
00039 #include <errno.h>
00040 
00041 /* alpm */
00042 #include <alpm.h>
00043 #include <alpm_list.h>
00044 
00045 /* pacman */
00046 #include "pacman.h"
00047 #include "util.h"
00048 #include "conf.h"
00049 
00050 /* list of targets specified on command line */
00051 static alpm_list_t *pm_targets;
00052 
00053 /* Used to sort the options in --help */
00054 static int options_cmp(const void *p1, const void *p2)
00055 {
00056     const char *s1 = p1;
00057     const char *s2 = p2;
00058 
00059     if(s1 == s2) return 0;
00060     if(!s1) return -1;
00061     if(!s2) return 1;
00062     /* First skip all spaces in both strings */
00063     while(isspace((unsigned char)*s1)) {
00064         s1++;
00065     }
00066     while(isspace((unsigned char)*s2)) {
00067         s2++;
00068     }
00069     /* If we compare a long option (--abcd) and a short one (-a),
00070      * the short one always wins */
00071     if(*s1 == '-' && *s2 == '-') {
00072         s1++;
00073         s2++;
00074         if(*s1 == '-' && *s2 == '-') {
00075             /* two long -> strcmp */
00076             s1++;
00077             s2++;
00078         } else if(*s2 == '-') {
00079             /* s1 short, s2 long */
00080             return -1;
00081         } else if(*s1 == '-') {
00082             /* s1 long, s2 short */
00083             return 1;
00084         }
00085         /* two short -> strcmp */
00086     }
00087 
00088     return strcmp(s1, s2);
00089 }
00090 
00091 /** Display usage/syntax for the specified operation.
00092  * @param op     the operation code requested
00093  * @param myname basename(argv[0])
00094  */
00095 static void usage(int op, const char * const myname)
00096 {
00097 #define addlist(s) (list = alpm_list_add(list, s))
00098     alpm_list_t *list = NULL, *i;
00099     /* prefetch some strings for usage below, which moves a lot of calls
00100      * out of gettext. */
00101     char const * const str_opt = _("options");
00102     char const * const str_file = _("file(s)");
00103     char const * const str_pkg = _("package(s)");
00104     char const * const str_usg = _("usage");
00105     char const * const str_opr = _("operation");
00106 
00107     /* please limit your strings to 80 characters in width */
00108     if(op == PM_OP_MAIN) {
00109         printf("%s:  %s <%s> [...]\n", str_usg, myname, str_opr);
00110         printf(_("operations:\n"));
00111         printf("    %s {-h --help}\n", myname);
00112         printf("    %s {-V --version}\n", myname);
00113         printf("    %s {-D --database} <%s> <%s>\n", myname, str_opt, str_pkg);
00114         printf("    %s {-Q --query}    [%s] [%s]\n", myname, str_opt, str_pkg);
00115         printf("    %s {-R --remove}   [%s] <%s>\n", myname, str_opt, str_pkg);
00116         printf("    %s {-S --sync}     [%s] [%s]\n", myname, str_opt, str_pkg);
00117         printf("    %s {-T --deptest}  [%s] [%s]\n", myname, str_opt, str_pkg);
00118         printf("    %s {-U --upgrade}  [%s] <%s>\n", myname, str_opt, str_file);
00119         printf(_("\nuse '%s {-h --help}' with an operation for available options\n"),
00120                 myname);
00121     } else {
00122         if(op == PM_OP_REMOVE) {
00123             printf("%s:  %s {-R --remove} [%s] <%s>\n", str_usg, myname, str_opt, str_pkg);
00124             printf("%s:\n", str_opt);
00125             addlist(_("  -c, --cascade        remove packages and all packages that depend on them\n"));
00126             addlist(_("  -n, --nosave         remove configuration files\n"));
00127             addlist(_("  -s, --recursive      remove unnecessary dependencies\n"
00128                       "                       (-ss includes explicitly installed dependencies)\n"));
00129             addlist(_("  -u, --unneeded       remove unneeded packages\n"));
00130         } else if(op == PM_OP_UPGRADE) {
00131             printf("%s:  %s {-U --upgrade} [%s] <%s>\n", str_usg, myname, str_opt, str_file);
00132             addlist(_("      --needed         do not reinstall up to date packages\n"));
00133             addlist(_("      --recursive      reinstall all dependencies of target packages\n"));
00134             printf("%s:\n", str_opt);
00135         } else if(op == PM_OP_QUERY) {
00136             printf("%s:  %s {-Q --query} [%s] [%s]\n", str_usg, myname, str_opt, str_pkg);
00137             printf("%s:\n", str_opt);
00138             addlist(_("  -c, --changelog      view the changelog of a package\n"));
00139             addlist(_("  -d, --deps           list packages installed as dependencies [filter]\n"));
00140             addlist(_("  -e, --explicit       list packages explicitly installed [filter]\n"));
00141             addlist(_("  -g, --groups         view all members of a package group\n"));
00142             addlist(_("  -i, --info           view package information (-ii for backup files)\n"));
00143             addlist(_("  -k, --check          check that the files owned by the package(s) are present\n"));
00144             addlist(_("  -l, --list           list the contents of the queried package\n"));
00145             addlist(_("  -m, --foreign        list installed packages not found in sync db(s) [filter]\n"));
00146             addlist(_("  -o, --owns <file>    query the package that owns <file>\n"));
00147             addlist(_("  -p, --file <package> query a package file instead of the database\n"));
00148             addlist(_("  -q, --quiet          show less information for query and search\n"));
00149             addlist(_("  -s, --search <regex> search locally-installed packages for matching strings\n"));
00150             addlist(_("  -t, --unrequired     list packages not required by any package [filter]\n"));
00151             addlist(_("  -u, --upgrades       list outdated packages [filter]\n"));
00152         } else if(op == PM_OP_SYNC) {
00153             printf("%s:  %s {-S --sync} [%s] [%s]\n", str_usg, myname, str_opt, str_pkg);
00154             printf("%s:\n", str_opt);
00155             addlist(_("  -c, --clean          remove old packages from cache directory (-cc for all)\n"));
00156             addlist(_("  -g, --groups         view all members of a package group\n"));
00157             addlist(_("  -i, --info           view package information\n"));
00158             addlist(_("  -l, --list <repo>    view a list of packages in a repo\n"));
00159             addlist(_("  -q, --quiet          show less information for query and search\n"));
00160             addlist(_("  -s, --search <regex> search remote repositories for matching strings\n"));
00161             addlist(_("  -u, --sysupgrade     upgrade installed packages (-uu allows downgrade)\n"));
00162             addlist(_("  -w, --downloadonly   download packages but do not install/upgrade anything\n"));
00163             addlist(_("  -y, --refresh        download fresh package databases from the server\n"));
00164             addlist(_("      --needed         do not reinstall up to date packages\n"));
00165             addlist(_("      --recursive      reinstall all dependencies of target packages\n"));
00166         } else if(op == PM_OP_DATABASE) {
00167             printf("%s:  %s {-D --database} <%s> <%s>\n", str_usg, myname, str_opt, str_pkg);
00168             printf("%s:\n", str_opt);
00169             addlist(_("      --asdeps         mark packages as non-explicitly installed\n"));
00170             addlist(_("      --asexplicit     mark packages as explicitly installed\n"));
00171         } else if(op == PM_OP_DEPTEST) {
00172             printf("%s:  %s {-T --deptest} [%s] [%s]\n", str_usg, myname, str_opt, str_pkg);
00173             printf("%s:\n", str_opt);
00174         }
00175         switch(op) {
00176             case PM_OP_SYNC:
00177             case PM_OP_UPGRADE:
00178                 addlist(_("      --force          force install, overwrite conflicting files\n"));
00179                 addlist(_("      --asdeps         install packages as non-explicitly installed\n"));
00180                 addlist(_("      --asexplicit     install packages as explicitly installed\n"));
00181                 addlist(_("      --ignore <pkg>   ignore a package upgrade (can be used more than once)\n"));
00182                 addlist(_("      --ignoregroup <grp>\n"
00183                           "                       ignore a group upgrade (can be used more than once)\n"));
00184                 /* pass through */
00185             case PM_OP_REMOVE:
00186                 addlist(_("  -d, --nodeps         skip dependency version checks (-dd to skip all checks)\n"));
00187                 addlist(_("      --dbonly         only modify database entries, not package files\n"));
00188                 addlist(_("      --noprogressbar  do not show a progress bar when downloading files\n"));
00189                 addlist(_("      --noscriptlet    do not execute the install scriptlet if one exists\n"));
00190                 addlist(_("  -p, --print          print the targets instead of performing the operation\n"));
00191                 addlist(_("      --print-format <string>\n"
00192                           "                       specify how the targets should be printed\n"));
00193                 break;
00194         }
00195 
00196         addlist(_("  -b, --dbpath <path>  set an alternate database location\n"));
00197         addlist(_("  -r, --root <path>    set an alternate installation root\n"));
00198         addlist(_("  -v, --verbose        be verbose\n"));
00199         addlist(_("      --arch <arch>    set an alternate architecture\n"));
00200         addlist(_("      --cachedir <dir> set an alternate package cache location\n"));
00201         addlist(_("      --config <path>  set an alternate configuration file\n"));
00202         addlist(_("      --debug          display debug messages\n"));
00203         addlist(_("      --gpgdir <path>  set an alternate home directory for GnuPG\n"));
00204         addlist(_("      --logfile <path> set an alternate log file\n"));
00205         addlist(_("      --noconfirm      do not ask for any confirmation\n"));
00206     }
00207     list = alpm_list_msort(list, alpm_list_count(list), options_cmp);
00208     for(i = list; i; i = alpm_list_next(i)) {
00209         fputs((const char *)i->data, stdout);
00210     }
00211     alpm_list_free(list);
00212 #undef addlist
00213 }
00214 
00215 /** Output pacman version and copyright.
00216  */
00217 static void version(void)
00218 {
00219     printf("\n");
00220     printf(" .--.                  Pacman v%s - libalpm v%s\n", PACKAGE_VERSION, alpm_version());
00221     printf("/ _.-' .-.  .-.  .-.   Copyright (C) 2006-2011 Pacman Development Team\n");
00222     printf("\\  '-. '-'  '-'  '-'   Copyright (C) 2002-2006 Judd Vinet\n");
00223     printf(" '--'\n");
00224     printf(_("                       This program may be freely redistributed under\n"
00225              "                       the terms of the GNU General Public License.\n"));
00226     printf("\n");
00227 }
00228 
00229 /** Sets up gettext localization. Safe to call multiple times.
00230  */
00231 /* Inspired by the monotone function localize_monotone. */
00232 #if defined(ENABLE_NLS)
00233 static void localize(void)
00234 {
00235     static int init = 0;
00236     if(!init) {
00237         setlocale(LC_ALL, "");
00238         bindtextdomain(PACKAGE, LOCALEDIR);
00239         textdomain(PACKAGE);
00240         init = 1;
00241     }
00242 }
00243 #endif
00244 
00245 /** Set user agent environment variable.
00246  */
00247 static void setuseragent(void)
00248 {
00249     char agent[101];
00250     struct utsname un;
00251 
00252     uname(&un);
00253     snprintf(agent, 100, "pacman/%s (%s %s) libalpm/%s",
00254             PACKAGE_VERSION, un.sysname, un.machine, alpm_version());
00255     setenv("HTTP_USER_AGENT", agent, 0);
00256 }
00257 
00258 /** Free the resources.
00259  *
00260  * @param ret the return value
00261  */
00262 static void cleanup(int ret) {
00263     /* free alpm library resources */
00264     if(config->handle && alpm_release(config->handle) == -1) {
00265         pm_printf(ALPM_LOG_ERROR, "error releasing alpm library\n");
00266     }
00267 
00268     /* free memory */
00269     FREELIST(pm_targets);
00270     if(config) {
00271         config_free(config);
00272         config = NULL;
00273     }
00274 
00275     exit(ret);
00276 }
00277 
00278 /** Write function that correctly handles EINTR.
00279  */
00280 static ssize_t xwrite(int fd, const void *buf, size_t count)
00281 {
00282     ssize_t ret;
00283     do {
00284         ret = write(fd, buf, count);
00285     } while(ret == -1 && errno == EINTR);
00286     return ret;
00287 }
00288 
00289 /** Catches thrown signals. Performs necessary cleanup to ensure database is
00290  * in a consistant state.
00291  * @param signum the thrown signal
00292  */
00293 static void handler(int signum)
00294 {
00295     int out = fileno(stdout);
00296     int err = fileno(stderr);
00297     const char *msg;
00298     if(signum == SIGSEGV) {
00299         msg = "\nerror: segmentation fault\n"
00300             "Please submit a full bug report with --debug if appropriate.\n";
00301         xwrite(err, msg, strlen(msg));
00302         exit(signum);
00303     } else if(signum == SIGINT || signum == SIGHUP) {
00304         if(signum == SIGINT) {
00305             msg = "\nInterrupt signal received\n";
00306         } else {
00307             msg = "\nHangup signal received\n";
00308         }
00309         xwrite(err, msg, strlen(msg));
00310         if(alpm_trans_interrupt(config->handle) == 0) {
00311             /* a transaction is being interrupted, don't exit pacman yet. */
00312             return;
00313         }
00314     }
00315     /* SIGINT: no commiting transaction, release it now and then exit pacman
00316      * SIGHUP, SIGTERM: release no matter what */
00317     alpm_trans_release(config->handle);
00318     /* output a newline to be sure we clear any line we may be on */
00319     xwrite(out, "\n", 1);
00320     cleanup(128 + signum);
00321 }
00322 
00323 #define check_optarg() if(!optarg) { return 1; }
00324 
00325 static int parsearg_util_addlist(alpm_list_t **list)
00326 {
00327     alpm_list_t *split, *item;
00328 
00329     check_optarg();
00330     split = strsplit(optarg, ',');
00331     for(item = split; item; item = alpm_list_next(item)) {
00332         *list = alpm_list_add(*list, item->data);
00333     }
00334     alpm_list_free(split);
00335     return 0;
00336 }
00337 
00338 /** Helper function for parsing operation from command-line arguments.
00339  * @param opt Keycode returned by getopt_long
00340  * @param dryrun If nonzero, application state is NOT changed
00341  * @return 0 if opt was handled, 1 if it was not handled
00342  */
00343 static int parsearg_op(int opt, int dryrun)
00344 {
00345     switch(opt) {
00346         /* operations */
00347         case 'D':
00348             if(dryrun) break;
00349             config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_DATABASE); break;
00350         case 'Q':
00351             if(dryrun) break;
00352             config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_QUERY); break;
00353         case 'R':
00354             if(dryrun) break;
00355             config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_REMOVE); break;
00356         case 'S':
00357             if(dryrun) break;
00358             config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_SYNC); break;
00359         case 'T':
00360             if(dryrun) break;
00361             config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_DEPTEST); break;
00362         case 'U':
00363             if(dryrun) break;
00364             config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_UPGRADE); break;
00365         case 'V':
00366             if(dryrun) break;
00367             config->version = 1; break;
00368         case 'h':
00369             if(dryrun) break;
00370             config->help = 1; break;
00371         default:
00372             return 1;
00373     }
00374     return 0;
00375 }
00376 
00377 /** Helper functions for parsing command-line arguments.
00378  * @param opt Keycode returned by getopt_long
00379  * @return 0 on success, 1 on failure
00380  */
00381 static int parsearg_global(int opt)
00382 {
00383     switch(opt) {
00384         case OP_ARCH:
00385             check_optarg();
00386             config_set_arch(optarg);
00387             break;
00388         case OP_ASK:
00389             check_optarg();
00390             config->noask = 1;
00391             config->ask = (unsigned int)atoi(optarg);
00392             break;
00393         case OP_CACHEDIR:
00394             check_optarg();
00395             config->cachedirs = alpm_list_add(config->cachedirs, strdup(optarg));
00396             break;
00397         case OP_CONFIG:
00398             check_optarg();
00399             if(config->configfile) {
00400                 free(config->configfile);
00401             }
00402             config->configfile = strndup(optarg, PATH_MAX);
00403             break;
00404         case OP_DEBUG:
00405             /* debug levels are made more 'human readable' than using a raw logmask
00406              * here, error and warning are set in config_new, though perhaps a
00407              * --quiet option will remove these later */
00408             if(optarg) {
00409                 unsigned short debug = (unsigned short)atoi(optarg);
00410                 switch(debug) {
00411                     case 2:
00412                         config->logmask |= ALPM_LOG_FUNCTION; /* fall through */
00413                     case 1:
00414                         config->logmask |= ALPM_LOG_DEBUG;
00415                         break;
00416                     default:
00417                         pm_printf(ALPM_LOG_ERROR, _("'%s' is not a valid debug level\n"),
00418                                 optarg);
00419                         return 1;
00420                 }
00421             } else {
00422                 config->logmask |= ALPM_LOG_DEBUG;
00423             }
00424             /* progress bars get wonky with debug on, shut them off */
00425             config->noprogressbar = 1;
00426             break;
00427         case OP_GPGDIR:
00428             config->gpgdir = strdup(optarg);
00429             break;
00430         case OP_LOGFILE:
00431             check_optarg();
00432             config->logfile = strndup(optarg, PATH_MAX);
00433             break;
00434         case OP_NOCONFIRM: config->noconfirm = 1; break;
00435         case 'b':
00436             check_optarg();
00437             config->dbpath = strdup(optarg);
00438             break;
00439         case 'r': check_optarg(); config->rootdir = strdup(optarg); break;
00440         case 'v': (config->verbose)++; break;
00441         default: return 1;
00442     }
00443     return 0;
00444 }
00445 
00446 static int parsearg_database(int opt)
00447 {
00448     switch(opt) {
00449         case OP_ASDEPS: config->flags |= ALPM_TRANS_FLAG_ALLDEPS; break;
00450         case OP_ASEXPLICIT: config->flags |= ALPM_TRANS_FLAG_ALLEXPLICIT; break;
00451         default: return 1;
00452     }
00453     return 0;
00454 }
00455 
00456 static int parsearg_query(int opt)
00457 {
00458     switch(opt) {
00459         case 'c': config->op_q_changelog = 1; break;
00460         case 'd': config->op_q_deps = 1; break;
00461         case 'e': config->op_q_explicit = 1; break;
00462         case 'g': (config->group)++; break;
00463         case 'i': (config->op_q_info)++; break;
00464         case 'k': config->op_q_check = 1; break;
00465         case 'l': config->op_q_list = 1; break;
00466         case 'm': config->op_q_foreign = 1; break;
00467         case 'o': config->op_q_owns = 1; break;
00468         case 'p': config->op_q_isfile = 1; break;
00469         case 'q': config->quiet = 1; break;
00470         case 's': config->op_q_search = 1; break;
00471         case 't': config->op_q_unrequired = 1; break;
00472         case 'u': config->op_q_upgrade = 1; break;
00473         default: return 1;
00474     }
00475     return 0;
00476 }
00477 
00478 /* options common to -S -R -U */
00479 static int parsearg_trans(int opt)
00480 {
00481     switch(opt) {
00482         case 'd':
00483             if(config->flags & ALPM_TRANS_FLAG_NODEPVERSION) {
00484                 config->flags |= ALPM_TRANS_FLAG_NODEPS;
00485             } else {
00486                 config->flags |= ALPM_TRANS_FLAG_NODEPVERSION;
00487             }
00488             break;
00489         case OP_DBONLY: config->flags |= ALPM_TRANS_FLAG_DBONLY; break;
00490         case OP_NOPROGRESSBAR: config->noprogressbar = 1; break;
00491         case OP_NOSCRIPTLET: config->flags |= ALPM_TRANS_FLAG_NOSCRIPTLET; break;
00492         case 'p': config->print = 1; break;
00493         case OP_PRINTFORMAT:
00494             check_optarg();
00495             config->print_format = strdup(optarg);
00496             break;
00497         default: return 1;
00498     }
00499     return 0;
00500 }
00501 
00502 static int parsearg_remove(int opt)
00503 {
00504     if(parsearg_trans(opt) == 0)
00505         return 0;
00506     switch(opt) {
00507         case 'c': config->flags |= ALPM_TRANS_FLAG_CASCADE; break;
00508         case 'n': config->flags |= ALPM_TRANS_FLAG_NOSAVE; break;
00509         case 's':
00510         case OP_RECURSIVE:
00511             /* 's' is the legacy flag here, but since recursive is used in -S without
00512              * a shortopt, we need to do funky tricks */
00513             if(config->flags & ALPM_TRANS_FLAG_RECURSE) {
00514                 config->flags |= ALPM_TRANS_FLAG_RECURSEALL;
00515             } else {
00516                 config->flags |= ALPM_TRANS_FLAG_RECURSE;
00517             }
00518             break;
00519         case 'u': config->flags |= ALPM_TRANS_FLAG_UNNEEDED; break;
00520         default: return 1;
00521     }
00522     return 0;
00523 }
00524 
00525 /* options common to -S -U */
00526 static int parsearg_upgrade(int opt)
00527 {
00528     if(parsearg_trans(opt) == 0)
00529         return 0;
00530     switch(opt) {
00531         case OP_FORCE: config->flags |= ALPM_TRANS_FLAG_FORCE; break;
00532         case OP_ASDEPS: config->flags |= ALPM_TRANS_FLAG_ALLDEPS; break;
00533         case OP_ASEXPLICIT: config->flags |= ALPM_TRANS_FLAG_ALLEXPLICIT; break;
00534         case OP_NEEDED: config->flags |= ALPM_TRANS_FLAG_NEEDED; break;
00535         case OP_RECURSIVE: config->flags |= ALPM_TRANS_FLAG_RECURSE; break;
00536         case OP_IGNORE:
00537             parsearg_util_addlist(&(config->ignorepkg));
00538             break;
00539         case OP_IGNOREGROUP:
00540             parsearg_util_addlist(&(config->ignoregrp));
00541             break;
00542         default: return 1;
00543     }
00544     return 0;
00545 }
00546 
00547 static int parsearg_sync(int opt)
00548 {
00549     if(parsearg_upgrade(opt) == 0)
00550         return 0;
00551     switch(opt) {
00552         case 'c': (config->op_s_clean)++; break;
00553         case 'g': (config->group)++; break;
00554         case 'i': (config->op_s_info)++; break;
00555         case 'l': config->op_q_list = 1; break;
00556         case 'q': config->quiet = 1; break;
00557         case 's': config->op_s_search = 1; break;
00558         case 'u': (config->op_s_upgrade)++; break;
00559         case 'w':
00560             config->op_s_downloadonly = 1;
00561             config->flags |= ALPM_TRANS_FLAG_DOWNLOADONLY;
00562             config->flags |= ALPM_TRANS_FLAG_NOCONFLICTS;
00563             break;
00564         case 'y': (config->op_s_sync)++; break;
00565         default: return 1;
00566     }
00567     return 0;
00568 }
00569 
00570 /** Parse command-line arguments for each operation.
00571  * @param argc argc
00572  * @param argv argv
00573  * @return 0 on success, 1 on error
00574  */
00575 static int parseargs(int argc, char *argv[])
00576 {
00577     int opt;
00578     int option_index = 0;
00579     int result;
00580     const char *optstring = "DQRSTUVb:cdefghiklmnopqr:stuvwy";
00581     static const struct option opts[] =
00582     {
00583         {"database",   no_argument,       0, 'D'},
00584         {"query",      no_argument,       0, 'Q'},
00585         {"remove",     no_argument,       0, 'R'},
00586         {"sync",       no_argument,       0, 'S'},
00587         {"deptest",    no_argument,       0, 'T'}, /* used by makepkg */
00588         {"upgrade",    no_argument,       0, 'U'},
00589         {"version",    no_argument,       0, 'V'},
00590         {"dbpath",     required_argument, 0, 'b'},
00591         {"cascade",    no_argument,       0, 'c'},
00592         {"changelog",  no_argument,       0, 'c'},
00593         {"clean",      no_argument,       0, 'c'},
00594         {"nodeps",     no_argument,       0, 'd'},
00595         {"deps",       no_argument,       0, 'd'},
00596         {"explicit",   no_argument,       0, 'e'},
00597         {"groups",     no_argument,       0, 'g'},
00598         {"help",       no_argument,       0, 'h'},
00599         {"info",       no_argument,       0, 'i'},
00600         {"check",      no_argument,       0, 'k'},
00601         {"list",       no_argument,       0, 'l'},
00602         {"foreign",    no_argument,       0, 'm'},
00603         {"nosave",     no_argument,       0, 'n'},
00604         {"owns",       no_argument,       0, 'o'},
00605         {"file",       no_argument,       0, 'p'},
00606         {"print",      no_argument,       0, 'p'},
00607         {"quiet",      no_argument,       0, 'q'},
00608         {"root",       required_argument, 0, 'r'},
00609         {"search",     no_argument,       0, 's'},
00610         {"unrequired", no_argument,       0, 't'},
00611         {"upgrades",   no_argument,       0, 'u'},
00612         {"sysupgrade", no_argument,       0, 'u'},
00613         {"unneeded",   no_argument,       0, 'u'},
00614         {"verbose",    no_argument,       0, 'v'},
00615         {"downloadonly", no_argument,     0, 'w'},
00616         {"refresh",    no_argument,       0, 'y'},
00617 
00618         {"noconfirm",  no_argument,       0, OP_NOCONFIRM},
00619         {"config",     required_argument, 0, OP_CONFIG},
00620         {"ignore",     required_argument, 0, OP_IGNORE},
00621         {"debug",      optional_argument, 0, OP_DEBUG},
00622         {"force",      no_argument,       0, OP_FORCE},
00623         {"noprogressbar", no_argument,    0, OP_NOPROGRESSBAR},
00624         {"noscriptlet", no_argument,      0, OP_NOSCRIPTLET},
00625         {"ask",        required_argument, 0, OP_ASK},
00626         {"cachedir",   required_argument, 0, OP_CACHEDIR},
00627         {"asdeps",     no_argument,       0, OP_ASDEPS},
00628         {"logfile",    required_argument, 0, OP_LOGFILE},
00629         {"ignoregroup", required_argument, 0, OP_IGNOREGROUP},
00630         {"needed",     no_argument,       0, OP_NEEDED},
00631         {"asexplicit",     no_argument,   0, OP_ASEXPLICIT},
00632         {"arch",       required_argument, 0, OP_ARCH},
00633         {"print-format", required_argument, 0, OP_PRINTFORMAT},
00634         {"gpgdir",     required_argument, 0, OP_GPGDIR},
00635         {"recursive",  no_argument,       0, OP_RECURSIVE},
00636         {"dbonly",     no_argument,       0, OP_DBONLY},
00637         {0, 0, 0, 0}
00638     };
00639 
00640     /* parse operation */
00641     while((opt = getopt_long(argc, argv, optstring, opts, &option_index))) {
00642         if(opt < 0) {
00643             break;
00644         } else if(opt == 0) {
00645             continue;
00646         } else if(opt == '?') {
00647             /* unknown option, getopt printed an error */
00648             return 1;
00649         }
00650         parsearg_op(opt, 0);
00651     }
00652 
00653     if(config->op == 0) {
00654         pm_printf(ALPM_LOG_ERROR, _("only one operation may be used at a time\n"));
00655         return 1;
00656     }
00657     if(config->help) {
00658         usage(config->op, mbasename(argv[0]));
00659         return 2;
00660     }
00661     if(config->version) {
00662         version();
00663         return 2;
00664     }
00665 
00666     /* parse all other options */
00667     optind = 1;
00668     while((opt = getopt_long(argc, argv, optstring, opts, &option_index))) {
00669         if(opt < 0) {
00670             break;
00671         } else if(opt == 0) {
00672             continue;
00673         } else if(opt == '?') {
00674             /* this should have failed during first pass already */
00675             return 1;
00676         } else if(parsearg_op(opt, 1) == 0) {
00677             /* opt is an operation */
00678             continue;
00679         }
00680 
00681         switch(config->op) {
00682             case PM_OP_DATABASE:
00683                 result = parsearg_database(opt);
00684                 break;
00685             case PM_OP_QUERY:
00686                 result = parsearg_query(opt);
00687                 break;
00688             case PM_OP_REMOVE:
00689                 result = parsearg_remove(opt);
00690                 break;
00691             case PM_OP_SYNC:
00692                 result = parsearg_sync(opt);
00693                 break;
00694             case PM_OP_UPGRADE:
00695                 result = parsearg_upgrade(opt);
00696                 break;
00697             case PM_OP_DEPTEST:
00698             default:
00699                 result = 1;
00700                 break;
00701         }
00702         if(result == 0) {
00703             continue;
00704         }
00705 
00706         /* fall back to global options */
00707         result = parsearg_global(opt);
00708         if(result != 0) {
00709             /* global option parsing failed, abort */
00710             pm_printf(ALPM_LOG_ERROR, _("invalid option\n"));
00711             return result;
00712         }
00713     }
00714 
00715     while(optind < argc) {
00716         /* add the target to our target array */
00717         pm_targets = alpm_list_add(pm_targets, strdup(argv[optind]));
00718         optind++;
00719     }
00720 
00721     return 0;
00722 }
00723 
00724 /** print commandline to logfile
00725  */
00726 static void cl_to_log(int argc, char* argv[])
00727 {
00728     size_t size = 0;
00729     int i;
00730     for(i = 0; i < argc; i++) {
00731         size += strlen(argv[i]) + 1;
00732     }
00733     if(!size) {
00734         return;
00735     }
00736     char *cl_text = malloc(size);
00737     if(!cl_text) {
00738         return;
00739     }
00740     char *p = cl_text;
00741     for(i = 0; i < argc - 1; i++) {
00742         strcpy(p, argv[i]);
00743         p += strlen(argv[i]);
00744         *p++ = ' ';
00745     }
00746     strcpy(p, argv[i]);
00747     alpm_logaction(config->handle, "Running '%s'\n", cl_text);
00748     free(cl_text);
00749 }
00750 
00751 /** Main function.
00752  * @param argc argc
00753  * @param argv argv
00754  * @return A return code indicating success, failure, etc.
00755  */
00756 int main(int argc, char *argv[])
00757 {
00758     int ret = 0;
00759     size_t i;
00760     struct sigaction new_action, old_action;
00761     const int signals[] = { SIGHUP, SIGINT, SIGTERM, SIGSEGV };
00762 #if defined(HAVE_GETEUID) && !defined(CYGWIN)
00763     /* geteuid undefined in CYGWIN */
00764     uid_t myuid = geteuid();
00765 #endif
00766 
00767     /* Set signal handlers */
00768     /* Set up the structure to specify the new action. */
00769     new_action.sa_handler = handler;
00770     sigemptyset(&new_action.sa_mask);
00771     new_action.sa_flags = 0;
00772 
00773     /* assign our handler to any signals we care about */
00774     for(i = 0; i < sizeof(signals) / sizeof(signals[0]); i++) {
00775         int signal = signals[i];
00776         sigaction(signal, NULL, &old_action);
00777         if(old_action.sa_handler != SIG_IGN) {
00778             sigaction(signal, &new_action, NULL);
00779         }
00780     }
00781 
00782     /* i18n init */
00783 #if defined(ENABLE_NLS)
00784     localize();
00785 #endif
00786 
00787     /* set user agent for downloading */
00788     setuseragent();
00789 
00790     /* init config data */
00791     config = config_new();
00792 
00793     /* disable progressbar if the output is redirected */
00794     if(!isatty(1)) {
00795         config->noprogressbar = 1;
00796     }
00797 
00798     /* Priority of options:
00799      * 1. command line
00800      * 2. config file
00801      * 3. compiled-in defaults
00802      * However, we have to parse the command line first because a config file
00803      * location can be specified here, so we need to make sure we prefer these
00804      * options over the config file coming second.
00805      */
00806 
00807     /* parse the command line */
00808     ret = parseargs(argc, argv);
00809     if(ret != 0) {
00810         cleanup(ret);
00811     }
00812 
00813     /* we support reading targets from stdin if a cmdline parameter is '-' */
00814     if(!isatty(fileno(stdin)) && alpm_list_find_str(pm_targets, "-")) {
00815         size_t current_size = PATH_MAX, i = 0;
00816         char *line = malloc(current_size);
00817 
00818         /* remove the '-' from the list */
00819         pm_targets = alpm_list_remove_str(pm_targets, "-", NULL);
00820 
00821         while((line[i] = (char)fgetc(stdin)) != EOF) {
00822             if(isspace((unsigned char)line[i])) {
00823                 /* avoid adding zero length arg when multiple spaces separate args */
00824                 if(i > 0) {
00825                     line[i] = '\0';
00826                     pm_targets = alpm_list_add(pm_targets, strdup(line));
00827                     i = 0;
00828                 }
00829             } else {
00830                 i++;
00831                 /* we may be at the end of our allocated buffer now */
00832                 if(i >= current_size) {
00833                     char *new = realloc(line, current_size * 2);
00834                     if(new) {
00835                         line = new;
00836                         current_size *= 2;
00837                     } else {
00838                         free(line);
00839                         line = NULL;
00840                         break;
00841                     }
00842                 }
00843             }
00844         }
00845         /* check for memory exhaustion */
00846         if(!line) {
00847             pm_printf(ALPM_LOG_ERROR, _("memory exhausted in argument parsing\n"));
00848             cleanup(EXIT_FAILURE);
00849         }
00850 
00851         /* end of stream -- check for data still in line buffer */
00852         if(i > 0) {
00853             line[i] = '\0';
00854             pm_targets = alpm_list_add(pm_targets, strdup(line));
00855         }
00856         free(line);
00857         if(!freopen(ctermid(NULL), "r", stdin)) {
00858             pm_printf(ALPM_LOG_ERROR, _("failed to reopen stdin for reading: (%s)\n"),
00859                     strerror(errno));
00860         }
00861     }
00862 
00863     /* parse the config file */
00864     ret = parseconfig(config->configfile);
00865     if(ret != 0) {
00866         cleanup(ret);
00867     }
00868 
00869     /* noask is meant to be non-interactive */
00870     if(config->noask) {
00871         config->noconfirm = 1;
00872     }
00873 
00874     /* set up the print operations */
00875     if(config->print && !config->op_s_clean) {
00876         config->noconfirm = 1;
00877         config->flags |= ALPM_TRANS_FLAG_NOCONFLICTS;
00878         config->flags |= ALPM_TRANS_FLAG_NOLOCK;
00879         /* Display only errors */
00880         config->logmask &= ~ALPM_LOG_WARNING;
00881     }
00882 
00883 #if defined(HAVE_GETEUID) && !defined(CYGWIN)
00884     /* check if we have sufficient permission for the requested operation */
00885     if(myuid > 0 && needs_root()) {
00886         pm_printf(ALPM_LOG_ERROR, _("you cannot perform this operation unless you are root.\n"));
00887         cleanup(EXIT_FAILURE);
00888     }
00889 #endif
00890 
00891     if(config->verbose > 0) {
00892         alpm_list_t *i;
00893         printf("Root      : %s\n", alpm_option_get_root(config->handle));
00894         printf("Conf File : %s\n", config->configfile);
00895         printf("DB Path   : %s\n", alpm_option_get_dbpath(config->handle));
00896         printf("Cache Dirs: ");
00897         for(i = alpm_option_get_cachedirs(config->handle); i; i = alpm_list_next(i)) {
00898             printf("%s  ", (const char *)i->data);
00899         }
00900         printf("\n");
00901         printf("Lock File : %s\n", alpm_option_get_lockfile(config->handle));
00902         printf("Log File  : %s\n", alpm_option_get_logfile(config->handle));
00903         printf("GPG Dir   : %s\n", alpm_option_get_gpgdir(config->handle));
00904         list_display("Targets   :", pm_targets);
00905     }
00906 
00907     /* Log commandline */
00908     if(needs_root()) {
00909         cl_to_log(argc, argv);
00910     }
00911 
00912     /* start the requested operation */
00913     switch(config->op) {
00914         case PM_OP_DATABASE:
00915             ret = pacman_database(pm_targets);
00916             break;
00917         case PM_OP_REMOVE:
00918             ret = pacman_remove(pm_targets);
00919             break;
00920         case PM_OP_UPGRADE:
00921             ret = pacman_upgrade(pm_targets);
00922             break;
00923         case PM_OP_QUERY:
00924             ret = pacman_query(pm_targets);
00925             break;
00926         case PM_OP_SYNC:
00927             ret = pacman_sync(pm_targets);
00928             break;
00929         case PM_OP_DEPTEST:
00930             ret = pacman_deptest(pm_targets);
00931             break;
00932         default:
00933             pm_printf(ALPM_LOG_ERROR, _("no operation specified (use -h for help)\n"));
00934             ret = EXIT_FAILURE;
00935     }
00936 
00937     cleanup(ret);
00938     /* not reached */
00939     return EXIT_SUCCESS;
00940 }
00941 
00942 /* vim: set ts=2 sw=2 noet: */