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