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