*** empty log message ***
[coreutils.git] / src / remove.c
blobe827ac7438852eb5a9b1f0a13a6e5e18dc8501d0
1 /* remove.c -- core functions for removing files and directories
2 Copyright (C) 88, 90, 91, 1994-1999 Free Software Foundation, Inc.
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.
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.
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. */
18 /* Extracted from rm.c and librarified by Jim Meyering. */
20 #ifdef _AIX
21 #pragma alloca
22 #endif
24 #include <config.h>
25 #include <stdio.h>
26 #include <sys/types.h>
27 #include <assert.h>
29 #if HAVE_STDBOOL_H
30 # include <stdbool.h>
31 #else
32 typedef enum {false = 0, true = 1} bool;
33 #endif
35 #include "save-cwd.h"
36 #include "system.h"
37 #include "error.h"
38 #include "obstack.h"
39 #include "hash.h"
40 #include "remove.h"
42 #define obstack_chunk_alloc malloc
43 #define obstack_chunk_free free
45 #ifndef PARAMS
46 # if defined (__GNUC__) || __STDC__
47 # define PARAMS(args) args
48 # else
49 # define PARAMS(args) ()
50 # endif
51 #endif
53 /* On systems with an lstat function that accepts the empty string,
54 arrange to make lstat calls go through the wrapper function. */
55 #if HAVE_LSTAT_EMPTY_STRING_BUG
56 int rpl_lstat PARAMS((const char *, struct stat *));
57 # define lstat(Name, Stat_buf) rpl_lstat(Name, Stat_buf)
58 #endif
60 #ifdef D_INO_IN_DIRENT
61 # define D_INO(dp) ((dp)->d_ino)
62 # define ENABLE_CYCLE_CHECK
63 #else
64 /* Some systems don't have inodes, so fake them to avoid lots of ifdefs. */
65 # define D_INO(dp) 1
66 #endif
68 #if !defined (S_ISLNK)
69 # define S_ISLNK(Mode) 0
70 #endif
72 #if defined strdupa
73 # define ASSIGN_STRDUPA(DEST, S) \
74 do { DEST = strdupa(S); } while (0)
75 #else
76 # define ASSIGN_STRDUPA(DEST, S) \
77 do \
78 { \
79 const char *s_ = (S); \
80 size_t len_ = strlen (s_) + 1; \
81 char *tmp_dest_ = (char *) alloca (len_); \
82 DEST = memcpy (tmp_dest_, (s_), len_); \
83 } \
84 while (0)
85 #endif
87 /* Initial capacity of per-directory hash table of entries that have
88 been processed but not been deleted. */
89 #define HT_INITIAL_CAPACITY 13
91 /* Initial capacity of the active directory hash table. This table will
92 be resized only for hierarchies more than about 45 levels deep. */
93 #define ACTIVE_DIR_INITIAL_CAPACITY 53
95 int euidaccess ();
96 int yesno ();
98 extern char *program_name;
100 /* state initialized by remove_init, freed by remove_fini */
102 /* An entry in the active_dir_map. */
103 struct active_dir_ent
105 ino_t inum;
106 unsigned int depth;
109 /* The name of the directory (starting with and relative to a command
110 line argument) being processed. When a subdirectory is entered, a new
111 component is appended (pushed). When RM chdir's out of a directory,
112 the top component is removed (popped). This is used to form a full
113 file name when necessary. */
114 static struct obstack dir_stack;
116 /* Stack of lengths of directory names (including trailing slash)
117 appended to dir_stack. We have to have a separate stack of lengths
118 (rather than just popping back to previous slash) because the first
119 element pushed onto the dir stack may contain slashes. */
120 static struct obstack len_stack;
122 /* Set of `active' directories from the current command-line argument
123 to the level in the hierarchy at which files are being removed.
124 A directory is added to the active set when RM begins removing it
125 (or its entries), and it is removed from the set just after RM has
126 finished processing it.
128 This is actually a map (not a set), implemented with a hash table.
129 For each active directory, it maps the directory's inode number to the
130 depth of that directory relative to the root of the tree being deleted.
131 A directory specified on the command line has depth zero.
132 This construct is used to detect directory cycles so that RM can warn
133 about them rather than iterating endlessly. */
134 #ifdef ENABLE_CYCLE_CHECK
135 static struct hash_table *active_dir_map;
136 #endif
138 static inline unsigned int
139 current_depth (void)
141 return obstack_object_size (&len_stack) / sizeof (size_t);
144 static void
145 print_nth_dir (FILE *stream, unsigned int depth)
147 size_t *length = (size_t *) obstack_base (&len_stack);
148 char *dir_name = (char *) obstack_base (&dir_stack);
149 unsigned int sum = 0;
150 unsigned int i;
152 assert (depth < current_depth ());
154 for (i = 0; i <= depth; i++)
156 sum += length[i];
159 fwrite (dir_name, 1, sum, stream);
162 static inline struct active_dir_ent *
163 make_active_dir_ent (ino_t inum, unsigned int depth)
165 struct active_dir_ent *ent;
166 ent = (struct active_dir_ent *) xmalloc (sizeof *ent);
167 ent->inum = inum;
168 ent->depth = depth;
169 return ent;
172 static unsigned int
173 hash_active_dir_ent (void const *x, unsigned int table_size)
175 struct active_dir_ent const *ade = x;
176 return ade->inum % table_size;
179 static bool
180 hash_compare_active_dir_ents (void const *x, void const *y)
182 struct active_dir_ent const *a = x;
183 struct active_dir_ent const *b = y;
184 return a->inum == b->inum;
187 /* A hash function for null-terminated char* strings using
188 the method described in Aho, Sethi, & Ullman, p 436. */
190 static unsigned int
191 hash_pjw (const void *x, unsigned int tablesize)
193 const char *s = x;
194 unsigned int h = 0;
195 unsigned int g;
197 while (*s != 0)
199 h = (h << 4) + *s++;
200 if ((g = h & (unsigned int) 0xf0000000) != 0)
201 h = (h ^ (g >> 24)) ^ g;
204 return (h % tablesize);
207 static bool
208 hash_compare_strings (void const *x, void const *y)
210 return STREQ (x, y) ? true : false;
213 static inline void
214 push_dir (const char *dir_name)
216 size_t len;
218 len = strlen (dir_name);
220 /* Append the string onto the stack. */
221 obstack_grow (&dir_stack, dir_name, len);
223 /* Append a trailing slash. */
224 obstack_1grow (&dir_stack, '/');
226 /* Add one for the slash. */
227 ++len;
229 /* Push the length (including slash) onto its stack. */
230 obstack_grow (&len_stack, &len, sizeof (len));
233 static inline void
234 pop_dir (void)
236 int n_lengths = obstack_object_size (&len_stack) / sizeof (size_t);
237 size_t *length = (size_t *) obstack_base (&len_stack);
238 size_t top_len;
240 assert (n_lengths > 0);
241 top_len = length[n_lengths - 1];
242 assert (top_len >= 2);
244 /* Pop off the specified length of pathname. */
245 assert (obstack_object_size (&dir_stack) >= top_len);
246 obstack_blank (&dir_stack, -top_len);
248 /* Pop the length stack, too. */
249 assert (obstack_object_size (&len_stack) >= sizeof (size_t));
250 obstack_blank (&len_stack, (int) -(sizeof (size_t)));
253 /* Copy the SRC_LEN bytes of data beginning at SRC into the DST_LEN-byte
254 buffer, DST, so that the last source byte is at the end of the destination
255 buffer. If SRC_LEN is longer than DST_LEN, then set *TRUNCATED to non-zero.
256 Set *RESULT to point to the beginning of (the portion of) the source data
257 in DST. Return the number of bytes remaining in the destination buffer. */
259 static size_t
260 right_justify (char *dst, size_t dst_len, const char *src, size_t src_len,
261 char **result, int *truncated)
263 const char *sp;
264 char *dp;
266 if (src_len <= dst_len)
268 sp = src;
269 dp = dst + (dst_len - src_len);
270 *truncated = 0;
272 else
274 sp = src + (src_len - dst_len);
275 dp = dst;
276 src_len = dst_len;
277 *truncated = 1;
280 *result = memcpy (dp, sp, src_len);
281 return dst_len - src_len;
284 /* Using the global directory name obstack, create the full path to FILENAME.
285 Return it in sometimes-realloc'd space that should not be freed by the
286 caller. Realloc as necessary. If realloc fails, use a static buffer
287 and put as long a suffix in that buffer as possible. */
289 static char *
290 full_filename (const char *filename)
292 static char *buf = NULL;
293 static size_t n_allocated = 0;
295 int dir_len = obstack_object_size (&dir_stack);
296 char *dir_name = (char *) obstack_base (&dir_stack);
297 size_t n_bytes_needed;
298 size_t filename_len;
300 filename_len = strlen (filename);
301 n_bytes_needed = dir_len + filename_len + 1;
303 if (n_bytes_needed > n_allocated)
305 /* This code requires that realloc accept NULL as the first arg.
306 This function must not use xrealloc. Otherwise, an out-of-memory
307 error involving a file name to be expanded here wouldn't ever
308 be issued. Use realloc and fall back on using a static buffer
309 if memory allocation fails. */
310 buf = realloc (buf, n_bytes_needed);
311 n_allocated = n_bytes_needed;
313 if (buf == NULL)
315 #define SBUF_SIZE 512
316 #define ELLIPSES_PREFIX "[...]"
317 static char static_buf[SBUF_SIZE];
318 int truncated;
319 size_t len;
320 char *p;
322 len = right_justify (static_buf, SBUF_SIZE, filename,
323 filename_len + 1, &p, &truncated);
324 right_justify (static_buf, len, dir_name, dir_len, &p, &truncated);
325 if (truncated)
327 memcpy (static_buf, ELLIPSES_PREFIX,
328 sizeof (ELLIPSES_PREFIX) - 1);
330 return p;
334 /* Copy directory part, including trailing slash, and then
335 append the filename part, including a trailing zero byte. */
336 memcpy (mempcpy (buf, dir_name, dir_len), filename, filename_len + 1);
338 assert (strlen (buf) + 1 == n_bytes_needed);
340 return buf;
343 void
344 fspec_init_file (struct File_spec *fs, const char *filename)
346 fs->filename = (char *) filename;
347 fs->have_full_mode = 0;
348 fs->have_filetype_mode = 0;
351 static inline void
352 fspec_init_dp (struct File_spec *fs, struct dirent *dp)
354 fs->filename = dp->d_name;
355 fs->have_full_mode = 0;
356 fs->have_filetype_mode = 0;
357 fs->inum = D_INO (dp);
359 #if D_TYPE_IN_DIRENT && defined (DT_UNKNOWN) && defined (DTTOIF)
360 if (dp->d_type != DT_UNKNOWN)
362 fs->have_filetype_mode = 1;
363 fs->mode = DTTOIF (dp->d_type);
365 #endif
368 static inline int
369 fspec_get_full_mode (struct File_spec *fs, mode_t *full_mode)
371 struct stat stat_buf;
373 if (fs->have_full_mode)
375 *full_mode = fs->mode;
376 return 0;
379 if (lstat (fs->filename, &stat_buf))
380 return 1;
382 fs->have_full_mode = 1;
383 fs->have_filetype_mode = 1;
384 fs->mode = stat_buf.st_mode;
385 fs->inum = stat_buf.st_ino;
387 *full_mode = stat_buf.st_mode;
388 return 0;
391 static inline int
392 fspec_get_filetype_mode (struct File_spec *fs, mode_t *filetype_mode)
394 int fail;
396 if (fs->have_filetype_mode)
398 *filetype_mode = fs->mode;
399 fail = 0;
401 else
403 fail = fspec_get_full_mode (fs, filetype_mode);
406 return fail;
409 static inline mode_t
410 fspec_filetype_mode (const struct File_spec *fs)
412 assert (fs->have_filetype_mode);
413 return fs->mode;
416 static int
417 same_file (const char *file_1, const char *file_2)
419 struct stat sb1, sb2;
420 return (lstat (file_1, &sb1) == 0
421 && lstat (file_2, &sb2) == 0
422 && SAME_INODE (sb1, sb2));
426 /* Recursively remove all of the entries in the current directory.
427 Return an indication of the success of the operation. */
429 static enum RM_status
430 remove_cwd_entries (const struct rm_options *x)
432 /* NOTE: this is static. */
433 static DIR *dirp = NULL;
435 /* NULL or a malloc'd and initialized hash table of entries in the
436 current directory that have been processed but not removed --
437 due either to an error or to an interactive `no' response. */
438 struct hash_table *ht = NULL;
440 /* FIXME: describe */
441 static struct obstack entry_name_pool;
442 static int first_call = 1;
444 enum RM_status status = RM_OK;
446 if (first_call)
448 first_call = 0;
449 obstack_init (&entry_name_pool);
452 if (dirp)
454 if (CLOSEDIR (dirp))
456 /* FIXME-someday: but this is actually the previously opened dir. */
457 error (0, errno, "%s", full_filename ("."));
458 status = RM_ERROR;
460 dirp = NULL;
465 /* FIXME: why do this? */
466 errno = 0;
468 dirp = opendir (".");
469 if (dirp == NULL)
471 if (errno != ENOENT || !x->ignore_missing_files)
473 error (0, errno, "%s", full_filename ("."));
474 status = RM_ERROR;
476 break;
479 while (1)
481 char *entry_name;
482 struct File_spec fs;
483 enum RM_status tmp_status;
484 struct dirent *dp;
486 /* FILE should be skipped if it is `.' or `..', or if it is in
487 the table, HT, of entries we've already processed. */
488 #define SKIPPABLE(Ht, File) \
489 (DOT_OR_DOTDOT(File) || (Ht && hash_lookup (Ht, File)))
491 /* FIXME: use readdir_r directly into an obstack to avoid
492 the obstack_copy0 below --
493 Suggestion from Uli. Be careful -- there are different
494 prototypes on e.g. Solaris.
496 Do something like this:
497 #define NAME_MAX_FOR(Parent_dir) pathconf ((Parent_dir),
498 _PC_NAME_MAX);
499 dp = obstack_alloc (sizeof (struct dirent)
500 + NAME_MAX_FOR (".") + 1);
501 fail = xreaddir (dirp, dp);
502 where xreaddir is ...
504 But what about systems like the hurd where NAME_MAX is supposed
505 to be effectively unlimited. We don't want to have to allocate
506 a huge buffer to accommodate maximum possible entry name. */
508 dp = readdir (dirp);
510 #if ! HAVE_WORKING_READDIR
511 if (dp == NULL)
513 /* Since we have probably modified the directory since it
514 was opened, readdir returning NULL does not necessarily
515 mean we have read the last entry. Rewind it and check
516 again. This happens on SunOS4.1.4 with 254 or more files
517 in a directory. */
518 rewinddir (dirp);
519 while ((dp = readdir (dirp)) && SKIPPABLE (ht, dp->d_name))
521 /* empty */
524 #endif
526 if (dp == NULL)
527 break;
529 if (SKIPPABLE (ht, dp->d_name))
530 continue;
532 fspec_init_dp (&fs, dp);
534 /* Save a copy of the name of this entry, in case we have
535 to add it to the set of unremoved entries below. */
536 entry_name = obstack_copy0 (&entry_name_pool,
537 dp->d_name, NLENGTH (dp));
539 /* CAUTION: after this call to rm, DP may not be valid --
540 it may have been freed due to a close in a recursive call
541 (through rm and remove_dir) to this function. */
542 tmp_status = rm (&fs, 0, x);
544 /* Update status. */
545 if (tmp_status > status)
546 status = tmp_status;
547 assert (VALID_STATUS (status));
549 /* If this entry was not removed (due either to an error or to
550 an interactive `no' response), record it in the hash table so
551 we don't consider it again if we reopen this directory later. */
552 if (status != RM_OK)
554 if (ht == NULL)
556 ht = hash_initialize (HT_INITIAL_CAPACITY, NULL, hash_pjw,
557 hash_compare_strings, NULL);
558 if (ht == NULL)
559 error (1, 0, _("virtual memory exhausted"));
561 if (! hash_insert (ht, entry_name))
562 error (1, 0, _("virtual memory exhausted"));
564 else
566 /* This entry was not saved in the hash table. Free it. */
567 obstack_free (&entry_name_pool, entry_name);
570 if (dirp == NULL)
571 break;
574 while (dirp == NULL);
576 if (dirp)
578 if (CLOSEDIR (dirp))
580 error (0, errno, "%s", full_filename ("."));
581 status = RM_ERROR;
583 dirp = NULL;
586 if (ht)
588 hash_free (ht);
591 if (obstack_object_size (&entry_name_pool) > 0)
592 obstack_free (&entry_name_pool, obstack_base (&entry_name_pool));
594 return status;
597 /* Query the user if appropriate, and if ok try to remove the
598 file or directory specified by FS. Return RM_OK if it is removed,
599 and RM_ERROR or RM_USER_DECLINED if not. */
601 static enum RM_status
602 remove_file (struct File_spec *fs, const struct rm_options *x)
604 int asked = 0;
605 char *pathname = fs->filename;
607 if (!x->ignore_missing_files && (x->interactive || x->stdin_tty)
608 && euidaccess (pathname, W_OK))
610 if (!S_ISLNK (fspec_filetype_mode (fs)))
612 fprintf (stderr,
613 (S_ISDIR (fspec_filetype_mode (fs))
614 ? _("%s: remove write-protected directory `%s'? ")
615 : _("%s: remove write-protected file `%s'? ")),
616 program_name, full_filename (pathname));
617 if (!yesno ())
618 return RM_USER_DECLINED;
620 asked = 1;
624 if (!asked && x->interactive)
626 /* FIXME: use a variant of error (instead of fprintf) that doesn't
627 append a newline. Then we won't have to declare program_name in
628 this file. */
629 fprintf (stderr,
630 (S_ISDIR (fspec_filetype_mode (fs))
631 ? _("%s: remove directory `%s'? ")
632 : _("%s: remove `%s'? ")),
633 program_name, full_filename (pathname));
634 if (!yesno ())
635 return RM_USER_DECLINED;
638 if (x->verbose)
639 printf (_("removing %s\n"), full_filename (pathname));
641 if (unlink (pathname) && (errno != ENOENT || !x->ignore_missing_files))
643 error (0, errno, _("cannot unlink `%s'"), full_filename (pathname));
644 return RM_ERROR;
646 return RM_OK;
649 /* If not in recursive mode, print an error message and return RM_ERROR.
650 Otherwise, query the user if appropriate, then try to recursively
651 remove the directory specified by FS. Return RM_OK if it is removed,
652 and RM_ERROR or RM_USER_DECLINED if not.
653 FIXME: describe need_save_cwd parameter. */
655 static enum RM_status
656 remove_dir (struct File_spec *fs, int need_save_cwd, const struct rm_options *x)
658 enum RM_status status;
659 struct saved_cwd cwd;
660 char *dir_name = fs->filename;
661 const char *fmt = NULL;
663 if (!x->recursive)
665 error (0, 0, _("%s: is a directory"), full_filename (dir_name));
666 return RM_ERROR;
669 if (!x->ignore_missing_files && (x->interactive || x->stdin_tty)
670 && euidaccess (dir_name, W_OK))
672 fmt = _("%s: directory `%s' is write protected; descend into it anyway? ");
674 else if (x->interactive)
676 fmt = _("%s: descend into directory `%s'? ");
679 if (fmt)
681 fprintf (stderr, fmt, program_name, full_filename (dir_name));
682 if (!yesno ())
683 return RM_USER_DECLINED;
686 if (x->verbose)
687 printf (_("removing all entries of directory %s\n"),
688 full_filename (dir_name));
690 /* Save cwd if needed. */
691 if (need_save_cwd && save_cwd (&cwd))
692 return RM_ERROR;
694 /* Make target directory the current one. */
695 if (chdir (dir_name) < 0)
697 error (0, errno, _("cannot change to directory %s"),
698 full_filename (dir_name));
699 if (need_save_cwd)
700 free_cwd (&cwd);
701 return RM_ERROR;
704 push_dir (dir_name);
706 /* Save a copy of dir_name. Otherwise, remove_cwd_entries may clobber
707 it because it is just a pointer to the dir entry's d_name field, and
708 remove_cwd_entries may close the directory. */
709 ASSIGN_STRDUPA (dir_name, dir_name);
711 status = remove_cwd_entries (x);
713 pop_dir ();
715 /* Restore cwd. */
716 if (need_save_cwd)
718 if (restore_cwd (&cwd, NULL, NULL))
720 free_cwd (&cwd);
721 return RM_ERROR;
723 free_cwd (&cwd);
725 else if (chdir ("..") < 0)
727 error (0, errno, _("cannot change back to directory %s via `..'"),
728 full_filename (dir_name));
729 return RM_ERROR;
732 if (x->interactive)
734 fprintf (stderr, _("%s: remove directory `%s'%s? "),
735 program_name,
736 full_filename (dir_name),
737 (status != RM_OK ? _(" (might be nonempty)") : ""));
738 if (!yesno ())
740 return RM_USER_DECLINED;
744 if (x->verbose)
745 printf (_("removing the directory itself: %s\n"), full_filename (dir_name));
747 if (rmdir (dir_name) && (errno != ENOENT || !x->ignore_missing_files))
749 int saved_errno = errno;
751 #ifndef EINVAL
752 # define EINVAL 0
753 #endif
754 /* See if rmdir just failed because DIR_NAME is the current directory.
755 If so, give a better diagnostic than `rm: cannot remove directory
756 `...': Invalid argument' */
757 if (errno == EINVAL && same_file (".", dir_name))
759 error (0, 0, _("cannot remove current directory `%s'"),
760 full_filename (dir_name));
762 else
764 error (0, saved_errno, _("cannot remove directory `%s'"),
765 full_filename (dir_name));
767 return RM_ERROR;
770 return status;
773 /* Remove the file or directory specified by FS after checking appropriate
774 things. Return RM_OK if it is removed, and RM_ERROR or RM_USER_DECLINED
775 if not. If USER_SPECIFIED_NAME is non-zero, then the name part of FS may
776 be `.', `..', or may contain slashes. Otherwise, it must be a simple file
777 name (and hence must specify a file in the current directory). */
779 enum RM_status
780 rm (struct File_spec *fs, int user_specified_name, const struct rm_options *x)
782 mode_t filetype_mode;
784 if (user_specified_name)
786 char *base = base_name (fs->filename);
788 if (DOT_OR_DOTDOT (base))
790 error (0, 0, _("cannot remove `.' or `..'"));
791 return RM_ERROR;
795 if (fspec_get_filetype_mode (fs, &filetype_mode))
797 if (x->ignore_missing_files && errno == ENOENT)
798 return RM_OK;
800 error (0, errno, _("cannot remove `%s'"), full_filename (fs->filename));
801 return RM_ERROR;
804 #ifdef ENABLE_CYCLE_CHECK
805 if (S_ISDIR (filetype_mode))
807 struct active_dir_ent *old_ent;
808 struct active_dir_ent *new_ent;
810 /* If there is already a directory in the map with the same inum,
811 then there's *probably* a directory cycle. This test can get
812 a false positive if two directories have the same inode number
813 but different device numbers, and one directory contains the
814 other. But since people don't often try to delete hierarchies
815 containing mount points, and when they do, duplicate inode
816 numbers are not that likely, this isn't worth detecting. */
818 new_ent = make_active_dir_ent (fs->inum, current_depth ());
819 if (hash_lookup (active_dir_map, new_ent))
821 error (0, 0, _("\
822 WARNING: Circular directory structure.\n\
823 This almost certainly means that you have a corrupted file system.\n\
824 NOTIFY YOUR SYSTEM MANAGER.\n\
825 The following two directories have the same inode number:\n"));
826 /* FIXME: test this!! */
827 print_nth_dir (stderr, current_depth ());
828 fputc ('\n', stderr);
829 print_nth_dir (stderr, old_ent->depth);
830 fputc ('\n', stderr);
831 fflush (stderr);
833 free (old_ent);
835 if (x->interactive)
837 error (0, 0, _("continue? "));
838 if (yesno ())
839 return RM_ERROR;
841 exit (1);
844 /* Put this directory in the active_dir_map. */
845 if (! hash_insert (active_dir_map, new_ent))
846 error (1, 0, _("virtual memory exhausted"));
848 #endif
850 if (!S_ISDIR (filetype_mode) || x->unlink_dirs)
852 return remove_file (fs, x);
854 else
856 int need_save_cwd = user_specified_name;
857 enum RM_status status;
859 if (need_save_cwd)
860 need_save_cwd = (strchr (fs->filename, '/') != NULL);
862 status = remove_dir (fs, need_save_cwd, x);
864 #ifdef ENABLE_CYCLE_CHECK
866 struct active_dir_ent tmp;
867 struct active_dir_ent *old_ent;
869 /* Remove this directory from the active_dir_map. */
870 tmp.inum = fs->inum;
871 old_ent = hash_delete (active_dir_map, &tmp);
872 assert (old_ent != NULL);
873 free (old_ent);
875 #endif
877 return status;
881 void
882 remove_init (void)
884 /* Initialize dir-stack obstacks. */
885 obstack_init (&dir_stack);
886 obstack_init (&len_stack);
888 #ifdef ENABLE_CYCLE_CHECK
889 active_dir_map = hash_initialize (ACTIVE_DIR_INITIAL_CAPACITY, NULL,
890 hash_active_dir_ent,
891 hash_compare_active_dir_ents, free);
892 #endif
895 void
896 remove_fini (void)
898 #ifdef ENABLE_CYCLE_CHECK
899 hash_free (active_dir_map);
900 #endif
902 obstack_free (&dir_stack, NULL);
903 obstack_free (&len_stack, NULL);