diff options
Diffstat (limited to 'src/pacman')
-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 |
9 files changed, 555 insertions, 274 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))); |