(remove_cwd_entries): Plug a gross leak -- don't call
[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' in 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       const char *s_ = (S);                     \
93       size_t len_ = strlen (s_) + 1;            \
94       char *tmp_dest_ = (char *) alloca (len_); \
95       DEST = memcpy (tmp_dest_, (s_), len_);    \
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 & (unsigned int) 0xf0000000) != 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   *result = memcpy (dp, sp, src_len);
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, and then
436      append the filename part, including a trailing zero byte.  */
437   memcpy (mempcpy (buf, dir_name, dir_len), filename, filename_len + 1);
438
439   assert (strlen (buf) + 1 == n_bytes_needed);
440
441   return buf;
442 }
443
444 static inline void
445 fspec_init_file (struct File_spec *fs, const char *filename)
446 {
447   fs->filename = (char *) filename;
448   fs->have_full_mode = 0;
449   fs->have_filetype_mode = 0;
450 }
451
452 static inline void
453 fspec_init_dp (struct File_spec *fs, struct dirent *dp)
454 {
455   fs->filename = dp->d_name;
456   fs->have_full_mode = 0;
457   fs->have_filetype_mode = 0;
458   fs->inum = D_INO (dp);
459
460 #if D_TYPE_IN_DIRENT && defined (DT_UNKNOWN) && defined (DTTOIF)
461   if (dp->d_type != DT_UNKNOWN)
462     {
463       fs->have_filetype_mode = 1;
464       fs->mode = DTTOIF (dp->d_type);
465     }
466 #endif
467 }
468
469 static inline int
470 fspec_get_full_mode (struct File_spec *fs, mode_t *full_mode)
471 {
472   struct stat stat_buf;
473
474   if (fs->have_full_mode)
475     {
476       *full_mode = fs->mode;
477       return 0;
478     }
479
480   if (lstat (fs->filename, &stat_buf))
481     return 1;
482
483   fs->have_full_mode = 1;
484   fs->have_filetype_mode = 1;
485   fs->mode = stat_buf.st_mode;
486   fs->inum = stat_buf.st_ino;
487
488   *full_mode = stat_buf.st_mode;
489   return 0;
490 }
491
492 static inline int
493 fspec_get_filetype_mode (struct File_spec *fs, mode_t *filetype_mode)
494 {
495   int fail;
496
497   if (fs->have_filetype_mode)
498     {
499       *filetype_mode = fs->mode;
500       fail = 0;
501     }
502   else
503     {
504       fail = fspec_get_full_mode (fs, filetype_mode);
505     }
506
507   return fail;
508 }
509
510 static inline mode_t
511 fspec_filetype_mode (const struct File_spec *fs)
512 {
513   assert (fs->have_filetype_mode);
514   return fs->mode;
515 }
516
517 /* Recursively remove all of the entries in the current directory.
518    Return an indication of the success of the operation.  */
519
520 enum RM_status
521 remove_cwd_entries (void)
522 {
523   /* NOTE: this is static.  */
524   static DIR *dirp = NULL;
525
526   /* NULL or a malloc'd and initialized hash table of entries in the
527      current directory that have been processed but not removed --
528      due either to an error or to an interactive `no' response.  */
529   struct HT *ht = NULL;
530
531   /* FIXME: describe */
532   struct obstack entry_name_pool;
533
534   enum RM_status status = RM_OK;
535
536   obstack_init (&entry_name_pool);
537
538   if (dirp)
539     {
540       if (CLOSEDIR (dirp))
541         {
542           /* FIXME-someday: but this is actually the previously opened dir.  */
543           error (0, errno, "%s", full_filename ("."));
544           status = RM_ERROR;
545         }
546       dirp = NULL;
547     }
548
549   do
550     {
551       /* FIXME: why do this?  */
552       errno = 0;
553
554       dirp = opendir (".");
555       if (dirp == NULL)
556         {
557           if (errno != ENOENT || !ignore_missing_files)
558             {
559               error (0, errno, "%s", full_filename ("."));
560               status = RM_ERROR;
561             }
562           break;
563         }
564
565       while (1)
566         {
567           char *entry_name;
568           struct File_spec fs;
569           enum RM_status tmp_status;
570           struct dirent *dp;
571
572 /* FILE should be skipped if it is `.' or `..', or if it is in
573    the table, HT, of entries we've already processed.  */
574 #define SKIPPABLE(Ht, File) (DOT_OR_DOTDOT(File) \
575                              || (Ht && hash_query_in_table (Ht, File)))
576
577           /* FIXME: use readdir_r directly into an obstack to avoid
578              the obstack_copy0 below --
579              Suggestion from Uli.  Be careful -- there are different
580              prototypes on e.g. Solaris.
581
582              Do something like this:
583              #define NAME_MAX_FOR(Parent_dir) pathconf ((Parent_dir),
584                                                          _PC_NAME_MAX);
585              dp = obstack_alloc (sizeof (struct dirent)
586                                  + NAME_MAX_FOR (".") + 1);
587              fail = xreaddir (dirp, dp);
588              where xreaddir is ...
589
590              But what about systems like the hurd where NAME_MAX is supposed
591              to be effectively unlimited.  We don't want to have to allocate
592              a huge buffer to accommodate maximum possible entry name.  */
593
594           dp = readdir (dirp);
595
596 #if ! HAVE_WORKING_READDIR
597           if (dp == NULL)
598             {
599               /* Since we have probably modified the directory since it
600                  was opened, readdir returning NULL does not necessarily
601                  mean we have read the last entry.  Rewind it and check
602                  again.  This happens on SunOS4.1.4 with 254 or more files
603                  in a directory.  */
604               rewinddir (dirp);
605               while ((dp = readdir (dirp)) && SKIPPABLE (ht, dp->d_name))
606                 {
607                   /* empty */
608                 }
609             }
610 #endif
611
612           if (dp == NULL)
613             break;
614
615           if (SKIPPABLE (ht, dp->d_name))
616             continue;
617
618           fspec_init_dp (&fs, dp);
619
620           /* Save a copy of the name of this entry, in case we have
621              to add it to the set of unremoved entries below.  */
622           entry_name = obstack_copy0 (&entry_name_pool,
623                                       dp->d_name, NLENGTH (dp));
624
625           /* CAUTION: after this call to rm, DP may not be valid --
626              it may have been freed due to a close in a recursive call
627              (through rm and remove_dir) to this function.  */
628           tmp_status = rm (&fs, 0);
629
630           /* Update status.  */
631           if (tmp_status > status)
632             status = tmp_status;
633           assert (VALID_STATUS (status));
634
635           /* If this entry was not removed (due either to an error or to
636              an interactive `no' response), record it in the hash table so
637              we don't consider it again if we reopen this directory later.  */
638           if (status != RM_OK)
639             {
640               int fail;
641
642               if (ht == NULL)
643                 {
644                   ht = hash_initialize (HT_INITIAL_CAPACITY, NULL,
645                                         hash_pjw, hash_compare_strings);
646                   if (ht == NULL)
647                     error (1, 0, _("Memory exhausted"));
648                 }
649               HASH_INSERT_NEW_ITEM (ht, entry_name, &fail);
650               if (fail)
651                 error (1, 0, _("Memory exhausted"));
652             }
653           else
654             {
655               /* This entry was not saved in the hash table.  Free it.  */
656               obstack_free (&entry_name_pool, entry_name);
657             }
658
659           if (dirp == NULL)
660             break;
661         }
662     }
663   while (dirp == NULL);
664
665   if (CLOSEDIR (dirp))
666     {
667       error (0, errno, "%s", full_filename ("."));
668       status = 1;
669     }
670   dirp = NULL;
671
672   if (ht)
673     {
674       hash_free (ht);
675     }
676
677   obstack_free (&entry_name_pool, NULL);
678
679   return status;
680 }
681
682 /* Query the user if appropriate, and if ok try to remove the
683    file or directory specified by FS.  Return RM_OK if it is removed,
684    and RM_ERROR or RM_USER_DECLINED if not.  */
685
686 static enum RM_status
687 remove_file (struct File_spec *fs)
688 {
689   int asked = 0;
690   char *pathname = fs->filename;
691
692   if (!ignore_missing_files && (interactive || stdin_tty)
693       && euidaccess (pathname, W_OK) )
694     {
695       if (!S_ISLNK (fspec_filetype_mode (fs)))
696         {
697           fprintf (stderr,
698                    (S_ISDIR (fspec_filetype_mode (fs))
699                     ? _("%s: remove write-protected directory `%s'? ")
700                     : _("%s: remove write-protected file `%s'? ")),
701                    program_name, full_filename (pathname));
702           if (!yesno ())
703             return RM_USER_DECLINED;
704
705           asked = 1;
706         }
707     }
708
709   if (!asked && interactive)
710     {
711       fprintf (stderr,
712                (S_ISDIR (fspec_filetype_mode (fs))
713                 ? _("%s: remove directory `%s'? ")
714                 : _("%s: remove `%s'? ")),
715                program_name, full_filename (pathname));
716       if (!yesno ())
717         return RM_USER_DECLINED;
718     }
719
720   if (verbose)
721     printf ("%s\n", full_filename (pathname));
722
723   if (unlink (pathname) && (errno != ENOENT || !ignore_missing_files))
724     {
725       error (0, errno, _("cannot unlink `%s'"), full_filename (pathname));
726       return RM_ERROR;
727     }
728   return RM_OK;
729 }
730
731 /* If not in recursive mode, print an error message and return RM_ERROR.
732    Otherwise, query the user if appropriate, then try to recursively
733    remove the directory specified by FS.  Return RM_OK if it is removed,
734    and RM_ERROR or RM_USER_DECLINED if not.
735    FIXME: describe need_save_cwd parameter.  */
736
737 static enum RM_status
738 remove_dir (struct File_spec *fs, int need_save_cwd)
739 {
740   enum RM_status status;
741   struct saved_cwd cwd;
742   char *dir_name = fs->filename;
743   const char *fmt = NULL;
744
745   if (!recursive)
746     {
747       error (0, 0, _("%s: is a directory"), full_filename (dir_name));
748       return RM_ERROR;
749     }
750
751   if (!ignore_missing_files && (interactive || stdin_tty)
752       && euidaccess (dir_name, W_OK))
753     {
754       fmt = _("%s: directory `%s' is write protected; descend into it anyway? ");
755     }
756   else if (interactive)
757     {
758       fmt = _("%s: descend into directory `%s'? ");
759     }
760
761   if (fmt)
762     {
763       fprintf (stderr, fmt, program_name, full_filename (dir_name));
764       if (!yesno ())
765         return RM_USER_DECLINED;
766     }
767
768   if (verbose)
769     printf ("%s\n", full_filename (dir_name));
770
771   /* Save cwd if needed.  */
772   if (need_save_cwd && save_cwd (&cwd))
773     return RM_ERROR;
774
775   /* Make target directory the current one.  */
776   if (chdir (dir_name) < 0)
777     {
778       error (0, errno, _("cannot change to directory %s"),
779              full_filename (dir_name));
780       if (need_save_cwd)
781         free_cwd (&cwd);
782       return RM_ERROR;
783     }
784
785   push_dir (dir_name);
786
787   /* Save a copy of dir_name.  Otherwise, remove_cwd_entries may clobber
788      it because it is just a pointer to the dir entry's d_name field, and
789      remove_cwd_entries may close the directory.  */
790   ASSIGN_STRDUPA (dir_name, dir_name);
791
792   status = remove_cwd_entries ();
793
794   pop_dir ();
795
796   /* Restore cwd.  */
797   if (need_save_cwd)
798     {
799       if (restore_cwd (&cwd, NULL, NULL))
800         {
801           free_cwd (&cwd);
802           return RM_ERROR;
803         }
804       free_cwd (&cwd);
805     }
806   else if (chdir ("..") < 0)
807     {
808       error (0, errno, _("cannot change back to directory %s via `..'"),
809              full_filename (dir_name));
810       return RM_ERROR;
811     }
812
813   if (interactive)
814     {
815       error (0, 0, _("remove directory `%s'%s? "), full_filename (dir_name),
816              (status != RM_OK ? _(" (might be nonempty)") : ""));
817       if (!yesno ())
818         {
819           return RM_USER_DECLINED;
820         }
821     }
822
823   if (rmdir (dir_name) && (errno != ENOENT || !ignore_missing_files))
824     {
825       error (0, errno, _("cannot remove directory `%s'"),
826              full_filename (dir_name));
827       return RM_ERROR;
828     }
829
830   return RM_OK;
831 }
832
833 /* Remove the file or directory specified by FS after checking appropriate
834    things.  Return RM_OK if it is removed, and RM_ERROR or RM_USER_DECLINED
835    if not.  If USER_SPECIFIED_NAME is non-zero, then the name part of FS may
836    be `.', `..', or may contain slashes.  Otherwise, it must be a simple file
837    name (and hence must specify a file in the current directory).  */
838
839 static enum RM_status
840 rm (struct File_spec *fs, int user_specified_name)
841 {
842   mode_t filetype_mode;
843
844   if (user_specified_name)
845     {
846       char *base = base_name (fs->filename);
847
848       if (DOT_OR_DOTDOT (base))
849         {
850           error (0, 0, _("cannot remove `.' or `..'"));
851           return RM_ERROR;
852         }
853     }
854
855   if (fspec_get_filetype_mode (fs, &filetype_mode))
856     {
857       if (ignore_missing_files && errno == ENOENT)
858         return RM_OK;
859
860       error (0, errno, _("cannot remove `%s'"), full_filename (fs->filename));
861       return RM_ERROR;
862     }
863
864 #ifdef ENABLE_CYCLE_CHECK
865   if (S_ISDIR (filetype_mode))
866     {
867       int fail;
868       struct active_dir_ent *old_ent;
869
870       /* Insert this directory in the active_dir_map.
871          If there is already a directory in the map with the same inum,
872          then there's *probably* a directory cycle.  This test can get
873          a false positive if two directories have the same inode number
874          but different device numbers and one directory contains the
875          other.  But since people don't often try to delete hierarchies
876          containing mount points, and when they do, duplicate inode
877          numbers are not that likely, this isn't worth detecting.  */
878       old_ent = hash_insert_if_absent (active_dir_map,
879                                        make_active_dir_ent (fs->inum,
880                                                             current_depth ()),
881                                        &fail);
882       if (fail)
883         error (1, 0, _("Memory exhausted"));
884
885       if (old_ent)
886         {
887           error (0, 0, _("\
888 WARNING: Circular directory structure.\n\
889 This almost certainly means that you have a corrupted file system.\n\
890 NOTIFY YOUR SYSTEM MANAGER.\n\
891 The following two directories have the same inode number:\n"));
892           /* FIXME: test this!!  */
893           print_nth_dir (stderr, current_depth ());
894           fputc ('\n', stderr);
895           print_nth_dir (stderr, old_ent->depth);
896           fputc ('\n', stderr);
897           fflush (stderr);
898
899           free (old_ent);
900
901           if (interactive)
902             {
903               error (0, 0, _("continue? "));
904               if (yesno ())
905                 return RM_ERROR;
906             }
907           exit (1);
908         }
909     }
910 #endif
911
912   if (!S_ISDIR (filetype_mode) || unlink_dirs)
913     {
914       return remove_file (fs);
915     }
916   else
917     {
918       int need_save_cwd = user_specified_name;
919       enum RM_status status;
920
921       if (need_save_cwd)
922         need_save_cwd = (strchr (fs->filename, '/') != NULL);
923
924       status = remove_dir (fs, need_save_cwd);
925
926 #ifdef ENABLE_CYCLE_CHECK
927       {
928         struct active_dir_ent tmp;
929         struct active_dir_ent *old_ent;
930
931         /* Remove this directory from the active_dir_map.  */
932         tmp.inum = fs->inum;
933         old_ent = hash_delete_if_present (active_dir_map, &tmp);
934         assert (old_ent != NULL);
935         free (old_ent);
936       }
937 #endif
938
939       return status;
940     }
941 }
942
943 int
944 main (int argc, char **argv)
945 {
946   int fail = 0;
947   int c;
948
949   program_name = argv[0];
950   setlocale (LC_ALL, "");
951   bindtextdomain (PACKAGE, LOCALEDIR);
952   textdomain (PACKAGE);
953
954   verbose = ignore_missing_files = recursive = interactive
955     = unlink_dirs = 0;
956
957   while ((c = getopt_long (argc, argv, "dfirvR", long_opts, NULL)) != -1)
958     {
959       switch (c)
960         {
961         case 0:         /* Long option.  */
962           break;
963         case 'd':
964           unlink_dirs = 1;
965           break;
966         case 'f':
967           interactive = 0;
968           ignore_missing_files = 1;
969           break;
970         case 'i':
971           interactive = 1;
972           ignore_missing_files = 0;
973           break;
974         case 'r':
975         case 'R':
976           recursive = 1;
977           break;
978         case 'v':
979           verbose = 1;
980           break;
981         default:
982           usage (1);
983         }
984     }
985
986   if (show_version)
987     {
988       printf ("rm (%s) %s\n", GNU_PACKAGE, VERSION);
989       exit (0);
990     }
991
992   if (show_help)
993     usage (0);
994
995   if (optind == argc)
996     {
997       if (ignore_missing_files)
998         exit (0);
999       else
1000         {
1001           error (0, 0, _("too few arguments"));
1002           usage (1);
1003         }
1004     }
1005
1006   stdin_tty = isatty (STDIN_FILENO);
1007
1008   /* Initialize dir-stack obstacks.  */
1009   obstack_init (&dir_stack);
1010   obstack_init (&len_stack);
1011
1012 #ifdef ENABLE_CYCLE_CHECK
1013   active_dir_map = hash_initialize (ACTIVE_DIR_INITIAL_CAPACITY, free,
1014                                     hash_active_dir_ent,
1015                                     hash_compare_active_dir_ents);
1016 #endif
1017
1018   for (; optind < argc; optind++)
1019     {
1020       struct File_spec fs;
1021       enum RM_status status;
1022
1023       /* Stripping slashes is harmless for rmdir;
1024          if the arg is not a directory, it will fail with ENOTDIR.  */
1025       strip_trailing_slashes (argv[optind]);
1026       fspec_init_file (&fs, argv[optind]);
1027       status = rm (&fs, 1);
1028       assert (VALID_STATUS (status));
1029       if (status == RM_ERROR)
1030         fail = 1;
1031     }
1032
1033 #ifdef ENABLE_CYCLE_CHECK
1034   hash_free (active_dir_map);
1035 #endif
1036
1037   exit (fail);
1038 }