⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 remove.c

📁 linux开发技术详解一书的源码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* remove.c -- core functions for removing files and directories
   Copyright (C) 88, 90, 91, 1994-2002 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

/* Extracted from rm.c and librarified, then rewritten by Jim Meyering.  */

#ifdef _AIX
 #pragma alloca
#endif

#include <config.h>
#include <stdio.h>
#include <sys/types.h>
#include <assert.h>

#include "save-cwd.h"
#include "system.h"
#include "dirname.h"
#include "error.h"
#include "file-type.h"
#include "hash.h"
#include "hash-pjw.h"
#include "obstack.h"
#include "quote.h"
#include "remove.h"

/* Avoid shadowing warnings because these are functions declared
   in dirname.h as well as locals used below.  */
#define dir_name rm_dir_name
#define dir_len rm_dir_len

#define obstack_chunk_alloc malloc
#define obstack_chunk_free free

#ifndef PARAMS
# if defined (__GNUC__) || __STDC__
#  define PARAMS(args) args
# else
#  define PARAMS(args) ()
# endif
#endif

/* FIXME: if possible, use autoconf...  */
#ifdef __GLIBC__
# define ROOT_CAN_UNLINK_DIRS 0
#else
# define ROOT_CAN_UNLINK_DIRS 1
#endif

enum Ternary
  {
    T_UNKNOWN = 2,
    T_NO,
    T_YES
  };
typedef enum Ternary Ternary;

/* The prompt function may be called twice a given directory.
   The first time, we ask whether to descend into it, and the
   second time, we ask whether to remove it.  */
enum Prompt_action
  {
    PA_DESCEND_INTO_DIR = 2,
    PA_REMOVE_DIR
  };

/* On systems with an lstat function that accepts the empty string,
   arrange to make lstat calls go through the wrapper function.  */
#if HAVE_LSTAT_EMPTY_STRING_BUG
int rpl_lstat PARAMS((const char *, struct stat *));
# define lstat(Name, Stat_buf) rpl_lstat(Name, Stat_buf)
#endif

#ifdef D_INO_IN_DIRENT
# define D_INO(dp) ((dp)->d_ino)
# define ENABLE_CYCLE_CHECK
#else
/* Some systems don't have inodes, so fake them to avoid lots of ifdefs.  */
# define D_INO(dp) 1
#endif

#if !defined S_ISLNK
# define S_ISLNK(Mode) 0
#endif

/* Initial capacity of per-directory hash table of entries that have
   been processed but not been deleted.  */
#define HT_UNREMOVABLE_INITIAL_CAPACITY 13

/* An entry in the active directory stack.
   Each entry corresponds to an `active' directory.  */
struct AD_ent
{
  /* For a given active directory, this is the set of names of
     entries in that directory that could/should not be removed.
     For example, `.' and `..', as well as files/dirs for which
     unlink/rmdir failed e.g., due to access restrictions.  */
  Hash_table *unremovable;

  /* Record the status for a given active directory; we need to know
     whether an entry was not removed, either because of an error or
     because the user declined.  */
  enum RM_status status;

  union
  {
    /* The directory's dev/ino.  Used to ensure that `chdir some-subdir', then
       `chdir ..' takes us back to the same directory from which we started).
       (valid for all but the bottommost entry on the stack.  */
    struct dev_ino a;

    /* Enough information to restore the initial working directory.
       (valid only for the bottommost entry on the stack)  */
    struct saved_cwd saved_cwd;
  } u;
};

int euidaccess ();
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;

static void
hash_freer (void *x)
{
  free (x);
}

static bool
hash_compare_strings (void const *x, void const *y)
{
  return STREQ (x, y) ? true : false;
}

static inline void
push_dir (const char *dir_name)
{
  size_t len;

  len = strlen (dir_name);

  /* Append the string onto the stack.  */
  obstack_grow (&dir_stack, dir_name, len);

  /* Append a trailing slash.  */
  obstack_1grow (&dir_stack, '/');

  /* Add one for the slash.  */
  ++len;

  /* Push the length (including slash) onto its stack.  */
  obstack_grow (&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)
{
  int n_lengths = obstack_object_size (&len_stack) / sizeof (size_t);
  size_t *length = (size_t *) obstack_base (&len_stack);
  size_t top_len = length[n_lengths - 1];
  char const *p = obstack_next_free (&dir_stack) - top_len;
  char *q = xmalloc (top_len);
  memcpy (q, p, top_len - 1);
  q[top_len - 1] = 0;
  return q;
}

static inline void
pop_dir (void)
{
  int n_lengths = obstack_object_size (&len_stack) / sizeof (size_t);
  size_t *length = (size_t *) obstack_base (&len_stack);
  size_t top_len;

  assert (n_lengths > 0);
  top_len = length[n_lengths - 1];
  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);

  /* Pop the length stack, too.  */
  assert (obstack_object_size (&len_stack) >= sizeof (size_t));
  obstack_blank (&len_stack, (int) -(sizeof (size_t)));
}

/* Copy the SRC_LEN bytes of data beginning at SRC into the DST_LEN-byte
   buffer, DST, so that the last source byte is at the end of the destination
   buffer.  If SRC_LEN is longer than DST_LEN, then set *TRUNCATED to non-zero.
   Set *RESULT to point to the beginning of (the portion of) the source data
   in DST.  Return the number of bytes remaining in the destination buffer.  */

static size_t
right_justify (char *dst, size_t dst_len, const char *src, size_t src_len,
	       char **result, int *truncated)
{
  const char *sp;
  char *dp;

  if (src_len <= dst_len)
    {
      sp = src;
      dp = dst + (dst_len - src_len);
      *truncated = 0;
    }
  else
    {
      sp = src + (src_len - dst_len);
      dp = dst;
      src_len = dst_len;
      *truncated = 1;
    }

  *result = memcpy (dp, sp, src_len);
  return dst_len - src_len;
}

/* Using the global directory name obstack, create the full path to FILENAME.
   Return it in sometimes-realloc'd space that should not be freed by the
   caller.  Realloc as necessary.  If realloc fails, use a static buffer
   and put as long a suffix in that buffer as possible.  */

static char *
full_filename (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);
  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)
    {
      /* This code requires that realloc accept NULL as the first arg.
         This function must not use xrealloc.  Otherwise, an out-of-memory
	 error involving a file name to be expanded here wouldn't ever
	 be issued.  Use realloc and fall back on using a static buffer
	 if memory allocation fails.  */
      buf = realloc (buf, n_bytes_needed);
      n_allocated = n_bytes_needed;

      if (buf == NULL)
	{
#define SBUF_SIZE 512
#define ELLIPSES_PREFIX "[...]"
	  static char static_buf[SBUF_SIZE];
	  int truncated;
	  size_t len;
	  char *p;

	  len = right_justify (static_buf, SBUF_SIZE, filename,
			       filename_len + 1, &p, &truncated);
	  right_justify (static_buf, len, dir_name, dir_len, &p, &truncated);
	  if (truncated)
	    {
	      memcpy (static_buf, ELLIPSES_PREFIX,
		      sizeof (ELLIPSES_PREFIX) - 1);
	    }
	  return p;
	}
    }

  /* Copy directory part, including trailing slash, and then
     append the filename part, including a trailing zero byte.  */
  memcpy (mempcpy (buf, dir_name, dir_len), filename, filename_len + 1);

  assert (strlen (buf) + 1 == n_bytes_needed);

  return buf;
}

static size_t
AD_stack_height (void)
{
  return obstack_object_size (&Active_dir) / sizeof (struct AD_ent);
}

static struct AD_ent *
AD_stack_top (void)
{
  return (struct AD_ent *)
    ((char *) obstack_next_free (&Active_dir) - sizeof (struct AD_ent));
}

static void
AD_stack_pop (void)
{
  /* operate on Active_dir.  pop and free top entry */
  struct AD_ent *top = AD_stack_top ();
  if (top->unremovable)
    hash_free (top->unremovable);
  obstack_blank (&Active_dir, -sizeof (struct AD_ent));
  pop_dir ();
}

/* chdir `up' one level.
   Whenever using chdir '..', verify that the post-chdir
   dev/ino numbers for `.' match the saved ones.
   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)
{
  /* 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;
  struct stat sb;
  struct AD_ent *top;

  AD_stack_pop ();

  /* Propagate any failure to parent.  */
  UPDATE_STATUS (AD_stack_top()->status, old_status);

  assert (AD_stack_height ());

  top = AD_stack_top ();
  if (1 < AD_stack_height ())
    {
      /* We can give a better diagnostic here, since the target is relative. */
      if (chdir (".."))
	{
	  error (EXIT_FAILURE, errno,
		 _("cannot chdir from %s to .."),
		 quote (full_filename (".")));
	}
    }
  else
    {
      if (restore_cwd (&top->u.saved_cwd, NULL, NULL))

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -