(main): Stat all non-`-' input file files (and fail if a
[coreutils.git] / src / remove.c
blob995e931859b7998bbfbaa05e676d5728cb9f68b4
1 /* remove.c -- core functions for removing files and directories
2 Copyright (C) 88, 90, 91, 94, 95, 96, 97, 1998 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 "closeout.h"
38 #include "error.h"
39 #include "obstack.h"
40 #include "hash.h"
41 #include "remove.h"
43 #define obstack_chunk_alloc malloc
44 #define obstack_chunk_free free
46 #ifndef PARAMS
47 # if defined (__GNUC__) || __STDC__
48 # define PARAMS(args) args
49 # else
50 # define PARAMS(args) ()
51 # endif
52 #endif
54 #ifdef D_INO_IN_DIRENT
55 # define D_INO(dp) ((dp)->d_ino)
56 # define ENABLE_CYCLE_CHECK
57 #else
58 /* Some systems don't have inodes, so fake them to avoid lots of ifdefs. */
59 # define D_INO(dp) 1
60 #endif
62 #if !defined (S_ISLNK)
63 # define S_ISLNK(Mode) 0
64 #endif
66 #define DOT_OR_DOTDOT(Basename) \
67 (Basename[0] == '.' && (Basename[1] == '\0' \
68 || (Basename[1] == '.' && Basename[2] == '\0')))
70 #if defined strdupa
71 # define ASSIGN_STRDUPA(DEST, S) \
72 do { DEST = strdupa(S); } while (0)
73 #else
74 # define ASSIGN_STRDUPA(DEST, S) \
75 do \
76 { \
77 const char *s_ = (S); \
78 size_t len_ = strlen (s_) + 1; \
79 char *tmp_dest_ = (char *) alloca (len_); \
80 DEST = memcpy (tmp_dest_, (s_), len_); \
81 } \
82 while (0)
83 #endif
85 /* Initial capacity of per-directory hash table of entries that have
86 been processed but not been deleted. */
87 #define HT_INITIAL_CAPACITY 13
89 /* Initial capacity of the active directory hash table. This table will
90 be resized only for hierarchies more than about 45 levels deep. */
91 #define ACTIVE_DIR_INITIAL_CAPACITY 53
93 char *base_name ();
94 int euidaccess ();
95 int yesno ();
97 extern char *program_name;
99 /* state initialized by remove_init, freed by remove_fini */
101 /* An entry in the active_dir_map. */
102 struct active_dir_ent
104 ino_t inum;
105 unsigned int depth;
108 /* The name of the directory (starting with and relative to a command
109 line argument) being processed. When a subdirectory is entered, a new
110 component is appended (pushed). When RM chdir's out of a directory,
111 the top component is removed (popped). This is used to form a full
112 file name when necessary. */
113 static struct obstack dir_stack;
115 /* Stack of lengths of directory names (including trailing slash)
116 appended to dir_stack. We have to have a separate stack of lengths
117 (rather than just popping back to previous slash) because the first
118 element pushed onto the dir stack may contain slashes. */
119 static struct obstack len_stack;
121 /* Set of `active' directories from the current command-line argument
122 to the level in the hierarchy at which files are being removed.
123 A directory is added to the active set when RM begins removing it
124 (or its entries), and it is removed from the set just after RM has
125 finished processing it.
127 This is actually a map (not a set), implemented with a hash table.
128 For each active directory, it maps the directory's inode number to the
129 depth of that directory relative to the root of the tree being deleted.
130 A directory specified on the command line has depth zero.
131 This construct is used to detect directory cycles so that RM can warn
132 about them rather than iterating endlessly. */
133 #ifdef ENABLE_CYCLE_CHECK
134 static struct hash_table *active_dir_map;
135 #endif
137 static inline unsigned int
138 current_depth (void)
140 return obstack_object_size (&len_stack) / sizeof (size_t);
143 static void
144 print_nth_dir (FILE *stream, unsigned int depth)
146 size_t *length = (size_t *) obstack_base (&len_stack);
147 char *dir_name = (char *) obstack_base (&dir_stack);
148 unsigned int sum = 0;
149 unsigned int i;
151 assert (depth < current_depth ());
153 for (i = 0; i <= depth; i++)
155 sum += length[i];
158 fwrite (dir_name, 1, sum, stream);
161 static inline struct active_dir_ent *
162 make_active_dir_ent (ino_t inum, unsigned int depth)
164 struct active_dir_ent *ent;
165 ent = (struct active_dir_ent *) xmalloc (sizeof *ent);
166 ent->inum = inum;
167 ent->depth = depth;
168 return ent;
171 static unsigned int
172 hash_active_dir_ent (void const *x, unsigned int table_size)
174 struct active_dir_ent const *ade = x;
175 return ade->inum % table_size;
178 static bool
179 hash_compare_active_dir_ents (void const *x, void const *y)
181 struct active_dir_ent const *a = x;
182 struct active_dir_ent const *b = y;
183 return a->inum == b->inum;
186 /* A hash function for null-terminated char* strings using
187 the method described in Aho, Sethi, & Ullman, p 436. */
189 static unsigned int
190 hash_pjw (const void *x, unsigned int tablesize)
192 const char *s = x;
193 unsigned int h = 0;
194 unsigned int g;
196 while (*s != 0)
198 h = (h << 4) + *s++;
199 if ((g = h & (unsigned int) 0xf0000000) != 0)
200 h = (h ^ (g >> 24)) ^ g;
203 return (h % tablesize);
206 static bool
207 hash_compare_strings (void const *x, void const *y)
209 return STREQ (x, y) ? true : false;
212 static inline void
213 push_dir (const char *dir_name)
215 size_t len;
217 len = strlen (dir_name);
219 /* Append the string onto the stack. */
220 obstack_grow (&dir_stack, dir_name, len);
222 /* Append a trailing slash. */
223 obstack_1grow (&dir_stack, '/');
225 /* Add one for the slash. */
226 ++len;
228 /* Push the length (including slash) onto its stack. */
229 obstack_grow (&len_stack, &len, sizeof (len));
232 static inline void
233 pop_dir (void)
235 int n_lengths = obstack_object_size (&len_stack) / sizeof (size_t);
236 size_t *length = (size_t *) obstack_base (&len_stack);
237 size_t top_len;
239 assert (n_lengths > 0);
240 top_len = length[n_lengths - 1];
241 assert (top_len >= 2);
243 /* Pop off the specified length of pathname. */
244 assert (obstack_object_size (&dir_stack) >= top_len);
245 obstack_blank (&dir_stack, -top_len);
247 /* Pop the length stack, too. */
248 assert (obstack_object_size (&len_stack) >= sizeof (size_t));
249 obstack_blank (&len_stack, -(sizeof (size_t)));
252 /* Copy the SRC_LEN bytes of data beginning at SRC into the DST_LEN-byte
253 buffer, DST, so that the last source byte is at the end of the destination
254 buffer. If SRC_LEN is longer than DST_LEN, then set *TRUNCATED to non-zero.
255 Set *RESULT to point to the beginning of (the portion of) the source data
256 in DST. Return the number of bytes remaining in the destination buffer. */
258 static size_t
259 right_justify (char *dst, size_t dst_len, const char *src, size_t src_len,
260 char **result, int *truncated)
262 const char *sp;
263 char *dp;
265 if (src_len <= dst_len)
267 sp = src;
268 dp = dst + (dst_len - src_len);
269 *truncated = 0;
271 else
273 sp = src + (src_len - dst_len);
274 dp = dst;
275 src_len = dst_len;
276 *truncated = 1;
279 *result = memcpy (dp, sp, src_len);
280 return dst_len - src_len;
283 /* Using the global directory name obstack, create the full path to FILENAME.
284 Return it in sometimes-realloc'd space that should not be freed by the
285 caller. Realloc as necessary. If realloc fails, use a static buffer
286 and put as long a suffix in that buffer as possible. */
288 static char *
289 full_filename (const char *filename)
291 static char *buf = NULL;
292 static size_t n_allocated = 0;
294 int dir_len = obstack_object_size (&dir_stack);
295 char *dir_name = (char *) obstack_base (&dir_stack);
296 size_t n_bytes_needed;
297 size_t filename_len;
299 filename_len = strlen (filename);
300 n_bytes_needed = dir_len + filename_len + 1;
302 if (n_bytes_needed > n_allocated)
304 /* This code requires that realloc accept NULL as the first arg.
305 This function must not use xrealloc. Otherwise, an out-of-memory
306 error involving a file name to be expanded here wouldn't ever
307 be issued. Use realloc and fall back on using a static buffer
308 if memory allocation fails. */
309 buf = realloc (buf, n_bytes_needed);
310 n_allocated = n_bytes_needed;
312 if (buf == NULL)
314 #define SBUF_SIZE 512
315 #define ELLIPSES_PREFIX "[...]"
316 static char static_buf[SBUF_SIZE];
317 int truncated;
318 size_t len;
319 char *p;
321 len = right_justify (static_buf, SBUF_SIZE, filename,
322 filename_len + 1, &p, &truncated);
323 right_justify (static_buf, len, dir_name, dir_len, &p, &truncated);
324 if (truncated)
326 memcpy (static_buf, ELLIPSES_PREFIX,
327 sizeof (ELLIPSES_PREFIX) - 1);
329 return p;
333 /* Copy directory part, including trailing slash, and then
334 append the filename part, including a trailing zero byte. */
335 memcpy (mempcpy (buf, dir_name, dir_len), filename, filename_len + 1);
337 assert (strlen (buf) + 1 == n_bytes_needed);
339 return buf;
342 void
343 fspec_init_file (struct File_spec *fs, const char *filename)
345 fs->filename = (char *) filename;
346 fs->have_full_mode = 0;
347 fs->have_filetype_mode = 0;
350 static inline void
351 fspec_init_dp (struct File_spec *fs, struct dirent *dp)
353 fs->filename = dp->d_name;
354 fs->have_full_mode = 0;
355 fs->have_filetype_mode = 0;
356 fs->inum = D_INO (dp);
358 #if D_TYPE_IN_DIRENT && defined (DT_UNKNOWN) && defined (DTTOIF)
359 if (dp->d_type != DT_UNKNOWN)
361 fs->have_filetype_mode = 1;
362 fs->mode = DTTOIF (dp->d_type);
364 #endif
367 static inline int
368 fspec_get_full_mode (struct File_spec *fs, mode_t *full_mode)
370 struct stat stat_buf;
372 if (fs->have_full_mode)
374 *full_mode = fs->mode;
375 return 0;
378 if (lstat (fs->filename, &stat_buf))
379 return 1;
381 fs->have_full_mode = 1;
382 fs->have_filetype_mode = 1;
383 fs->mode = stat_buf.st_mode;
384 fs->inum = stat_buf.st_ino;
386 *full_mode = stat_buf.st_mode;
387 return 0;
390 static inline int
391 fspec_get_filetype_mode (struct File_spec *fs, mode_t *filetype_mode)
393 int fail;
395 if (fs->have_filetype_mode)
397 *filetype_mode = fs->mode;
398 fail = 0;
400 else
402 fail = fspec_get_full_mode (fs, filetype_mode);
405 return fail;
408 static inline mode_t
409 fspec_filetype_mode (const struct File_spec *fs)
411 assert (fs->have_filetype_mode);
412 return fs->mode;
415 static int
416 same_file (const char *file_1, const char *file_2)
418 struct stat sb1, sb2;
419 return (lstat (file_1, &sb1) == 0
420 && lstat (file_2, &sb2) == 0
421 && SAME_INODE (sb1, sb2));
425 /* Recursively remove all of the entries in the current directory.
426 Return an indication of the success of the operation. */
428 enum RM_status
429 remove_cwd_entries (const struct rm_options *x)
431 /* NOTE: this is static. */
432 static DIR *dirp = NULL;
434 /* NULL or a malloc'd and initialized hash table of entries in the
435 current directory that have been processed but not removed --
436 due either to an error or to an interactive `no' response. */
437 struct hash_table *ht = NULL;
439 /* FIXME: describe */
440 static struct obstack entry_name_pool;
441 static int first_call = 1;
443 enum RM_status status = RM_OK;
445 if (first_call)
447 first_call = 0;
448 obstack_init (&entry_name_pool);
451 if (dirp)
453 if (CLOSEDIR (dirp))
455 /* FIXME-someday: but this is actually the previously opened dir. */
456 error (0, errno, "%s", full_filename ("."));
457 status = RM_ERROR;
459 dirp = NULL;
464 /* FIXME: why do this? */
465 errno = 0;
467 dirp = opendir (".");
468 if (dirp == NULL)
470 if (errno != ENOENT || !x->ignore_missing_files)
472 error (0, errno, "%s", full_filename ("."));
473 status = RM_ERROR;
475 break;
478 while (1)
480 char *entry_name;
481 struct File_spec fs;
482 enum RM_status tmp_status;
483 struct dirent *dp;
485 /* FILE should be skipped if it is `.' or `..', or if it is in
486 the table, HT, of entries we've already processed. */
487 #define SKIPPABLE(Ht, File) \
488 (DOT_OR_DOTDOT(File) || (Ht && hash_lookup (Ht, File)))
490 /* FIXME: use readdir_r directly into an obstack to avoid
491 the obstack_copy0 below --
492 Suggestion from Uli. Be careful -- there are different
493 prototypes on e.g. Solaris.
495 Do something like this:
496 #define NAME_MAX_FOR(Parent_dir) pathconf ((Parent_dir),
497 _PC_NAME_MAX);
498 dp = obstack_alloc (sizeof (struct dirent)
499 + NAME_MAX_FOR (".") + 1);
500 fail = xreaddir (dirp, dp);
501 where xreaddir is ...
503 But what about systems like the hurd where NAME_MAX is supposed
504 to be effectively unlimited. We don't want to have to allocate
505 a huge buffer to accommodate maximum possible entry name. */
507 dp = readdir (dirp);
509 #if ! HAVE_WORKING_READDIR
510 if (dp == NULL)
512 /* Since we have probably modified the directory since it
513 was opened, readdir returning NULL does not necessarily
514 mean we have read the last entry. Rewind it and check
515 again. This happens on SunOS4.1.4 with 254 or more files
516 in a directory. */
517 rewinddir (dirp);
518 while ((dp = readdir (dirp)) && SKIPPABLE (ht, dp->d_name))
520 /* empty */
523 #endif
525 if (dp == NULL)
526 break;
528 if (SKIPPABLE (ht, dp->d_name))
529 continue;
531 fspec_init_dp (&fs, dp);
533 /* Save a copy of the name of this entry, in case we have
534 to add it to the set of unremoved entries below. */
535 entry_name = obstack_copy0 (&entry_name_pool,
536 dp->d_name, NLENGTH (dp));
538 /* CAUTION: after this call to rm, DP may not be valid --
539 it may have been freed due to a close in a recursive call
540 (through rm and remove_dir) to this function. */
541 tmp_status = rm (&fs, 0, x);
543 /* Update status. */
544 if (tmp_status > status)
545 status = tmp_status;
546 assert (VALID_STATUS (status));
548 /* If this entry was not removed (due either to an error or to
549 an interactive `no' response), record it in the hash table so
550 we don't consider it again if we reopen this directory later. */
551 if (status != RM_OK)
553 bool done;
555 if (ht == NULL)
557 ht = hash_initialize (HT_INITIAL_CAPACITY, hash_pjw,
558 hash_compare_strings, NULL);
559 if (ht == NULL)
560 error (1, 0, _("virtual memory exhausted"));
562 hash_insert (ht, entry_name, &done);
563 if (!done)
564 error (1, 0, _("virtual memory exhausted"));
566 else
568 /* This entry was not saved in the hash table. Free it. */
569 obstack_free (&entry_name_pool, entry_name);
572 if (dirp == NULL)
573 break;
576 while (dirp == NULL);
578 if (CLOSEDIR (dirp))
580 error (0, errno, "%s", full_filename ("."));
581 status = RM_OK;
583 dirp = NULL;
585 if (ht)
587 hash_free (ht);
590 if (obstack_object_size (&entry_name_pool) > 0)
591 obstack_free (&entry_name_pool, obstack_base (&entry_name_pool));
593 return status;
596 /* Query the user if appropriate, and if ok try to remove the
597 file or directory specified by FS. Return RM_OK if it is removed,
598 and RM_ERROR or RM_USER_DECLINED if not. */
600 static enum RM_status
601 remove_file (struct File_spec *fs, const struct rm_options *x)
603 int asked = 0;
604 char *pathname = fs->filename;
606 if (!x->ignore_missing_files && (x->interactive || x->stdin_tty)
607 && euidaccess (pathname, W_OK))
609 if (!S_ISLNK (fspec_filetype_mode (fs)))
611 fprintf (stderr,
612 (S_ISDIR (fspec_filetype_mode (fs))
613 ? _("%s: remove write-protected directory `%s'? ")
614 : _("%s: remove write-protected file `%s'? ")),
615 program_name, full_filename (pathname));
616 if (!yesno ())
617 return RM_USER_DECLINED;
619 asked = 1;
623 if (!asked && x->interactive)
625 /* FIXME: use a variant of error (instead of fprintf) that doesn't
626 append a newline. Then we won't have to declare program_name in
627 this file. */
628 fprintf (stderr,
629 (S_ISDIR (fspec_filetype_mode (fs))
630 ? _("%s: remove directory `%s'? ")
631 : _("%s: remove `%s'? ")),
632 program_name, full_filename (pathname));
633 if (!yesno ())
634 return RM_USER_DECLINED;
637 if (x->verbose)
638 printf (_("removing %s\n"), full_filename (pathname));
640 if (unlink (pathname) && (errno != ENOENT || !x->ignore_missing_files))
642 error (0, errno, _("cannot unlink `%s'"), full_filename (pathname));
643 return RM_ERROR;
645 return RM_OK;
648 /* If not in recursive mode, print an error message and return RM_ERROR.
649 Otherwise, query the user if appropriate, then try to recursively
650 remove the directory specified by FS. Return RM_OK if it is removed,
651 and RM_ERROR or RM_USER_DECLINED if not.
652 FIXME: describe need_save_cwd parameter. */
654 static enum RM_status
655 remove_dir (struct File_spec *fs, int need_save_cwd, const struct rm_options *x)
657 enum RM_status status;
658 struct saved_cwd cwd;
659 char *dir_name = fs->filename;
660 const char *fmt = NULL;
662 if (!x->recursive)
664 error (0, 0, _("%s: is a directory"), full_filename (dir_name));
665 return RM_ERROR;
668 if (!x->ignore_missing_files && (x->interactive || x->stdin_tty)
669 && euidaccess (dir_name, W_OK))
671 fmt = _("%s: directory `%s' is write protected; descend into it anyway? ");
673 else if (x->interactive)
675 fmt = _("%s: descend into directory `%s'? ");
678 if (fmt)
680 fprintf (stderr, fmt, program_name, full_filename (dir_name));
681 if (!yesno ())
682 return RM_USER_DECLINED;
685 if (x->verbose)
686 printf (_("removing all entries of directory %s\n"),
687 full_filename (dir_name));
689 /* Save cwd if needed. */
690 if (need_save_cwd && save_cwd (&cwd))
691 return RM_ERROR;
693 /* Make target directory the current one. */
694 if (chdir (dir_name) < 0)
696 error (0, errno, _("cannot change to directory %s"),
697 full_filename (dir_name));
698 if (need_save_cwd)
699 free_cwd (&cwd);
700 return RM_ERROR;
703 push_dir (dir_name);
705 /* Save a copy of dir_name. Otherwise, remove_cwd_entries may clobber
706 it because it is just a pointer to the dir entry's d_name field, and
707 remove_cwd_entries may close the directory. */
708 ASSIGN_STRDUPA (dir_name, dir_name);
710 status = remove_cwd_entries (x);
712 pop_dir ();
714 /* Restore cwd. */
715 if (need_save_cwd)
717 if (restore_cwd (&cwd, NULL, NULL))
719 free_cwd (&cwd);
720 return RM_ERROR;
722 free_cwd (&cwd);
724 else if (chdir ("..") < 0)
726 error (0, errno, _("cannot change back to directory %s via `..'"),
727 full_filename (dir_name));
728 return RM_ERROR;
731 if (x->interactive)
733 fprintf (stderr, _("%s: remove directory `%s'%s? "),
734 program_name,
735 full_filename (dir_name),
736 (status != RM_OK ? _(" (might be nonempty)") : ""));
737 if (!yesno ())
739 return RM_USER_DECLINED;
743 if (x->verbose)
744 printf (_("removing the directory itself: %s\n"), full_filename (dir_name));
746 if (rmdir (dir_name) && (errno != ENOENT || !x->ignore_missing_files))
748 int saved_errno = errno;
750 #ifndef EINVAL
751 # define EINVAL 0
752 #endif
753 /* See if rmdir just failed because DIR_NAME is the current directory.
754 If so, give a better diagnostic than `rm: cannot remove directory
755 `...': Invalid argument' */
756 if (errno == EINVAL && same_file (".", dir_name))
758 error (0, 0, _("cannot remove current directory `%s'"),
759 full_filename (dir_name));
761 else
763 error (0, saved_errno, _("cannot remove directory `%s'"),
764 full_filename (dir_name));
766 return RM_ERROR;
769 return RM_OK;
772 /* Remove the file or directory specified by FS after checking appropriate
773 things. Return RM_OK if it is removed, and RM_ERROR or RM_USER_DECLINED
774 if not. If USER_SPECIFIED_NAME is non-zero, then the name part of FS may
775 be `.', `..', or may contain slashes. Otherwise, it must be a simple file
776 name (and hence must specify a file in the current directory). */
778 enum RM_status
779 rm (struct File_spec *fs, int user_specified_name, const struct rm_options *x)
781 mode_t filetype_mode;
783 if (user_specified_name)
785 char *base = base_name (fs->filename);
787 if (DOT_OR_DOTDOT (base))
789 error (0, 0, _("cannot remove `.' or `..'"));
790 return RM_ERROR;
794 if (fspec_get_filetype_mode (fs, &filetype_mode))
796 if (x->ignore_missing_files && errno == ENOENT)
797 return RM_OK;
799 error (0, errno, _("cannot remove `%s'"), full_filename (fs->filename));
800 return RM_ERROR;
803 #ifdef ENABLE_CYCLE_CHECK
804 if (S_ISDIR (filetype_mode))
806 bool done;
807 struct active_dir_ent *old_ent;
809 /* Insert this directory in the active_dir_map.
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. */
817 old_ent = hash_insert (active_dir_map,
818 make_active_dir_ent (fs->inum, current_depth ()),
819 &done);
820 if (!done)
821 error (1, 0, _("virtual memory exhausted"));
823 if (old_ent)
825 error (0, 0, _("\
826 WARNING: Circular directory structure.\n\
827 This almost certainly means that you have a corrupted file system.\n\
828 NOTIFY YOUR SYSTEM MANAGER.\n\
829 The following two directories have the same inode number:\n"));
830 /* FIXME: test this!! */
831 print_nth_dir (stderr, current_depth ());
832 fputc ('\n', stderr);
833 print_nth_dir (stderr, old_ent->depth);
834 fputc ('\n', stderr);
835 fflush (stderr);
837 free (old_ent);
839 if (x->interactive)
841 error (0, 0, _("continue? "));
842 if (yesno ())
843 return RM_ERROR;
845 exit (1);
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,
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);