Guard cycle-detecting code inside #ifdef ENABLE_CYCLE_CHECK.
[platform/upstream/coreutils.git] / src / rm.c
1 /* `rm' file deletion utility for GNU.
2    Copyright (C) 88, 90, 91, 94, 95, 96, 1997 Free Software Foundation, Inc.
3
4    This program is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 2, or (at your option)
7    any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program; if not, write to the Free Software Foundation,
16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18 /* Written by Paul Rubin, David MacKenzie, and Richard Stallman.
19    Reworked to use chdir and hash tables by Jim Meyering.  */
20
21 /* Implementation overview:
22
23    In the `usual' case RM saves no state for directories it is processing.
24    When a removal fails (either due to an error or to an interactive `no'
25    reply), the failure is noted (see description of `ht' remove_cwd_entries)
26    so that when/if the containing directory is reopened, RM doesn't try to
27    remove the entry again.
28
29    RM may delete arbitrarily deep hierarchies -- even ones in which file
30    names (from root to leaf) are longer than the system-imposed maximum.
31    It does this by using chdir to change to each directory in turn before
32    removing the entries in that directory.
33
34    RM detects directory cycles by maintaining a table of the currently
35    active directories.  See the description of active_dir_map below.
36
37    RM is careful to avoid forming full file names whenever possible.
38    A full file name is formed only when it is about to be used -- e.g.
39    in a diagnostic or in an interactive-mode prompt.
40
41    RM minimizes the number of lstat system calls it makes.  On systems
42    that have valid d_type data in directory entries, RM makes only one
43    lstat call per command line argument -- regardless of the depth of
44    the hierarchy.  */
45
46 #include <config.h>
47 #include <stdio.h>
48 #include <getopt.h>
49 #include <sys/types.h>
50 #include <assert.h>
51
52 #include "save-cwd.h"
53 #include "system.h"
54 #include "error.h"
55 #include "obstack.h"
56 #include "hash.h"
57
58 #ifndef PARAMS
59 # if defined (__GNUC__) || __STDC__
60 #  define PARAMS(args) args
61 # else
62 #  define PARAMS(args) ()
63 # endif
64 #endif
65
66 #define obstack_chunk_alloc malloc
67 #define obstack_chunk_free free
68
69 #ifdef D_INO_IN_DIRENT
70 # define D_INO(dp) ((dp)->d_ino)
71 # define ENABLE_CYCLE_CHECK
72 #else
73 /* Some systems don't have inodes, so fake them to avoid lots of ifdefs.  */
74 # define D_INO(dp) 1
75 #endif
76
77 #if !defined (S_ISLNK)
78 # define S_ISLNK(Mode) 0
79 #endif
80
81 #define DOT_OR_DOTDOT(Basename) \
82   (Basename[0] == '.' && (Basename[1] == '\0' \
83                           || (Basename[1] == '.' && Basename[2] == '\0')))
84
85 #if defined strdupa
86 # define ASSIGN_STRDUPA(DEST, S)                \
87   do { DEST = strdupa(S); } while (0)
88 #else
89 # define ASSIGN_STRDUPA(DEST, S)                \
90   do                                            \
91     {                                           \
92       size_t len_ = strlen (S) + 1;             \
93       char *tmp_dest_ = alloca (len_);          \
94       memcpy (tmp_dest_, (S), len_);            \
95       DEST = tmp_dest_;                         \
96     }                                           \
97   while (0)
98 #endif
99
100 enum RM_status
101 {
102   /* These must be listed in order of increasing seriousness. */
103   RM_OK,
104   RM_USER_DECLINED,
105   RM_ERROR
106 };
107
108 #define VALID_STATUS(S) \
109   ((S) == RM_OK || (S) == RM_USER_DECLINED || (S) == RM_ERROR)
110
111 /* Initial capacity of per-directory hash table of entries that have
112    been processed but not been deleted.  */
113 #define HT_INITIAL_CAPACITY 13
114
115 /* Initial capacity of the active directory hash table.  This table will
116    be resized only for hierarchies more than about 45 levels deep. */
117 #define ACTIVE_DIR_INITIAL_CAPACITY 53
118
119 struct File_spec
120 {
121   char *filename;
122   unsigned int have_filetype_mode:1;
123   unsigned int have_full_mode:1;
124   mode_t mode;
125   ino_t inum;
126 };
127
128 char *base_name ();
129 int euidaccess ();
130 void strip_trailing_slashes ();
131 int yesno ();
132
133 /* Forward dcl for recursively called function.  */
134 static enum RM_status rm PARAMS ((struct File_spec *fs,
135                                   int user_specified_name));
136
137 /* Name this program was run with.  */
138 char *program_name;
139
140 /* If nonzero, display the name of each file removed.  */
141 static int verbose;
142
143 /* If nonzero, ignore nonexistant files.  */
144 static int ignore_missing_files;
145
146 /* If nonzero, recursively remove directories.  */
147 static int recursive;
148
149 /* If nonzero, query the user about whether to remove each file.  */
150 static int interactive;
151
152 /* If nonzero, remove directories with unlink instead of rmdir, and don't
153    require a directory to be empty before trying to unlink it.
154    Only works for the super-user.  */
155 static int unlink_dirs;
156
157 /* If nonzero, stdin is a tty.  */
158 static int stdin_tty;
159
160 /* If nonzero, display usage information and exit.  */
161 static int show_help;
162
163 /* If nonzero, print the version on standard output and exit.  */
164 static int show_version;
165
166 /* The name of the directory (starting with and relative to a command
167    line argument) being processed.  When a subdirectory is entered, a new
168    component is appended (pushed).  When RM chdir's out of a directory,
169    the top component is removed (popped).  This is used to form a full
170    file name when necessary.  */
171 static struct obstack dir_stack;
172
173 /* Stack of lengths of directory names (including trailing slash)
174    appended to dir_stack.  We have to have a separate stack of lengths
175    (rather than just popping back to previous slash) because the first
176    element pushed onto the dir stack may contain slashes.  */
177 static struct obstack len_stack;
178
179 /* Set of `active' directories from the current command-line argument
180    to the level in the hierarchy at which files are being removed.
181    A directory is added to the active set when RM begins removing it
182    (or its entries), and it is removed from the set just after RM has
183    finished processing it.
184
185    This is actually a map (not a set), implemented with a hash table.
186    For each active directory, it maps the directory's inode number to the
187    depth of that directory relative to the root of the tree being deleted.
188    A directory specified on the command line has depth zero.
189    This construct is used to detect directory cycles so that RM can warn
190    about them rather than iterating endlessly.  */
191 #ifdef ENABLE_CYCLE_CHECK
192 static struct HT *active_dir_map;
193 #endif
194
195 /* An entry in the active_dir_map.  */
196 struct active_dir_ent
197 {
198   ino_t inum;
199   unsigned int depth;
200 };
201
202 static struct option const long_opts[] =
203 {
204   {"directory", no_argument, &unlink_dirs, 1},
205   {"force", no_argument, NULL, 'f'},
206   {"interactive", no_argument, NULL, 'i'},
207   {"recursive", no_argument, &recursive, 1},
208   {"verbose", no_argument, &verbose, 1},
209   {"help", no_argument, &show_help, 1},
210   {"version", no_argument, &show_version, 1},
211   {NULL, 0, NULL, 0}
212 };
213
214 static inline unsigned int
215 current_depth (void)
216 {
217   return obstack_object_size (&len_stack) / sizeof (size_t);
218 }
219
220 static void
221 print_nth_dir (FILE *stream, unsigned int depth)
222 {
223   size_t *length = (size_t *) obstack_base (&len_stack);
224   char *dir_name = (char *) obstack_base (&dir_stack);
225   unsigned int sum = 0;
226   unsigned int i;
227
228   assert (0 <= depth && depth < current_depth ());
229
230   for (i = 0; i <= depth; i++)
231     {
232       sum += length[i];
233     }
234
235   fwrite (dir_name, 1, sum, stream);
236 }
237
238 static inline struct active_dir_ent *
239 make_active_dir_ent (ino_t inum, unsigned int depth)
240 {
241   struct active_dir_ent *ent;
242   ent = (struct active_dir_ent *) xmalloc (sizeof *ent);
243   ent->inum = inum;
244   ent->depth = depth;
245   return ent;
246 }
247
248 static unsigned int
249 hash_active_dir_ent (void const *x, unsigned int table_size)
250 {
251   struct active_dir_ent const *ade = x;
252   return ade->inum % table_size;
253 }
254
255 static int
256 hash_compare_active_dir_ents (void const *x, void const *y)
257 {
258   struct active_dir_ent const *a = x;
259   struct active_dir_ent const *b = y;
260   return (a->inum == b->inum ? 0 : 1);
261 }
262
263 /* A hash function for null-terminated char* strings using
264    the method described in Aho, Sethi, & Ullman, p 436. */
265
266 static unsigned int
267 hash_pjw (const void *x, unsigned int tablesize)
268 {
269   const char *s = x;
270   unsigned int h = 0;
271   unsigned int g;
272
273   while (*s != 0)
274     {
275       h = (h << 4) + *s++;
276       if ((g = h & 0xf0000000U) != 0)
277         h = (h ^ (g >> 24)) ^ g;
278     }
279
280   return (h % tablesize);
281 }
282
283 static int
284 hash_compare_strings (void const *x, void const *y)
285 {
286   return strcmp (x, y);
287 }
288
289 static void
290 usage (int status)
291 {
292   if (status != 0)
293     fprintf (stderr, _("Try `%s --help' for more information.\n"),
294              program_name);
295   else
296     {
297       printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
298       printf (_("\
299 Remove (unlink) the FILE(s).\n\
300 \n\
301   -d, --directory       unlink directory, even if non-empty (super-user only)\n\
302   -f, --force           ignore nonexistent files, never prompt\n\
303   -i, --interactive     prompt before any removal\n\
304   -r, -R, --recursive   remove the contents of directories recursively\n\
305   -v, --verbose         explain what is being done\n\
306       --help            display this help and exit\n\
307       --version         output version information and exit\n\
308 "));
309       puts (_("\nReport bugs to <fileutils-bugs@gnu.org>."));
310     }
311   exit (status);
312 }
313
314 static inline void
315 push_dir (const char *dir_name)
316 {
317   size_t len;
318
319   len = strlen (dir_name);
320
321   /* Append the string onto the stack.  */
322   obstack_grow (&dir_stack, dir_name, len);
323
324   /* Append a trailing slash.  */
325   obstack_1grow (&dir_stack, '/');
326
327   /* Add one for the slash.  */
328   ++len;
329
330   /* Push the length (including slash) onto its stack.  */
331   obstack_grow (&len_stack, &len, sizeof (len));
332 }
333
334 static inline void
335 pop_dir (void)
336 {
337   int n_lengths = obstack_object_size (&len_stack) / sizeof (size_t);
338   size_t *length = (size_t *) obstack_base (&len_stack);
339   size_t top_len;
340
341   assert (n_lengths > 0);
342   top_len = length[n_lengths - 1];
343   assert (top_len >= 2);
344
345   /* Pop off the specified length of pathname.  */
346   assert (obstack_object_size (&dir_stack) >= top_len);
347   obstack_blank (&dir_stack, -top_len);
348
349   /* Pop the length stack, too.  */
350   assert (obstack_object_size (&len_stack) >= sizeof (size_t));
351   obstack_blank (&len_stack, -(sizeof (size_t)));
352 }
353
354 /* Copy the SRC_LEN bytes of data beginning at SRC into the DST_LEN-byte
355    buffer, DST, so that the last source byte is at the end of the destination
356    buffer.  If SRC_LEN is longer than DST_LEN, then set *TRUNCATED to non-zero.
357    Set *RESULT to point to the beginning of (the portion of) the source data
358    in DST.  Return the number of bytes remaining in the destination buffer.  */
359
360 static size_t
361 right_justify (char *dst, size_t dst_len, const char *src, size_t src_len,
362                char **result, int *truncated)
363 {
364   const char *sp;
365   char *dp;
366
367   if (src_len <= dst_len)
368     {
369       sp = src;
370       dp = dst + (dst_len - src_len);
371       *truncated = 0;
372     }
373   else
374     {
375       sp = src + (src_len - dst_len);
376       dp = dst;
377       src_len = dst_len;
378       *truncated = 1;
379     }
380
381   memcpy (dp, sp, src_len);
382
383   *result = dp;
384   return dst_len - src_len;
385 }
386
387 /* Using the global directory name obstack, create the full path to FILENAME.
388    Return it in sometimes-realloc'd space that should not be freed by the
389    caller.  Realloc as necessary.  If realloc fails, use a static buffer
390    and put as long a suffix in that buffer as possible.  */
391
392 static char *
393 full_filename (const char *filename)
394 {
395   static char *buf = NULL;
396   static size_t n_allocated = 0;
397
398   int dir_len = obstack_object_size (&dir_stack);
399   char *dir_name = (char *) obstack_base (&dir_stack);
400   size_t n_bytes_needed;
401   size_t filename_len;
402
403   filename_len = strlen (filename);
404   n_bytes_needed = dir_len + filename_len + 1;
405
406   if (n_bytes_needed > n_allocated)
407     {
408       /* This code requires that realloc accept NULL as the first arg.
409          This function must not use xrealloc.  Otherwise, an out-of-memory
410          error involving a file name to be expanded here wouldn't ever
411          be issued.  Use realloc and fall back on using a static buffer
412          if memory allocation fails.  */
413       buf = realloc (buf, n_bytes_needed);
414       n_allocated = n_bytes_needed;
415
416       if (buf == NULL)
417         {
418 #define SBUF_SIZE 512
419 #define ELLIPSES_PREFIX "[...]"
420           static char static_buf[SBUF_SIZE];
421           int truncated;
422           size_t len;
423           char *p;
424
425           len = right_justify (static_buf, SBUF_SIZE, filename,
426                                filename_len + 1, &p, &truncated);
427           right_justify (static_buf, len, dir_name, dir_len, &p, &truncated);
428           if (truncated)
429             {
430               memcpy (static_buf, ELLIPSES_PREFIX,
431                       sizeof (ELLIPSES_PREFIX) - 1);
432             }
433           return p;
434         }
435     }
436
437   /* Copy directory part, including trailing slash.  */
438   memcpy (buf, dir_name, dir_len);
439
440   /* Append filename part, including trailing zero byte.  */
441   memcpy (buf + dir_len, filename, filename_len + 1);
442
443   assert (strlen (buf) + 1 == n_bytes_needed);
444
445   return buf;
446 }
447
448 static inline void
449 fspec_init_file (struct File_spec *fs, const char *filename)
450 {
451   fs->filename = (char *) filename;
452   fs->have_full_mode = 0;
453   fs->have_filetype_mode = 0;
454 }
455
456 static inline void
457 fspec_init_dp (struct File_spec *fs, struct dirent *dp)
458 {
459   fs->filename = dp->d_name;
460   fs->have_full_mode = 0;
461   fs->have_filetype_mode = 0;
462   fs->inum = D_INO (dp);
463
464 #if D_TYPE_IN_DIRENT && defined (DT_UNKNOWN) && defined (DTTOIF)
465   if (dp->d_type != DT_UNKNOWN)
466     {
467       fs->have_filetype_mode = 1;
468       fs->mode = DTTOIF (dp->d_type);
469     }
470 #endif
471 }
472
473 static inline int
474 fspec_get_full_mode (struct File_spec *fs, mode_t *full_mode)
475 {
476   struct stat stat_buf;
477
478   if (fs->have_full_mode)
479     {
480       *full_mode = fs->mode;
481       return 0;
482     }
483
484   if (lstat (fs->filename, &stat_buf))
485     return 1;
486
487   fs->have_full_mode = 1;
488   fs->have_filetype_mode = 1;
489   fs->mode = stat_buf.st_mode;
490   fs->inum = stat_buf.st_ino;
491
492   *full_mode = stat_buf.st_mode;
493   return 0;
494 }
495
496 static inline int
497 fspec_get_filetype_mode (struct File_spec *fs, mode_t *filetype_mode)
498 {
499   int fail;
500
501   if (fs->have_filetype_mode)
502     {
503       *filetype_mode = fs->mode;
504       fail = 0;
505     }
506   else
507     {
508       fail = fspec_get_full_mode (fs, filetype_mode);
509     }
510
511   return fail;
512 }
513
514 static inline mode_t
515 fspec_filetype_mode (const struct File_spec *fs)
516 {
517   assert (fs->have_filetype_mode);
518   return fs->mode;
519 }
520
521 /* Recursively remove all of the entries in the current directory.
522    Return an indication of the success of the operation.  */
523
524 enum RM_status
525 remove_cwd_entries (void)
526 {
527   /* NOTE: this is static.  */
528   static DIR *dirp = NULL;
529
530   /* NULL or a malloc'd and initialized hash table of entries in the
531      current directory that have been processed but not removed --
532      due either to an error or to an interactive `no' response.  */
533   struct HT *ht = NULL;
534
535   enum RM_status status = RM_OK;
536
537   if (dirp)
538     {
539       if (CLOSEDIR (dirp))
540         {
541           /* FIXME-someday: but this is actually the previously opened dir.  */
542           error (0, errno, "%s", full_filename ("."));
543           status = RM_ERROR;
544         }
545       dirp = NULL;
546     }
547
548   do
549     {
550       /* FIXME: why do this?  */
551       errno = 0;
552
553       dirp = opendir (".");
554       if (dirp == NULL)
555         {
556           if (errno != ENOENT || !ignore_missing_files)
557             {
558               error (0, errno, "%s", full_filename ("."));
559               status = RM_ERROR;
560             }
561           break;
562         }
563
564       while (1)
565         {
566           char *entry_name;
567           struct File_spec fs;
568           enum RM_status tmp_status;
569           struct dirent *dp;
570
571 /* FILE should be skipped if it is `.' or `..', or if it is in
572    the table, HT, of entries we've already processed.  */
573 #define SKIPPABLE(Ht, File) (DOT_OR_DOTDOT(File) \
574                              || (Ht && hash_query_in_table (Ht, File)))
575
576           dp = readdir (dirp);
577           if (dp == NULL)
578             {
579               /* Since we have probably modified the directory since it
580                  was opened, readdir returning NULL does not necessarily
581                  mean we have read the last entry.  Rewind it and check
582                  again.  This happens on SunOS4.1.4 with 254 or more files
583                  in a directory.  */
584               rewinddir (dirp);
585               while ((dp = readdir (dirp)) && SKIPPABLE (ht, dp->d_name))
586                 {
587                   /* empty */
588                 }
589             }
590
591           if (dp == NULL)
592             break;
593
594           if (SKIPPABLE (ht, dp->d_name))
595             continue;
596
597           fspec_init_dp (&fs, dp);
598
599           /* Save a copy of the name of this entry, in case we have
600              to add it to the set of unremoved entries below.  */
601           ASSIGN_STRDUPA (entry_name, dp->d_name);
602
603           /* CAUTION: after this call to rm, DP may not be valid --
604              it may have been freed due to a close in a recursive call
605              (through rm and remove_dir) to this function.  */
606           tmp_status = rm (&fs, 0);
607
608           /* Update status.  */
609           if (tmp_status > status)
610             status = tmp_status;
611           assert (VALID_STATUS (status));
612
613           /* If this entry was not removed (due either to an error or to
614              an interactive `no' response), record it in the hash table so
615              we don't consider it again if we reopen this directory later.  */
616           if (status != RM_OK)
617             {
618               int fail;
619
620               if (ht == NULL)
621                 {
622                   ht = hash_initialize (HT_INITIAL_CAPACITY, NULL,
623                                         hash_pjw, hash_compare_strings);
624                   if (ht == NULL)
625                     error (1, 0, _("Memory exhausted"));
626                 }
627               HASH_INSERT_NEW_ITEM (ht, entry_name, &fail);
628               if (fail)
629                 error (1, 0, _("Memory exhausted"));
630             }
631
632           if (dirp == NULL)
633             break;
634         }
635     }
636   while (dirp == NULL);
637
638   if (CLOSEDIR (dirp))
639     {
640       error (0, errno, "%s", full_filename ("."));
641       status = 1;
642     }
643   dirp = NULL;
644
645   if (ht)
646     {
647       hash_free (ht);
648     }
649
650   return status;
651 }
652
653 /* Query the user if appropriate, and if ok try to remove the
654    file or directory specified by FS.  Return RM_OK if it is removed,
655    and RM_ERROR or RM_USER_DECLINED if not.  */
656
657 static enum RM_status
658 remove_file (struct File_spec *fs)
659 {
660   int asked = 0;
661   char *pathname = fs->filename;
662
663   if (!ignore_missing_files && (interactive || stdin_tty)
664       && euidaccess (pathname, W_OK) )
665     {
666       if (!S_ISLNK (fspec_filetype_mode (fs)))
667         {
668           fprintf (stderr,
669                    (S_ISDIR (fspec_filetype_mode (fs))
670                     ? _("%s: remove write-protected directory `%s'? ")
671                     : _("%s: remove write-protected file `%s'? ")),
672                    program_name, full_filename (pathname));
673           if (!yesno ())
674             return RM_USER_DECLINED;
675
676           asked = 1;
677         }
678     }
679
680   if (!asked && interactive)
681     {
682       fprintf (stderr,
683                (S_ISDIR (fspec_filetype_mode (fs))
684                 ? _("%s: remove directory `%s'? ")
685                 : _("%s: remove `%s'? ")),
686                program_name, full_filename (pathname));
687       if (!yesno ())
688         return RM_USER_DECLINED;
689     }
690
691   if (verbose)
692     printf ("%s\n", full_filename (pathname));
693
694   if (unlink (pathname) && (errno != ENOENT || !ignore_missing_files))
695     {
696       error (0, errno, _("cannot unlink `%s'"), full_filename (pathname));
697       return RM_ERROR;
698     }
699   return RM_OK;
700 }
701
702 /* If not in recursive mode, print an error message and return RM_ERROR.
703    Otherwise, query the user if appropriate, then try to recursively
704    remove the directory specified by FS.  Return RM_OK if it is removed,
705    and RM_ERROR or RM_USER_DECLINED if not.
706    FIXME: describe need_save_cwd parameter.  */
707
708 static enum RM_status
709 remove_dir (struct File_spec *fs, int need_save_cwd)
710 {
711   enum RM_status status;
712   struct saved_cwd cwd;
713   char *dir_name = fs->filename;
714   const char *fmt = NULL;
715
716   if (!recursive)
717     {
718       error (0, 0, _("%s: is a directory"), full_filename (dir_name));
719       return RM_ERROR;
720     }
721
722   if (!ignore_missing_files && (interactive || stdin_tty)
723       && euidaccess (dir_name, W_OK))
724     {
725       fmt = _("%s: directory `%s' is write protected; descend into it anyway? ");
726     }
727   else if (interactive)
728     {
729       fmt = _("%s: descend into directory `%s'? ");
730     }
731
732   if (fmt)
733     {
734       fprintf (stderr, fmt, program_name, full_filename (dir_name));
735       if (!yesno ())
736         return RM_USER_DECLINED;
737     }
738
739   if (verbose)
740     printf ("%s\n", full_filename (dir_name));
741
742   /* Save cwd if needed.  */
743   if (need_save_cwd && save_cwd (&cwd))
744     return RM_ERROR;
745
746   /* Make target directory the current one.  */
747   if (chdir (dir_name) < 0)
748     {
749       error (0, errno, _("cannot change to directory %s"),
750              full_filename (dir_name));
751       if (need_save_cwd)
752         free_cwd (&cwd);
753       return RM_ERROR;
754     }
755
756   push_dir (dir_name);
757
758   /* Save a copy of dir_name.  Otherwise, remove_cwd_entries may clobber
759      dir_name because dir_name is just a pointer to the dir entry's d_name
760      field, and remove_cwd_entries may close the directory.  */
761   ASSIGN_STRDUPA (dir_name, dir_name);
762
763   status = remove_cwd_entries ();
764
765   pop_dir ();
766
767   /* Restore cwd.  */
768   if (need_save_cwd)
769     {
770       if (restore_cwd (&cwd, NULL, NULL))
771         {
772           free_cwd (&cwd);
773           return RM_ERROR;
774         }
775       free_cwd (&cwd);
776     }
777   else if (chdir ("..") < 0)
778     {
779       error (0, errno, _("cannot change back to directory %s via `..'"),
780              full_filename (dir_name));
781       return RM_ERROR;
782     }
783
784   if (interactive)
785     {
786       error (0, 0, _("remove directory `%s'%s? "), full_filename (dir_name),
787              (status != RM_OK ? _(" (might be nonempty)") : ""));
788       if (!yesno ())
789         {
790           return RM_USER_DECLINED;
791         }
792     }
793
794   if (rmdir (dir_name) && (errno != ENOENT || !ignore_missing_files))
795     {
796       error (0, errno, _("cannot remove directory `%s'"),
797              full_filename (dir_name));
798       return RM_ERROR;
799     }
800
801   return RM_OK;
802 }
803
804 /* Remove the file or directory specified by FS after checking appropriate
805    things.  Return RM_OK if it is removed, and RM_ERROR or RM_USER_DECLINED
806    if not.  If USER_SPECIFIED_NAME is non-zero, then the name part of FS may
807    be `.', `..', or may contain slashes.  Otherwise, it must be a simple file
808    name (and hence must specify a file in the current directory).  */
809
810 static enum RM_status
811 rm (struct File_spec *fs, int user_specified_name)
812 {
813   mode_t filetype_mode;
814
815   if (user_specified_name)
816     {
817       char *base = base_name (fs->filename);
818
819       if (DOT_OR_DOTDOT (base))
820         {
821           error (0, 0, _("cannot remove `.' or `..'"));
822           return RM_ERROR;
823         }
824     }
825
826   if (fspec_get_filetype_mode (fs, &filetype_mode))
827     {
828       if (ignore_missing_files && errno == ENOENT)
829         return RM_OK;
830
831       error (0, errno, _("cannot remove `%s'"), full_filename (fs->filename));
832       return RM_ERROR;
833     }
834
835 #ifdef ENABLE_CYCLE_CHECK
836   if (S_ISDIR (filetype_mode))
837     {
838       int fail;
839       struct active_dir_ent *old_ent;
840
841       /* Insert this directory in the active_dir_map.
842          If there is already a directory in the map with the same inum,
843          then there's *probably* a directory cycle.  This test can get
844          a false positive if two directories have the same inode number
845          but different device numbers and one directory contains the
846          other.  But since people don't often try to delete hierarchies
847          containing mount points, and when they do, duplicate inode
848          numbers are not that likely, this isn't worth detecting.  */
849       old_ent = hash_insert_if_absent (active_dir_map,
850                                        make_active_dir_ent (fs->inum,
851                                                             current_depth ()),
852                                        &fail);
853       if (fail)
854         error (1, 0, _("Memory exhausted"));
855
856       if (old_ent)
857         {
858           error (0, 0, _("\
859 WARNING: Circular directory structure.\n\
860 This almost certainly means that you have a corrupted file system.\n\
861 NOTIFY YOUR SYSTEM MANAGER.\n\
862 The following two directories have the same inode number:\n"));
863           /* FIXME: test this!!  */
864           print_nth_dir (stderr, current_depth ());
865           fputc ('\n', stderr);
866           print_nth_dir (stderr, old_ent->depth);
867           fputc ('\n', stderr);
868           fflush (stderr);
869
870           free (old_ent);
871
872           if (interactive)
873             {
874               error (0, 0, _("continue? "));
875               if (yesno ())
876                 return RM_ERROR;
877             }
878           exit (1);
879         }
880     }
881 #endif
882
883   if (!S_ISDIR (filetype_mode) || unlink_dirs)
884     {
885       return remove_file (fs);
886     }
887   else
888     {
889       int need_save_cwd = user_specified_name;
890       enum RM_status status;
891
892       if (need_save_cwd)
893         need_save_cwd = (strchr (fs->filename, '/') != NULL);
894
895       status = remove_dir (fs, need_save_cwd);
896
897 #ifdef ENABLE_CYCLE_CHECK
898       {
899         struct active_dir_ent tmp;
900         struct active_dir_ent *old_ent;
901
902         /* Remove this directory from the active_dir_map.  */
903         tmp.inum = fs->inum;
904         old_ent = hash_delete_if_present (active_dir_map, &tmp);
905         assert (old_ent != NULL);
906         free (old_ent);
907       }
908 #endif
909
910       return status;
911     }
912 }
913
914 int
915 main (int argc, char **argv)
916 {
917   int fail = 0;
918   int c;
919
920   program_name = argv[0];
921   setlocale (LC_ALL, "");
922   bindtextdomain (PACKAGE, LOCALEDIR);
923   textdomain (PACKAGE);
924
925   verbose = ignore_missing_files = recursive = interactive
926     = unlink_dirs = 0;
927
928   while ((c = getopt_long (argc, argv, "dfirvR", long_opts, NULL)) != -1)
929     {
930       switch (c)
931         {
932         case 0:         /* Long option.  */
933           break;
934         case 'd':
935           unlink_dirs = 1;
936           break;
937         case 'f':
938           interactive = 0;
939           ignore_missing_files = 1;
940           break;
941         case 'i':
942           interactive = 1;
943           ignore_missing_files = 0;
944           break;
945         case 'r':
946         case 'R':
947           recursive = 1;
948           break;
949         case 'v':
950           verbose = 1;
951           break;
952         default:
953           usage (1);
954         }
955     }
956
957   if (show_version)
958     {
959       printf ("rm (%s) %s\n", GNU_PACKAGE, VERSION);
960       exit (0);
961     }
962
963   if (show_help)
964     usage (0);
965
966   if (optind == argc)
967     {
968       if (ignore_missing_files)
969         exit (0);
970       else
971         {
972           error (0, 0, _("too few arguments"));
973           usage (1);
974         }
975     }
976
977   stdin_tty = isatty (STDIN_FILENO);
978
979   /* Initialize dir-stack obstacks.  */
980   obstack_init (&dir_stack);
981   obstack_init (&len_stack);
982
983 #ifdef ENABLE_CYCLE_CHECK
984   active_dir_map = hash_initialize (ACTIVE_DIR_INITIAL_CAPACITY, free,
985                                     hash_active_dir_ent,
986                                     hash_compare_active_dir_ents);
987 #endif
988
989   for (; optind < argc; optind++)
990     {
991       struct File_spec fs;
992       enum RM_status status;
993
994       /* Stripping slashes is harmless for rmdir;
995          if the arg is not a directory, it will fail with ENOTDIR.  */
996       strip_trailing_slashes (argv[optind]);
997       fspec_init_file (&fs, argv[optind]);
998       status = rm (&fs, 1);
999       assert (VALID_STATUS (status));
1000       if (status == RM_ERROR)
1001         fail = 1;
1002     }
1003
1004 #ifdef ENABLE_CYCLE_CHECK
1005   hash_free (active_dir_map);
1006 #endif
1007
1008   exit (fail);
1009 }