diff options
author | Jim Meyering <jim@meyering.net> | 2003-01-04 09:41:24 +0000 |
---|---|---|
committer | Jim Meyering <jim@meyering.net> | 2003-01-04 09:41:24 +0000 |
commit | 818c25f6862a2e53b8bf95415e20b1504173d610 (patch) | |
tree | 177bc684a8ed6be492fa1964be52893f9d43a59d /src | |
parent | 7e4c16df3db616031255dd33f206207ccc3a57fe (diff) | |
download | coreutils-818c25f6862a2e53b8bf95415e20b1504173d610.tar.xz |
(struct dirstack_state): Define.
To be used in place of these file-scoped globals ...
(dir_stack, len_stack, Active_dir): Remove globals.
(ds_init, ds_free): New functions.
(full_filename): Define.
(full_filename_): Rename from full_filename.
Begin to make AD_* functions more generic.
(AD_push_initial): Don't set status to RM_OK here.
(AD_push): Likewise.
(AD_INIT_OTHER_MEMBERS): Define.
(remove_dir): Define the `status' member manually after each
call to AD_push or AD_push_initial.
Diffstat (limited to 'src')
-rw-r--r-- | src/remove.c | 242 |
1 files changed, 138 insertions, 104 deletions
diff --git a/src/remove.c b/src/remove.c index bc838df25..377f96395 100644 --- a/src/remove.c +++ b/src/remove.c @@ -121,25 +121,47 @@ int yesno (); extern char *program_name; -/* The name of the directory (starting with and relative to a command - line argument) being processed. When a subdirectory is entered, a new - component is appended (pushed). When RM chdir's out of a directory, - the top component is removed (popped). This is used to form a full - file name when necessary. */ -static struct obstack dir_stack; - -/* Stack of lengths of directory names (including trailing slash) - appended to dir_stack. We have to have a separate stack of lengths - (rather than just popping back to previous slash) because the first - element pushed onto the dir stack may contain slashes. */ -static struct obstack len_stack; - -/* Stack of active directory entries. - The first `active' directory is the initial working directory. - Additional active dirs are pushed onto the stack as rm `chdir's - into each nonempty directory it must remove. When rm has finished - removing the hierarchy under a directory, it pops the active dir stack. */ -static struct obstack Active_dir; +struct dirstack_state +{ + /* The name of the directory (starting with and relative to a command + line argument) being processed. When a subdirectory is entered, a new + component is appended (pushed). Remove (pop) the top component + upon chdir'ing out of a directory. This is used to form the full + name of the current directory or a file therein, when necessary. */ + struct obstack dir_stack; + + /* Stack of lengths of directory names (including trailing slash) + appended to dir_stack. We have to have a separate stack of lengths + (rather than just popping back to previous slash) because the first + element pushed onto the dir stack may contain slashes. */ + struct obstack len_stack; + + /* Stack of active directory entries. + The first `active' directory is the initial working directory. + Additional active dirs are pushed onto the stack as we `chdir' + into each directory to be processed. When finished with the + hierarchy under a directory, pop the active dir stack. */ + struct obstack Active_dir; +}; +typedef struct dirstack_state DS; + +DS * +ds_init () +{ + DS *ds = XMALLOC (struct dirstack_state, 1); + obstack_init (&(ds->dir_stack)); + obstack_init (&(ds->len_stack)); + obstack_init (&(ds->Active_dir)); + return ds; +} + +void +ds_free (DS *ds) +{ + obstack_free (&(ds->dir_stack), NULL); + obstack_free (&(ds->len_stack), NULL); + obstack_free (&(ds->Active_dir), NULL); +} static void hash_freer (void *x) @@ -154,34 +176,34 @@ hash_compare_strings (void const *x, void const *y) } static inline void -push_dir (const char *dir_name) +push_dir (DS *ds, const char *dir_name) { size_t len; len = strlen (dir_name); /* Append the string onto the stack. */ - obstack_grow (&dir_stack, dir_name, len); + obstack_grow (&ds->dir_stack, dir_name, len); /* Append a trailing slash. */ - obstack_1grow (&dir_stack, '/'); + obstack_1grow (&ds->dir_stack, '/'); /* Add one for the slash. */ ++len; /* Push the length (including slash) onto its stack. */ - obstack_grow (&len_stack, &len, sizeof (len)); + obstack_grow (&ds->len_stack, &len, sizeof (len)); } /* Return the entry name of the directory on the top of the stack in malloc'd storage. */ static inline char * -top_dir (void) +top_dir (DS const *ds) { - int n_lengths = obstack_object_size (&len_stack) / sizeof (size_t); - size_t *length = (size_t *) obstack_base (&len_stack); + int n_lengths = obstack_object_size (&ds->len_stack) / sizeof (size_t); + size_t *length = (size_t *) obstack_base (&ds->len_stack); size_t top_len = length[n_lengths - 1]; - char const *p = obstack_next_free (&dir_stack) - top_len; + char const *p = obstack_next_free (&ds->dir_stack) - top_len; char *q = xmalloc (top_len); memcpy (q, p, top_len - 1); q[top_len - 1] = 0; @@ -189,10 +211,10 @@ top_dir (void) } static inline void -pop_dir (void) +pop_dir (DS *ds) { - int n_lengths = obstack_object_size (&len_stack) / sizeof (size_t); - size_t *length = (size_t *) obstack_base (&len_stack); + int n_lengths = obstack_object_size (&ds->len_stack) / sizeof (size_t); + size_t *length = (size_t *) obstack_base (&ds->len_stack); size_t top_len; assert (n_lengths > 0); @@ -200,12 +222,12 @@ pop_dir (void) assert (top_len >= 2); /* Pop off the specified length of pathname. */ - assert (obstack_object_size (&dir_stack) >= top_len); - obstack_blank (&dir_stack, -top_len); + assert (obstack_object_size (&ds->dir_stack) >= top_len); + obstack_blank (&ds->dir_stack, -top_len); /* Pop the length stack, too. */ - assert (obstack_object_size (&len_stack) >= sizeof (size_t)); - obstack_blank (&len_stack, (int) -(sizeof (size_t))); + assert (obstack_object_size (&ds->len_stack) >= sizeof (size_t)); + obstack_blank (&ds->len_stack, (int) -(sizeof (size_t))); } /* Copy the SRC_LEN bytes of data beginning at SRC into the DST_LEN-byte @@ -244,21 +266,22 @@ right_justify (char *dst, size_t dst_len, const char *src, size_t src_len, caller. Realloc as necessary. If realloc fails, use a static buffer and put as long a suffix in that buffer as possible. */ +#define full_filename(Filename) full_filename_ (ds, Filename) static char * -full_filename (const char *filename) +full_filename_ (DS const *ds, const char *filename) { static char *buf = NULL; static size_t n_allocated = 0; - int dir_len = obstack_object_size (&dir_stack); - char *dir_name = (char *) obstack_base (&dir_stack); + int dir_len = obstack_object_size (&ds->dir_stack); + char *dir_name = (char *) obstack_base (&ds->dir_stack); size_t n_bytes_needed; size_t filename_len; filename_len = strlen (filename); n_bytes_needed = dir_len + filename_len + 1; - if (n_bytes_needed > n_allocated) + if (n_allocated < n_bytes_needed) { /* This code requires that realloc accept NULL as the first arg. This function must not use xrealloc. Otherwise, an out-of-memory @@ -299,27 +322,27 @@ full_filename (const char *filename) } static size_t -AD_stack_height (void) +AD_stack_height (DS const *ds) { - return obstack_object_size (&Active_dir) / sizeof (struct AD_ent); + return obstack_object_size (&ds->Active_dir) / sizeof (struct AD_ent); } static struct AD_ent * -AD_stack_top (void) +AD_stack_top (DS const *ds) { return (struct AD_ent *) - ((char *) obstack_next_free (&Active_dir) - sizeof (struct AD_ent)); + ((char *) obstack_next_free (&ds->Active_dir) - sizeof (struct AD_ent)); } static void -AD_stack_pop (void) +AD_stack_pop (DS *ds) { /* operate on Active_dir. pop and free top entry */ - struct AD_ent *top = AD_stack_top (); + struct AD_ent *top = AD_stack_top (ds); if (top->unremovable) hash_free (top->unremovable); - obstack_blank (&Active_dir, -sizeof (struct AD_ent)); - pop_dir (); + obstack_blank (&ds->Active_dir, -sizeof (struct AD_ent)); + pop_dir (ds); } /* chdir `up' one level. @@ -328,23 +351,23 @@ AD_stack_pop (void) Return the name (in malloc'd storage) of the directory (usually now empty) from which we're coming. */ static char * -AD_pop_and_chdir (void) +AD_pop_and_chdir (DS *ds) { /* Get the name of the current directory from the top of the stack. */ - char *dir = top_dir (); - enum RM_status old_status = AD_stack_top()->status; + char *dir = top_dir (ds); + enum RM_status old_status = AD_stack_top(ds)->status; struct stat sb; struct AD_ent *top; - AD_stack_pop (); + AD_stack_pop (ds); /* Propagate any failure to parent. */ - UPDATE_STATUS (AD_stack_top()->status, old_status); + UPDATE_STATUS (AD_stack_top(ds)->status, old_status); - assert (AD_stack_height ()); + assert (AD_stack_height (ds)); - top = AD_stack_top (); - if (1 < AD_stack_height ()) + top = AD_stack_top (ds); + if (1 < AD_stack_height (ds)) { /* We can give a better diagnostic here, since the target is relative. */ if (chdir ("..")) @@ -364,7 +387,7 @@ AD_pop_and_chdir (void) error (EXIT_FAILURE, errno, _("cannot lstat `.' in %s"), quote (full_filename ("."))); - if (1 < AD_stack_height ()) + if (1 < AD_stack_height (ds)) { /* Ensure that post-chdir dev/ino match the stored ones. */ if ( ! SAME_INODE (sb, top->u.a)) @@ -391,9 +414,9 @@ AD_mark_helper (Hash_table **ht, char const *filename) /* Mark FILENAME (in current directory) as unremovable. */ static void -AD_mark_as_unremovable (char const *filename) +AD_mark_as_unremovable (DS *ds, char const *filename) { - AD_mark_helper (&AD_stack_top()->unremovable, xstrdup (filename)); + AD_mark_helper (&AD_stack_top(ds)->unremovable, xstrdup (filename)); } /* Mark the current directory as unremovable. I.e., mark the entry @@ -401,12 +424,12 @@ AD_mark_as_unremovable (char const *filename) This happens e.g., when an opendir fails and the only name the caller has conveniently at hand is `.'. */ static void -AD_mark_current_as_unremovable (void) +AD_mark_current_as_unremovable (DS *ds) { - struct AD_ent *top = AD_stack_top (); - const char *curr = top_dir (); + struct AD_ent *top = AD_stack_top (ds); + const char *curr = top_dir (ds); - assert (1 < AD_stack_height ()); + assert (1 < AD_stack_height (ds)); --top; AD_mark_helper (&top->unremovable, curr); @@ -415,17 +438,16 @@ AD_mark_current_as_unremovable (void) /* Push the initial cwd info onto the stack. This will always be the bottommost entry on the stack. */ static void -AD_push_initial (struct saved_cwd const *cwd) +AD_push_initial (DS *ds, struct saved_cwd const *cwd) { struct AD_ent *top; /* Extend the stack. */ - obstack_blank (&Active_dir, sizeof (struct AD_ent)); + obstack_blank (&ds->Active_dir, sizeof (struct AD_ent)); /* Fill in the new values. */ - top = AD_stack_top (); + top = AD_stack_top (ds); top->u.saved_cwd = *cwd; - top->status = RM_OK; top->unremovable = NULL; } @@ -434,12 +456,12 @@ AD_push_initial (struct saved_cwd const *cwd) which we've just `chdir'd to this directory. DIR_SB_FROM_PARENT is the result of calling lstat on DIR from the parent of DIR. */ static void -AD_push (char const *dir, struct stat const *dir_sb_from_parent) +AD_push (DS *ds, char const *dir, struct stat const *dir_sb_from_parent) { struct stat sb; struct AD_ent *top; - push_dir (dir); + push_dir (ds, dir); if (lstat (".", &sb)) error (EXIT_FAILURE, errno, @@ -450,20 +472,19 @@ AD_push (char const *dir, struct stat const *dir_sb_from_parent) _("%s changed dev/ino"), quote (full_filename ("."))); /* Extend the stack. */ - obstack_blank (&Active_dir, sizeof (struct AD_ent)); + obstack_blank (&ds->Active_dir, sizeof (struct AD_ent)); /* Fill in the new values. */ - top = AD_stack_top (); + top = AD_stack_top (ds); top->u.a.st_dev = sb.st_dev; top->u.a.st_ino = sb.st_ino; - top->status = RM_OK; top->unremovable = NULL; } static int -AD_is_removable (char const *file) +AD_is_removable (DS const *ds, char const *file) { - struct AD_ent *top = AD_stack_top (); + struct AD_ent *top = AD_stack_top (ds); return ! (top->unremovable && hash_lookup (top->unremovable, file)); } @@ -474,7 +495,7 @@ is_power_of_two (unsigned int i) } static void -cycle_check (struct stat const *sb) +cycle_check (DS const *ds, struct stat const *sb) { #ifdef ENABLE_CYCLE_CHECK /* If there is a directory cycle, detect it (lazily) and die. */ @@ -551,7 +572,7 @@ is_empty_dir (char const *dir) Set *IS_DIR to T_YES or T_NO if we happen to determine whether FILENAME is a directory. */ static enum RM_status -prompt (char const *filename, struct rm_options const *x, +prompt (DS const *ds, char const *filename, struct rm_options const *x, enum Prompt_action mode, Ternary *is_dir, Ternary *is_empty) { int write_protected = 0; @@ -664,12 +685,12 @@ prompt (char const *filename, struct rm_options const *x, But if FILENAME specifies a non-empty directory, return RM_NONEMPTY_DIR. */ static enum RM_status -remove_entry (char const *filename, struct rm_options const *x, +remove_entry (DS const *ds, char const *filename, struct rm_options const *x, struct dirent const *dp) { Ternary is_dir; Ternary is_empty_directory; - enum RM_status s = prompt (filename, x, PA_DESCEND_INTO_DIR, + enum RM_status s = prompt (ds, filename, x, PA_DESCEND_INTO_DIR, &is_dir, &is_empty_directory); if (s != RM_OK) @@ -793,11 +814,11 @@ remove_entry (char const *filename, struct rm_options const *x, the user declines to remove at least one entry. Remove as much as possible, continuing even if we fail to remove some entries. */ static enum RM_status -remove_cwd_entries (char **subdir, struct stat *subdir_sb, +remove_cwd_entries (DS *ds, char **subdir, struct stat *subdir_sb, struct rm_options const *x) { DIR *dirp = opendir ("."); - struct AD_ent *top = AD_stack_top (); + struct AD_ent *top = AD_stack_top (ds); enum RM_status status = top->status; assert (VALID_STATUS (status)); @@ -842,14 +863,14 @@ remove_cwd_entries (char **subdir, struct stat *subdir_sb, continue; /* Skip files we've already tried/failed to remove. */ - if ( ! AD_is_removable (f)) + if ( ! AD_is_removable (ds, f)) continue; /* Pass dp->d_type info to remove_entry so the non-glibc case can decide whether to use unlink or chdir. Systems without the d_type member will have to endure the performance hit of first calling lstat F. */ - tmp_status = remove_entry (f, x, dp); + tmp_status = remove_entry (ds, f, x, dp); switch (tmp_status) { case RM_OK: @@ -858,7 +879,7 @@ remove_cwd_entries (char **subdir, struct stat *subdir_sb, case RM_ERROR: case RM_USER_DECLINED: - AD_mark_as_unremovable (f); + AD_mark_as_unremovable (ds, f); UPDATE_STATUS (status, tmp_status); break; @@ -874,11 +895,11 @@ remove_cwd_entries (char **subdir, struct stat *subdir_sb, { error (0, errno, _("cannot chdir from %s to %s"), quote_n (0, full_filename (".")), quote_n (1, f)); - AD_mark_as_unremovable (f); + AD_mark_as_unremovable (ds, f); status = RM_ERROR; break; } - cycle_check (subdir_sb); + cycle_check (ds, subdir_sb); *subdir = xstrdup (f); break; @@ -902,6 +923,16 @@ remove_cwd_entries (char **subdir, struct stat *subdir_sb, return status; } +/* Do this after each call to AD_push or AD_push_initial. + Because the status = RM_OK bit is too remove-specific to + go into the general-purpose AD_* package. */ +#define AD_INIT_OTHER_MEMBERS() \ + do \ + { \ + AD_stack_top(ds)->status = RM_OK; \ + } \ + while (0) + /* Remove the hierarchy rooted at DIR. Do that by changing into DIR, then removing its contents, then returning to the original working directory and removing DIR itself. @@ -912,7 +943,7 @@ remove_cwd_entries (char **subdir, struct stat *subdir_sb, If the working directory cannot be restored, exit immediately. */ static enum RM_status -remove_dir (char const *dir, struct saved_cwd **cwd_state, +remove_dir (DS *ds, char const *dir, struct saved_cwd **cwd_state, struct rm_options const *x) { enum RM_status status; @@ -927,7 +958,8 @@ remove_dir (char const *dir, struct saved_cwd **cwd_state, *cwd_state = XMALLOC (struct saved_cwd, 1); if (save_cwd (*cwd_state)) return RM_ERROR; - AD_push_initial (*cwd_state); + AD_push_initial (ds, *cwd_state); + AD_INIT_OTHER_MEMBERS (); } /* There is a race condition in that an attacker could replace the nonempty @@ -963,7 +995,8 @@ remove_dir (char const *dir, struct saved_cwd **cwd_state, return RM_ERROR; } - AD_push (dir, &dir_sb); + AD_push (ds, dir, &dir_sb); + AD_INIT_OTHER_MEMBERS (); status = RM_OK; @@ -971,15 +1004,18 @@ remove_dir (char const *dir, struct saved_cwd **cwd_state, { char *subdir = NULL; struct stat subdir_sb; - enum RM_status tmp_status = remove_cwd_entries (&subdir, &subdir_sb, x); + enum RM_status tmp_status = remove_cwd_entries (ds, + &subdir, &subdir_sb, x); if (tmp_status != RM_OK) { UPDATE_STATUS (status, tmp_status); - AD_mark_current_as_unremovable (); + AD_mark_current_as_unremovable (ds); } if (subdir) { - AD_push (subdir, &subdir_sb); + AD_push (ds, subdir, &subdir_sb); + AD_INIT_OTHER_MEMBERS (); + free (subdir); continue; } @@ -987,7 +1023,7 @@ remove_dir (char const *dir, struct saved_cwd **cwd_state, /* Execution reaches this point when we've removed the last removable entry from the current directory. */ { - char *d = AD_pop_and_chdir (); + char *d = AD_pop_and_chdir (ds); /* Try to remove D only if remove_cwd_entries succeeded. */ if (tmp_status == RM_OK) @@ -998,7 +1034,8 @@ remove_dir (char const *dir, struct saved_cwd **cwd_state, But that's no big deal since we're interactive. */ Ternary is_dir; Ternary is_empty; - enum RM_status s = prompt (d, x, PA_REMOVE_DIR, &is_dir, &is_empty); + enum RM_status s = prompt (ds, d, x, PA_REMOVE_DIR, + &is_dir, &is_empty); if (s != RM_OK) { @@ -1016,15 +1053,15 @@ remove_dir (char const *dir, struct saved_cwd **cwd_state, { error (0, errno, _("cannot remove directory %s"), quote (full_filename (d))); - AD_mark_as_unremovable (d); + AD_mark_as_unremovable (ds, d); status = RM_ERROR; - UPDATE_STATUS (AD_stack_top()->status, status); + UPDATE_STATUS (AD_stack_top(ds)->status, status); } } free (d); - if (AD_stack_height () == 1) + if (AD_stack_height (ds) == 1) break; } } @@ -1040,7 +1077,7 @@ remove_dir (char const *dir, struct saved_cwd **cwd_state, a malloc'd `struct saved_cwd' that may be freed. */ static enum RM_status -rm_1 (char const *filename, +rm_1 (DS *ds, char const *filename, struct rm_options const *x, struct saved_cwd **cwd_state) { char *base = base_name (filename); @@ -1052,11 +1089,11 @@ rm_1 (char const *filename, return RM_ERROR; } - status = remove_entry (filename, x, NULL); + status = remove_entry (ds, filename, x, NULL); if (status != RM_NONEMPTY_DIR) return status; - return remove_dir (filename, cwd_state, x); + return remove_dir (ds, filename, cwd_state, x); } /* Remove all files and/or directories specified by N_FILES and FILE. @@ -1067,21 +1104,18 @@ rm (size_t n_files, char const *const *file, struct rm_options const *x) struct saved_cwd *cwd_state = NULL; enum RM_status status = RM_OK; size_t i; + DS *ds; - obstack_init (&dir_stack); - obstack_init (&len_stack); - obstack_init (&Active_dir); + ds = ds_init (); for (i = 0; i < n_files; i++) { - enum RM_status s = rm_1 (file[i], x, &cwd_state); + enum RM_status s = rm_1 (ds, file[i], x, &cwd_state); assert (VALID_STATUS (s)); UPDATE_STATUS (status, s); } - obstack_free (&dir_stack, NULL); - obstack_free (&len_stack, NULL); - obstack_free (&Active_dir, NULL); + ds_free (ds); XFREE (cwd_state); |