index : pacman | |
Archlinux32 fork of pacman | gitolite user |
summaryrefslogtreecommitdiff |
-rw-r--r-- | src/pacman/Makefile.am | 2 | ||||
-rw-r--r-- | src/pacman/callback.c | 122 | ||||
-rw-r--r-- | src/pacman/callback.h | 4 | ||||
-rw-r--r-- | src/pacman/conf.c | 2 | ||||
-rw-r--r-- | src/pacman/conf.h | 6 | ||||
-rw-r--r-- | src/pacman/database.c | 6 | ||||
-rw-r--r-- | src/pacman/deptest.c | 16 | ||||
-rw-r--r-- | src/pacman/package.c | 79 | ||||
-rw-r--r-- | src/pacman/package.h | 2 | ||||
-rw-r--r-- | src/pacman/pacman.c | 721 | ||||
-rw-r--r-- | src/pacman/pacman.h | 2 | ||||
-rw-r--r-- | src/pacman/query.c | 73 | ||||
-rw-r--r-- | src/pacman/remove.c | 35 | ||||
-rw-r--r-- | src/pacman/sync.c | 259 | ||||
-rw-r--r-- | src/pacman/upgrade.c | 17 | ||||
-rw-r--r-- | src/pacman/util.c | 274 | ||||
-rw-r--r-- | src/pacman/util.h | 11 | ||||
-rw-r--r-- | src/util/.gitignore | 15 | ||||
-rw-r--r-- | src/util/Makefile.am | 5 | ||||
-rw-r--r-- | src/util/cleanupdelta.c | 16 | ||||
-rw-r--r-- | src/util/pactree.c | 372 | ||||
-rw-r--r-- | src/util/testdb.c | 79 | ||||
-rw-r--r-- | src/util/testpkg.c | 4 | ||||
-rw-r--r-- | src/util/vercmp.c | 34 |
diff --git a/src/pacman/Makefile.am b/src/pacman/Makefile.am index 8c14562c..31e8b134 100644 --- a/src/pacman/Makefile.am +++ b/src/pacman/Makefile.am @@ -18,7 +18,7 @@ INCLUDES = -I$(top_srcdir)/lib/libalpm AM_CFLAGS = -pedantic -D_GNU_SOURCE if USE_GIT_VERSION -GIT_VERSION := $(shell sh -c 'git describe --abbrev=4 | sed s/^v//')-dirty +GIT_VERSION := $(shell sh -c 'git describe --abbrev=4 --dirty | sed s/^v//') DEFS += -DGIT_VERSION=\"$(GIT_VERSION)\" endif diff --git a/src/pacman/callback.c b/src/pacman/callback.c index f5bf17d1..f1e71bb4 100644 --- a/src/pacman/callback.c +++ b/src/pacman/callback.c @@ -1,7 +1,7 @@ /* * callback.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * * This program is free software; you can redistribute it and/or modify @@ -26,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> @@ -37,7 +36,7 @@ #include "conf.h" /* download progress bar */ -static float rate_last; +static double rate_last; static off_t xfered_last; static off_t list_xfered = 0.0; static off_t list_total = 0.0; @@ -56,9 +55,9 @@ static alpm_list_t *output = NULL; * * first_call indicates if this is the first time it is called, for * initialization purposes */ -static float get_update_timediff(int first_call) +static double get_update_timediff(int first_call) { - float retval = 0.0; + double retval = 0.0; static struct timeval last_time = {0, 0}; /* on first call, simply set the last time and return */ @@ -66,7 +65,7 @@ static float get_update_timediff(int first_call) gettimeofday(&last_time, NULL); } else { struct timeval this_time; - float diff_sec, diff_usec; + double diff_sec, diff_usec; gettimeofday(&this_time, NULL); diff_sec = this_time.tv_sec - last_time.tv_sec; @@ -79,7 +78,6 @@ static float get_update_timediff(int first_call) retval = 0.0; } else { last_time = this_time; - /* printf("\nupdate retval: %f\n", retval); DEBUG*/ } } @@ -163,7 +161,7 @@ void cb_trans_evt(pmtransevt_t event, void *data1, void *data2) break; case PM_TRANS_EVT_FILECONFLICTS_START: if(config->noprogressbar) { - printf(_("checking for file conflicts...\n")); + printf(_("checking for file conflicts...\n")); } break; case PM_TRANS_EVT_RESOLVEDEPS_START: @@ -206,7 +204,9 @@ void cb_trans_evt(pmtransevt_t event, void *data1, void *data2) display_new_optdepends(data2,data1); break; case PM_TRANS_EVT_INTEGRITY_START: - printf(_("checking package integrity...\n")); + if(config->noprogressbar) { + printf(_("checking package integrity...\n")); + } break; case PM_TRANS_EVT_DELTA_INTEGRITY_START: printf(_("checking delta integrity...\n")); @@ -229,6 +229,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 +242,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 +268,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, @@ -279,18 +285,36 @@ void cb_trans_conv(pmtransconv_t event, void *data1, void *data2, { alpm_list_t *unresolved = (alpm_list_t *) data1; alpm_list_t *namelist = NULL, *i; + size_t count = alpm_list_count(namelist); for (i = unresolved; i; i = i->next) { 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", + count)); 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?", + count)); alpm_list_free(namelist); } break; + case PM_TRANS_CONV_SELECT_PROVIDER: + { + alpm_list_t *providers = (alpm_list_t *)data1; + int count = alpm_list_count(providers); + char *depstring = alpm_dep_compute_string((pmdepend_t *)data2); + printf(_(":: There are %d providers available for %s:\n"), count, + depstring); + free(depstring); + select_display(providers); + *response = select_question(count); + } + break; case PM_TRANS_CONV_LOCAL_NEWER: if(!config->op_s_downloadonly) { *response = yesno(_(":: %s-%s: local version is newer. Upgrade anyway?"), @@ -315,13 +339,12 @@ void cb_trans_conv(pmtransconv_t event, void *data1, void *data2, /* callback to handle display of transaction progress */ void cb_trans_progress(pmtransprog_t event, const char *pkgname, int percent, - int howmany, int remain) + size_t howmany, size_t current) { - float timediff; - /* size of line to allocate for text printing (e.g. not progressbar) */ int infolen; - int tmp, digits, textlen; + int digits, textlen; + size_t tmp; char *opr = NULL; /* used for wide character width determination and printing */ int len, wclen, wcwid, padwid; @@ -331,32 +354,24 @@ void cb_trans_progress(pmtransprog_t event, const char *pkgname, int percent, return; } - infolen = getcols() * 6 / 10; - if (infolen < 50) { - infolen = 50; - } - if(percent == 0) { - timediff = get_update_timediff(1); + get_update_timediff(1); + } else if(percent == 100) { + /* no need for timediff update, but unconditionally continue unless we + * already completed on a previous call */ + if(prevpercent == 100) { + return; + } } else { - timediff = get_update_timediff(0); - } - - if(percent > 0 && percent < 100 && !timediff) { - /* only update the progress bar when - * a) we first start - * b) we end the progress - * c) it has been long enough since the last call - */ - return; + if(!pkgname || percent == prevpercent || get_update_timediff(0) < UPDATE_SPEED_SEC) { + /* only update the progress bar when we have a package name, the + * percentage has changed, and it has been long enough. */ + return; + } } - /* if no pkgname, percent is too high or unchanged, then return */ - if(!pkgname || percent == prevpercent) { - return; - } + prevpercent = percent; - prevpercent=percent; /* set text of message to display */ switch (event) { case PM_TRANS_PROGRESS_ADD_START: @@ -371,10 +386,21 @@ 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; + case PM_TRANS_PROGRESS_INTEGRITY_START: + opr = _("checking package integrity"); + break; default: return; } + infolen = getcols() * 6 / 10; + if (infolen < 50) { + infolen = 50; + } + /* find # of digits in package counts to scale output */ digits = 1; tmp = howmany; @@ -389,7 +415,7 @@ void cb_trans_progress(pmtransprog_t event, const char *pkgname, int percent, * done here to figure out the actual number of screen columns used * by the output, and then pad it accordingly so we fill the terminal. */ - /* len = opr len + pkgname len (if available) + space + null */ + /* len = opr len + pkgname len (if available) + space + null */ len = strlen(opr) + ((pkgname) ? strlen(pkgname) : 0) + 2; wcstr = calloc(len, sizeof(wchar_t)); /* print our strings to the alloc'ed memory */ @@ -421,8 +447,8 @@ void cb_trans_progress(pmtransprog_t event, const char *pkgname, int percent, } - printf("(%*d/%*d) %ls%-*s", digits, remain, digits, howmany, - wcstr, padwid, ""); + printf("(%*ld/%*ld) %ls%-*s", digits, (unsigned long)current, + digits, (unsigned long)howmany, wcstr, padwid, ""); free(wcstr); @@ -465,7 +491,7 @@ void cb_dl_progress(const char *filename, off_t file_xfered, off_t file_total) int totaldownload = 0; off_t xfered, total; - float rate = 0.0, timediff = 0.0, f_xfered = 0.0; + double rate = 0.0, timediff = 0.0, f_xfered = 0.0; unsigned int eta_h = 0, eta_m = 0, eta_s = 0; int file_percent = 0, total_percent = 0; char rate_size = 'K', xfered_size = 'K'; @@ -524,7 +550,7 @@ void cb_dl_progress(const char *filename, off_t file_xfered, off_t file_total) } else if(file_xfered == file_total) { /* compute final values */ struct timeval current_time; - float diff_sec, diff_usec; + double diff_sec, diff_usec; gettimeofday(¤t_time, NULL); diff_sec = current_time.tv_sec - initial_time.tv_sec; @@ -550,11 +576,11 @@ void cb_dl_progress(const char *filename, off_t file_xfered, off_t file_total) xfered_last = xfered; } - file_percent = (int)((float)file_xfered) / ((float)file_total) * 100; + file_percent = (file_xfered * 100) / file_total; if(totaldownload) { - total_percent = (int)((float)list_xfered + file_xfered) / - ((float)list_total) * 100; + total_percent = ((list_xfered + file_xfered) * 100) / + list_total; /* if we are at the end, add the completed file to list_xfered */ if(file_xfered == file_total) { @@ -570,7 +596,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/callback.h b/src/pacman/callback.h index c56eaa74..f5bf1c1d 100644 --- a/src/pacman/callback.h +++ b/src/pacman/callback.h @@ -1,7 +1,7 @@ /* * callback.h * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * * This program is free software; you can redistribute it and/or modify @@ -33,7 +33,7 @@ void cb_trans_conv(pmtransconv_t event, void *data1, void *data2, /* callback to handle display of transaction progress */ void cb_trans_progress(pmtransprog_t event, const char *pkgname, int percent, - int howmany, int remain); + size_t howmany, size_t remain); /* callback to handle receipt of total download value */ void cb_dl_total(off_t total); diff --git a/src/pacman/conf.c b/src/pacman/conf.c index 71e8dd80..e2a168ee 100644 --- a/src/pacman/conf.c +++ b/src/pacman/conf.c @@ -1,7 +1,7 @@ /* * conf.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * * This program is free software; you can redistribute it and/or modify diff --git a/src/pacman/conf.h b/src/pacman/conf.h index 8b1bc2e1..92c379fc 100644 --- a/src/pacman/conf.h +++ b/src/pacman/conf.h @@ -1,7 +1,7 @@ /* * conf.h * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * * This program is free software; you can redistribute it and/or modify @@ -111,8 +111,8 @@ enum { /* clean method */ enum { - PM_CLEAN_KEEPINST = 0, /* default */ - PM_CLEAN_KEEPCUR + PM_CLEAN_KEEPINST = 1, + PM_CLEAN_KEEPCUR = (1 << 1) }; /* global config variable */ diff --git a/src/pacman/database.c b/src/pacman/database.c index 9e335fe1..36433f33 100644 --- a/src/pacman/database.c +++ b/src/pacman/database.c @@ -1,7 +1,7 @@ /* * database.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * * This program is free software; you can redistribute it and/or modify @@ -31,8 +31,6 @@ #include "conf.h" #include "util.h" -extern pmdb_t *db_local; - /** * @brief Modify the 'local' package database. * @@ -43,6 +41,7 @@ extern pmdb_t *db_local; int pacman_database(alpm_list_t *targets) { alpm_list_t *i; + pmdb_t *db_local; int retval = 0; pmpkgreason_t reason; @@ -65,6 +64,7 @@ int pacman_database(alpm_list_t *targets) return(1); } + db_local = alpm_option_get_localdb(); for(i = targets; i; i = alpm_list_next(i)) { char *pkgname = i->data; if(alpm_db_set_pkgreason(db_local, pkgname, reason) == -1) { diff --git a/src/pacman/deptest.c b/src/pacman/deptest.c index f043c201..0d265c70 100644 --- a/src/pacman/deptest.c +++ b/src/pacman/deptest.c @@ -1,7 +1,7 @@ /* * deptest.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * * This program is free software; you can redistribute it and/or modify @@ -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_list(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..77a5ee72 100644 --- a/src/pacman/package.c +++ b/src/pacman/package.c @@ -1,7 +1,7 @@ /* * package.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * * This program is free software; you can redistribute it and/or modify @@ -25,6 +25,7 @@ #include <string.h> #include <unistd.h> #include <limits.h> +#include <errno.h> #include <wchar.h> #include <alpm.h> @@ -106,15 +107,15 @@ void dump_pkg_full(pmpkg_t *pkg, int level) list_display(_("Replaces :"), alpm_pkg_get_replaces(pkg)); if(level < 0) { printf(_("Download Size : %6.2f K\n"), - (float)alpm_pkg_get_size(pkg) / 1024.0); + (double)alpm_pkg_get_size(pkg) / 1024.0); } if(level == 0) { printf(_("Compressed Size: %6.2f K\n"), - (float)alpm_pkg_get_size(pkg) / 1024.0); + (double)alpm_pkg_get_size(pkg) / 1024.0); } printf(_("Installed Size : %6.2f K\n"), - (float)alpm_pkg_get_isize(pkg) / 1024.0); + (double)alpm_pkg_get_isize(pkg) / 1024.0); string_display(_("Packager :"), alpm_pkg_get_packager(pkg)); string_display(_("Architecture :"), alpm_pkg_get_arch(pkg)); string_display(_("Build Date :"), bdatestr); @@ -157,6 +158,46 @@ void dump_pkg_sync(pmpkg_t *pkg, const char *treename, int level) dump_pkg_full(pkg, -level); } +static const char *get_backup_file_status(const char *root, + const char *filename, const char *expected_md5) +{ + char path[PATH_MAX]; + char *ret; + + snprintf(path, PATH_MAX, "%s%s", root, filename); + + /* if we find the file, calculate checksums, otherwise it is missing */ + if(access(path, R_OK) == 0) { + char *md5sum = alpm_compute_md5sum(path); + + if(md5sum == NULL) { + pm_fprintf(stderr, PM_LOG_ERROR, + _("could not calculate checksums for %s\n"), path); + return(NULL); + } + + /* if checksums don't match, file has been modified */ + if (strcmp(md5sum, expected_md5) != 0) { + ret = "MODIFIED"; + } else { + ret = "UNMODIFIED"; + } + free(md5sum); + } else { + switch(errno) { + case EACCES: + ret = "UNREADABLE"; + break; + case ENOENT: + ret = "MISSING"; + break; + default: + ret = "UNKNOWN"; + } + } + return(ret); +} + /* Display list of backup files and their modification states */ void dump_pkg_backups(pmpkg_t *pkg) @@ -167,37 +208,17 @@ void dump_pkg_backups(pmpkg_t *pkg) if(alpm_pkg_get_backup(pkg)) { /* package has backup files, so print them */ for(i = alpm_pkg_get_backup(pkg); i; i = alpm_list_next(i)) { - char path[PATH_MAX]; + const char *value; char *str = strdup(alpm_list_getdata(i)); - char *ptr = index(str, '\t'); + char *ptr = strchr(str, '\t'); if(ptr == NULL) { free(str); continue; } *ptr = '\0'; ptr++; - snprintf(path, PATH_MAX-1, "%s%s", root, str); - /* if we find the file, calculate checksums, otherwise it is missing */ - if(access(path, R_OK) == 0) { - char *md5sum = alpm_compute_md5sum(path); - - if(md5sum == NULL) { - pm_fprintf(stderr, PM_LOG_ERROR, - _("could not calculate checksums for %s\n"), path); - free(str); - continue; - } - - /* if checksums don't match, file has been modified */ - if (strcmp(md5sum, ptr)) { - printf(_("MODIFIED\t%s\n"), path); - } else { - printf(_("Not Modified\t%s\n"), path); - } - free(md5sum); - } else { - printf(_("MISSING\t\t%s\n"), path); - } + value = get_backup_file_status(root, str, ptr); + printf("%s\t%s%s\n", value, root, str); free(str); } } else { @@ -242,7 +263,7 @@ void dump_pkg_changelog(pmpkg_t *pkg) } else { /* allocate a buffer to get the changelog back in chunks */ char buf[CLBUF_SIZE]; - int ret = 0; + size_t ret = 0; while((ret = alpm_pkg_changelog_read(buf, CLBUF_SIZE, pkg, fp))) { if(ret < CLBUF_SIZE) { /* if we hit the end of the file, we need to add a null terminator */ diff --git a/src/pacman/package.h b/src/pacman/package.h index 006ea1c9..26333c5a 100644 --- a/src/pacman/package.h +++ b/src/pacman/package.h @@ -1,7 +1,7 @@ /* * package.h * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * * This program is free software; you can redistribute it and/or modify diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c index 7fbffc36..984bd1b7 100644 --- a/src/pacman/pacman.c +++ b/src/pacman/pacman.c @@ -1,7 +1,7 @@ /* * pacman.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * * This program is free software; you can redistribute it and/or modify @@ -26,8 +26,10 @@ #define PACKAGE_VERSION GIT_VERSION #endif +#include <ctype.h> /* isspace */ #include <stdlib.h> /* atoi */ #include <stdio.h> +#include <ctype.h> /* isspace */ #include <limits.h> #include <getopt.h> #include <string.h> @@ -55,16 +57,55 @@ #include "conf.h" #include "package.h" -pmdb_t *db_local; /* list of targets specified on command line */ static alpm_list_t *pm_targets; +/* Used to sort the options in --help */ +static int options_cmp(const void *p1, const void *p2) +{ + const char *s1 = p1; + const char *s2 = p2; + + if(s1 == s2) return(0); + if(!s1) return(-1); + if(!s2) return(1); + /* First skip all spaces in both strings */ + while(isspace((unsigned char)*s1)) { + s1++; + } + while(isspace((unsigned char)*s2)) { + s2++; + } + /* If we compare a long option (--abcd) and a short one (-a), + * the short one always wins */ + if(*s1 == '-' && *s2 == '-') { + s1++; + s2++; + if(*s1 == '-' && *s2 == '-') { + /* two long -> strcmp */ + s1++; + s2++; + } else if(*s2 == '-') { + /* s1 short, s2 long */ + return(-1); + } else if(*s1 == '-') { + /* s1 long, s2 short */ + return(1); + } + /* two short -> strcmp */ + } + + return(strcmp(s1, s2)); +} + /** Display usage/syntax for the specified operation. * @param op the operation code requested * @param myname basename(argv[0]) */ static void usage(int op, const char * const myname) { +#define addlist(s) (list = alpm_list_add(list, s)) + alpm_list_t *list = NULL, *i; /* prefetch some strings for usage below, which moves a lot of calls * out of gettext. */ char const * const str_opt = _("options"); @@ -89,85 +130,87 @@ static void usage(int op, const char * const myname) if(op == PM_OP_REMOVE) { printf("%s: %s {-R --remove} [%s] <%s>\n", str_usg, myname, str_opt, str_pkg); printf("%s:\n", str_opt); - printf(_(" -c, --cascade remove packages and all packages that depend on them\n")); - printf(_(" -d, --nodeps skip dependency checks\n")); - printf(_(" -k, --dbonly only remove database entries, do not remove files\n")); - printf(_(" -n, --nosave remove configuration files as well\n")); - printf(_(" -s, --recursive remove dependencies also (that won't break packages)\n" + addlist(_(" -c, --cascade remove packages and all packages that depend on them\n")); + addlist(_(" -n, --nosave remove configuration files as well\n")); + addlist(_(" -s, --recursive remove dependencies also (that won't break packages)\n" " (-ss includes explicitly installed dependencies too)\n")); - printf(_(" -u, --unneeded remove unneeded packages (that won't break packages)\n")); - printf(_(" --print only print the targets instead of performing the operation\n")); - printf(_(" --print-format <string>\n" - " specify how the targets should be printed\n")); + addlist(_(" -u, --unneeded remove unneeded packages (that won't break packages)\n")); } else if(op == PM_OP_UPGRADE) { printf("%s: %s {-U --upgrade} [%s] <%s>\n", str_usg, myname, str_opt, str_file); printf("%s:\n", str_opt); - printf(_(" --asdeps install packages as non-explicitly installed\n")); - printf(_(" --asexplicit install packages as explicitly installed\n")); - printf(_(" -d, --nodeps skip dependency checks\n")); - printf(_(" -f, --force force install, overwrite conflicting files\n")); - printf(_(" -k, --dbonly add database entries, do not install or keep existing files\n")); - printf(_(" --print only print the targets instead of performing the operation\n")); - printf(_(" --print-format <string>\n" - " specify how the targets should be printed\n")); } else if(op == PM_OP_QUERY) { printf("%s: %s {-Q --query} [%s] [%s]\n", str_usg, myname, str_opt, str_pkg); printf("%s:\n", str_opt); - printf(_(" -c, --changelog view the changelog of a package\n")); - printf(_(" -d, --deps list packages installed as dependencies [filter]\n")); - printf(_(" -e, --explicit list packages explicitly installed [filter]\n")); - printf(_(" -g, --groups view all members of a package group\n")); - printf(_(" -i, --info view package information (-ii for backup files)\n")); - printf(_(" -k, --check check that the files owned by the package(s) are present\n")); - printf(_(" -l, --list list the contents of the queried package\n")); - printf(_(" -m, --foreign list installed packages not found in sync db(s) [filter]\n")); - printf(_(" -o, --owns <file> query the package that owns <file>\n")); - printf(_(" -p, --file <package> query a package file instead of the database\n")); - printf(_(" -s, --search <regex> search locally-installed packages for matching strings\n")); - printf(_(" -t, --unrequired list packages not required by any package [filter]\n")); - printf(_(" -u, --upgrades list outdated packages [filter]\n")); - printf(_(" -q, --quiet show less information for query and search\n")); + addlist(_(" -c, --changelog view the changelog of a package\n")); + addlist(_(" -d, --deps list packages installed as dependencies [filter]\n")); + addlist(_(" -e, --explicit list packages explicitly installed [filter]\n")); + addlist(_(" -g, --groups view all members of a package group\n")); + addlist(_(" -i, --info view package information (-ii for backup files)\n")); + addlist(_(" -k, --check check that the files owned by the package(s) are present\n")); + addlist(_(" -l, --list list the contents of the queried package\n")); + addlist(_(" -m, --foreign list installed packages not found in sync db(s) [filter]\n")); + addlist(_(" -o, --owns <file> query the package that owns <file>\n")); + addlist(_(" -p, --file <package> query a package file instead of the database\n")); + addlist(_(" -q, --quiet show less information for query and search\n")); + addlist(_(" -s, --search <regex> search locally-installed packages for matching strings\n")); + addlist(_(" -t, --unrequired list packages not required by any package [filter]\n")); + addlist(_(" -u, --upgrades list outdated packages [filter]\n")); } else if(op == PM_OP_SYNC) { printf("%s: %s {-S --sync} [%s] [%s]\n", str_usg, myname, str_opt, str_pkg); printf("%s:\n", str_opt); - printf(_(" --asdeps install packages as non-explicitly installed\n")); - printf(_(" --asexplicit install packages as explicitly installed\n")); - printf(_(" -c, --clean remove old packages from cache directory (-cc for all)\n")); - printf(_(" -d, --nodeps skip dependency checks\n")); - printf(_(" -f, --force force install, overwrite conflicting files\n")); - printf(_(" -g, --groups view all members of a package group\n")); - printf(_(" -i, --info view package information\n")); - printf(_(" -l, --list <repo> view a list of packages in a repo\n")); - printf(_(" -s, --search <regex> search remote repositories for matching strings\n")); - printf(_(" -u, --sysupgrade upgrade installed packages (-uu allows downgrade)\n")); - printf(_(" -w, --downloadonly download packages but do not install/upgrade anything\n")); - printf(_(" -y, --refresh download fresh package databases from the server\n")); - printf(_(" --needed don't reinstall up to date packages\n")); - printf(_(" --ignore <pkg> ignore a package upgrade (can be used more than once)\n")); - printf(_(" --ignoregroup <grp>\n" - " ignore a group upgrade (can be used more than once)\n")); - printf(_(" --print only print the targets instead of performing the operation\n")); - printf(_(" --print-format <string>\n" - " specify how the targets should be printed\n")); - printf(_(" -q, --quiet show less information for query and search\n")); + addlist(_(" -c, --clean remove old packages from cache directory (-cc for all)\n")); + addlist(_(" -g, --groups view all members of a package group\n")); + addlist(_(" -i, --info view package information\n")); + addlist(_(" -l, --list <repo> view a list of packages in a repo\n")); + addlist(_(" -q, --quiet show less information for query and search\n")); + addlist(_(" -s, --search <regex> search remote repositories for matching strings\n")); + addlist(_(" -u, --sysupgrade upgrade installed packages (-uu allows downgrade)\n")); + addlist(_(" -w, --downloadonly download packages but do not install/upgrade anything\n")); + addlist(_(" -y, --refresh download fresh package databases from the server\n")); + addlist(_(" --needed don't reinstall up to date packages\n")); } else if (op == PM_OP_DATABASE) { printf("%s: %s {-D --database} <%s> <%s>\n", str_usg, myname, str_opt, str_pkg); printf("%s:\n", str_opt); - printf(_(" --asdeps mark packages as non-explicitly installed\n")); - printf(_(" --asexplicit mark packages as explicitly installed\n")); + addlist(_(" --asdeps mark packages as non-explicitly installed\n")); + addlist(_(" --asexplicit mark packages as explicitly installed\n")); + } + switch(op) { + case PM_OP_SYNC: + case PM_OP_UPGRADE: + addlist(_(" -f, --force force install, overwrite conflicting files\n")); + addlist(_(" --asdeps install packages as non-explicitly installed\n")); + addlist(_(" --asexplicit install packages as explicitly installed\n")); + addlist(_(" --ignore <pkg> ignore a package upgrade (can be used more than once)\n")); + addlist(_(" --ignoregroup <grp>\n" + " ignore a group upgrade (can be used more than once)\n")); + /* pass through */ + case PM_OP_REMOVE: + addlist(_(" -d, --nodeps skip dependency checks\n")); + addlist(_(" -k, --dbonly only modify database entries, not package files\n")); + addlist(_(" --noprogressbar do not show a progress bar when downloading files\n")); + addlist(_(" --noscriptlet do not execute the install scriptlet if one exists\n")); + addlist(_(" --print only print the targets instead of performing the operation\n")); + addlist(_(" --print-format <string>\n" + " specify how the targets should be printed\n")); + break; } - printf(_(" --config <path> set an alternate configuration file\n")); - printf(_(" --logfile <path> set an alternate log file\n")); - printf(_(" --noconfirm do not ask for any confirmation\n")); - printf(_(" --noprogressbar do not show a progress bar when downloading files\n")); - printf(_(" --noscriptlet do not execute the install scriptlet if one exists\n")); - printf(_(" -v, --verbose be verbose\n")); - printf(_(" --debug display debug messages\n")); - printf(_(" -r, --root <path> set an alternate installation root\n")); - printf(_(" -b, --dbpath <path> set an alternate database location\n")); - printf(_(" --cachedir <dir> set an alternate package cache location\n")); - printf(_(" --arch <arch> set an alternate architecture\n")); + + addlist(_(" -b, --dbpath <path> set an alternate database location\n")); + addlist(_(" -r, --root <path> set an alternate installation root\n")); + addlist(_(" -v, --verbose be verbose\n")); + addlist(_(" --arch <arch> set an alternate architecture\n")); + addlist(_(" --cachedir <dir> set an alternate package cache location\n")); + addlist(_(" --config <path> set an alternate configuration file\n")); + addlist(_(" --debug display debug messages\n")); + addlist(_(" --logfile <path> set an alternate log file\n")); + addlist(_(" --noconfirm do not ask for any confirmation\n")); + } + list = alpm_list_msort(list, alpm_list_count(list), options_cmp); + for (i = list; i; i = alpm_list_next(i)) { + printf("%s", (char *)alpm_list_getdata(i)); } + alpm_list_free(list); +#undef addlist } /** Output pacman version and copyright. @@ -261,7 +304,7 @@ static ssize_t xwrite(int fd, const void *buf, size_t count) * in a consistant state. * @param signum the thrown signal */ -static RETSIGTYPE handler(int signum) +static void handler(int signum) { int out = fileno(stdout); int err = fileno(stderr); @@ -352,6 +395,247 @@ static void setlibpaths(void) #define check_optarg() if(!optarg) { return(1); } +typedef void (*fn_add) (const char *s); + +static int parsearg_util_addlist(fn_add fn) +{ + alpm_list_t *list = NULL, *item = NULL; /* lists for splitting strings */ + + check_optarg(); + list = strsplit(optarg, ','); + for(item = list; item; item = alpm_list_next(item)) { + fn((char *)alpm_list_getdata(item)); + } + FREELIST(list); + return(0); +} + +/** Helper function for parsing operation from command-line arguments. + * @param opt Keycode returned by getopt_long + * @param dryrun If nonzero, application state is NOT changed + * @return 0 if opt was handled, 1 if it was not handled + */ +static int parsearg_op(int opt, int dryrun) +{ + switch(opt) { + /* operations */ + case 'D': + if(dryrun) break; + config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_DATABASE); break; + case 'Q': + if(dryrun) break; + config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_QUERY); break; + case 'R': + if(dryrun) break; + config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_REMOVE); break; + case 'S': + if(dryrun) break; + config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_SYNC); break; + case 'T': + if(dryrun) break; + config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_DEPTEST); break; + case 'U': + if(dryrun) break; + config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_UPGRADE); break; + case 'V': + if(dryrun) break; + config->version = 1; break; + case 'h': + if(dryrun) break; + config->help = 1; break; + default: + return(1); + } + return(0); +} + +/** Helper functions for parsing command-line arguments. + * @param opt Keycode returned by getopt_long + * @return 0 on success, 1 on failure + */ +static int parsearg_global(int opt) +{ + switch(opt) { + case OP_ARCH: check_optarg(); setarch(optarg); break; + case OP_ASK: + check_optarg(); + config->noask = 1; + config->ask = (unsigned int)atoi(optarg); + break; + case OP_CACHEDIR: + check_optarg(); + if(alpm_option_add_cachedir(optarg) != 0) { + pm_printf(PM_LOG_ERROR, _("problem adding cachedir '%s' (%s)\n"), + optarg, alpm_strerrorlast()); + return(1); + } + break; + case OP_CONFIG: + check_optarg(); + if(config->configfile) { + free(config->configfile); + } + config->configfile = strndup(optarg, PATH_MAX); + break; + case OP_DEBUG: + /* debug levels are made more 'human readable' than using a raw logmask + * here, error and warning are set in config_new, though perhaps a + * --quiet option will remove these later */ + if(optarg) { + unsigned short debug = (unsigned short)atoi(optarg); + switch(debug) { + case 2: + config->logmask |= PM_LOG_FUNCTION; /* fall through */ + case 1: + config->logmask |= PM_LOG_DEBUG; + break; + default: + pm_printf(PM_LOG_ERROR, _("'%s' is not a valid debug level\n"), + optarg); + return(1); + } + } else { + config->logmask |= PM_LOG_DEBUG; + } + /* progress bars get wonky with debug on, shut them off */ + config->noprogressbar = 1; + break; + case OP_LOGFILE: + check_optarg(); + config->logfile = strndup(optarg, PATH_MAX); + break; + case OP_NOCONFIRM: config->noconfirm = 1; break; + case 'b': + check_optarg(); + config->dbpath = strdup(optarg); + break; + case 'r': check_optarg(); config->rootdir = strdup(optarg); break; + case 'v': (config->verbose)++; break; + default: return(1); + } + return(0); +} + +static int parsearg_database(int opt) +{ + switch(opt) { + case OP_ASDEPS: config->flags |= PM_TRANS_FLAG_ALLDEPS; break; + case OP_ASEXPLICIT: config->flags |= PM_TRANS_FLAG_ALLEXPLICIT; break; + default: return(1); + } + return(0); +} + +static int parsearg_query(int opt) +{ + switch(opt) { + case 'c': config->op_q_changelog = 1; break; + case 'd': config->op_q_deps = 1; break; + case 'e': config->op_q_explicit = 1; break; + case 'g': (config->group)++; break; + case 'i': (config->op_q_info)++; break; + case 'k': config->op_q_check = 1; break; + case 'l': config->op_q_list = 1; break; + case 'm': config->op_q_foreign = 1; break; + case 'o': config->op_q_owns = 1; break; + case 'p': config->op_q_isfile = 1; break; + case 'q': config->quiet = 1; break; + case 's': config->op_q_search = 1; break; + case 't': config->op_q_unrequired = 1; break; + case 'u': config->op_q_upgrade = 1; break; + default: return(1); + } + return(0); +} + +/* options common to -S -R -U */ +static int parsearg_trans(int opt) +{ + switch(opt) { + case 'd': + if(config->flags & PM_TRANS_FLAG_NODEPVERSION) { + config->flags |= PM_TRANS_FLAG_NODEPS; + } else { + config->flags |= PM_TRANS_FLAG_NODEPVERSION; + } + break; + case 'k': config->flags |= PM_TRANS_FLAG_DBONLY; break; + case OP_NOPROGRESSBAR: config->noprogressbar = 1; break; + case OP_NOSCRIPTLET: config->flags |= PM_TRANS_FLAG_NOSCRIPTLET; break; + case 'p': config->print = 1; break; + case OP_PRINTFORMAT: + check_optarg(); + config->print_format = strdup(optarg); + break; + default: return(1); + } + return(0); +} + +static int parsearg_remove(int opt) +{ + if (parsearg_trans(opt) == 0) + return(0); + switch(opt) { + case 'c': config->flags |= PM_TRANS_FLAG_CASCADE; break; + case 'n': config->flags |= PM_TRANS_FLAG_NOSAVE; break; + case 's': + if(config->flags & PM_TRANS_FLAG_RECURSE) { + config->flags |= PM_TRANS_FLAG_RECURSEALL; + } else { + config->flags |= PM_TRANS_FLAG_RECURSE; + } + break; + case 'u': config->flags |= PM_TRANS_FLAG_UNNEEDED; break; + default: return(1); + } + return(0); +} + +/* options common to -S -U */ +static int parsearg_upgrade(int opt) +{ + if (parsearg_trans(opt) == 0) + return(0); + switch(opt) { + case 'f': config->flags |= PM_TRANS_FLAG_FORCE; break; + case OP_ASDEPS: config->flags |= PM_TRANS_FLAG_ALLDEPS; break; + case OP_ASEXPLICIT: config->flags |= PM_TRANS_FLAG_ALLEXPLICIT; break; + case OP_IGNORE: + parsearg_util_addlist(alpm_option_add_ignorepkg); + break; + case OP_IGNOREGROUP: + parsearg_util_addlist(alpm_option_add_ignoregrp); + break; + default: return(1); + } + return(0); +} + +static int parsearg_sync(int opt) +{ + if (parsearg_upgrade(opt) == 0) + return(0); + switch(opt) { + case OP_NEEDED: config->flags |= PM_TRANS_FLAG_NEEDED; break; + case 'c': (config->op_s_clean)++; break; + case 'g': (config->group)++; break; + case 'i': (config->op_s_info)++; break; + case 'l': config->op_q_list = 1; break; + case 'q': config->quiet = 1; break; + case 's': config->op_s_search = 1; break; + case 'u': (config->op_s_upgrade)++; break; + case 'w': + config->op_s_downloadonly = 1; + config->flags |= PM_TRANS_FLAG_DOWNLOADONLY; + config->flags |= PM_TRANS_FLAG_NOCONFLICTS; + break; + case 'y': (config->op_s_sync)++; break; + default: return(1); + } + return(0); +} + /** Parse command-line arguments for each operation. * @param argc argc * @param argv argv @@ -361,6 +645,8 @@ static int parseargs(int argc, char *argv[]) { int opt; int option_index = 0; + int result; + const char *optstring = "DQRSTUVb:cdefghiklmnopqr:stuvwy"; static struct option opts[] = { {"database", no_argument, 0, 'D'}, @@ -418,175 +704,23 @@ static int parseargs(int argc, char *argv[]) {0, 0, 0, 0} }; - while((opt = getopt_long(argc, argv, "DQRSTUVb:cdefghiklmnopqr:stuvwy", opts, &option_index))) { - alpm_list_t *list = NULL, *item = NULL; /* lists for splitting strings */ - + /* parse operation */ + while((opt = getopt_long(argc, argv, optstring, opts, &option_index))) { if(opt < 0) { break; + } else if(opt == 0) { + continue; + } else if(opt == '?') { + /* unknown option, getopt printed an error */ + return(1); } - switch(opt) { - case 0: break; - case OP_NOCONFIRM: config->noconfirm = 1; break; - case OP_CONFIG: - check_optarg(); - if(config->configfile) { - free(config->configfile); - } - config->configfile = strndup(optarg, PATH_MAX); - break; - case OP_IGNORE: - check_optarg(); - list = strsplit(optarg, ','); - for(item = list; item; item = alpm_list_next(item)) { - alpm_option_add_ignorepkg((char *)alpm_list_getdata(item)); - } - FREELIST(list); - break; - case OP_DEBUG: - /* debug levels are made more 'human readable' than using a raw logmask - * here, error and warning are set in config_new, though perhaps a - * --quiet option will remove these later */ - if(optarg) { - unsigned short debug = atoi(optarg); - switch(debug) { - case 2: - config->logmask |= PM_LOG_FUNCTION; /* fall through */ - case 1: - config->logmask |= PM_LOG_DEBUG; - break; - default: - pm_printf(PM_LOG_ERROR, _("'%s' is not a valid debug level\n"), - optarg); - return(1); - } - } else { - config->logmask |= PM_LOG_DEBUG; - } - /* progress bars get wonky with debug on, shut them off */ - config->noprogressbar = 1; - break; - case OP_NOPROGRESSBAR: config->noprogressbar = 1; break; - case OP_NOSCRIPTLET: config->flags |= PM_TRANS_FLAG_NOSCRIPTLET; break; - case OP_ASK: - check_optarg(); - config->noask = 1; - config->ask = atoi(optarg); - break; - case OP_CACHEDIR: - check_optarg(); - if(alpm_option_add_cachedir(optarg) != 0) { - pm_printf(PM_LOG_ERROR, _("problem adding cachedir '%s' (%s)\n"), - optarg, alpm_strerrorlast()); - return(1); - } - break; - case OP_ASDEPS: - config->flags |= PM_TRANS_FLAG_ALLDEPS; - break; - case OP_LOGFILE: - check_optarg(); - config->logfile = strndup(optarg, PATH_MAX); - break; - case OP_IGNOREGROUP: - check_optarg(); - list = strsplit(optarg, ','); - for(item = list; item; item = alpm_list_next(item)) { - alpm_option_add_ignoregrp((char *)alpm_list_getdata(item)); - } - FREELIST(list); - break; - case OP_NEEDED: config->flags |= PM_TRANS_FLAG_NEEDED; break; - case OP_ASEXPLICIT: - config->flags |= PM_TRANS_FLAG_ALLEXPLICIT; - break; - case OP_ARCH: - check_optarg(); - setarch(optarg); - break; - case OP_PRINTFORMAT: - check_optarg(); - config->print_format = strdup(optarg); - break; - case 'D': config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_DATABASE); break; - case 'Q': config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_QUERY); break; - case 'R': config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_REMOVE); break; - case 'S': config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_SYNC); break; - case 'T': config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_DEPTEST); break; - case 'U': config->op = (config->op != PM_OP_MAIN ? 0 : PM_OP_UPGRADE); break; - case 'V': config->version = 1; break; - case 'b': - check_optarg(); - config->dbpath = strdup(optarg); - break; - case 'c': - (config->op_s_clean)++; - config->flags |= PM_TRANS_FLAG_CASCADE; - config->op_q_changelog = 1; - break; - case 'd': - config->op_q_deps = 1; - config->flags |= PM_TRANS_FLAG_NODEPS; - break; - case 'e': - config->op_q_explicit = 1; - break; - case 'f': config->flags |= PM_TRANS_FLAG_FORCE; break; - case 'g': (config->group)++; break; - case 'h': config->help = 1; break; - case 'i': (config->op_q_info)++; (config->op_s_info)++; break; - case 'k': - config->flags |= PM_TRANS_FLAG_DBONLY; - config->op_q_check = 1; - break; - case 'l': config->op_q_list = 1; break; - case 'm': config->op_q_foreign = 1; break; - case 'n': config->flags |= PM_TRANS_FLAG_NOSAVE; break; - case 'o': config->op_q_owns = 1; break; - case 'p': - config->op_q_isfile = 1; - config->print = 1; - break; - case 'q': - config->quiet = 1; - break; - case 'r': - check_optarg(); - config->rootdir = strdup(optarg); - break; - case 's': - config->op_s_search = 1; - config->op_q_search = 1; - if(config->flags & PM_TRANS_FLAG_RECURSE) { - config->flags |= PM_TRANS_FLAG_RECURSEALL; - } else { - config->flags |= PM_TRANS_FLAG_RECURSE; - } - break; - case 't': - config->op_q_unrequired = 1; - break; - case 'u': - (config->op_s_upgrade)++; - config->op_q_upgrade = 1; - config->flags |= PM_TRANS_FLAG_UNNEEDED; - break; - case 'v': (config->verbose)++; break; - case 'w': - config->op_s_downloadonly = 1; - config->flags |= PM_TRANS_FLAG_DOWNLOADONLY; - config->flags |= PM_TRANS_FLAG_NOCONFLICTS; - break; - case 'y': (config->op_s_sync)++; break; - case '?': return(1); - default: return(1); - } + parsearg_op(opt, 0); } if(config->op == 0) { pm_printf(PM_LOG_ERROR, _("only one operation may be used at a time\n")); return(1); } - if(config->help) { usage(config->op, mbasename(argv[0])); return(2); @@ -596,6 +730,55 @@ static int parseargs(int argc, char *argv[]) return(2); } + /* parse all other options */ + optind = 1; + while((opt = getopt_long(argc, argv, optstring, opts, &option_index))) { + if(opt < 0) { + break; + } else if(opt == 0) { + continue; + } else if(opt == '?') { + /* this should have failed during first pass already */ + return(1); + } else if(parsearg_op(opt, 1) == 0) { /* opt is an operation */ + continue; + } + + switch(config->op) { + case PM_OP_DATABASE: + result = parsearg_database(opt); + break; + case PM_OP_QUERY: + result = parsearg_query(opt); + break; + case PM_OP_REMOVE: + result = parsearg_remove(opt); + break; + case PM_OP_SYNC: + result = parsearg_sync(opt); + break; + case PM_OP_DEPTEST: + result = 1; + break; + case PM_OP_UPGRADE: + result = parsearg_upgrade(opt); + break; + default: + pm_printf(PM_LOG_ERROR, _("no operation specified (use -h for help)\n")); + return(1); + } + if (result == 0) + continue; + + /* fall back to global options */ + result = parsearg_global(opt); + if(result != 0) { + /* global option parsing failed, abort */ + pm_printf(PM_LOG_ERROR, _("invalid option\n")); + return(result); + } + } + while(optind < argc) { /* add the target to our target array */ pm_targets = alpm_list_add(pm_targets, strdup(argv[optind])); @@ -615,6 +798,18 @@ static void option_add_syncfirst(const char *name) { config->syncfirst = alpm_list_add(config->syncfirst, strdup(name)); } +/* helper for being used with setrepeatingoption */ +static void option_add_cleanmethod(const char *value) { + if (strcmp(value, "KeepInstalled") == 0) { + config->cleanmethod |= PM_CLEAN_KEEPINST; + } else if (strcmp(value, "KeepCurrent") == 0) { + config->cleanmethod |= PM_CLEAN_KEEPCUR; + } else { + pm_printf(PM_LOG_ERROR, _("invalid value for 'CleanMethod' : '%s'\n"), + value); + } +} + /** Add repeating options such as NoExtract, NoUpgrade, etc to libalpm * settings. Refactored out of the parseconfig code since all of them did * the exact same thing and duplicated code. @@ -668,7 +863,7 @@ static char *get_tempfile(const char *path, const char *filename) { } /** External fetch callback */ -int download_with_xfercommand(const char *url, const char *localpath, +static int download_with_xfercommand(const char *url, const char *localpath, int force) { int ret = 0; int retval; @@ -676,6 +871,7 @@ int download_with_xfercommand(const char *url, const char *localpath, struct stat st; char *parsedcmd,*tempcmd; char cwd[PATH_MAX]; + int restore_cwd = 0; char *destfile, *tempfile, *filename; if(!config->xfercommand) { @@ -708,8 +904,14 @@ int download_with_xfercommand(const char *url, const char *localpath, parsedcmd = strreplace(tempcmd, "%u", url); free(tempcmd); + /* save the cwd so we can restore it later */ + if(getcwd(cwd, PATH_MAX) == NULL) { + pm_printf(PM_LOG_ERROR, _("could not get current working directory\n")); + } else { + restore_cwd = 1; + } + /* cwd to the download directory */ - getcwd(cwd, PATH_MAX); if(chdir(localpath)) { pm_printf(PM_LOG_WARNING, _("could not chdir to download directory %s\n"), localpath); ret = -1; @@ -736,7 +938,11 @@ int download_with_xfercommand(const char *url, const char *localpath, } cleanup: - chdir(cwd); + /* restore the old cwd if we have it */ + if(restore_cwd && chdir(cwd) != 0) { + pm_printf(PM_LOG_ERROR, _("could not change directory to %s (%s)\n"), cwd, strerror(errno)); + } + if(ret == -1) { /* hack to let an user the time to cancel a download */ sleep(2); @@ -767,6 +973,8 @@ static int _parse_options(char *key, char *value) } else if(strcmp(key, "TotalDownload") == 0) { config->totaldownload = 1; pm_printf(PM_LOG_DEBUG, "config: totaldownload\n"); + } else if(strcmp(key, "CheckSpace") == 0) { + alpm_option_set_checkspace(1); } else { pm_printf(PM_LOG_ERROR, _("directive '%s' without value not recognized\n"), key); return(1); @@ -818,15 +1026,7 @@ static int _parse_options(char *key, char *value) alpm_option_set_fetchcb(download_with_xfercommand); pm_printf(PM_LOG_DEBUG, "config: xfercommand: %s\n", value); } else if (strcmp(key, "CleanMethod") == 0) { - if (strcmp(value, "KeepInstalled") == 0) { - config->cleanmethod = PM_CLEAN_KEEPINST; - } else if (strcmp(value, "KeepCurrent") == 0) { - config->cleanmethod = PM_CLEAN_KEEPCUR; - } else { - pm_printf(PM_LOG_ERROR, _("invalid value for 'CleanMethod' : '%s'\n"), value); - return(1); - } - pm_printf(PM_LOG_DEBUG, "config: cleanmethod: %s\n", value); + setrepeatingoption(value, "CleanMethod", option_add_cleanmethod); } else { pm_printf(PM_LOG_ERROR, _("directive '%s' with a value not recognized\n"), key); return(1); @@ -989,7 +1189,7 @@ static int _parseconfig(const char *file, const char *givensection, file, linenum, value); break; default: - for(int gindex = 0; gindex < globbuf.gl_pathc; gindex++) { + for(size_t gindex = 0; gindex < globbuf.gl_pathc; gindex++) { pm_printf(PM_LOG_DEBUG, "config file %s, line %d: including %s\n", file, linenum, globbuf.gl_pathv[gindex]); _parseconfig(globbuf.gl_pathv[gindex], section, db); @@ -1161,6 +1361,39 @@ int main(int argc, char *argv[]) cleanup(ret); } + /* we also support reading targets from stdin */ + if(!isatty(fileno(stdin))) { + char line[PATH_MAX]; + int i = 0; + while(i < PATH_MAX && (line[i] = (char)fgetc(stdin)) != EOF) { + if(isspace((unsigned char)line[i])) { + /* avoid adding zero length arg when multiple spaces separate args */ + if(i > 0) { + line[i] = '\0'; + pm_targets = alpm_list_add(pm_targets, strdup(line)); + i = 0; + } + } else { + i++; + } + } + /* check for buffer overflow */ + if (i >= PATH_MAX) { + pm_printf(PM_LOG_ERROR, _("buffer overflow detected in arg parsing\n")); + cleanup(EXIT_FAILURE); + } + + /* end of stream -- check for data still in line buffer */ + if(i > 0) { + line[i] = '\0'; + pm_targets = alpm_list_add(pm_targets, strdup(line)); + } + if (!freopen(ctermid(NULL), "r", stdin)) { + pm_printf(PM_LOG_ERROR, _("failed to reopen stdin for reading: (%s)\n"), + strerror(errno)); + } + } + /* parse the config file */ ret = parseconfig(config->configfile); if(ret != 0) { @@ -1209,14 +1442,6 @@ int main(int argc, char *argv[]) list_display("Targets :", pm_targets); } - /* Opening local database */ - db_local = alpm_db_register_local(); - if(db_local == NULL) { - pm_printf(PM_LOG_ERROR, _("could not register 'local' database (%s)\n"), - alpm_strerrorlast()); - cleanup(EXIT_FAILURE); - } - /* Log commandline */ if(needs_root()) { cl_to_log(argc, argv); diff --git a/src/pacman/pacman.h b/src/pacman/pacman.h index f8443cf6..f5b972bb 100644 --- a/src/pacman/pacman.h +++ b/src/pacman/pacman.h @@ -1,7 +1,7 @@ /* * pacman.h * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * * This program is free software; you can redistribute it and/or modify diff --git a/src/pacman/query.c b/src/pacman/query.c index 7f064f23..734875be 100644 --- a/src/pacman/query.c +++ b/src/pacman/query.c @@ -1,7 +1,7 @@ /* * query.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * * This program is free software; you can redistribute it and/or modify @@ -37,13 +37,11 @@ #include "conf.h" #include "util.h" -extern pmdb_t *db_local; - -static char *resolve_path(const char* file) +static char *resolve_path(const char *file) { char *str = NULL; - str = calloc(PATH_MAX+1, sizeof(char)); + str = calloc(PATH_MAX + 1, sizeof(char)); if(!str) { return(NULL); } @@ -97,8 +95,12 @@ static int search_path(char **filename, struct stat *bufptr) static int query_fileowner(alpm_list_t *targets) { int ret = 0; - char *filename; + char path[PATH_MAX]; + const char *root; + char *append; + size_t max_length; alpm_list_t *t; + pmdb_t *db_local; /* This code is here for safety only */ if(targets == NULL) { @@ -106,13 +108,24 @@ static int query_fileowner(alpm_list_t *targets) return(1); } + /* Set up our root path buffer. We only need to copy the location of root in + * once, then we can just overwrite whatever file was there on the previous + * iteration. */ + root = alpm_option_get_root(); + strncpy(path, root, PATH_MAX - 1); + append = path + strlen(path); + max_length = PATH_MAX - (append - path) - 1; + + db_local = alpm_option_get_localdb(); + for(t = targets; t; t = alpm_list_next(t)) { + char *filename, *dname, *rpath; + const char *bname; + struct stat buf; + alpm_list_t *i; int found = 0; + filename = strdup(alpm_list_getdata(t)); - char *bname, *dname, *rpath; - const char *root; - struct stat buf; - alpm_list_t *i, *j; if(lstat(filename, &buf) == -1) { /* if it is not a path but a program name, then check in PATH */ @@ -144,32 +157,38 @@ static int query_fileowner(alpm_list_t *targets) bname = mbasename(filename); dname = mdirname(filename); rpath = resolve_path(dname); - free(dname); - if(!rpath) { + /* this odd conditional is to ensure files in '/' can be checked */ + if(!rpath && strcmp(dname, "") != 0) { pm_fprintf(stderr, PM_LOG_ERROR, _("cannot determine real path for '%s': %s\n"), filename, strerror(errno)); free(filename); + free(dname); free(rpath); ret++; continue; } + free(dname); - root = alpm_option_get_root(); - - for(i = alpm_db_get_pkgcache(db_local); i && !found; i = alpm_list_next(i)) { + for(i = alpm_db_get_pkgcache_list(db_local); i && !found; i = alpm_list_next(i)) { + alpm_list_t *j; pmpkg_t *info = alpm_list_getdata(i); for(j = alpm_pkg_get_files(info); j && !found; j = alpm_list_next(j)) { - char path[PATH_MAX], *ppath, *pdname; - snprintf(path, PATH_MAX, "%s%s", - root, (const char *)alpm_list_getdata(j)); + char *ppath, *pdname; + const char *pkgfile = alpm_list_getdata(j); /* avoid the costly resolve_path usage if the basenames don't match */ - if(strcmp(mbasename(path), bname) != 0) { + if(strcmp(mbasename(pkgfile), bname) != 0) { continue; } + if(strlen(pkgfile) > max_length) { + pm_fprintf(stderr, PM_LOG_ERROR, _("Path too long: %s%s\n"), root, pkgfile); + } + /* concatenate our file and the root path */ + strcpy(append, pkgfile); + pdname = mdirname(path); ppath = resolve_path(pdname); free(pdname); @@ -202,13 +221,14 @@ static int query_search(alpm_list_t *targets) { alpm_list_t *i, *searchlist; int freelist; + pmdb_t *db_local = alpm_option_get_localdb(); /* if we have a targets list, search for packages matching it */ if(targets) { searchlist = alpm_db_search(db_local, targets); freelist = 1; } else { - searchlist = alpm_db_get_pkgcache(db_local); + searchlist = alpm_db_get_pkgcache_list(db_local); freelist = 0; } if(searchlist == NULL) { @@ -268,6 +288,8 @@ static int query_group(alpm_list_t *targets) alpm_list_t *i, *j; char *grpname = NULL; int ret = 0; + pmdb_t *db_local = alpm_option_get_localdb(); + if(targets == NULL) { for(j = alpm_db_get_grpcache(db_local); j; j = alpm_list_next(j)) { pmgrp_t *grp = alpm_list_getdata(j); @@ -406,8 +428,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, ", + (unsigned long)allfiles), pkgname, allfiles); + printf(_n("%d missing file\n", "%d missing files\n", + (unsigned long)errors), errors); } return(errors != 0 ? 1 : 0); @@ -451,6 +475,7 @@ int pacman_query(alpm_list_t *targets) int match = 0; alpm_list_t *i; pmpkg_t *pkg = NULL; + pmdb_t *db_local; /* First: operations that do not require targets */ @@ -475,6 +500,8 @@ int pacman_query(alpm_list_t *targets) } } + db_local = alpm_option_get_localdb(); + /* operations on all packages in the local DB * valid: no-op (plain -Q), list, info, check * invalid: isfile, owns */ @@ -484,7 +511,7 @@ int pacman_query(alpm_list_t *targets) return(1); } - for(i = alpm_db_get_pkgcache(db_local); i; i = alpm_list_next(i)) { + for(i = alpm_db_get_pkgcache_list(db_local); i; i = alpm_list_next(i)) { pkg = alpm_list_getdata(i); if(filter(pkg)) { int value = display(pkg); diff --git a/src/pacman/remove.c b/src/pacman/remove.c index 1e2cadaf..fb02e242 100644 --- a/src/pacman/remove.c +++ b/src/pacman/remove.c @@ -1,7 +1,7 @@ /* * remove.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * * This program is free software; you can redistribute it and/or modify @@ -31,7 +31,35 @@ #include "util.h" #include "conf.h" -extern pmdb_t *db_local; +static int remove_target(char *target) +{ + pmpkg_t *info; + pmdb_t *db_local = alpm_option_get_localdb(); + alpm_list_t *p; + + if((info = alpm_db_get_pkg(db_local, target)) != NULL) { + if(alpm_remove_pkg(info) == -1) { + pm_fprintf(stderr, PM_LOG_ERROR, "'%s': %s\n", target, alpm_strerrorlast()); + return(-1); + } + return(0); + } + + /* fallback to group */ + pmgrp_t *grp = alpm_db_readgrp(db_local, target); + if(grp == NULL) { + pm_fprintf(stderr, PM_LOG_ERROR, "'%s': target not found\n", target); + return(-1); + } + for(p = alpm_grp_get_pkgs(grp); p; p = alpm_list_next(p)) { + pmpkg_t *pkg = alpm_list_getdata(p); + if(alpm_remove_pkg(pkg) == -1) { + pm_fprintf(stderr, PM_LOG_ERROR, "'%s': %s\n", target, alpm_strerrorlast()); + return(-1); + } + } + return(0); +} /** * @brief Remove a specified list of packages. @@ -64,8 +92,7 @@ int pacman_remove(alpm_list_t *targets) } else { targ = target; } - if(alpm_remove_target(targ) == -1) { - pm_fprintf(stderr, PM_LOG_ERROR, "'%s': %s\n", targ, alpm_strerrorlast()); + if(remove_target(targ) == -1) { retval = 1; goto cleanup; } diff --git a/src/pacman/sync.c b/src/pacman/sync.c index b2994389..7af1667a 100644 --- a/src/pacman/sync.c +++ b/src/pacman/sync.c @@ -1,7 +1,7 @@ /* * sync.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * * This program is free software; you can redistribute it and/or modify @@ -37,13 +37,12 @@ #include "package.h" #include "conf.h" -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; struct dirent *ent; + alpm_list_t *syncdbs; dir = opendir(dbpath); if(dir == NULL) { @@ -51,39 +50,56 @@ static int sync_cleandb(const char *dbpath, int keep_used) { return(1); } + syncdbs = alpm_option_get_syncdbs(); + rewinddir(dir); /* step through the directory one file at a time */ while((ent = readdir(dir)) != NULL) { char path[PATH_MAX]; struct stat buf; - 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) { - syncdbs = alpm_option_get_syncdbs(); + alpm_list_t *i; + len = strlen(dname); + char *dbname = strndup(dname, len - 3); 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); } @@ -112,9 +128,9 @@ static int sync_cleandb_all(void) { if(!yesno(_("Do you want to remove unused repositories?"))) { 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/ */ + /* The sync dbs were previously put in dbpath/ but are now in dbpath/sync/. + * We will clean everything in dbpath/ except local/, sync/ and db.lck, and + * only the unused sync dbs in dbpath/sync/ */ ret += sync_cleandb(dbpath, 0); sprintf(newdbpath, "%s%s", dbpath, "sync/"); @@ -128,27 +144,28 @@ static int sync_cleancache(int level) { alpm_list_t *i; alpm_list_t *sync_dbs = alpm_option_get_syncdbs(); + pmdb_t *db_local = alpm_option_get_localdb(); int ret = 0; for(i = alpm_option_get_cachedirs(); i; i = alpm_list_next(i)) { printf(_("Cache directory: %s\n"), (char*)alpm_list_getdata(i)); } + if(!config->cleanmethod) { + /* default to KeepInstalled if user did not specify */ + config->cleanmethod = PM_CLEAN_KEEPINST; + } + if(level == 1) { - switch(config->cleanmethod) { - case PM_CLEAN_KEEPINST: - if(!yesno(_("Do you want to remove uninstalled packages from cache?"))) { - return(0); - } - break; - case PM_CLEAN_KEEPCUR: - if(!yesno(_("Do you want to remove outdated packages from cache?"))) { - return(0); - } - break; - default: - /* this should not happen : the config parsing doesn't set any other value */ - return(1); + printf(_("Packages to keep:\n")); + if(config->cleanmethod & PM_CLEAN_KEEPINST) { + printf(_(" All locally installed packages\n")); + } + if(config->cleanmethod & PM_CLEAN_KEEPCUR) { + printf(_(" All current sync database packages\n")); + } + if(!yesno(_("Do you want to remove all other packages from cache?"))) { + return(0); } printf(_("removing old packages from cache...\n")); } else { @@ -176,9 +193,10 @@ static int sync_cleancache(int level) char path[PATH_MAX]; int delete = 1; pmpkg_t *localpkg = NULL, *pkg = NULL; + const char *local_name, *local_version; 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 */ @@ -202,32 +220,33 @@ static int sync_cleancache(int level) } continue; } - switch(config->cleanmethod) { - case PM_CLEAN_KEEPINST: - /* check if this package is in the local DB */ - pkg = alpm_db_get_pkg(db_local, alpm_pkg_get_name(localpkg)); - if(pkg != NULL && alpm_pkg_vercmp(alpm_pkg_get_version(localpkg), + local_name = alpm_pkg_get_name(localpkg); + local_version = alpm_pkg_get_version(localpkg); + + if(config->cleanmethod & PM_CLEAN_KEEPINST) { + /* check if this package is in the local DB */ + pkg = alpm_db_get_pkg(db_local, local_name); + if(pkg != NULL && alpm_pkg_vercmp(local_version, + alpm_pkg_get_version(pkg)) == 0) { + /* package was found in local DB and version matches, keep it */ + pm_printf(PM_LOG_DEBUG, "pkg %s-%s found in local db\n", + local_name, local_version); + delete = 0; + } + } + if(config->cleanmethod & PM_CLEAN_KEEPCUR) { + /* check if this package is in a sync DB */ + for(j = sync_dbs; j && delete; j = alpm_list_next(j)) { + pmdb_t *db = alpm_list_getdata(j); + pkg = alpm_db_get_pkg(db, local_name); + if(pkg != NULL && alpm_pkg_vercmp(local_version, alpm_pkg_get_version(pkg)) == 0) { - /* package was found in local DB and version matches, keep it */ + /* package was found in a sync DB and version matches, keep it */ + pm_printf(PM_LOG_DEBUG, "pkg %s-%s found in sync db\n", + local_name, local_version); delete = 0; } - break; - case PM_CLEAN_KEEPCUR: - /* check if this package is in a sync DB */ - for(j = sync_dbs; j && delete; j = alpm_list_next(j)) { - pmdb_t *db = alpm_list_getdata(j); - pkg = alpm_db_get_pkg(db, alpm_pkg_get_name(localpkg)); - if(pkg != NULL && alpm_pkg_vercmp(alpm_pkg_get_version(localpkg), - alpm_pkg_get_version(pkg)) == 0) { - /* package was found in a sync DB and version matches, keep it */ - delete = 0; - } - } - break; - default: - /* this should not happen : the config parsing doesn't set any other value */ - delete = 0; - break; + } } /* free the local file package */ alpm_pkg_free(localpkg); @@ -279,7 +298,7 @@ static int sync_synctree(int level, alpm_list_t *syncs) return(success > 0); } -static void print_installed(pmpkg_t *pkg) +static void print_installed(pmdb_t *db_local, pmpkg_t *pkg) { const char *pkgname = alpm_pkg_get_name(pkg); const char *pkgver = alpm_pkg_get_version(pkg); @@ -300,6 +319,7 @@ static int sync_search(alpm_list_t *syncs, alpm_list_t *targets) alpm_list_t *i, *j, *ret; int freelist; int found = 0; + pmdb_t *db_local = alpm_option_get_localdb(); for(i = syncs; i; i = alpm_list_next(i)) { pmdb_t *db = alpm_list_getdata(i); @@ -308,7 +328,7 @@ static int sync_search(alpm_list_t *syncs, alpm_list_t *targets) ret = alpm_db_search(db, targets); freelist = 1; } else { - ret = alpm_db_get_pkgcache(db); + ret = alpm_db_get_pkgcache_list(db); freelist = 0; } if(ret == NULL) { @@ -330,7 +350,7 @@ static int sync_search(alpm_list_t *syncs, alpm_list_t *targets) /* print the package size with the output if ShowSize option set */ if(!config->quiet && config->showsize) { /* Convert byte size to MB */ - double mbsize = alpm_pkg_get_size(pkg) / (1024.0 * 1024.0); + double mbsize = (double)alpm_pkg_get_size(pkg) / (1024.0 * 1024.0); printf(" [%.2f MB]", mbsize); } @@ -350,7 +370,7 @@ static int sync_search(alpm_list_t *syncs, alpm_list_t *targets) printf(")"); } - print_installed(pkg); + print_installed(db_local, pkg); /* we need a newline and initial indent first */ printf("\n "); @@ -449,7 +469,7 @@ static int sync_info(alpm_list_t *syncs, alpm_list_t *targets) return(1); } - for(k = alpm_db_get_pkgcache(db); k; k = alpm_list_next(k)) { + for(k = alpm_db_get_pkgcache_list(db); k; k = alpm_list_next(k)) { pmpkg_t *pkg = alpm_list_getdata(k); if(strcmp(alpm_pkg_get_name(pkg), pkgstr) == 0) { @@ -470,7 +490,7 @@ static int sync_info(alpm_list_t *syncs, alpm_list_t *targets) for(j = syncs; j; j = alpm_list_next(j)) { pmdb_t *db = alpm_list_getdata(j); - for(k = alpm_db_get_pkgcache(db); k; k = alpm_list_next(k)) { + for(k = alpm_db_get_pkgcache_list(db); k; k = alpm_list_next(k)) { pmpkg_t *pkg = alpm_list_getdata(k); if(strcmp(alpm_pkg_get_name(pkg), pkgstr) == 0) { @@ -491,7 +511,7 @@ static int sync_info(alpm_list_t *syncs, alpm_list_t *targets) for(i = syncs; i; i = alpm_list_next(i)) { pmdb_t *db = alpm_list_getdata(i); - for(j = alpm_db_get_pkgcache(db); j; j = alpm_list_next(j)) { + for(j = alpm_db_get_pkgcache_list(db); j; j = alpm_list_next(j)) { dump_pkg_sync(alpm_list_getdata(j), alpm_db_get_name(db), config->op_s_info); } } @@ -503,6 +523,7 @@ static int sync_info(alpm_list_t *syncs, alpm_list_t *targets) static int sync_list(alpm_list_t *syncs, alpm_list_t *targets) { alpm_list_t *i, *j, *ls = NULL; + pmdb_t *db_local = alpm_option_get_localdb(); if(targets) { for(i = targets; i; i = alpm_list_next(i)) { @@ -534,13 +555,13 @@ static int sync_list(alpm_list_t *syncs, alpm_list_t *targets) for(i = ls; i; i = alpm_list_next(i)) { pmdb_t *db = alpm_list_getdata(i); - for(j = alpm_db_get_pkgcache(db); j; j = alpm_list_next(j)) { + for(j = alpm_db_get_pkgcache_list(db); j; j = alpm_list_next(j)) { pmpkg_t *pkg = alpm_list_getdata(j); if (!config->quiet) { printf("%s %s %s", alpm_db_get_name(db), alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg)); - print_installed(pkg); + print_installed(db_local, pkg); printf("\n"); } else { printf("%s\n", alpm_pkg_get_name(pkg)); @@ -555,7 +576,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)) { @@ -573,6 +594,82 @@ static alpm_list_t *syncfirst() { return(res); } +static pmdb_t *get_db(const char *dbname) +{ + alpm_list_t *i; + for(i = alpm_option_get_syncdbs(); i; i = i->next) { + pmdb_t *db = i->data; + if(strcmp(alpm_db_get_name(db), dbname) == 0) { + return(db); + } + } + return(NULL); +} + +static int process_pkg(pmpkg_t *pkg) +{ + int ret = alpm_add_pkg(pkg); + + if(ret == -1) { + if(pm_errno == PM_ERR_TRANS_DUP_TARGET + || pm_errno == PM_ERR_PKG_IGNORED) { + /* just skip duplicate or ignored targets */ + pm_printf(PM_LOG_WARNING, _("skipping target: %s\n"), alpm_pkg_get_name(pkg)); + return(0); + } else { + pm_fprintf(stderr, PM_LOG_ERROR, "'%s': %s\n", alpm_pkg_get_name(pkg), + alpm_strerrorlast()); + return(1); + } + } + return(0); +} + +static int process_group(alpm_list_t *dbs, char *group) +{ + int ret = 0; + alpm_list_t *i; + alpm_list_t *pkgs = alpm_find_grp_pkgs(dbs, group); + int count = alpm_list_count(pkgs); + + if(!count) { + pm_fprintf(stderr, PM_LOG_ERROR, _("target not found: %s\n"), group); + return(1); + } + + printf(_(":: There are %d members in group %s:\n"), count, + group); + select_display(pkgs); + char *array = malloc(count); + multiselect_question(array, count); + int n = 0; + for(i = pkgs; i; i = alpm_list_next(i)) { + if(array[n++] == 0) + continue; + pmpkg_t *pkg = alpm_list_getdata(i); + + if(process_pkg(pkg) == 1) { + ret = 1; + goto cleanup; + } + } +cleanup: + alpm_list_free(pkgs); + free(array); + return(ret); +} + +static int process_targname(alpm_list_t *dblist, char *targname) +{ + pmpkg_t *pkg = alpm_find_dbs_satisfier(dblist, targname); + + if(pkg) { + return(process_pkg(pkg)); + } + /* fallback on group */ + return(process_group(dblist, targname)); +} + static int process_target(char *target) { /* process targets */ @@ -580,28 +677,30 @@ static int process_target(char *target) char *targname = strchr(targstring, '/'); char *dbname = NULL; int ret = 0; + alpm_list_t *dblist = NULL; + if(targname) { + pmdb_t *db = NULL; + *targname = '\0'; targname++; dbname = targstring; - ret = alpm_sync_dbtarget(dbname,targname); - } else { - targname = targstring; - ret = alpm_sync_target(targname); - } - - if(ret == -1) { - if(pm_errno == PM_ERR_TRANS_DUP_TARGET - || pm_errno == PM_ERR_PKG_IGNORED) { - /* just skip duplicate or ignored targets */ - pm_printf(PM_LOG_WARNING, _("skipping target: %s\n"), target); - } else { - pm_fprintf(stderr, PM_LOG_ERROR, "'%s': %s\n", target, - alpm_strerrorlast()); + db = get_db(dbname); + if(!db) { + pm_fprintf(stderr, PM_LOG_ERROR, _("database not found: %s\n"), + dbname); ret = 1; + goto cleanup; } + dblist = alpm_list_add(dblist, db); + ret = process_targname(dblist, targname); + alpm_list_free(dblist); + } else { + targname = targstring; + dblist = alpm_option_get_syncdbs(); + ret = process_targname(dblist, targname); } - +cleanup: free(targstring); return(ret); } @@ -666,7 +765,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..8cd29da0 100644 --- a/src/pacman/upgrade.c +++ b/src/pacman/upgrade.c @@ -1,7 +1,7 @@ /* * upgrade.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * * This program is free software; you can redistribute it and/or modify @@ -55,6 +55,8 @@ int pacman_upgrade(alpm_list_t *targets) if(strstr(i->data, "://")) { char *str = alpm_fetch_pkgurl(i->data); if(str == NULL) { + pm_fprintf(stderr, PM_LOG_ERROR, "'%s': %s\n", + (char *)i->data, alpm_strerrorlast()); return(1); } else { free(i->data); @@ -71,9 +73,18 @@ int pacman_upgrade(alpm_list_t *targets) /* add targets to the created transaction */ for(i = targets; i; i = alpm_list_next(i)) { char *targ = alpm_list_getdata(i); - if(alpm_add_target(targ) == -1) { + pmpkg_t *pkg; + + if(alpm_pkg_load(targ, 1, &pkg) != 0) { + pm_fprintf(stderr, PM_LOG_ERROR, "'%s': %s\n", + targ, alpm_strerrorlast()); + trans_release(); + return(1); + } + if(alpm_add_pkg(pkg) == -1) { pm_fprintf(stderr, PM_LOG_ERROR, "'%s': %s\n", targ, alpm_strerrorlast()); + alpm_pkg_free(pkg); trans_release(); return(1); } @@ -112,7 +123,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..c08ebb15 100644 --- a/src/pacman/util.c +++ b/src/pacman/util.c @@ -1,7 +1,7 @@ /* * util.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * * This program is free software; you can redistribute it and/or modify @@ -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); } } @@ -157,25 +157,17 @@ int rmrf(const char *path) } /** Parse the basename of a program from a path. -* Grabbed from the uClibc source. * @param path path to parse basename from * * @return everything following the final '/' */ -char *mbasename(const char *path) +const char *mbasename(const char *path) { - const char *s; - const char *p; - - p = s = path; - - while (*s) { - if (*s++ == '/') { - p = s; - } + const char *last = strrchr(path, '/'); + if(last) { + return(last + 1); } - - return (char *)p; + return(path); } /** Parse the dirname of a program from a path. @@ -275,7 +267,7 @@ char *strtoupper(char *str) char *ptr = str; while(*ptr) { - (*ptr) = toupper((unsigned char)*ptr); + (*ptr) = (char)toupper((unsigned char)*ptr); ptr++; } return str; @@ -355,7 +347,7 @@ char *strreplace(const char *str, const char *needle, const char *replace) q = alpm_list_getdata(i); if(q > p){ /* add chars between this occurence and last occurence, if any */ - strncpy(newp, p, q - p); + strncpy(newp, p, (size_t)(q - p)); newp += q - p; } strncpy(newp, replace, replacesz); @@ -389,7 +381,7 @@ alpm_list_t *strsplit(const char *str, const char splitchar) char *dup = NULL; while((str = strchr(str, splitchar))) { - dup = strndup(prev, str - prev); + dup = strndup(prev, (size_t)(str - prev)); if(dup == NULL) { return(NULL); } @@ -523,29 +515,30 @@ void display_targets(const alpm_list_t *pkgs, int install) for(i = pkgs; i; i = alpm_list_next(i)) { pmpkg_t *pkg = alpm_list_getdata(i); - dlsize += alpm_pkg_download_size(pkg); + if(install) { + dlsize += alpm_pkg_download_size(pkg); + } isize += alpm_pkg_get_isize(pkg); /* print the package size with the output if ShowSize option set */ if(config->showsize) { - double mbsize = 0.0; - mbsize = alpm_pkg_get_size(pkg) / (1024.0 * 1024.0); + double mbsize = (double)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); } /* Convert byte sizes to MB */ - mbdlsize = dlsize / (1024.0 * 1024.0); - mbisize = isize / (1024.0 * 1024.0); + mbdlsize = (double)dlsize / (1024.0 * 1024.0); + mbisize = (double)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 +548,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 +582,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); } } @@ -645,13 +638,11 @@ void print_packages(const alpm_list_t *packages) /* %s : size */ if(strstr(temp,"%s")) { char *size; - double mbsize = 0.0; - mbsize = pkg_get_size(pkg) / (1024.0 * 1024.0); - asprintf(&size, "%.2f", mbsize); + double mbsize = (double)pkg_get_size(pkg) / (1024.0 * 1024.0); + pm_asprintf(&size, "%.2f", mbsize); string = strreplace(temp, "%s", size); free(size); free(temp); - temp = string; } printf("%s\n",string); free(string); @@ -686,11 +677,194 @@ void display_optdepends(pmpkg_t *pkg) } } +static void display_repo_list(const char *dbname, alpm_list_t *list) +{ + const char *prefix= " "; + + printf(":: "); + printf(_("Repository %s\n"), dbname); + list_display(prefix, list); +} + +void select_display(const alpm_list_t *pkglist) +{ + const alpm_list_t *i; + int nth = 1; + alpm_list_t *list = NULL; + char *string = NULL; + const char *dbname = NULL; + + for (i = pkglist; i; i = i->next) { + pmpkg_t *pkg = alpm_list_getdata(i); + pmdb_t *db = alpm_pkg_get_db(pkg); + + if(!dbname) + dbname = alpm_db_get_name(db); + if(strcmp(alpm_db_get_name(db), dbname) != 0) { + display_repo_list(dbname, list); + FREELIST(list); + dbname = alpm_db_get_name(db); + } + string = NULL; + pm_asprintf(&string, "%d) %s", nth, alpm_pkg_get_name(pkg)); + list = alpm_list_add(list, string); + nth++; + } + display_repo_list(dbname, list); + FREELIST(list); +} + +static int parseindex(char *s, int *val, int min, int max) +{ + char *endptr = NULL; + int n = strtol(s, &endptr, 10); + if(*endptr == '\0') { + if(n < min || n > max) { + fprintf(stderr, _("Invalid value: %d is not between %d and %d\n"), + n, min, max); + return(-1); + } + *val = n; + return(0); + } else { + fprintf(stderr, _("Invalid number: %s\n"), s); + return(-1); + } +} + +static int multiselect_parse(char *array, int count, char *response) +{ + char *str, *saveptr; + + for (str = response; ; str = NULL) { + int include = 1; + int start, end; + char *ends = NULL; + char *starts = strtok_r(str, " ", &saveptr); + + if (starts == NULL) + break; + strtrim(starts); + int len = strlen(starts); + if(len == 0) + continue; + + if (*starts == '^') { + starts++; + len--; + include = 0; + } else if(str) { + /* if first token is including, we unselect all targets */ + memset(array, 0, count); + } + + if(len > 1) { + /* check for range */ + char *p; + if((p = strchr(starts+1, '-'))) { + *p = 0; + ends = p+1; + } + } + + if(parseindex(starts, &start, 1, count) != 0) + return(-1); + + if(!ends) { + array[start-1] = include; + } else { + if(parseindex(ends, &end, start, count) != 0) + return(-1); + for(int d = start; d <= end; d++) { + array[d-1] = include; + } + } + } + + return(0); +} + +int multiselect_question(char *array, int count) +{ + char response[64]; + FILE *stream; + + if(config->noconfirm) { + stream = stdout; + } else { + /* Use stderr so questions are always displayed when redirecting output */ + stream = stderr; + } + + while(1) { + memset(array, 1, count); + + fprintf(stream, "\n"); + fprintf(stream, _("Enter a selection (default=all)")); + fprintf(stream, ": "); + + if(config->noconfirm) { + fprintf(stream, "\n"); + break; + } + + if(fgets(response, sizeof(response), stdin)) { + strtrim(response); + if(strlen(response) > 0) { + if(multiselect_parse(array, count, response) == -1) { + /* only loop if user gave an invalid answer */ + continue; + } + } + } + break; + } + return(0); +} + +int select_question(int count) +{ + char response[32]; + FILE *stream; + int preset = 1; + + if(config->noconfirm) { + stream = stdout; + } else { + /* Use stderr so questions are always displayed when redirecting output */ + stream = stderr; + } + + while(1) { + fprintf(stream, "\n"); + fprintf(stream, _("Enter a number (default=%d)"), preset); + fprintf(stream, ": "); + + if(config->noconfirm) { + fprintf(stream, "\n"); + break; + } + + if(fgets(response, sizeof(response), stdin)) { + strtrim(response); + if(strlen(response) > 0) { + int n; + if(parseindex(response, &n, 1, count) != 0) + continue; + return(n-1); + } + } + break; + } + + return(preset-1); +} + + /* presents a prompt and gets a Y/N answer */ 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 +887,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 +952,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 +984,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 +1014,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..234a631d 100644 --- a/src/pacman/util.h +++ b/src/pacman/util.h @@ -1,7 +1,7 @@ /* * util.h * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org> * * This program is free software; you can redistribute it and/or modify @@ -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 */ @@ -42,7 +44,7 @@ int trans_release(void); int needs_root(void); int getcols(void); int rmrf(const char *path); -char *mbasename(const char *path); +const char *mbasename(const char *path); char *mdirname(const char *path); void indentprint(const char *str, int indent); char *strtoupper(char *str); @@ -53,13 +55,18 @@ 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); +void select_display(const alpm_list_t *pkglist); +int select_question(int count); +int multiselect_question(char *array, int count); 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..36a4e072 100644 --- a/src/util/cleanupdelta.c +++ b/src/util/cleanupdelta.c @@ -1,7 +1,7 @@ /* * cleanupdelta.c : return list of unused delta in a given sync database * - * Copyright (c) 2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2011 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 @@ -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> @@ -35,7 +37,7 @@ static void cleanup(int signum) { exit(signum); } -void output_cb(pmloglevel_t level, char *fmt, va_list args) +static void output_cb(pmloglevel_t level, char *fmt, va_list args) { if(strlen(fmt)) { switch(level) { @@ -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; @@ -77,19 +79,19 @@ void checkdbs(char *dbpath, alpm_list_t *dbnames) { alpm_strerrorlast()); return; } - checkpkgs(alpm_db_get_pkgcache(db)); + checkpkgs(alpm_db_get_pkgcache_list(db)); } } -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..947ed61c --- /dev/null +++ b/src/util/pactree.c @@ -0,0 +1,372 @@ +/* + * pactree.c - a simple dependency tree viewer + * + * Copyright (c) 2010-2011 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_option_get_localdb(); + 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 = (int)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_list(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_list(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..124a66dd 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()); @@ -43,7 +39,7 @@ static void cleanup(int signum) { exit(signum); } -void output_cb(pmloglevel_t level, char *fmt, va_list args) +static void output_cb(pmloglevel_t level, char *fmt, va_list args) { if(strlen(fmt)) { switch(level) { @@ -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,44 +126,35 @@ 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); } - db = alpm_db_register_local(); + db = alpm_option_get_localdb(); if(db == NULL) { fprintf(stderr, "error: could not register 'local' database (%s)\n", alpm_strerrorlast()); cleanup(EXIT_FAILURE); } - pkglist = alpm_db_get_pkgcache(db); + pkglist = alpm_db_get_pkgcache_list(db); ret += checkdeps(pkglist); ret += checkconflicts(pkglist); 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", @@ -180,7 +162,7 @@ int check_syncdbs(char *dbpath, alpm_list_t *dbnames) { ret = 1; goto cleanup; } - pkglist = alpm_db_get_pkgcache(db); + pkglist = alpm_db_get_pkgcache_list(db); syncpkglist = alpm_list_join(syncpkglist, alpm_list_copy(pkglist)); } ret += checkdeps(syncpkglist); @@ -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..0dd8db8f 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 */ @@ -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..adb5a42a 100644 --- a/src/util/vercmp.c +++ b/src/util/vercmp.c @@ -1,7 +1,7 @@ /* * vercmp.c * - * Copyright (c) 2006-2010 Pacman Development Team <pacman-dev@archlinux.org> + * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org> * Copyright (c) 2002-2005 by Judd Vinet <jvinet@zeroflux.org> * * This program is free software; you can redistribute it and/or modify @@ -18,23 +18,20 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "config.h" - +#include <stdlib.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 */ +/* forward declaration, comes from version.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"); + fprintf(stderr, "output values:\n"); fprintf(stderr, " < 0 : if ver1 < ver2\n"); fprintf(stderr, " 0 : if ver1 == ver2\n"); fprintf(stderr, " > 0 : if ver1 > ver2\n"); @@ -42,8 +39,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,19 +53,14 @@ 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); printf("%d\n", ret); - return(ret); + return(EXIT_SUCCESS); } |