diff --git a/doc/pacman.8.asciidoc b/doc/pacman.8.asciidoc index c77a1e7c..4031144a 100644 --- a/doc/pacman.8.asciidoc +++ b/doc/pacman.8.asciidoc @@ -378,6 +378,9 @@ Query Options (apply to '-Q')[[QO]] replacements are not checked here. This option works best if the sync database is refreshed using '-Sy'. +*-w, \--backup*:: + List all modified backup files owened by a given package. Multiple packages can + be specified on the command line. Pass twice to print all backup files. Remove Options (apply to '-R')[[RO]] ------------------------------------ diff --git a/src/pacman/conf.h b/src/pacman/conf.h index ec9caa32..10a53a35 100644 --- a/src/pacman/conf.h +++ b/src/pacman/conf.h @@ -83,6 +83,7 @@ typedef struct __config_t { unsigned short op_q_upgrade; unsigned short op_q_check; unsigned short op_q_locality; + unsigned short op_q_backup; unsigned short op_s_clean; unsigned short op_s_downloadonly; @@ -185,6 +186,7 @@ enum { OP_DBPATH, OP_CASCADE, OP_CHANGELOG, + OP_BACKUP, OP_CLEAN, OP_NODEPS, OP_DEPS, diff --git a/src/pacman/package.c b/src/pacman/package.c index 8e33d2bd..92a5f66f 100644 --- a/src/pacman/package.c +++ b/src/pacman/package.c @@ -351,7 +351,7 @@ void dump_pkg_full(alpm_pkg_t *pkg, int extra) /* Print additional package info if info flag passed more than once */ if(from == ALPM_PKG_FROM_LOCALDB && extra) { - dump_pkg_backups(pkg); + dump_backup_status(pkg); } /* final newline to separate packages */ @@ -362,6 +362,34 @@ void dump_pkg_full(alpm_pkg_t *pkg, int extra) alpm_list_free(validation); } +static int get_backup_file_changed(const char *root, + const alpm_backup_t *backup) +{ + char path[PATH_MAX]; + int ret = 0; + + snprintf(path, PATH_MAX, "%s%s", root, backup->name); + + /* 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_printf(ALPM_LOG_ERROR, + _("could not calculate checksums for %s\n"), path); + return 0; + } + + /* if checksums don't match, file has been modified */ + ret = strcmp(md5sum, backup->hash) != 0; + free(md5sum); + } else if(errno != ENOENT) { + pm_printf(ALPM_LOG_ERROR, + _("could not read %s: %s\n"), path, strerror(errno)); + } + return ret; +} + static const char *get_backup_file_status(const char *root, const alpm_backup_t *backup) { @@ -404,7 +432,7 @@ static const char *get_backup_file_status(const char *root, /* Display list of backup files and their modification states */ -void dump_pkg_backups(alpm_pkg_t *pkg) +void dump_backup_status(alpm_pkg_t *pkg) { alpm_list_t *i; const char *root = alpm_option_get_root(config->handle); @@ -453,6 +481,32 @@ void dump_pkg_files(alpm_pkg_t *pkg, int quiet) fflush(stdout); } +void dump_pkg_backups(alpm_pkg_t *pkg, int quiet, int all) +{ + const char *pkgname, *root; + alpm_list_t *backups; + alpm_list_t *i; + + pkgname = alpm_pkg_get_name(pkg); + backups = alpm_pkg_get_backup(pkg); + root = alpm_option_get_root(config->handle); + + for(i = backups; i; i = i->next) { + alpm_backup_t *backup = i->data; + + if(!all && backup->hash && !get_backup_file_changed(root, backup)) { + continue; + } + + if(!quiet) { + printf("%s%s%s ", config->colstr.title, pkgname, config->colstr.nocolor); + } + printf("%s%s\n", root, backup->name); + } + + fflush(stdout); +} + /* Display the changelog of a package */ void dump_pkg_changelog(alpm_pkg_t *pkg) diff --git a/src/pacman/package.h b/src/pacman/package.h index c1f3d5b3..8cd61e4d 100644 --- a/src/pacman/package.h +++ b/src/pacman/package.h @@ -24,7 +24,8 @@ void dump_pkg_full(alpm_pkg_t *pkg, int extra); -void dump_pkg_backups(alpm_pkg_t *pkg); +void dump_backup_status(alpm_pkg_t *pkg); +void dump_pkg_backups(alpm_pkg_t *pkg, int quiet, int all); void dump_pkg_files(alpm_pkg_t *pkg, int quiet); void dump_pkg_changelog(alpm_pkg_t *pkg); diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c index 163bb2f1..6e28fcb0 100644 --- a/src/pacman/pacman.c +++ b/src/pacman/pacman.c @@ -149,6 +149,7 @@ static void usage(int op, const char * const myname) addlist(_(" -t, --unrequired list packages not (optionally) required by any\n" " package (-tt to ignore optdepends) [filter]\n")); addlist(_(" -u, --upgrades list outdated packages [filter]\n")); + addlist(_(" -w, --backup list modified backup files of a package (-xx for all backup files)\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); @@ -530,6 +531,10 @@ static int parsearg_query(int opt) case 'c': config->op_q_changelog = 1; break; + case OP_BACKUP: + case 'w': + (config->op_q_backup)++; + break; case OP_DEPS: case 'd': config->op_q_deps = 1; @@ -597,6 +602,7 @@ static void checkargs_query_display_opts(const char *opname) { invalid_opt(config->op_q_check, opname, "--check"); invalid_opt(config->op_q_info, opname, "--info"); invalid_opt(config->op_q_list, opname, "--list"); + invalid_opt(config->op_q_backup, opname, "--backup"); } static void checkargs_query_filter_opts(const char *opname) { @@ -917,6 +923,7 @@ static int parseargs(int argc, char *argv[]) {"dbpath", required_argument, 0, OP_DBPATH}, {"cascade", no_argument, 0, OP_CASCADE}, {"changelog", no_argument, 0, OP_CHANGELOG}, + {"backup", no_argument, 0, OP_BACKUP}, {"clean", no_argument, 0, OP_CLEAN}, {"nodeps", no_argument, 0, OP_NODEPS}, {"deps", no_argument, 0, OP_DEPS}, diff --git a/src/pacman/query.c b/src/pacman/query.c index f28c0f89..32405fe1 100644 --- a/src/pacman/query.c +++ b/src/pacman/query.c @@ -315,6 +315,9 @@ static int display(alpm_pkg_t *pkg) if(config->op_q_list) { dump_pkg_files(pkg, config->quiet); } + if(config->op_q_backup) { + dump_pkg_backups(pkg, config->quiet, config->op_q_backup != 1); + } if(config->op_q_changelog) { dump_pkg_changelog(pkg); } @@ -325,8 +328,8 @@ static int display(alpm_pkg_t *pkg) ret = check_pkg_full(pkg); } } - if(!config->op_q_info && !config->op_q_list - && !config->op_q_changelog && !config->op_q_check) { + if(!config->op_q_info && !config->op_q_list && !config->op_q_changelog + && !config->op_q_check && !config->op_q_backup) { if(!config->quiet) { const colstr_t *colstr = &config->colstr; char *note = alpm_pkg_get_note(pkg); @@ -437,7 +440,7 @@ int pacman_query(alpm_list_t *targets) db_local = alpm_get_localdb(config->handle); /* operations on all packages in the local DB - * valid: no-op (plain -Q), list, info, check + * valid: no-op (plain -Q), list, info, check, backup * invalid: isfile, owns */ if(targets == NULL) { if(config->op_q_isfile || config->op_q_owns) {