(full_filename): Use memcpy, not mempcpy when not using the return value.
[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_ = 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   if (dirp)
537     {
538       if (CLOSEDIR (dirp))
539         {
540           /* FIXME-someday: but this is actually the previously opened dir.  */
541           error (0, errno, "%s", full_filename ("."));
542           status = RM_ERROR;
543         }
544       dirp = NULL;
545     }
546
547   do
548     {
549       /* FIXME: why do this?  */
550       errno = 0;
551
552       dirp = opendir (".");
553       if (dirp == NULL)
554         {
555           if (errno != ENOENT || !ignore_missing_files)
556             {
557               error (0, errno, "%s", full_filename ("."));
558               status = RM_ERROR;
559             }
560           break;
561         }
562
563       while (1)
564         {
565           char *entry_name;
566           struct File_spec fs;
567           enum RM_status tmp_status;
568           struct dirent *dp;
569
570           obstack_init (&entry_name_pool);
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 /* FIXME: add autoconf test to detect this.  */
597 #ifndef HAVE_WORKING_READDIR
598           if (dp == NULL)
599             {
600               /* Since we have probably modified the directory since it
601                  was opened, readdir returning NULL does not necessarily
602                  mean we have read the last entry.  Rewind it and check
603                  again.  This happens on SunOS4.1.4 with 254 or more files
604                  in a directory.  */
605               rewinddir (dirp);
606               while ((dp = readdir (dirp)) && SKIPPABLE (ht, dp->d_name))
607                 {
608                   /* empty */
609                 }
610             }
611 #endif
612
613           if (dp == NULL)
614             break;
615
616           if (SKIPPABLE (ht, dp->d_name))
617             continue;
618
619           fspec_init_dp (&fs, dp);
620
621           /* Save a copy of the name of this entry, in case we have
622              to add it to the set of unremoved entries below.  */
623           entry_name = obstack_copy0 (&entry_name_pool,
624                                       dp->d_name, NLENGTH (dp));
625
626           /* CAUTION: after this call to rm, DP may not be valid --
627              it may have been freed due to a close in a recursive call
628              (through rm and remove_dir) to this function.  */
629           tmp_status = rm (&fs, 0);
630
631           /* Update status.  */
632           if (tmp_status > status)
633             status = tmp_status;
634           assert (VALID_STATUS (status));
635
636           /* If this entry was not removed (due either to an error or to
637              an interactive `no' response), record it in the hash table so
638              we don't consider it again if we reopen this directory later.  */
639           if (status != RM_OK)
640             {
641               int fail;
642
643               if (ht == NULL)
644                 {
645                   ht = hash_initialize (HT_INITIAL_CAPACITY, NULL,
646                                         hash_pjw, hash_compare_strings);
647                   if (ht == NULL)
648                     error (1, 0, _("Memory exhausted"));
649                 }
650               HASH_INSERT_NEW_ITEM (ht, entry_name, &fail);
651               if (fail)
652                 error (1, 0, _("Memory exhausted"));
653             }
654           else
655             {
656               /* This entry was not saved in the hash table.  Free it.  */
657               obstack_free (&entry_name_pool, entry_name);
658             }
659
660           if (dirp == NULL)
661             break;
662         }
663     }
664   while (dirp == NULL);
665
666   if (CLOSEDIR (dirp))
667     {
668       error (0, errno, "%s", full_filename ("."));
669       status = 1;
670     }
671   dirp = NULL;
672
673   if (ht)
674     {
675       hash_free (ht);
676     }
677
678   obstack_free (&entry_name_pool, NULL);
679
680   return status;
681 }
682
683 /* Query the user if appropriate, and if ok try to remove the
684    file or directory specified by FS.  Return RM_OK if it is removed,
685    and RM_ERROR or RM_USER_DECLINED if not.  */
686
687 static enum RM_status
688 remove_file (struct File_spec *fs)
689 {
690   int asked = 0;
691   char *pathname = fs->filename;
692
693   if (!ignore_missing_files && (interactive || stdin_tty)
694       && euidaccess (pathname, W_OK) )
695     {
696       if (!S_ISLNK (fspec_filetype_mode (fs)))
697         {
698           fprintf (stderr,
699                    (S_ISDIR (fspec_filetype_mode (fs))
700                     ? _("%s: remove write-protected directory `%s'? ")
701                     : _("%s: remove write-protected file `%s'? ")),
702                    program_name, full_filename (pathname));
703           if (!yesno ())
704             return RM_USER_DECLINED;
705
706           asked = 1;
707         }
708     }
709
710   if (!asked && interactive)
711     {
712       fprintf (stderr,
713                (S_ISDIR (fspec_filetype_mode (fs))
714                 ? _("%s: remove directory `%s'? ")
715                 : _("%s: remove `%s'? ")),
716                program_name, full_filename (pathname));
717       if (!yesno ())
718         return RM_USER_DECLINED;
719     }
720
721   if (verbose)
722     printf ("%s\n", full_filename (pathname));
723
724   if (unlink (pathname) && (errno != ENOENT || !ignore_missing_files))
725     {
726       error (0, errno, _("cannot unlink `%s'"), full_filename (pathname));
727       return RM_ERROR;
728     }
729   return RM_OK;
730 }
731
732 /* If not in recursive mode, print an error message and return RM_ERROR.
733    Otherwise, query the user if appropriate, then try to recursively
734    remove the directory specified by FS.  Return RM_OK if it is removed,
735    and RM_ERROR or RM_USER_DECLINED if not.
736    FIXME: describe need_save_cwd parameter.  */
737
738 static enum RM_status
739 remove_dir (struct File_spec *fs, int need_save_cwd)
740 {
741   enum RM_status status;
742   struct saved_cwd cwd;
743   char *dir_name = fs->filename;
744   const char *fmt = NULL;
745
746   if (!recursive)
747     {
748       error (0, 0, _("%s: is a directory"), full_filename (dir_name));
749       return RM_ERROR;
750     }
751
752   if (!ignore_missing_files && (interactive || stdin_tty)
753       && euidaccess (dir_name, W_OK))
754     {
755       fmt = _("%s: directory `%s' is write protected; descend into it anyway? ");
756     }
757   else if (interactive)
758     {
759       fmt = _("%s: descend into directory `%s'? ");
760     }
761
762   if (fmt)
763     {
764       fprintf (stderr, fmt, program_name, full_filename (dir_name));
765       if (!yesno ())
766         return RM_USER_DECLINED;
767     }
768
769   if (verbose)
770     printf ("%s\n", full_filename (dir_name));
771
772   /* Save cwd if needed.  */
773   if (need_save_cwd && save_cwd (&cwd))
774     return RM_ERROR;
775
776   /* Make target directory the current one.  */
777   if (chdir (dir_name) < 0)
778     {
779       error (0, errno, _("cannot change to directory %s"),
780              full_filename (dir_name));
781       if (need_save_cwd)
782         free_cwd (&cwd);
783       return RM_ERROR;
784     }
785
786   push_dir (dir_name);
787
788   /* Save a copy of dir_name.  Otherwise, remove_cwd_entries may clobber
789      it because it is just a pointer to the dir entry's d_name field, and
790      remove_cwd_entries may close the directory.  */
791   ASSIGN_STRDUPA (dir_name, dir_name);
792
793   status = remove_cwd_entries ();
794
795   pop_dir ();
796
797   /* Restore cwd.  */
798   if (need_save_cwd)
799     {
800       if (restore_cwd (&cwd, NULL, NULL))
801         {
802           free_cwd (&cwd);
803           return RM_ERROR;
804         }
805       free_cwd (&cwd);
806     }
807   else if (chdir ("..") < 0)
808     {
809       error (0, errno, _("cannot change back to directory %s via `..'"),
810              full_filename (dir_name));
811       return RM_ERROR;
812     }
813
814   if (interactive)
815     {
816       error (0, 0, _("remove directory `%s'%s? "), full_filename (dir_name),
817              (status != RM_OK ? _(" (might be nonempty)") : ""));
818       if (!yesno ())
819         {
820           return RM_USER_DECLINED;
821         }
822     }
823
824   if (rmdir (dir_name) && (errno != ENOENT || !ignore_missing_files))
825     {
826       error (0, errno, _("cannot remove directory `%s'"),
827              full_filename (dir_name));
828       return RM_ERROR;
829     }
830
831   return RM_OK;
832 }
833
834 /* Remove the file or directory specified by FS after checking appropriate
835    things.  Return RM_OK if it is removed, and RM_ERROR or RM_USER_DECLINED
836    if not.  If USER_SPECIFIED_NAME is non-zero, then the name part of FS may
837    be `.', `..', or may contain slashes.  Otherwise, it must be a simple file
838    name (and hence must specify a file in the current directory).  */
839
840 static enum RM_status
841 rm (struct File_spec *fs, int user_specified_name)
842 {
843   mode_t filetype_mode;
844
845   if (user_specified_name)
846     {
847       char *base = base_name (fs->filename);
848
849       if (DOT_OR_DOTDOT (base))
850         {
851           error (0, 0, _("cannot remove `.' or `..'"));
852           return RM_ERROR;
853         }
854     }
855
856   if (fspec_get_filetype_mode (fs, &filetype_mode))
857     {
858       if (ignore_missing_files && errno == ENOENT)
859         return RM_OK;
860
861       error (0, errno, _("cannot remove `%s'"), full_filename (fs->filename));
862       return RM_ERROR;
863     }
864
865 #ifdef ENABLE_CYCLE_CHECK
866   if (S_ISDIR (filetype_mode))
867     {
868       int fail;
869       struct active_dir_ent *old_ent;
870
871       /* Insert this directory in the active_dir_map.
872          If there is already a directory in the map with the same inum,
873          then there's *probably* a directory cycle.  This test can get
874          a false positive if two directories have the same inode number
875          but different device numbers and one directory contains the
876          other.  But since people don't often try to delete hierarchies
877          containing mount points, and when they do, duplicate inode
878          numbers are not that likely, this isn't worth detecting.  */
879       old_ent = hash_insert_if_absent (active_dir_map,
880                                        make_active_dir_ent (fs->inum,
881                                                             current_depth ()),
882                                        &fail);
883       if (fail)
884         error (1, 0, _("Memory exhausted"));
885
886       if (old_ent)
887         {
888           error (0, 0, _("\
889 WARNING: Circular directory structure.\n\
890 This almost certainly means that you have a corrupted file system.\n\
891 NOTIFY YOUR SYSTEM MANAGER.\n\
892 The following two directories have the same inode number:\n"));
893           /* FIXME: test this!!  */
894           print_nth_dir (stderr, current_depth ());
895           fputc ('\n', stderr);
896           print_nth_dir (stderr, old_ent->depth);
897           fputc ('\n', stderr);
898           fflush (stderr);
899
900           free (old_ent);
901
902           if (interactive)
903             {
904               error (0, 0, _("continue? "));
905               if (yesno ())
906                 return RM_ERROR;
907             }
908           exit (1);
909         }
910     }
911 #endif
912
913   if (!S_ISDIR (filetype_mode) || unlink_dirs)
914     {
915       return remove_file (fs);
916     }
917   else
918     {
919       int need_save_cwd = user_specified_name;
920       enum RM_status status;
921
922       if (need_save_cwd)
923         need_save_cwd = (strchr (fs->filename, '/') != NULL);
924
925       status = remove_dir (fs, need_save_cwd);
926
927 #ifdef ENABLE_CYCLE_CHECK
928       {
929         struct active_dir_ent tmp;
930         struct active_dir_ent *old_ent;
931
932         /* Remove this directory from the active_dir_map.  */
933         tmp.inum = fs->inum;
934         old_ent = hash_delete_if_present (active_dir_map, &tmp);
935         assert (old_ent != NULL);
936         free (old_ent);
937       }
938 #endif
939
940       return status;
941     }
942 }
943
944 int
945 main (int argc, char **argv)
946 {
947   int fail = 0;
948   int c;
949
950   program_name = argv[0];
951   setlocale (LC_ALL, "");
952   bindtextdomain (PACKAGE, LOCALEDIR);
953   textdomain (PACKAGE);
954
955   verbose = ignore_missing_files = recursive = interactive
956     = unlink_dirs = 0;
957
958   while ((c = getopt_long (argc, argv, "dfirvR", long_opts, NULL)) != -1)
959     {
960       switch (c)
961         {
962         case 0:         /* Long option.  */
963           break;
964         case 'd':
965           unlink_dirs = 1;
966           break;
967         case 'f':
968           interactive = 0;
969           ignore_missing_files = 1;
970           break;
971         case 'i':
972           interactive = 1;
973           ignore_missing_files = 0;
974           break;
975         case 'r':
976         case 'R':
977           recursive = 1;
978           break;
979         case 'v':
980           verbose = 1;
981           break;
982         default:
983           usage (1);
984         }
985     }
986
987   if (show_version)
988     {
989       printf ("rm (%s) %s\n", GNU_PACKAGE, VERSION);
990       exit (0);
991     }
992
993   if (show_help)
994     usage (0);
995
996   if (optind == argc)
997     {
998       if (ignore_missing_files)
999         exit (0);
1000       else
1001         {
1002           error (0, 0, _("too few arguments"));
1003           usage (1);
1004         }
1005     }
1006
1007   stdin_tty = isatty (STDIN_FILENO);
1008
1009   /* Initialize dir-stack obstacks.  */
1010   obstack_init (&dir_stack);
1011   obstack_init (&len_stack);
1012
1013 #ifdef ENABLE_CYCLE_CHECK
1014   active_dir_map = hash_initialize (ACTIVE_DIR_INITIAL_CAPACITY, free,
1015                                     hash_active_dir_ent,
1016                                     hash_compare_active_dir_ents);
1017 #endif
1018
1019   for (; optind < argc; optind++)
1020     {
1021       struct File_spec fs;
1022       enum RM_status status;
1023
1024       /* Stripping slashes is harmless for rmdir;
1025          if the arg is not a directory, it will fail with ENOTDIR.  */
1026       strip_trailing_slashes (argv[optind]);
1027       fspec_init_file (&fs, argv[optind]);
1028       status = rm (&fs, 1);
1029       assert (VALID_STATUS (status));
1030       if (status == RM_ERROR)
1031         fail = 1;
1032     }
1033
1034 #ifdef ENABLE_CYCLE_CHECK
1035   hash_free (active_dir_map);
1036 #endif
1037
1038   exit (fail);
1039 }