/* rmdir -- remove directories                                                  This is the rmdir utility
                                                                                
   Copyright (C) 1990-2018 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 <https://www.gnu.org/licenses/>.  */   The GNUv3 license
                                                                                
/* Options:                                                                     
   -p, --parent  Remove any parent dirs that are explicitly mentioned           
                        in an argument, if they become empty after the          
                        argument file is removed.                               
                                                                                
   David MacKenzie <djm@ai.mit.edu>  */                                         
                                                                                
#include <config.h>                                                             Provides system specific information
#include <stdio.h>                                                              Provides standard I/O capability
#include <getopt.h>                                                             ...!includes auto-comment...
#include <sys/types.h>                                                          Provides system data types
                                                                                
#include "system.h"                                                             ...!includes auto-comment...
#include "error.h"                                                              ...!includes auto-comment...
#include "prog-fprintf.h"                                                       ...!includes auto-comment...
                                                                                
/* The official name of this program (e.g., no 'g' prefix).  */                 
#define PROGRAM_NAME "rmdir"                                                    Line 35
                                                                                
#define AUTHORS proper_name ("David MacKenzie")                                 Line 37
                                                                                
/* If true, remove empty parent directories.  */                                
static bool remove_empty_parents;                                               Line 40
                                                                                
/* If true, don't treat failure to remove a nonempty directory                  
   as an error.  */                                                             
static bool ignore_fail_on_non_empty;                                           Line 44
                                                                                
/* If true, output a diagnostic for every directory processed.  */              
static bool verbose;                                                            Line 47
                                                                                
/* For long options that have no equivalent short option, use a                 
   non-character as a pseudo short option, starting with CHAR_MAX + 1.  */      
enum                                                                            Line 51
{                                                                               
  IGNORE_FAIL_ON_NON_EMPTY_OPTION = CHAR_MAX + 1                                Line 53
};                                                                              Block 1
                                                                                
static struct option const longopts[] =                                         Line 56
{                                                                               
  /* Don't name this '--force' because it's not close enough in meaning         
     to e.g. rm's -f option.  */                                                
  {"ignore-fail-on-non-empty", no_argument, NULL,                               Line 60
   IGNORE_FAIL_ON_NON_EMPTY_OPTION},                                            Line 61
                                                                                
  {"path", no_argument, NULL, 'p'},  /* Deprecated.  */                         Line 63
  {"parents", no_argument, NULL, 'p'},                                          Line 64
  {"verbose", no_argument, NULL, 'v'},                                          Line 65
  {GETOPT_HELP_OPTION_DECL},                                                    Line 66
  {GETOPT_VERSION_OPTION_DECL},                                                 Line 67
  {NULL, 0, NULL, 0}                                                            Line 68
};                                                                              
                                                                                
/* Return true if ERROR_NUMBER is one of the values associated                  
   with a failed rmdir due to non-empty target directory.  */                   
static bool                                                                     Line 73
errno_rmdir_non_empty (int error_number)                                        Line 74
{                                                                               
  return error_number == ENOTEMPTY || error_number == EEXIST;                   Line 76
}                                                                               Block 3
                                                                                
/* Return true if when rmdir fails with errno == ERROR_NUMBER                   
   the directory may be empty.  */                                              
static bool                                                                     Line 81
errno_may_be_empty (int error_number)                                           Line 82
{                                                                               
  switch (error_number)                                                         Line 84
    {                                                                           
    case EACCES:                                                                Line 86
    case EPERM:                                                                 Line 87
    case EROFS:                                                                 Line 88
    case EEXIST:                                                                Line 89
    case EBUSY:                                                                 Line 90
      return true;                                                              Line 91
    default:                                                                    Line 92
      return false;                                                             Line 93
    }                                                                           
}                                                                               Block 4
                                                                                
/* Return true if an rmdir failure with errno == error_number                   
   for DIR is ignorable.  */                                                    
static bool                                                                     Line 99
ignorable_failure (int error_number, char const *dir)                           Line 100
{                                                                               
  return (ignore_fail_on_non_empty                                              Line 102
          && (errno_rmdir_non_empty (error_number)                              Line 103
              || (errno_may_be_empty (error_number)                             Line 104
                  && is_empty_dir (AT_FDCWD, dir))));                           Line 105
}                                                                               Block 5
                                                                                
/* Remove any empty parent directories of DIR.                                  
   If DIR contains slash characters, at least one of them                       
   (beginning with the rightmost) is replaced with a NUL byte.                  
   Return true if successful.  */                                               
                                                                                
static bool                                                                     Line 113
remove_parents (char *dir)                                                      Line 114
{                                                                               
  char *slash;                                                                  Line 116
  bool ok = true;                                                               Line 117
                                                                                
  strip_trailing_slashes (dir);                                                 Line 119
  while (1)                                                                     Line 120
    {                                                                           
      slash = strrchr (dir, '/');                                               Line 122
      if (slash == NULL)                                                        Line 123
        break;                                                                  Line 124
      /* Remove any characters after the slash, skipping any extra              
         slashes in a row. */                                                   
      while (slash > dir && *slash == '/')                                      Line 127
        --slash;                                                                Line 128
      slash[1] = 0;                                                             Line 129
                                                                                
      /* Give a diagnostic for each attempted removal if --verbose.  */         
      if (verbose)                                                              Line 132
        prog_fprintf (stdout, _("removing directory, %s"), quoteaf (dir));      Line 133
                                                                                
      ok = (rmdir (dir) == 0);                                                  Line 135
                                                                                
      if (!ok)                                                                  Line 137
        {                                                                       
          /* Stop quietly if --ignore-fail-on-non-empty. */                     
          if (ignorable_failure (errno, dir))                                   Line 140
            {                                                                   
              ok = true;                                                        Line 142
            }                                                                   
          else                                                                  Line 144
            {                                                                   
              /* Barring race conditions, DIR is expected to be a directory.  */
              error (0, errno, _("failed to remove directory %s"),              Line 147
                     quoteaf (dir));                                            Line 148
            }                                                                   
          break;                                                                Line 150
        }                                                                       
    }                                                                           
  return ok;                                                                    Line 153
}                                                                               Block 6
                                                                                
void                                                                            Line 156
usage (int status)                                                              Line 157
{                                                                               
  if (status != EXIT_SUCCESS)                                                   Line 159
    emit_try_help ();                                                           ...!common auto-comment...
  else                                                                          Line 161
    {                                                                           
      printf (_("Usage: %s [OPTION]... DIRECTORY...\n"), program_name);         Line 163
      fputs (_("\                                                               Line 164
Remove the DIRECTORY(ies), if they are empty.\n\                                Line 165
\n\                                                                             
      --ignore-fail-on-non-empty\n\                                             Line 167
                  ignore each failure that is solely because a directory\n\     Line 168
                    is non-empty\n\                                             Line 169
"), stdout);                                                                    Line 170
      fputs (_("\                                                               Line 171
  -p, --parents   remove DIRECTORY and its ancestors; e.g., 'rmdir -p a/b/c' is\Line 172
\n\                                                                             
                    similar to 'rmdir a/b/c a/b a'\n\                           Line 174
  -v, --verbose   output a diagnostic for every directory processed\n\          Line 175
"), stdout);                                                                    Line 176
      fputs (HELP_OPTION_DESCRIPTION, stdout);                                  Line 177
      fputs (VERSION_OPTION_DESCRIPTION, stdout);                               Line 178
      emit_ancillary_info (PROGRAM_NAME);                                       Line 179
    }                                                                           
  exit (status);                                                                Line 181
}                                                                               Block 7
                                                                                
int                                                                             
main (int argc, char **argv)                                                    Line 185
{                                                                               
  bool ok = true;                                                               Line 187
  int optc;                                                                     Line 188
                                                                                
  initialize_main (&argc, &argv);                                               VMS-specific entry point handling wildcard expansion
  set_program_name (argv[0]);                                                   Retains program name and discards path
  setlocale (LC_ALL, "");                                                       Sets up internationalization (i18n)
  bindtextdomain (PACKAGE, LOCALEDIR);                                          Assigns i18n directorySets text domain for _() [gettext()] function
  textdomain (PACKAGE);                                                         Sets text domain for _() [gettext()] function
                                                                                
  atexit (close_stdout);                                                        Close stdout on exit (see gnulib)
                                                                                
  remove_empty_parents = false;                                                 Line 198
                                                                                
  while ((optc = getopt_long (argc, argv, "pv", longopts, NULL)) != -1)         Line 200
    {                                                                           
      switch (optc)                                                             Line 202
        {                                                                       
        case 'p':                                                               Line 204
          remove_empty_parents = true;                                          Line 205
          break;                                                                Line 206
        case IGNORE_FAIL_ON_NON_EMPTY_OPTION:                                   Line 207
          ignore_fail_on_non_empty = true;                                      Line 208
          break;                                                                Line 209
        case 'v':                                                               Line 210
          verbose = true;                                                       Line 211
          break;                                                                Line 212
        case_GETOPT_HELP_CHAR;                                                  Line 213
        case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);                       Line 214
        default:                                                                Line 215
          usage (EXIT_FAILURE);                                                 Line 216
        }                                                                       
    }                                                                           
                                                                                
  if (optind == argc)                                                           Line 220
    {                                                                           
      error (0, 0, _("missing operand"));                                       Line 222
      usage (EXIT_FAILURE);                                                     Line 223
    }                                                                           
                                                                                
  for (; optind < argc; ++optind)                                               Line 226
    {                                                                           
      char *dir = argv[optind];                                                 Line 228
                                                                                
      /* Give a diagnostic for each attempted removal if --verbose.  */         
      if (verbose)                                                              Line 231
        prog_fprintf (stdout, _("removing directory, %s"), quoteaf (dir));      Line 232
                                                                                
      if (rmdir (dir) != 0)                                                     Line 234
        {                                                                       
          if (ignorable_failure (errno, dir))                                   Line 236
            continue;                                                           Line 237
                                                                                
          /* Here, the diagnostic is less precise, since we have no idea        
             whether DIR is a directory.  */                                    
          error (0, errno, _("failed to remove %s"), quoteaf (dir));            Line 241
          ok = false;                                                           Line 242
        }                                                                       
      else if (remove_empty_parents)                                            Line 244
        {                                                                       
          ok &= remove_parents (dir);                                           Line 246
        }                                                                       
    }                                                                           
                                                                                
  return ok ? EXIT_SUCCESS : EXIT_FAILURE;                                      Line 250
}                                                                               Block 8