diff --git a/bin/xbps-remove/clean-cache.c b/bin/xbps-remove/clean-cache.c
index ce62c259..dbdc983f 100644
--- a/bin/xbps-remove/clean-cache.c
+++ b/bin/xbps-remove/clean-cache.c
@@ -64,22 +64,23 @@ binpkg_parse(char *buf, size_t bufsz, const char *path, const char **pkgver, con
return 0;
}
+struct cleaner_data {
+ bool dry;
+ bool uninstalled;
+};
+
static int
cleaner_cb(struct xbps_handle *xhp, xbps_object_t obj,
const char *key UNUSED, void *arg,
bool *done UNUSED)
{
char buf[PATH_MAX];
- xbps_dictionary_t repo_pkgd;
+ xbps_dictionary_t pkgd;
const char *binpkg, *rsha256;
const char *binpkgver, *binpkgarch;
- bool drun = false;
+ struct cleaner_data *data = arg;
int r;
- /* Extract drun (dry-run) flag from arg*/
- if (arg != NULL)
- drun = *(bool*)arg;
-
binpkg = xbps_string_cstring_nocopy(obj);
r = binpkg_parse(buf, sizeof(buf), binpkg, &binpkgver, &binpkgarch);
if (r < 0) {
@@ -96,9 +97,13 @@ cleaner_cb(struct xbps_handle *xhp, xbps_object_t obj,
* Remove binary pkg if it's not registered in any repository
* or if hash doesn't match.
*/
- repo_pkgd = xbps_rpool_get_pkg(xhp, binpkgver);
- if (repo_pkgd) {
- xbps_dictionary_get_cstring_nocopy(repo_pkgd,
+ if (data->uninstalled) {
+ pkgd = xbps_pkgdb_get_pkg(xhp, binpkgver);
+ } else {
+ pkgd = xbps_rpool_get_pkg(xhp, binpkgver);
+ }
+ if (pkgd) {
+ xbps_dictionary_get_cstring_nocopy(pkgd,
"filename-sha256", &rsha256);
r = xbps_file_sha256_check(binpkg, rsha256);
if (r == 0) {
@@ -111,13 +116,13 @@ cleaner_cb(struct xbps_handle *xhp, xbps_object_t obj,
}
}
snprintf(buf, sizeof(buf), "%s.sig", binpkg);
- if (!drun && unlink(binpkg) == -1) {
+ if (!data->dry && unlink(binpkg) == -1) {
xbps_error_printf("Failed to remove `%s': %s\n",
binpkg, strerror(errno));
} else {
printf("Removed %s from cachedir (obsolete)\n", binpkg);
}
- if (!drun && unlink(buf) == -1 && errno != ENOENT) {
+ if (!data->dry && unlink(buf) == -1 && errno != ENOENT) {
xbps_error_printf("Failed to remove `%s': %s\n",
buf, strerror(errno));
}
@@ -126,7 +131,7 @@ cleaner_cb(struct xbps_handle *xhp, xbps_object_t obj,
}
int
-clean_cachedir(struct xbps_handle *xhp, bool drun)
+clean_cachedir(struct xbps_handle *xhp, bool uninstalled, bool drun)
{
xbps_array_t array = NULL;
DIR *dirp;
@@ -158,7 +163,11 @@ clean_cachedir(struct xbps_handle *xhp, bool drun)
(void)closedir(dirp);
if (xbps_array_count(array)) {
- rv = xbps_array_foreach_cb_multi(xhp, array, NULL, cleaner_cb, (void*)&drun);
+ struct cleaner_data data = {
+ .dry = drun,
+ .uninstalled = uninstalled,
+ };
+ rv = xbps_array_foreach_cb_multi(xhp, array, NULL, cleaner_cb, (void*)&data);
xbps_object_release(array);
}
return rv;
diff --git a/bin/xbps-remove/defs.h b/bin/xbps-remove/defs.h
index 0788812c..5b39ba85 100644
--- a/bin/xbps-remove/defs.h
+++ b/bin/xbps-remove/defs.h
@@ -27,6 +27,6 @@
#define _XBPS_REMOVE_DEFS_H_
/* From clean-cache.c */
-int clean_cachedir(struct xbps_handle *, bool drun);
+int clean_cachedir(struct xbps_handle *, bool uninstalled, bool drun);
#endif /* !_XBPS_REMOVE_DEFS_H_ */
diff --git a/bin/xbps-remove/main.c b/bin/xbps-remove/main.c
index 60df2976..7c24d614 100644
--- a/bin/xbps-remove/main.c
+++ b/bin/xbps-remove/main.c
@@ -53,7 +53,8 @@ usage(bool fail)
" -f, --force Force package files removal\n"
" -h, --help Show usage\n"
" -n, --dry-run Dry-run mode\n"
- " -O, --clean-cache Remove obsolete packages in cachedir\n"
+ " -O, --clean-cache Remove outdated packages from the cache\n"
+ " If specified twice, also remove uninstalled packages\n"
" -o, --remove-orphans Remove package orphans\n"
" -R, --recursive Recursively remove dependencies\n"
" -r, --rootdir
Full path to rootdir\n"
@@ -179,12 +180,13 @@ main(int argc, char **argv)
struct xbps_handle xh;
const char *rootdir, *cachedir, *confdir;
int c, flags, rv;
- bool yes, drun, recursive, clean_cache, orphans;
+ bool yes, drun, recursive, orphans;
int maxcols, missing;
+ int clean_cache = 0;
rootdir = cachedir = confdir = NULL;
flags = rv = 0;
- drun = recursive = clean_cache = yes = orphans = false;
+ drun = recursive = yes = orphans = false;
while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
switch (c) {
@@ -210,7 +212,7 @@ main(int argc, char **argv)
drun = true;
break;
case 'O':
- clean_cache = true;
+ clean_cache++;
break;
case 'o':
orphans = true;
@@ -236,7 +238,7 @@ main(int argc, char **argv)
/* NOTREACHED */
}
}
- if (!clean_cache && !orphans && (argc == optind)) {
+ if (clean_cache == 0 && !orphans && (argc == optind)) {
usage(true);
/* NOTREACHED */
}
@@ -263,8 +265,8 @@ main(int argc, char **argv)
maxcols = get_maxcols();
- if (clean_cache) {
- rv = clean_cachedir(&xh, drun);
+ if (clean_cache > 0) {
+ rv = clean_cachedir(&xh, clean_cache > 1, drun);
if (!orphans || rv)
exit(rv);;
}
diff --git a/bin/xbps-remove/xbps-remove.1 b/bin/xbps-remove/xbps-remove.1
index a2aa7a64..1b07bd39 100644
--- a/bin/xbps-remove/xbps-remove.1
+++ b/bin/xbps-remove/xbps-remove.1
@@ -79,7 +79,9 @@ Show the help message.
Dry-run mode. Show what actions would be done but don't do anything. The current output
prints 6 arguments: " ".
.It Fl O, Fl -clean-cache
-Cleans cache directory removing obsolete binary packages.
+Cleans cache directory removing outdated binary packages.
+If specified twice,
+also remove packages that are not installed from the cache.
.It Fl o, Fl -remove-orphans
Removes installed package orphans that were installed automatically
(as dependencies) and are not currently dependencies of any installed package.
diff --git a/tests/xbps/xbps-remove/basic_test.sh b/tests/xbps/xbps-remove/basic_test.sh
index 0f9fd577..716fb4eb 100755
--- a/tests/xbps/xbps-remove/basic_test.sh
+++ b/tests/xbps/xbps-remove/basic_test.sh
@@ -53,13 +53,15 @@ clean_cache_head() {
}
clean_cache_body() {
- mkdir -p repo pkg_A/B/C
+ mkdir -p repo pkg_A/B/C pkg_B
touch pkg_A/
cd repo
xbps-create -A noarch -n A-1.0_1 -s "A pkg" ../pkg_A
atf_check_equal $? 0
xbps-create -A noarch -n A-1.0_2 -s "A pkg" ../pkg_A
atf_check_equal $? 0
+ xbps-create -A noarch -n B-1.0_1 -s "B pkg" ../pkg_B
+ atf_check_equal $? 0
xbps-rindex -d -a $PWD/*.xbps
atf_check_equal $? 0
cd ..
@@ -69,12 +71,15 @@ clean_cache_body() {
cp repo/*.xbps root/var/cache/xbps
atf_check_equal $? 0
echo "repository=https://localhost/" >root/etc/xbps.d/localrepo.conf
+ xbps-install -r root -C etc/xbps.d -R repo -dvy B
xbps-remove -r root -C etc/xbps.d -dvO
atf_check_equal $? 0
test -f root/var/cache/xbps/A-1.0_2.noarch.xbps
atf_check_equal $? 0
test -f root/var/cache/xbps/A-1.0_1.noarch.xbps
atf_check_equal $? 1
+ test -f root/var/cache/xbps/B-1.0_1.noarch.xbps
+ atf_check_equal $? 0
}
atf_test_case clean_cache_dry_run
@@ -136,10 +141,46 @@ clean_cache_dry_run_perm_body() {
atf_check_equal "$out" "Removed A-1.0_1.noarch.xbps from cachedir (obsolete)"
}
+clean_cache_uninstalled_head() {
+ atf_set "descr" "xbps-remove(1): clean uninstalled package from cache"
+}
+
+clean_cache_uninstalled_body() {
+ mkdir -p repo pkg_A/B/C pkg_B
+ touch pkg_A/
+ cd repo
+ xbps-create -A noarch -n A-1.0_1 -s "A pkg" ../pkg_A
+ atf_check_equal $? 0
+ xbps-create -A noarch -n A-1.0_2 -s "A pkg" ../pkg_A
+ atf_check_equal $? 0
+ xbps-create -A noarch -n B-1.0_1 -s "B pkg" ../pkg_B
+ atf_check_equal $? 0
+ xbps-rindex -d -a $PWD/*.xbps
+ atf_check_equal $? 0
+ cd ..
+ mkdir -p root/etc/xbps.d root/var/db/xbps/https___localhost_ root/var/cache/xbps
+ cp repo/*-repodata root/var/db/xbps/https___localhost_
+ atf_check_equal $? 0
+ cp repo/*.xbps root/var/cache/xbps
+ atf_check_equal $? 0
+ echo "repository=https://localhost/" >root/etc/xbps.d/localrepo.conf
+ xbps-install -r root -C etc/xbps.d -R repo -dvy B
+ atf_check_equal $? 0
+ xbps-remove -r root -C etc/xbps.d -dvOO
+ atf_check_equal $? 0
+ test -f root/var/cache/xbps/A-1.0_2.noarch.xbps
+ atf_check_equal $? 1
+ test -f root/var/cache/xbps/A-1.0_1.noarch.xbps
+ atf_check_equal $? 1
+ test -f root/var/cache/xbps/B-1.0_1.noarch.xbps
+ atf_check_equal $? 0
+}
+
atf_init_test_cases() {
atf_add_test_case remove_directory
atf_add_test_case remove_orphans
atf_add_test_case clean_cache
atf_add_test_case clean_cache_dry_run
atf_add_test_case clean_cache_dry_run_perm
+ atf_add_test_case clean_cache_uninstalled
}