commit: b8bd2cabd1671d6de244fb0cefe009fd7769b20e
parent fa5fdb6f5acff7449c927c89b95e56958f420b8f
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date: Fri, 30 Aug 2024 09:33:07 +0200
cmd/rmdir: Add --ignore-fail-on-non-empty support
Diffstat:
4 files changed, 56 insertions(+), 4 deletions(-)
diff --git a/cmd/rmdir.1 b/cmd/rmdir.1
@@ -1,7 +1,7 @@
.\" utils-std: Collection of commonly available Unix tools
.\" Copyright 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
.\" SPDX-License-Identifier: MPL-2.0
-.Dd 2024-03-26
+.Dd 2024-08-30
.Dt RMDIR 1
.Os
.Sh NAME
@@ -10,6 +10,7 @@
.Sh SYNOPSIS
.Nm
.Op Fl pv
+.Op Fl -ignore-fail-on-non-empty
.Ar directory...
.Sh DESCRIPTION
The
@@ -18,6 +19,8 @@ utility removes each given
.Ar directory .
.Sh OPTIONS
.Bl -tag -width aa
+.It Fl -ignore-fail-on-non-empty
+Ignore failures to remove non-empty directories
.It Fl p
Remove all parents directories present in the
.Ar directory
@@ -41,6 +44,8 @@ specification.
.Pp
The
.Fl v
-option is an extension.
+and
+.Fl -ignore-fail-on-non-empty
+options are extensions.
.Sh AUTHORS
.An Haelwenn (lanodan) Monnier Aq Mt contact+utils@hacktivis.me
diff --git a/cmd/rmdir.c b/cmd/rmdir.c
@@ -16,19 +16,26 @@
static void
usage()
{
+#ifdef HAS_GETOPT_LONG
+ fprintf(stderr, "Usage: rmdir [-pv] [--ignore-fail-on-non-empty] directory...\n");
+#else
fprintf(stderr, "Usage: rmdir [-pv] directory...\n");
+#endif
}
int
main(int argc, char *argv[])
{
- bool parents = false, verbose = false;
+ bool parents = false, verbose = false, ign_enotempty = false;
int c = -1;
#ifdef HAS_GETOPT_LONG
- // Strictly for GNUisms compatibility so no long-only options
// clang-format off
+ enum long_opt_vals {
+ IGN_ENOTEMPTY = 1
+ };
static struct option opts[] = {
+ {"ignore-fail-on-non-empty", no_argument, 0, IGN_ENOTEMPTY},
{"parents", no_argument, 0, 'p'},
{"verbose", no_argument, 0, 'v'},
{0, 0, 0, 0},
@@ -43,6 +50,11 @@ main(int argc, char *argv[])
{
switch(c)
{
+#ifdef HAS_GETOPT_LONG
+ case IGN_ENOTEMPTY:
+ ign_enotempty = true;
+ break;
+#endif
case 'p':
parents = true;
break;
@@ -81,6 +93,8 @@ main(int argc, char *argv[])
char *path = argv[i];
if(rmdir(path) < 0)
{
+ if(ign_enotempty && (errno == ENOTEMPTY || errno == EEXIST)) continue;
+
fprintf(stderr, "rmdir: Failed removing '%s': %s\n", path, strerror(errno));
err = 1;
continue;
@@ -102,6 +116,7 @@ main(int argc, char *argv[])
if(rmdir(path) < 0)
{
if(errno == ENOTDIR) break;
+ if(ign_enotempty && (errno == ENOTEMPTY || errno == EEXIST)) break;
fprintf(stderr, "rmdir: Failed removing '%s': %s\n", path, strerror(errno));
err = 1;
diff --git a/test-cmd/rmdir.t b/test-cmd/rmdir.t
@@ -9,6 +9,7 @@
$ mkdir -p no_p/bar
$ rmdir no_p/bar
$ test -d no_p
+ $ rm -r no_p
$ mkdir -p p/bar
$ rmdir -p p/bar
@@ -18,6 +19,7 @@
$ rmdir -v v_no_p/bar
rmdir: Removed 'v_no_p/bar'
$ test -d v_no_p
+ $ rm -r v_no_p
$ mkdir -p v_p/bar
$ rmdir -pv v_p/bar
@@ -38,3 +40,15 @@
$ rmdir -pv file
rmdir: Failed removing 'file': Not a directory
[1]
+ $ rm file
+
+ $ mkdir -p e_not_empty/a/b/c/d empty/a/b/c
+ $ rmdir -p e_not_empty/a/b/c empty/a/b/c
+ rmdir: Failed removing 'e_not_empty/a/b/c': Directory not empty
+ [1]
+ $ test -d e_not_empty/a/b/c
+ $ test ! -e empty
+ $ rm -r e_not_empty
+
+ $ find .
+ .
diff --git a/test-cmd/rmdir_long.t b/test-cmd/rmdir_long.t
@@ -0,0 +1,18 @@
+#!/usr/bin/env cram
+# SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
+# SPDX-License-Identifier: MPL-2.0
+
+ $ export PATH="$TESTDIR/../cmd:$PATH"
+
+ $ test "$(command -v rmdir)" = "$TESTDIR/../cmd/rmdir"
+
+ $ grep -q HAS_GETOPT_LONG "${TESTDIR}/../config.mk" || return 80
+
+ $ mkdir -p e_not_empty-ign/a/b/c/d empty-ign/a/b/c
+ $ rmdir -p --ignore-fail-on-non-empty e_not_empty-ign/a/b/c empty-ign/a/b/c
+ $ test -d e_not_empty-ign/a/b/c
+ $ test ! -e empty-ign
+ $ rm -r e_not_empty-ign
+
+ $ find .
+ .