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

Reply via email to