Hello again, I have finally completed my --directory (-d) feature, like the FreeBSD one. The directory option deletes a directory only if the directory in question is empty. This is a safer alternative to the recursive option is some cases where you don't want to delete unempty directories.
I have documented the features. Although I am not the best technical writer, I hope that what I have done is adequate. One thing that I have neglected to do, intentionally, is to write an automated test. I am not skilled enough or confident enough in my Perl or shell scripting ability to write such a test. I would love to hear what I could have done better, or possibly what I have done wrong. Any comments on my code are very much appreciated. That is a big part of why I am doing this is to become a better programmer. Thank you, and with out further ado, here is the patch. P.S. I am sorry if this was already posted, but I never got a copy and was not sure if it went through. Thank you for your patience.
>From 0dc91f3088b27053afe7db2c293ebaad38a51a68 Mon Sep 17 00:00:00 2001 From: Patrick W. Plusnick II <pwplusni...@gmail.com> Date: Fri, 5 Mar 2010 22:22:00 -0600 Subject: [PATCH] rm: add the --directory (-d) option that removes empty directories *doc/coreutils.texi: Added some documentation about the changes *src/remove.c: Changes are mostly in the rm_fts function under the FTS_D case. I added the proper conditions for the removal of only empty directories. *src/remove.h: I only added a flag to RM_option. *src/rm.c: I added facilities to set the directory (-d) flag. --- doc/coreutils.texi | 8 ++++++++ src/remove.c | 48 ++++++++++++++++++++++++++++++++++++++++++------ src/remove.h | 3 +++ src/rm.c | 8 ++++++++ 4 files changed, 61 insertions(+), 6 deletions(-) diff --git a/doc/coreutils.texi b/doc/coreutils.texi index fcee336..24732be 100644 --- a/doc/coreutils.texi +++ b/doc/coreutils.texi @@ -8393,6 +8393,14 @@ The program accepts the following options. Also see @ref{Common options}. @table @samp +...@item -d +...@itemx --directory +...@opindex -d +...@opindex --directory +Remove a directory only if it is empty. +Skipped if the @option{--recursive} (@option{-r} or @option{-R}) is +specified. + @item -f @itemx --force @opindex -f diff --git a/src/remove.c b/src/remove.c index b6cfc8e..3e6857f 100644 --- a/src/remove.c +++ b/src/remove.c @@ -274,7 +274,7 @@ prompt (FTS const *fts, FTSENT const *ent, bool is_dir, break; case DT_DIR: - if (!x->recursive) + if (!x->recursive && !x->directory) { write_protected = -1; wp_errno = EISDIR; @@ -472,7 +472,7 @@ 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->directory) { /* This is the first (pre-order) encounter with a directory. Not recursive, so arrange to skip contents. */ @@ -507,19 +507,55 @@ rm_fts (FTS *fts, FTSENT *ent, struct rm_options const *x) return RM_ERROR; } } + /* The recursive option takes precedence over the directory option */ + if (x->recursive) + { + Ternary is_empty_directory; + enum RM_status s = prompt (fts, ent, true /*is_dir*/, x, + PA_DESCEND_INTO_DIR, &is_empty_directory); + + if (s == RM_OK && is_empty_directory == T_YES) + { + /* When we know (from prompt when in interactive mode) + that this is an empty directory, don't prompt twice. */ + s = excise (fts, ent, x, true); + fts_skip_tree (fts, ent); + } + + if (s != RM_OK) + { + mark_ancestor_dirs (ent); + fts_skip_tree (fts, ent); + } + return s; + } + /* If the directory option is set, remove the directory + if it is empty. */ { Ternary is_empty_directory; enum RM_status s = prompt (fts, ent, true /*is_dir*/, x, - PA_DESCEND_INTO_DIR, &is_empty_directory); - + PA_REMOVE_DIR, &is_empty_directory); if (s == RM_OK && is_empty_directory == T_YES) { - /* When we know (from prompt when in interactive mode) - that this is an empty directory, don't prompt twice. */ s = excise (fts, ent, x, true); fts_skip_tree (fts, ent); } + else if (s == RM_USER_DECLINED) /* Get out of the if */ + ; + /* The directory may still be empty */ + else if (is_empty_directory == T_UNKNOWN && + is_empty_dir (fts->fts_cwd_fd, ent->fts_accpath)) + { + s = excise (fts, ent, x, true); + fts_skip_tree (fts, ent); + } + else + { + s = RM_ERROR; + error (0, ENOTEMPTY, _("cannot remove %s"), + quote (fts->fts_path)); + } if (s != RM_OK) { diff --git a/src/remove.h b/src/remove.h index f860cb4..7fb9f93 100644 --- a/src/remove.h +++ b/src/remove.h @@ -66,6 +66,9 @@ struct rm_options restore cwd (e.g., mv) and some others do not (e.g., rm, in many cases). */ bool require_restore_cwd; + + /* If true, delete a directory if it is empty */ + bool directory; }; enum RM_status diff --git a/src/rm.c b/src/rm.c index d9a1f8b..b237da9 100644 --- a/src/rm.c +++ b/src/rm.c @@ -137,6 +137,7 @@ usage (int status) fputs (_("\ Remove (unlink) the FILE(s).\n\ \n\ + -d, --directory remove empty directories\n\ -f, --force ignore nonexistent files, never prompt\n\ -i prompt before every removal\n\ "), stdout); @@ -164,6 +165,9 @@ Remove (unlink) the FILE(s).\n\ \n\ By default, rm does not remove directories. Use the --recursive (-r or -R)\n\ option to remove each listed directory, too, along with all of its contents.\n\ +Use --directory (-d) option to remove each listed directory only if it is\n\ +empty. If you specify both the recursive and the directory options, the\n\ +recursive option takes precendence.\n\ "), stdout); printf (_("\ \n\ @@ -188,6 +192,7 @@ truly unrecoverable, consider using shred.\n\ static void rm_option_init (struct rm_options *x) { + x->directory = false; x->ignore_missing_files = false; x->interactive = RMI_SOMETIMES; x->one_file_system = false; @@ -231,6 +236,9 @@ main (int argc, char **argv) coreutils 5.92. FIXME: Some time after 2005, change this to report an error (or perhaps behave like FreeBSD does) instead of ignoring the option. */ + /* I have chosen to take the route of FreeBSD, that is remove + a directory only if it is empty */ + x.directory = true; break; case 'f': -- 1.7.0.1