diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/pacman/callback.c | 29 | ||||
-rw-r--r-- | src/pacman/deptest.c | 14 | ||||
-rw-r--r-- | src/pacman/package.c | 2 | ||||
-rw-r--r-- | src/pacman/pacman.c | 680 | ||||
-rw-r--r-- | src/pacman/query.c | 6 | ||||
-rw-r--r-- | src/pacman/sync.c | 42 | ||||
-rw-r--r-- | src/pacman/upgrade.c | 2 | ||||
-rw-r--r-- | src/pacman/util.c | 50 | ||||
-rw-r--r-- | src/pacman/util.h | 4 | ||||
-rw-r--r-- | src/util/.gitignore | 15 | ||||
-rw-r--r-- | src/util/Makefile.am | 5 | ||||
-rw-r--r-- | src/util/cleanupdelta.c | 10 | ||||
-rw-r--r-- | src/util/pactree.c | 372 | ||||
-rw-r--r-- | src/util/testdb.c | 71 | ||||
-rw-r--r-- | src/util/testpkg.c | 6 | ||||
-rw-r--r-- | src/util/vercmp.c | 23 |
16 files changed, 982 insertions, 349 deletions
diff --git a/src/pacman/callback.c b/src/pacman/callback.c index f5bf17d1..c8f604fc 100644 --- a/src/pacman/callback.c +++ b/src/pacman/callback.c @@ -26,7 +26,6 @@ #include <sys/time.h> #include <sys/types.h> /* off_t */ #include <unistd.h> -#include <dirent.h> #include <wchar.h> #include <alpm.h> @@ -229,6 +228,11 @@ void cb_trans_evt(pmtransevt_t event, void *data1, void *data2) case PM_TRANS_EVT_RETRIEVE_START: printf(_(":: Retrieving packages from %s...\n"), (char*)data1); break; + case PM_TRANS_EVT_DISKSPACE_START: + if(config->noprogressbar) { + printf(_("checking available disk space...\n")); + } + break; /* all the simple done events, with fallthrough for each */ case PM_TRANS_EVT_FILECONFLICTS_DONE: case PM_TRANS_EVT_CHECKDEPS_DONE: @@ -237,6 +241,7 @@ void cb_trans_evt(pmtransevt_t event, void *data1, void *data2) case PM_TRANS_EVT_INTEGRITY_DONE: case PM_TRANS_EVT_DELTA_INTEGRITY_DONE: case PM_TRANS_EVT_DELTA_PATCHES_DONE: + case PM_TRANS_EVT_DISKSPACE_DONE: /* nothing */ break; } @@ -262,7 +267,7 @@ void cb_trans_conv(pmtransconv_t event, void *data1, void *data2, case PM_TRANS_CONV_CONFLICT_PKG: /* data parameters: target package, local package, conflict (strings) */ /* print conflict only if it contains new information */ - if(!strcmp(data1, data3) || !strcmp(data2, data3)) { + if(strcmp(data1, data3) == 0 || strcmp(data2, data3) == 0) { *response = noyes(_(":: %s and %s are in conflict. Remove %s?"), (char *)data1, (char *)data2, @@ -283,11 +288,16 @@ void cb_trans_conv(pmtransconv_t event, void *data1, void *data2, namelist = alpm_list_add(namelist, (char *)alpm_pkg_get_name(i->data)); } - printf(_(":: the following package(s) cannot be upgraded due to " - "unresolvable dependencies:\n")); + printf(_n( + ":: The following package cannot be upgraded due to unresolvable dependencies:\n", + ":: The following packages cannot be upgraded due to unresolvable dependencies:\n", + alpm_list_count(namelist))); list_display(" ", namelist); - *response = noyes(_("\nDo you want to skip the above " - "package(s) for this upgrade?")); + printf("\n"); + *response = noyes(_n( + "Do you want to skip the above package for this upgrade?", + "Do you want to skip the above packages for this upgrade?", + alpm_list_count(namelist))); alpm_list_free(namelist); } break; @@ -342,7 +352,7 @@ void cb_trans_progress(pmtransprog_t event, const char *pkgname, int percent, timediff = get_update_timediff(0); } - if(percent > 0 && percent < 100 && !timediff) { + if(percent > 0 && percent < 100 && timediff > 0) { /* only update the progress bar when * a) we first start * b) we end the progress @@ -371,6 +381,9 @@ void cb_trans_progress(pmtransprog_t event, const char *pkgname, int percent, case PM_TRANS_PROGRESS_CONFLICTS_START: opr = _("checking for file conflicts"); break; + case PM_TRANS_PROGRESS_DISKSPACE_START: + opr = _("checking available disk space"); + break; default: return; } @@ -570,7 +583,7 @@ void cb_dl_progress(const char *filename, off_t file_xfered, off_t file_total) fname = strdup(filename); /* strip package or DB extension for cleaner look */ - if((p = strstr(fname, ".pkg.tar.")) || (p = strstr(fname, ".db.tar."))) { + if((p = strstr(fname, ".pkg")) || (p = strstr(fname, ".db"))) { *p = '\0'; } /* In order to deal with characters from all locales, we have to worry diff --git a/src/pacman/deptest.c b/src/pacman/deptest.c index f043c201..82c0cd6f 100644 --- a/src/pacman/deptest.c +++ b/src/pacman/deptest.c @@ -35,16 +35,24 @@ int pacman_deptest(alpm_list_t *targets) { alpm_list_t *i; + alpm_list_t *deps = NULL; + pmdb_t *localdb = alpm_option_get_localdb(); + + for(i = targets; i; i = alpm_list_next(i)) { + char *target = alpm_list_getdata(i); + + if(!alpm_find_satisfier(alpm_db_get_pkgcache(localdb), target)) { + deps = alpm_list_add(deps, target); + } + } - alpm_list_t *deps = alpm_deptest(alpm_option_get_localdb(), targets); if(deps == NULL) { return(0); } for(i = deps; i; i = alpm_list_next(i)) { - const char *dep; + const char *dep = alpm_list_getdata(i); - dep = alpm_list_getdata(i); printf("%s\n", dep); } alpm_list_free(deps); diff --git a/src/pacman/package.c b/src/pacman/package.c index 413754c7..ac84a0c7 100644 --- a/src/pacman/package.c +++ b/src/pacman/package.c @@ -189,7 +189,7 @@ void dump_pkg_backups(pmpkg_t *pkg) } /* if checksums don't match, file has been modified */ - if (strcmp(md5sum, ptr)) { + if (strcmp(md5sum, ptr) != 0) { printf(_("MODIFIED\t%s\n"), path); } else { printf(_("Not Modified\t%s\n"), path); diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c index 78407d67..700b74db 100644 --- a/src/pacman/pacman.c +++ b/src/pacman/pacman.c @@ -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> @@ -59,12 +61,52 @@ 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 +131,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 +305,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 +396,241 @@ 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 = 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 = 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': config->flags |= PM_TRANS_FLAG_NODEPS; 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 +640,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 +699,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 +725,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])); @@ -676,6 +854,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 +887,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 +921,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 +956,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); @@ -989,7 +1180,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 +1352,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] = 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) { diff --git a/src/pacman/query.c b/src/pacman/query.c index 7f064f23..5538e811 100644 --- a/src/pacman/query.c +++ b/src/pacman/query.c @@ -406,8 +406,10 @@ static int check(pmpkg_t *pkg) } if(!config->quiet) { - printf(_("%s: %d total files, %d missing file(s)\n"), - pkgname, allfiles, errors); + printf(_n("%s: %d total file, ", "%s: %d total files, ", allfiles), + pkgname, allfiles); + printf(_n("%d missing file\n", "%d missing files\n", errors), + errors); } return(errors != 0 ? 1 : 0); diff --git a/src/pacman/sync.c b/src/pacman/sync.c index b2994389..6961f3cf 100644 --- a/src/pacman/sync.c +++ b/src/pacman/sync.c @@ -39,7 +39,7 @@ extern pmdb_t *db_local; -/* if keep_used != 0, then the dirnames which match an used syncdb +/* if keep_used != 0, then the db files which match an used syncdb * will be kept */ static int sync_cleandb(const char *dbpath, int keep_used) { DIR *dir; @@ -59,31 +59,47 @@ static int sync_cleandb(const char *dbpath, int keep_used) { alpm_list_t *syncdbs = NULL, *i; int found = 0; const char *dname = ent->d_name; + size_t len; - if(!strcmp(dname, ".") || !strcmp(dname, "..")) { + if(strcmp(dname, ".") == 0 || strcmp(dname, "..") == 0) { continue; } /* skip the local and sync directories */ - if(!strcmp(dname, "sync") || !strcmp(dname, "local")) { + if(strcmp(dname, "sync") == 0 || strcmp(dname, "local") == 0) { + continue; + } + /* skip the db.lck file */ + if(strcmp(dname, "db.lck") == 0) { continue; } /* build the full path */ snprintf(path, PATH_MAX, "%s%s", dbpath, dname); - /* skip entries that are not dirs (lock file, etc.) */ + + /* remove all non-skipped directories and non-database files */ stat(path, &buf); - if(!S_ISDIR(buf.st_mode)) { + len = strlen(path); + if(S_ISDIR(buf.st_mode) || strcmp(path+(len-3),".db") != 0) { + if(rmrf(path)) { + pm_fprintf(stderr, PM_LOG_ERROR, + _("could not remove %s\n"), path); + closedir(dir); + return(1); + } continue; } if(keep_used) { + len = strlen(dname); + char *dbname = strndup(dname, len-3); syncdbs = alpm_option_get_syncdbs(); for(i = syncdbs; i && !found; i = alpm_list_next(i)) { pmdb_t *db = alpm_list_getdata(i); - found = !strcmp(dname, alpm_db_get_name(db)); + found = !strcmp(dbname, alpm_db_get_name(db)); } + free(dbname); } - /* We have a directory that doesn't match any syncdb. + /* We have a database that doesn't match any syncdb. * Ask the user if he wants to remove it. */ if(!found) { if(!yesno(_("Do you want to remove %s?"), path)) { @@ -92,7 +108,7 @@ static int sync_cleandb(const char *dbpath, int keep_used) { if(rmrf(path)) { pm_fprintf(stderr, PM_LOG_ERROR, - _("could not remove repository directory\n")); + _("could not remove %s\n"), path); closedir(dir); return(1); } @@ -113,8 +129,8 @@ static int sync_cleandb_all(void) { return(0); } /* The sync dbs were previously put in dbpath/, but are now in dbpath/sync/, - * so we will clean everything in dbpath/ (except dbpath/local/ and dbpath/sync/, - * and only the unused sync dbs in dbpath/sync/ */ + * so we will clean everything in dbpath/ (except dbpath/local/ and dbpath/sync/ + * and db.lck) and only the unused sync dbs in dbpath/sync/ */ ret += sync_cleandb(dbpath, 0); sprintf(newdbpath, "%s%s", dbpath, "sync/"); @@ -178,7 +194,7 @@ static int sync_cleancache(int level) pmpkg_t *localpkg = NULL, *pkg = NULL; alpm_list_t *j; - if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) { + if(strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) { continue; } /* build the full filepath */ @@ -555,7 +571,7 @@ static int sync_list(alpm_list_t *syncs, alpm_list_t *targets) return(0); } -static alpm_list_t *syncfirst() { +static alpm_list_t *syncfirst(void) { alpm_list_t *i, *res = NULL; for(i = config->syncfirst; i; i = alpm_list_next(i)) { @@ -666,7 +682,7 @@ static int sync_trans(alpm_list_t *targets) const char *package2 = alpm_conflict_get_package2(conflict); const char *reason = alpm_conflict_get_reason(conflict); /* only print reason if it contains new information */ - if(!strcmp(package1, reason) || !strcmp(package2, reason)) { + if(strcmp(package1, reason) == 0 || strcmp(package2, reason) == 0) { printf(_(":: %s and %s are in conflict\n"), package1, package2); } else { printf(_(":: %s and %s are in conflict (%s)\n"), package1, package2, reason); diff --git a/src/pacman/upgrade.c b/src/pacman/upgrade.c index 1442eb56..c9c8301f 100644 --- a/src/pacman/upgrade.c +++ b/src/pacman/upgrade.c @@ -112,7 +112,7 @@ int pacman_upgrade(alpm_list_t *targets) const char *package2 = alpm_conflict_get_package2(conflict); const char *reason = alpm_conflict_get_reason(conflict); /* only print reason if it contains new information */ - if(!strcmp(package1, reason) || !strcmp(package2, reason)) { + if(strcmp(package1, reason) == 0 || strcmp(package2, reason) == 0) { printf(_(":: %s and %s are in conflict\n"), package1, package2); } else { printf(_(":: %s and %s are in conflict (%s)\n"), package1, package2, reason); diff --git a/src/pacman/util.c b/src/pacman/util.c index 557696b0..31966caa 100644 --- a/src/pacman/util.c +++ b/src/pacman/util.c @@ -143,7 +143,7 @@ int rmrf(const char *path) if(dp->d_ino) { char name[PATH_MAX]; sprintf(name, "%s/%s", path, dp->d_name); - if(strcmp(dp->d_name, "..") && strcmp(dp->d_name, ".")) { + if(strcmp(dp->d_name, "..") != 0 && strcmp(dp->d_name, ".") != 0) { errflag += rmrf(name); } } @@ -531,10 +531,10 @@ void display_targets(const alpm_list_t *pkgs, int install) double mbsize = 0.0; mbsize = alpm_pkg_get_size(pkg) / (1024.0 * 1024.0); - asprintf(&str, "%s-%s [%.2f MB]", alpm_pkg_get_name(pkg), + pm_asprintf(&str, "%s-%s [%.2f MB]", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg), mbsize); } else { - asprintf(&str, "%s-%s", alpm_pkg_get_name(pkg), + pm_asprintf(&str, "%s-%s", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg)); } targets = alpm_list_add(targets, str); @@ -545,7 +545,7 @@ void display_targets(const alpm_list_t *pkgs, int install) mbisize = isize / (1024.0 * 1024.0); if(install) { - asprintf(&str, _("Targets (%d):"), alpm_list_count(targets)); + pm_asprintf(&str, _("Targets (%d):"), alpm_list_count(targets)); list_display(str, targets); free(str); printf("\n"); @@ -555,7 +555,7 @@ void display_targets(const alpm_list_t *pkgs, int install) printf(_("Total Installed Size: %.2f MB\n"), mbisize); } } else { - asprintf(&str, _("Remove (%d):"), alpm_list_count(targets)); + pm_asprintf(&str, _("Remove (%d):"), alpm_list_count(targets)); list_display(str, targets); free(str); printf("\n"); @@ -589,14 +589,14 @@ static char *pkg_get_location(pmpkg_t *pkg) dburl = alpm_db_get_url(db); if(dburl) { char *pkgurl = NULL; - asprintf(&pkgurl, "%s/%s", dburl, alpm_pkg_get_filename(pkg)); + pm_asprintf(&pkgurl, "%s/%s", dburl, alpm_pkg_get_filename(pkg)); return(pkgurl); } case PM_OP_UPGRADE: return(strdup(alpm_pkg_get_filename(pkg))); default: string = NULL; - asprintf(&string, "%s-%s", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg)); + pm_asprintf(&string, "%s-%s", alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg)); return(string); } } @@ -647,11 +647,10 @@ void print_packages(const alpm_list_t *packages) char *size; double mbsize = 0.0; mbsize = pkg_get_size(pkg) / (1024.0 * 1024.0); - asprintf(&size, "%.2f", mbsize); + pm_asprintf(&size, "%.2f", mbsize); string = strreplace(temp, "%s", size); free(size); free(temp); - temp = string; } printf("%s\n",string); free(string); @@ -690,7 +689,6 @@ void display_optdepends(pmpkg_t *pkg) static int question(short preset, char *fmt, va_list args) { char response[32]; - int sresponse = sizeof(response)-1; FILE *stream; if(config->noconfirm) { @@ -713,15 +711,15 @@ static int question(short preset, char *fmt, va_list args) return(preset); } - if(fgets(response, sresponse, stdin)) { + if(fgets(response, sizeof(response), stdin)) { strtrim(response); if(strlen(response) == 0) { return(preset); } - if(!strcasecmp(response, _("Y")) || !strcasecmp(response, _("YES"))) { + if(strcasecmp(response, _("Y")) == 0 || strcasecmp(response, _("YES")) == 0) { return(1); - } else if (!strcasecmp(response, _("N")) || !strcasecmp(response, _("NO"))) { + } else if (strcasecmp(response, _("N")) == 0 || strcasecmp(response, _("NO")) == 0) { return(0); } } @@ -778,6 +776,22 @@ int pm_fprintf(FILE *stream, pmloglevel_t level, const char *format, ...) return(ret); } +int pm_asprintf(char **string, const char *format, ...) +{ + int ret = 0; + va_list args; + + /* print the message using va_arg list */ + va_start(args, format); + if(vasprintf(string, format, args) == -1) { + pm_fprintf(stderr, PM_LOG_ERROR, _("failed to allocate string\n")); + ret = -1; + } + va_end(args); + + return(ret); +} + int pm_vasprintf(char **string, pmloglevel_t level, const char *format, va_list args) { int ret = 0; @@ -794,16 +808,16 @@ int pm_vasprintf(char **string, pmloglevel_t level, const char *format, va_list /* print a prefix to the message */ switch(level) { case PM_LOG_DEBUG: - asprintf(string, "debug: %s", msg); + pm_asprintf(string, "debug: %s", msg); break; case PM_LOG_ERROR: - asprintf(string, _("error: %s"), msg); + pm_asprintf(string, _("error: %s"), msg); break; case PM_LOG_WARNING: - asprintf(string, _("warning: %s"), msg); + pm_asprintf(string, _("warning: %s"), msg); break; case PM_LOG_FUNCTION: - asprintf(string, _("function: %s"), msg); + pm_asprintf(string, _("function: %s"), msg); break; default: break; @@ -824,7 +838,7 @@ int pm_vfprintf(FILE *stream, pmloglevel_t level, const char *format, va_list ar #if defined(PACMAN_DEBUG) /* If debug is on, we'll timestamp the output */ - if(config->logmask & PM_LOG_DEBUG) { + if(config->logmask & PM_LOG_DEBUG) { time_t t; struct tm *tmp; char timestr[10] = {0}; diff --git a/src/pacman/util.h b/src/pacman/util.h index bbf43827..0308f6b5 100644 --- a/src/pacman/util.h +++ b/src/pacman/util.h @@ -30,8 +30,10 @@ #include <libintl.h> /* here so it doesn't need to be included elsewhere */ /* define _() as shortcut for gettext() */ #define _(str) gettext(str) +#define _n(str1, str2, ct) ngettext(str1, str2, ct) #else #define _(str) str +#define _n(str1, str2, ct) (ct == 1 ? str1 : str2) #endif /* update speed for the fill_progress based functions */ @@ -53,6 +55,7 @@ void string_display(const char *title, const char *string); void list_display(const char *title, const alpm_list_t *list); void list_display_linebreak(const char *title, const alpm_list_t *list); void display_targets(const alpm_list_t *pkgs, int install); +int str_cmp(const void *s1, const void *s2); void display_new_optdepends(pmpkg_t *oldpkg, pmpkg_t *newpkg); void display_optdepends(pmpkg_t *pkg); void print_packages(const alpm_list_t *packages); @@ -60,6 +63,7 @@ int yesno(char *fmt, ...); int noyes(char *fmt, ...); int pm_printf(pmloglevel_t level, const char *format, ...) __attribute__((format(printf,2,3))); int pm_fprintf(FILE *stream, pmloglevel_t level, const char *format, ...) __attribute__((format(printf,3,4))); +int pm_asprintf(char **string, const char *format, ...); int pm_vfprintf(FILE *stream, pmloglevel_t level, const char *format, va_list args) __attribute__((format(printf,3,0))); int pm_vasprintf(char **string, pmloglevel_t level, const char *format, va_list args) __attribute__((format(printf,3,0))); diff --git a/src/util/.gitignore b/src/util/.gitignore index 9c855dff..2880ce2a 100644 --- a/src/util/.gitignore +++ b/src/util/.gitignore @@ -1,11 +1,12 @@ .deps .libs -vercmp -vercmp.exe -testpkg -testpkg.exe -testdb -testdb.exe cleanupdelta cleanupdelta.exe - +pactree +pactree.exe +testdb +testdb.exe +testpkg +testpkg.exe +vercmp +vercmp.exe diff --git a/src/util/Makefile.am b/src/util/Makefile.am index 7dce9dcc..30a2ee35 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -3,7 +3,7 @@ conffile = ${sysconfdir}/pacman.conf dbpath = ${localstatedir}/lib/pacman/ cachedir = ${localstatedir}/cache/pacman/pkg/ -bin_PROGRAMS = vercmp testpkg testdb cleanupdelta +bin_PROGRAMS = vercmp testpkg testdb cleanupdelta pactree DEFS = -DLOCALEDIR=\"@localedir@\" \ -DCONFFILE=\"$(conffile)\" \ @@ -27,5 +27,8 @@ testdb_LDADD = $(top_builddir)/lib/libalpm/.libs/libalpm.la cleanupdelta_SOURCES = cleanupdelta.c cleanupdelta_LDADD = $(top_builddir)/lib/libalpm/.libs/libalpm.la +pactree_SOURCES = pactree.c +pactree_LDADD = $(top_builddir)/lib/libalpm/.libs/libalpm.la + # vim:set ts=2 sw=2 noet: diff --git a/src/util/cleanupdelta.c b/src/util/cleanupdelta.c index ffcfaba5..20102824 100644 --- a/src/util/cleanupdelta.c +++ b/src/util/cleanupdelta.c @@ -17,6 +17,8 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "config.h" + #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -49,7 +51,7 @@ void output_cb(pmloglevel_t level, char *fmt, va_list args) } -void checkpkgs(alpm_list_t *pkglist) +static void checkpkgs(alpm_list_t *pkglist) { alpm_list_t *i, *j; for(i = pkglist; i; i = alpm_list_next(i)) { @@ -63,7 +65,7 @@ void checkpkgs(alpm_list_t *pkglist) } } -void checkdbs(char *dbpath, alpm_list_t *dbnames) { +static void checkdbs(char *dbpath, alpm_list_t *dbnames) { char syncdbpath[PATH_MAX]; pmdb_t *db = NULL; alpm_list_t *i; @@ -82,14 +84,14 @@ void checkdbs(char *dbpath, alpm_list_t *dbnames) { } -void usage() { +static void usage(void) { fprintf(stderr, "usage:\n"); fprintf(stderr, "\t%s [-b <pacman db>] core extra ... : check the listed sync databases\n", BASENAME); exit(1); } -int main(int argc, char **argv) +int main(int argc, char *argv[]) { char *dbpath = DBPATH; int a = 1; diff --git a/src/util/pactree.c b/src/util/pactree.c new file mode 100644 index 00000000..967f5c8c --- /dev/null +++ b/src/util/pactree.c @@ -0,0 +1,372 @@ +/* + * pactree.c - a simple dependency tree viewer + * + * Copyright (c) 2010 Pacman Development Team <pacman-dev@archlinux.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 + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#include <getopt.h> +#include <stdio.h> +#include <string.h> + +#include <alpm.h> +#include <alpm_list.h> + +/* output */ +char *provides = " provides"; +char *unresolvable = " [unresolvable]"; +char *branch_tip1 = "|--"; +char *branch_tip2 = "+--"; +int indent_size = 3; + +/* color */ +char *branch1_color = "\033[0;33m"; /* yellow */ +char *branch2_color = "\033[0;37m"; /* white */ +char *leaf1_color = "\033[1;32m"; /* bold green */ +char *leaf2_color = "\033[0;32m"; /* green */ +char *color_off = "\033[0m"; + +/* globals */ +pmdb_t *db_local; +alpm_list_t *walked = NULL; +alpm_list_t *provisions = NULL; + +/* options */ +int color = 0; +int graphviz = 0; +int linear = 0; +int max_depth = -1; +int reverse = 0; +int unique = 0; +char *dbpath = NULL; + +static int alpm_local_init(void) +{ + int ret; + + ret = alpm_initialize(); + if(ret != 0) { + return(ret); + } + + ret = alpm_option_set_root(ROOTDIR); + if(ret != 0) { + return(ret); + } + + if(dbpath) { + ret = alpm_option_set_dbpath(dbpath); + } else { + ret = alpm_option_set_dbpath(DBPATH); + } + if(ret != 0) { + return(ret); + } + + db_local = alpm_db_register_local(); + if(!db_local) { + return(1); + } + + return(0); +} + +static int parse_options(int argc, char *argv[]) +{ + int opt, option_index = 0; + char *endptr = NULL; + + static struct option opts[] = { + {"dbpath", required_argument, 0, 'b'}, + {"color", no_argument, 0, 'c'}, + {"depth", required_argument, 0, 'd'}, + {"graph", no_argument, 0, 'g'}, + {"help", no_argument, 0, 'h'}, + {"linear", no_argument, 0, 'l'}, + {"reverse", no_argument, 0, 'r'}, + {"unique", no_argument, 0, 'u'}, + {0, 0, 0, 0} + }; + + while((opt = getopt_long(argc, argv, "b:cd:ghlru", opts, &option_index))) { + if(opt < 0) { + break; + } + + switch(opt) { + case 'b': + dbpath = strdup(optarg); + break; + case 'c': + color = 1; + break; + case 'd': + /* validate depth */ + max_depth = strtol(optarg, &endptr, 10); + if(*endptr != '\0') { + fprintf(stderr, "error: invalid depth -- %s\n", optarg); + return 1; + } + break; + case 'g': + graphviz = 1; + break; + case 'l': + linear = 1; + break; + case 'r': + reverse = 1; + break; + case 'u': + unique = linear = 1; + break; + case 'h': + case '?': + default: + return(1); + } + } + + if(!argv[optind]) { + return(1); + } + + if(!color) { + branch1_color = ""; + branch2_color = ""; + leaf1_color = ""; + leaf2_color = ""; + color_off = ""; + } + if(linear) { + provides = ""; + branch_tip1 = ""; + branch_tip2 = ""; + indent_size = 0; + } + + return(0); +} + +static void usage(void) +{ + fprintf(stderr, "pactree v" PACKAGE_VERSION "\n" + "Usage: pactree [options] PACKAGE\n\n" + " -b, --dbpath <path> set an alternate database location\n" + " -c, --color colorize output\n" + " -d, --depth <#> limit the depth of recursion\n" + " -g, --graph generate output for graphviz's dot\n" + " -l, --linear enable linear output\n" + " -r, --reverse show reverse dependencies\n" + " -u, --unique show dependencies with no duplicates (implies -l)\n\n" + " -h, --help display this help message\n"); +} + +static void cleanup(void) +{ + if(dbpath) { + free(dbpath); + } + + alpm_list_free(walked); + alpm_list_free(provisions); + alpm_release(); +} + +/* pkg provides provision */ +static void print_text(const char *pkg, const char *provision, int depth) +{ + int indent_sz = (depth + 1) * indent_size; + + if(!pkg && !provision) { + /* not much we can do */ + return; + } + + if(!pkg && provision) { + /* we failed to resolve provision */ + printf("%s%*s%s%s%s%s%s\n", branch1_color, indent_sz, branch_tip1, + leaf1_color, provision, branch1_color, unresolvable, color_off); + } else if(provision && strcmp(pkg, provision) != 0) { + /* pkg provides provision */ + printf("%s%*s%s%s%s%s %s%s%s\n", branch2_color, indent_sz, branch_tip2, + leaf1_color, pkg, leaf2_color, provides, leaf1_color, provision, + color_off); + } else { + /* pkg is a normal package */ + printf("%s%*s%s%s%s\n", branch1_color, indent_sz, branch_tip1, leaf1_color, + pkg, color_off); + } +} + +static void print_graph(const char *parentname, const char *pkgname, const char *depname) +{ + if(depname) { + printf("\"%s\" -> \"%s\" [color=chocolate4];\n", parentname, depname); + if(pkgname && strcmp(depname, pkgname) != 0 && !alpm_list_find_str(provisions, depname)) { + printf("\"%s\" -> \"%s\" [arrowhead=none, color=grey];\n", depname, pkgname); + provisions = alpm_list_add(provisions, (char *)depname); + } + } else if(pkgname) { + printf("\"%s\" -> \"%s\" [color=chocolate4];\n", parentname, pkgname); + } +} + +/* parent depends on dep which is satisfied by pkg */ +static void print(const char *parentname, const char *pkgname, const char *depname, int depth) +{ + if(graphviz) { + print_graph(parentname, pkgname, depname); + } else { + print_text(pkgname, depname, depth); + } +} + +static void print_start(const char *pkgname, const char *provname) +{ + if(graphviz) { + printf("digraph G { START [color=red, style=filled];\n" + "node [style=filled, color=green];\n" + " \"START\" -> \"%s\";\n", pkgname); + } else { + print_text(pkgname, provname, 0); + } +} + +static void print_end(void) +{ + if(graphviz) { + /* close graph output */ + printf("}\n"); + } +} + + +/** + * walk dependencies in reverse, showing packages which require the target + */ +static void walk_reverse_deps(pmpkg_t *pkg, int depth) +{ + alpm_list_t *required_by, *i; + + if((max_depth >= 0) && (depth == max_depth + 1)) { + return; + } + + walked = alpm_list_add(walked, (void*)alpm_pkg_get_name(pkg)); + required_by = alpm_pkg_compute_requiredby(pkg); + + for(i = required_by; i; i = alpm_list_next(i)) { + const char *pkgname = alpm_list_getdata(i); + + if(alpm_list_find_str(walked, pkgname)) { + /* if we've already seen this package, don't print in "unique" output + * and don't recurse */ + if(!unique) { + print(alpm_pkg_get_name(pkg), pkgname, NULL, depth); + } + } else { + print(alpm_pkg_get_name(pkg), pkgname, NULL, depth); + walk_reverse_deps(alpm_db_get_pkg(db_local, pkgname), depth + 1); + } + } + + FREELIST(required_by); +} + +/** + * walk dependencies, showing dependencies of the target + */ +static void walk_deps(pmpkg_t *pkg, int depth) +{ + alpm_list_t *i; + + if((max_depth >= 0) && (depth == max_depth + 1)) { + return; + } + + walked = alpm_list_add(walked, (void*)alpm_pkg_get_name(pkg)); + + for(i = alpm_pkg_get_depends(pkg); i; i = alpm_list_next(i)) { + pmdepend_t *depend = alpm_list_getdata(i); + pmpkg_t *provider = alpm_find_satisfier(alpm_db_get_pkgcache(db_local), + alpm_dep_get_name(depend)); + + if(provider) { + const char *provname = alpm_pkg_get_name(provider); + + if(alpm_list_find_str(walked, provname)) { + /* if we've already seen this package, don't print in "unique" output + * and don't recurse */ + if(!unique) { + print(alpm_pkg_get_name(pkg), provname, alpm_dep_get_name(depend), depth); + } + } else { + print(alpm_pkg_get_name(pkg), provname, alpm_dep_get_name(depend), depth); + walk_deps(provider, depth + 1); + } + } else { + /* unresolvable package */ + print(alpm_pkg_get_name(pkg), NULL, alpm_dep_get_name(depend), depth); + } + } +} + +int main(int argc, char *argv[]) +{ + int ret; + const char *target_name; + pmpkg_t *pkg; + + ret = parse_options(argc, argv); + if(ret != 0) { + usage(); + goto finish; + } + + ret = alpm_local_init(); + if(ret != 0) { + fprintf(stderr, "error: cannot initialize alpm: %s\n", alpm_strerrorlast()); + goto finish; + } + + /* we only care about the first non option arg for walking */ + target_name = argv[optind]; + + pkg = alpm_find_satisfier(alpm_db_get_pkgcache(db_local), target_name); + if(!pkg) { + fprintf(stderr, "error: package '%s' not found\n", target_name); + ret = 1; + goto finish; + } + + print_start(alpm_pkg_get_name(pkg), target_name); + + if(reverse) { + walk_reverse_deps(pkg, 1); + } else { + walk_deps(pkg, 1); + } + + print_end(); + +finish: + cleanup(); + return(ret); +} + +/* vim: set ts=2 sw=2 noet: */ diff --git a/src/util/testdb.c b/src/util/testdb.c index 6d351ebd..332224e1 100644 --- a/src/util/testdb.c +++ b/src/util/testdb.c @@ -17,11 +17,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "config.h" + #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> -#include <limits.h> #include <string.h> #include <dirent.h> @@ -30,11 +31,6 @@ #define BASENAME "testdb" -int str_cmp(const void *s1, const void *s2) -{ - return(strcmp(s1, s2)); -} - static void cleanup(int signum) { if(alpm_release() == -1) { fprintf(stderr, "error releasing alpm: %s\n", alpm_strerrorlast()); @@ -55,42 +51,37 @@ void output_cb(pmloglevel_t level, char *fmt, va_list args) } } -static int db_test(char *dbpath, int local) +static int check_localdb_files(void) { struct dirent *ent; - char path[PATH_MAX]; + const char *dbpath; + char path[4096]; int ret = 0; - DIR *dir; - if(!(dir = opendir(dbpath))) { - fprintf(stderr, "error : %s : %s\n", dbpath, strerror(errno)); + dbpath = alpm_option_get_dbpath(); + snprintf(path, sizeof(path), "%slocal", dbpath); + if(!(dir = opendir(path))) { + fprintf(stderr, "error : %s : %s\n", path, strerror(errno)); return(1); } while ((ent = readdir(dir)) != NULL) { - if(!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..") + if(strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0 || ent->d_name[0] == '.') { continue; } - /* check for desc, depends, and files */ - snprintf(path, PATH_MAX, "%s/%s/desc", dbpath, ent->d_name); + /* check for known db files in local database */ + snprintf(path, sizeof(path), "%slocal/%s/desc", dbpath, ent->d_name); if(access(path, F_OK)) { printf("%s: description file is missing\n", ent->d_name); ret++; } - snprintf(path, PATH_MAX, "%s/%s/depends", dbpath, ent->d_name); + snprintf(path, sizeof(path), "%slocal/%s/files", dbpath, ent->d_name); if(access(path, F_OK)) { - printf("%s: dependency file is missing\n", ent->d_name); + printf("%s: file list is missing\n", ent->d_name); ret++; } - if(local) { - snprintf(path, PATH_MAX, "%s/%s/files", dbpath, ent->d_name); - if(access(path, F_OK)) { - printf("%s: file list is missing\n", ent->d_name); - ret++; - } - } } if(closedir(dir)) { fprintf(stderr, "error closing dbpath : %s\n", strerror(errno)); @@ -100,7 +91,7 @@ static int db_test(char *dbpath, int local) return(ret); } -int checkdeps(alpm_list_t *pkglist) +static int checkdeps(alpm_list_t *pkglist) { alpm_list_t *data, *i; int ret = 0; @@ -119,7 +110,7 @@ int checkdeps(alpm_list_t *pkglist) return(ret); } -int checkconflicts(alpm_list_t *pkglist) +static int checkconflicts(alpm_list_t *pkglist) { alpm_list_t *data, *i; int ret = 0; @@ -135,14 +126,12 @@ int checkconflicts(alpm_list_t *pkglist) return(ret); } -int check_localdb(char *dbpath) { - char localdbpath[PATH_MAX]; +static int check_localdb(void) { int ret = 0; pmdb_t *db = NULL; alpm_list_t *pkglist; - snprintf(localdbpath, PATH_MAX, "%s/local", dbpath); - ret = db_test(localdbpath, 1); + ret = check_localdb_files(); if(ret) { return(ret); } @@ -159,20 +148,13 @@ int check_localdb(char *dbpath) { return(ret); } -int check_syncdbs(char *dbpath, alpm_list_t *dbnames) { - char syncdbpath[PATH_MAX]; +static int check_syncdbs(alpm_list_t *dbnames) { int ret = 0; pmdb_t *db = NULL; alpm_list_t *i, *pkglist, *syncpkglist = NULL; for(i = dbnames; i; i = alpm_list_next(i)) { char *dbname = alpm_list_getdata(i); - snprintf(syncdbpath, PATH_MAX, "%s/sync/%s", dbpath, dbname); - ret = db_test(syncdbpath, 0); - if(ret) { - ret = 1; - goto cleanup; - } db = alpm_db_register_sync(dbname); if(db == NULL) { fprintf(stderr, "error: could not register sync database (%s)\n", @@ -190,7 +172,7 @@ cleanup: return(ret); } -void usage() { +static void usage(void) { fprintf(stderr, "usage:\n"); fprintf(stderr, "\t%s [-b <pacman db>] : check the local database\n", BASENAME); @@ -199,7 +181,7 @@ void usage() { exit(1); } -int main(int argc, char **argv) +int main(int argc, char *argv[]) { int ret = 0; char *dbpath = DBPATH; @@ -224,18 +206,21 @@ int main(int argc, char **argv) if(alpm_initialize() == -1) { fprintf(stderr, "cannot initialize alpm: %s\n", alpm_strerrorlast()); - return(1); + return(EXIT_FAILURE); } /* let us get log messages from libalpm */ alpm_option_set_logcb(output_cb); - alpm_option_set_dbpath(dbpath); + if(alpm_option_set_dbpath(dbpath) != 0) { + fprintf(stderr, "cannot set dbpath: %s\n", alpm_strerrorlast()); + return(EXIT_FAILURE); + } if(!dbnames) { - ret = check_localdb(dbpath); + ret = check_localdb(); } else { - ret = check_syncdbs(dbpath,dbnames); + ret = check_syncdbs(dbnames); alpm_list_free(dbnames); } diff --git a/src/util/testpkg.c b/src/util/testpkg.c index d86fb1e0..6fc0ce00 100644 --- a/src/util/testpkg.c +++ b/src/util/testpkg.c @@ -17,8 +17,6 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "config.h" - #include <stdio.h> /* printf */ #include <stdarg.h> /* va_list */ @@ -26,7 +24,7 @@ #define BASENAME "testpkg" -static void output_cb(pmloglevel_t level, char *fmt, va_list args) +void output_cb(pmloglevel_t level, char *fmt, va_list args) { if(fmt[0] == '\0') { return; @@ -39,7 +37,7 @@ static void output_cb(pmloglevel_t level, char *fmt, va_list args) vprintf(fmt, args); } -int main(int argc, char **argv) +int main(int argc, char *argv[]) { int retval = 1; /* default = false */ pmpkg_t *pkg = NULL; diff --git a/src/util/vercmp.c b/src/util/vercmp.c index 959dc137..8a785bb8 100644 --- a/src/util/vercmp.c +++ b/src/util/vercmp.c @@ -18,20 +18,16 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "config.h" - #include <stdio.h> /* printf */ #include <string.h> /* strncpy */ #define BASENAME "vercmp" -#define MAX_LEN 255 - /* forward declaration, comes from vercmp.o in libalpm source that is linked in * directly so we don't have any library deps */ int alpm_pkg_vercmp(const char *a, const char *b); -static void usage() +static void usage(void) { fprintf(stderr, "usage: %s <ver1> <ver2>\n\n", BASENAME); fprintf(stderr, "return values:\n"); @@ -42,8 +38,8 @@ static void usage() int main(int argc, char *argv[]) { - char s1[MAX_LEN] = ""; - char s2[MAX_LEN] = ""; + const char *s1 = ""; + const char *s2 = ""; int ret; if(argc == 1) { @@ -56,16 +52,11 @@ int main(int argc, char *argv[]) usage(); return(0); } - if(argc > 1) { - strncpy(s1, argv[1], MAX_LEN); - s1[MAX_LEN -1] = '\0'; - } if(argc > 2) { - strncpy(s2, argv[2], MAX_LEN); - s2[MAX_LEN -1] = '\0'; - } else { - printf("0\n"); - return(0); + s2 = argv[2]; + } + if(argc > 1) { + s1 = argv[1]; } ret = alpm_pkg_vercmp(s1, s2); |