LCOV - code coverage report
Current view: top level - src - rmdir.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 65 77 84.4 %
Date: 2018-01-30 Functions: 6 6 100.0 %

          Line data    Source code
       1             : /* rmdir -- remove directories
       2             : 
       3             :    Copyright (C) 90, 91, 1995-2002, 2004-2008 Free Software
       4             :    Foundation, Inc.
       5             : 
       6             :    This program is free software: you can redistribute it and/or modify
       7             :    it under the terms of the GNU General Public License as published by
       8             :    the Free Software Foundation, either version 3 of the License, or
       9             :    (at your option) any later version.
      10             : 
      11             :    This program is distributed in the hope that it will be useful,
      12             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :    GNU General Public License for more details.
      15             : 
      16             :    You should have received a copy of the GNU General Public License
      17             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
      18             : 
      19             : /* Options:
      20             :    -p, --parent         Remove any parent dirs that are explicitly mentioned
      21             :                         in an argument, if they become empty after the
      22             :                         argument file is removed.
      23             : 
      24             :    David MacKenzie <djm@ai.mit.edu>  */
      25             : 
      26             : #include <config.h>
      27             : #include <stdio.h>
      28             : #include <getopt.h>
      29             : #include <sys/types.h>
      30             : 
      31             : #include "system.h"
      32             : #include "error.h"
      33             : #include "prog-fprintf.h"
      34             : #include "quote.h"
      35             : 
      36             : /* The official name of this program (e.g., no `g' prefix).  */
      37             : #define PROGRAM_NAME "rmdir"
      38             : 
      39             : #define AUTHORS "David MacKenzie"
      40             : 
      41             : /* The name this program was run with. */
      42             : char *program_name;
      43             : 
      44             : /* If true, remove empty parent directories.  */
      45             : static bool remove_empty_parents;
      46             : 
      47             : /* If true, don't treat failure to remove a nonempty directory
      48             :    as an error.  */
      49             : static bool ignore_fail_on_non_empty;
      50             : 
      51             : /* If true, output a diagnostic for every directory processed.  */
      52             : static bool verbose;
      53             : 
      54             : /* For long options that have no equivalent short option, use a
      55             :    non-character as a pseudo short option, starting with CHAR_MAX + 1.  */
      56             : enum
      57             : {
      58             :   IGNORE_FAIL_ON_NON_EMPTY_OPTION = CHAR_MAX + 1
      59             : };
      60             : 
      61             : static struct option const longopts[] =
      62             : {
      63             :   /* Don't name this `--force' because it's not close enough in meaning
      64             :      to e.g. rm's -f option.  */
      65             :   {"ignore-fail-on-non-empty", no_argument, NULL,
      66             :    IGNORE_FAIL_ON_NON_EMPTY_OPTION},
      67             : 
      68             :   {"path", no_argument, NULL, 'p'},  /* Deprecated.  */
      69             :   {"parents", no_argument, NULL, 'p'},
      70             :   {"verbose", no_argument, NULL, 'v'},
      71             :   {GETOPT_HELP_OPTION_DECL},
      72             :   {GETOPT_VERSION_OPTION_DECL},
      73             :   {NULL, 0, NULL, 0}
      74             : };
      75             : 
      76             : /* Return true if ERROR_NUMBER is one of the values associated
      77             :    with a failed rmdir due to non-empty target directory.  */
      78             : static bool
      79          10 : errno_rmdir_non_empty (int error_number)
      80             : {
      81          10 :   return (error_number == RMDIR_ERRNO_NOT_EMPTY);
      82             : }
      83             : 
      84             : /* Return true if when rmdir fails with errno == ERROR_NUMBER
      85             :    the directory may be empty.  */
      86             : static bool
      87          10 : errno_may_be_empty (int error_number)
      88             : {
      89          10 :   switch (error_number)
      90             :     {
      91           2 :     case EACCES:
      92             :     case EPERM:
      93             :     case EROFS:
      94             :     case EEXIST:
      95             :     case EBUSY:
      96           2 :       return true;
      97           8 :     default:
      98           8 :       return false;
      99             :     }
     100             : }
     101             : 
     102             : /* Return true if an rmdir failure with errno == error_number
     103             :    for DIR is ignorable.  */
     104             : static bool
     105          61 : ignorable_failure (int error_number, char const *dir)
     106             : {
     107          61 :   return (ignore_fail_on_non_empty
     108          61 :           && (errno_rmdir_non_empty (error_number)
     109          10 :               || (errno_may_be_empty (error_number)
     110           2 :                   && is_empty_dir (AT_FDCWD, dir))));
     111             : }
     112             : 
     113             : /* Remove any empty parent directories of DIR.
     114             :    If DIR contains slash characters, at least one of them
     115             :    (beginning with the rightmost) is replaced with a NUL byte.
     116             :    Return true if successful.  */
     117             : 
     118             : static bool
     119           1 : remove_parents (char *dir)
     120             : {
     121             :   char *slash;
     122           1 :   bool ok = true;
     123             : 
     124           1 :   strip_trailing_slashes (dir);
     125             :   while (1)
     126             :     {
     127           1 :       slash = strrchr (dir, '/');
     128           1 :       if (slash == NULL)
     129           1 :         break;
     130             :       /* Remove any characters after the slash, skipping any extra
     131             :          slashes in a row. */
     132           0 :       while (slash > dir && *slash == '/')
     133           0 :         --slash;
     134           0 :       slash[1] = 0;
     135             : 
     136             :       /* Give a diagnostic for each attempted removal if --verbose.  */
     137           0 :       if (verbose)
     138           0 :         prog_fprintf (stdout, _("removing directory, %s"), quote (dir));
     139             : 
     140           0 :       ok = (rmdir (dir) == 0);
     141             : 
     142           0 :       if (!ok)
     143             :         {
     144             :           /* Stop quietly if --ignore-fail-on-non-empty. */
     145           0 :           if (ignorable_failure (errno, dir))
     146             :             {
     147           0 :               ok = true;
     148             :             }
     149             :           else
     150             :             {
     151             :               /* Barring race conditions, DIR is expected to be a directory.  */
     152           0 :               error (0, errno, _("failed to remove directory %s"),
     153             :                      quote (dir));
     154             :             }
     155           0 :           break;
     156             :         }
     157             :     }
     158           1 :   return ok;
     159             : }
     160             : 
     161             : void
     162          13 : usage (int status)
     163             : {
     164          13 :   if (status != EXIT_SUCCESS)
     165          12 :     fprintf (stderr, _("Try `%s --help' for more information.\n"),
     166             :              program_name);
     167             :   else
     168             :     {
     169           1 :       printf (_("Usage: %s [OPTION]... DIRECTORY...\n"), program_name);
     170           1 :       fputs (_("\
     171             : Remove the DIRECTORY(ies), if they are empty.\n\
     172             : \n\
     173             :       --ignore-fail-on-non-empty\n\
     174             :                   ignore each failure that is solely because a directory\n\
     175             :                   is non-empty\n\
     176             : "), stdout);
     177           1 :       fputs (_("\
     178             :   -p, --parents   Remove DIRECTORY and its ancestors.  E.g., `rmdir -p a/b/c' is\n\
     179             :                   similar to `rmdir a/b/c a/b a'.\n\
     180             :   -v, --verbose   output a diagnostic for every directory processed\n\
     181             : "), stdout);
     182           1 :       fputs (HELP_OPTION_DESCRIPTION, stdout);
     183           1 :       fputs (VERSION_OPTION_DESCRIPTION, stdout);
     184           1 :       emit_bug_reporting_address ();
     185             :     }
     186          13 :   exit (status);
     187             : }
     188             : 
     189             : int
     190          52 : main (int argc, char **argv)
     191             : {
     192          52 :   bool ok = true;
     193             :   int optc;
     194             : 
     195             :   initialize_main (&argc, &argv);
     196          52 :   program_name = argv[0];
     197          52 :   setlocale (LC_ALL, "");
     198             :   bindtextdomain (PACKAGE, LOCALEDIR);
     199             :   textdomain (PACKAGE);
     200             : 
     201          52 :   atexit (close_stdout);
     202             : 
     203          52 :   remove_empty_parents = false;
     204             : 
     205         122 :   while ((optc = getopt_long (argc, argv, "pv", longopts, NULL)) != -1)
     206             :     {
     207          25 :       switch (optc)
     208             :         {
     209           7 :         case 'p':
     210           7 :           remove_empty_parents = true;
     211           7 :           break;
     212           8 :         case IGNORE_FAIL_ON_NON_EMPTY_OPTION:
     213           8 :           ignore_fail_on_non_empty = true;
     214           8 :           break;
     215           3 :         case 'v':
     216           3 :           verbose = true;
     217           3 :           break;
     218           1 :         case_GETOPT_HELP_CHAR;
     219           1 :         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
     220           5 :         default:
     221           5 :           usage (EXIT_FAILURE);
     222             :         }
     223             :     }
     224             : 
     225          45 :   if (optind == argc)
     226             :     {
     227           7 :       error (0, 0, _("missing operand"));
     228           7 :       usage (EXIT_FAILURE);
     229             :     }
     230             : 
     231         103 :   for (; optind < argc; ++optind)
     232             :     {
     233          65 :       char *dir = argv[optind];
     234             : 
     235             :       /* Give a diagnostic for each attempted removal if --verbose.  */
     236          65 :       if (verbose)
     237           3 :         prog_fprintf (stdout, _("removing directory, %s"), quote (dir));
     238             : 
     239          65 :       if (rmdir (dir) != 0)
     240             :         {
     241          61 :           if (ignorable_failure (errno, dir))
     242           0 :             continue;
     243             : 
     244             :           /* Here, the diagnostic is less precise, since we have no idea
     245             :              whether DIR is a directory.  */
     246          61 :           error (0, errno, _("failed to remove %s"), quote (dir));
     247          61 :           ok = false;
     248             :         }
     249           4 :       else if (remove_empty_parents)
     250             :         {
     251           1 :           ok &= remove_parents (dir);
     252             :         }
     253             :     }
     254             : 
     255          38 :   exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
     256             : }

Generated by: LCOV version 1.10