summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKrzysztof Goj <krzysztof.goj@gmail.com>2012-01-22 01:39:59 +0100
committerJim Meyering <meyering@redhat.com>2012-08-14 18:54:16 +0200
commitfdc2da7165d93c8065365999341173ad2a818833 (patch)
tree25ef66176016b61abb71b56175b6cdb42fb54ee4
parent46afefaaa8ea95b5eb63a62792774cd18738234a (diff)
downloadcoreutils-fdc2da7165d93c8065365999341173ad2a818833.tar.xz
rm: new option --dir (-d) to remove empty directories
Add new option to rm (-d/--dir), which allows removal of empty directories, while still safely disallowing removal of non-empty ones. This improves compatibility with Mac OS X and BSD systems, which honor the -d option. * src/remove.c (rm_fts): Remove empty directories when requested. * src/remove.h (rm_options) [remove_empty_directories]: New member. * src/rm.c (long_opts, usage, main): Update usage and option parsing. (rm_option_init): Initialize the new member. * src/mv.c (rm_option_init): Initialize the new member. * tests/rm/d-1: New test case - successfully delete empty dir. * tests/rm/d-2: New test case - refuse to delete nonempty dir. * tests/Makefile.am (TESTS): Add them.
-rw-r--r--NEWS9
-rw-r--r--doc/coreutils.texi7
-rw-r--r--src/mv.c1
-rw-r--r--src/remove.c10
-rw-r--r--src/remove.h3
-rw-r--r--src/rm.c9
-rw-r--r--tests/Makefile.am2
-rwxr-xr-xtests/rm/d-138
-rwxr-xr-xtests/rm/d-233
9 files changed, 108 insertions, 4 deletions
diff --git a/NEWS b/NEWS
index 46d0a4131..012a63379 100644
--- a/NEWS
+++ b/NEWS
@@ -2,11 +2,20 @@ GNU coreutils NEWS -*- outline -*-
* Noteworthy changes in release ?.? (????-??-??) [?]
+** Bug fixes
+
df now fails when the list of mounted file systems (/etc/mtab) cannot
be read, yet the file system type information is needed to process
certain options like -a, -l, -t and -x.
[This bug was present in "the beginning".]
+** New features
+
+ rm now accepts the --dir (-d) option which makes it remove empty directories.
+ Since removing empty directories is relatively safe, this option can be
+ used as a part of the alias rm='rm --dir'. This improves compatibility
+ with Mac OS X and BSD systems which also honor the -d option.
+
* Noteworthy changes in release 8.18 (2012-08-12) [stable]
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index 744b41a9f..62b31fe1a 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -8807,6 +8807,13 @@ The program accepts the following options. Also see @ref{Common options}.
@table @samp
+@item -d
+@itemx --dir
+@opindex -d
+@opindex --dir
+@cindex directories, removing
+Remove the listed directories if they are empty.
+
@item -f
@itemx --force
@opindex -f
diff --git a/src/mv.c b/src/mv.c
index ee2f5a10c..4f5708eb2 100644
--- a/src/mv.c
+++ b/src/mv.c
@@ -73,6 +73,7 @@ static void
rm_option_init (struct rm_options *x)
{
x->ignore_missing_files = false;
+ x->remove_empty_directories = true;
x->recursive = true;
x->one_file_system = false;
diff --git a/src/remove.c b/src/remove.c
index 5ebd2ce43..61ba5f372 100644
--- a/src/remove.c
+++ b/src/remove.c
@@ -414,11 +414,15 @@ rm_fts (FTS *fts, FTSENT *ent, struct rm_options const *x)
switch (ent->fts_info)
{
case FTS_D: /* preorder directory */
- if (! x->recursive)
+ if (! x->recursive
+ && !(x->remove_empty_directories
+ && is_empty_dir (fts->fts_cwd_fd, ent->fts_accpath)))
{
- /* This is the first (pre-order) encounter with a directory.
+ /* This is the first (pre-order) encounter with a directory
+ that we can not delete.
Not recursive, so arrange to skip contents. */
- error (0, EISDIR, _("cannot remove %s"), quote (ent->fts_path));
+ int err = x->remove_empty_directories ? ENOTEMPTY : EISDIR;
+ error (0, err, _("cannot remove %s"), quote (ent->fts_path));
mark_ancestor_dirs (ent);
fts_skip_tree (fts, ent);
return RM_ERROR;
diff --git a/src/remove.h b/src/remove.h
index 4eab2821f..f994517af 100644
--- a/src/remove.h
+++ b/src/remove.h
@@ -49,6 +49,9 @@ struct rm_options
/* If true, recursively remove directories. */
bool recursive;
+ /* If true, remove empty directories. */
+ bool remove_empty_directories;
+
/* Pointer to the device and inode numbers of '/', when --recursive
and preserving '/'. Otherwise NULL. */
struct dev_ino *root_dev_ino;
diff --git a/src/rm.c b/src/rm.c
index 02809f214..a45594e42 100644
--- a/src/rm.c
+++ b/src/rm.c
@@ -77,6 +77,7 @@ static struct option const long_opts[] =
{"-presume-input-tty", no_argument, NULL, PRESUME_INPUT_TTY_OPTION},
{"recursive", no_argument, NULL, 'r'},
+ {"dir", no_argument, NULL, 'd'},
{"verbose", no_argument, NULL, 'v'},
{GETOPT_HELP_OPTION_DECL},
{GETOPT_VERSION_OPTION_DECL},
@@ -154,6 +155,7 @@ Remove (unlink) the FILE(s).\n\
--no-preserve-root do not treat '/' specially\n\
--preserve-root do not remove '/' (default)\n\
-r, -R, --recursive remove directories and their contents recursively\n\
+ -d, --dir remove empty directories\n\
-v, --verbose explain what is being done\n\
"), stdout);
fputs (HELP_OPTION_DESCRIPTION, stdout);
@@ -189,6 +191,7 @@ rm_option_init (struct rm_options *x)
x->ignore_missing_files = false;
x->interactive = RMI_SOMETIMES;
x->one_file_system = false;
+ x->remove_empty_directories = false;
x->recursive = false;
x->root_dev_ino = NULL;
x->stdin_tty = isatty (STDIN_FILENO);
@@ -220,10 +223,14 @@ main (int argc, char **argv)
/* Try to disable the ability to unlink a directory. */
priv_set_remove_linkdir ();
- while ((c = getopt_long (argc, argv, "firvIR", long_opts, NULL)) != -1)
+ while ((c = getopt_long (argc, argv, "dfirvIR", long_opts, NULL)) != -1)
{
switch (c)
{
+ case 'd':
+ x.remove_empty_directories = true;
+ break;
+
case 'f':
x.interactive = RMI_NEVER;
x.ignore_missing_files = true;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 273405f59..09d2658e7 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -98,6 +98,8 @@ TESTS = \
chgrp/basic \
rm/dangling-symlink \
misc/ls-time \
+ rm/d-1 \
+ rm/d-2 \
rm/deep-1 \
rm/deep-2 \
rm/dir-no-w \
diff --git a/tests/rm/d-1 b/tests/rm/d-1
new file mode 100755
index 000000000..f35e95125
--- /dev/null
+++ b/tests/rm/d-1
@@ -0,0 +1,38 @@
+#!/bin/sh
+# Test "rm --dir --verbose".
+
+# Copyright (C) 2012 Free Software Foundation, Inc.
+
+# 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 3 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/>.
+
+. "${srcdir=.}/init.sh"; path_prepend_ ../src
+print_ver_ rm
+
+mkdir a || framework_failure_
+> b || framework_failure_
+
+rm --verbose --dir a b > out || fail=1
+
+cat <<\EOF > exp || framework_failure_
+removed directory: 'a'
+removed 'b'
+EOF
+
+test -e a && fail=1
+test -e b && fail=1
+
+# Compare expected and actual output.
+compare exp out || fail=1
+
+Exit $fail
diff --git a/tests/rm/d-2 b/tests/rm/d-2
new file mode 100755
index 000000000..a63cff60d
--- /dev/null
+++ b/tests/rm/d-2
@@ -0,0 +1,33 @@
+#!/bin/sh
+# Ensure that 'rm -d dir' (i.e., without --recursive) gives a reasonable
+# diagnostic when failing.
+
+# Copyright (C) 2012 Free Software Foundation, Inc.
+
+# 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 3 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/>.
+
+. "${srcdir=.}/init.sh"; path_prepend_ ../src
+print_ver_ rm
+
+mkdir d || framework_failure_
+> d/a || framework_failure_
+
+rm -d d 2> out && fail=1
+printf "%s\n" \
+ "rm: cannot remove 'd': Directory not empty" \
+ > exp || framework_failure_
+
+compare exp out || fail=1
+
+Exit $fail