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

📄 cp.c

📁 《linux应用开发技术详解》的配套代码
💻 C
📖 第 1 页 / 共 3 页
字号:

   SRC_OFFSET is the index in CONST_DIRPATH (which is a destination
   path) of the beginning of the source directory name.
   Create any leading directories that don't already exist.
   If VERBOSE_FMT_STRING is nonzero, use it as a printf format
   string for printing a message after successfully making a directory.
   The format should take two string arguments: the names of the
   source and destination directories.
   Creates a linked list of attributes of intermediate directories,
   *ATTR_LIST, for re_protect to use after calling copy.
   Sets *NEW_DST to 1 if this function creates parent of CONST_DIRPATH.

   Return 0 if parent of CONST_DIRPATH exists as a directory with the proper
   permissions when done, otherwise 1. */

/* FIXME: find a way to synch this function with the one in lib/makepath.c. */

static int
make_path_private (const char *const_dirpath, int src_offset,
		   const char *verbose_fmt_string, struct dir_attr **attr_list,
		   int *new_dst, struct cp_options const *x)
{
  struct stat stats;
  char *dirpath;		/* A copy of CONST_DIRPATH we can change. */
  char *src;			/* Source name in `dirpath'. */
  char *dst_dirname;		/* Leading path of `dirpath'. */
  size_t dirlen;		/* Length of leading path of `dirpath'. */

  dirpath = (char *) alloca (strlen (const_dirpath) + 1);
  strcpy (dirpath, const_dirpath);

  src = dirpath + src_offset;

  dirlen = dir_len (dirpath);
  dst_dirname = (char *) alloca (dirlen + 1);
  memcpy (dst_dirname, dirpath, dirlen);
  dst_dirname[dirlen] = '\0';

  *attr_list = NULL;

  if ((*x->xstat) (dst_dirname, &stats))
    {
      /* Parent of CONST_DIRNAME does not exist.
	 Make all missing intermediate directories. */
      char *slash;

      slash = src;
      while (*slash == '/')
	slash++;
      while ((slash = strchr (slash, '/')))
	{
	  /* Add this directory to the list of directories whose modes need
	     fixing later. */
	  struct dir_attr *new =
	    (struct dir_attr *) xmalloc (sizeof (struct dir_attr));
	  new->slash_offset = slash - dirpath;
	  new->next = *attr_list;
	  *attr_list = new;

	  *slash = '\0';
	  if ((*x->xstat) (dirpath, &stats))
	    {
	      mode_t src_mode;

	      /* This element of the path does not exist.  We must set
		 *new_dst and new->mode inside this loop because,
		 for example, in the command `cp --parents ../a/../b/c e_dir',
		 make_path_private creates only e_dir/../a if ./b already
		 exists. */
	      *new_dst = 1;

	      if ((*x->xstat) (src, &stats))
	        {
		  error (0, errno, _("failed to get attributes of %s"),
			 quote (src));
		  return 1;
		}
	      src_mode = stats.st_mode;

	      if (mkdir (dirpath, src_mode))
		{
		  error (0, errno, _("cannot make directory %s"),
			 quote (dirpath));
		  return 1;
		}
	      else
		{
		  if (verbose_fmt_string != NULL)
		    printf (verbose_fmt_string, src, dirpath);
		}

	      /* We need search and write permissions to the new directory
	         for writing the directory's contents. Check if these
		 permissions are there.  */

	      if (lstat (dirpath, &stats))
		{
		  error (0, errno, _("failed to get attributes of %s"),
			 quote (dirpath));
		  return 1;
		}
	      else
	        {
		  if (x->preserve_mode) {
		    new->mode = src_mode;
		    new->mode_valid = (src_mode != stats.st_mode);
		  } else {
		    new->mode = stats.st_mode;
		    new->mode_valid = 0;
		  }

		  if ((stats.st_mode & S_IRWXU) != S_IRWXU)
		    {
		      /* Make the new directory searchable and writable. The
			 original permissions will be restored later.  */

		      new->mode_valid = 1;

		      if (chmod (dirpath, stats.st_mode | S_IRWXU))
			{
			  error (0, errno, _("setting permissions for %s"),
				 quote (dirpath));
			  return 1;
			}
		    }
		}
	    }
	  else if (!S_ISDIR (stats.st_mode))
	    {
	      error (0, 0, _("%s exists but is not a directory"),
		     quote (dirpath));
	      return 1;
	    }
	  else
	    {
	      new->mode_valid = 0;
	      *new_dst = 0;
	    }
	  *slash++ = '/';

	  /* Avoid unnecessary calls to `stat' when given
	     pathnames containing multiple adjacent slashes.  */
	  while (*slash == '/')
	    slash++;
	}
    }

  /* We get here if the parent of `dirpath' already exists. */

  else if (!S_ISDIR (stats.st_mode))
    {
      error (0, 0, _("%s exists but is not a directory"), quote (dst_dirname));
      return 1;
    }
  else
    {
      *new_dst = 0;
    }
  return 0;
}

/* Scan the arguments, and copy each by calling copy.
   Return 0 if successful, 1 if any errors occur. */

static int
do_copy (int n_files, char **file, const char *target_directory,
	 struct cp_options *x)
{
  const char *dest;
  struct stat sb;
  int new_dst = 0;
  int ret = 0;
  int dest_is_dir = 0;

  if (n_files <= 0)
    {
      error (0, 0, _("missing file arguments"));
      usage (EXIT_FAILURE);
    }
  if (n_files == 1 && !target_directory)
    {
      error (0, 0, _("missing destination file"));
      usage (EXIT_FAILURE);
    }

  if (target_directory)
    dest = target_directory;
  else
    {
      dest = file[n_files - 1];
      --n_files;
    }

  /* Initialize these hash tables only if we'll need them.
     The problems they're used to detect can arise only if
     there are two or more files to copy.  */
  if (n_files >= 2)
    {
      dest_info_init (x);
      src_info_init (x);
    }

  if (lstat (dest, &sb))
    {
      if (errno != ENOENT)
	{
	  error (0, errno, _("accessing %s"), quote (dest));
	  return 1;
	}

      new_dst = 1;
    }
  else
    {
      struct stat sbx;

      /* If `dest' is not a symlink to a nonexistent file, use
	 the results of stat instead of lstat, so we can copy files
	 into symlinks to directories. */
      if (stat (dest, &sbx) == 0)
	sb = sbx;

      dest_is_dir = S_ISDIR (sb.st_mode);
    }

  if (!dest_is_dir)
    {
      if (target_directory)
	{
	  error (0, 0, _("%s: specified target is not a directory"),
		 quote (dest));
	  usage (EXIT_FAILURE);
	}

      if (n_files > 1)
	{
	  error (0, 0,
	 _("copying multiple files, but last argument %s is not a directory"),
	     quote (dest));
	  usage (EXIT_FAILURE);
	}
    }

  if (dest_is_dir)
    {
      /* cp file1...filen edir
	 Copy the files `file1' through `filen'
	 to the existing directory `edir'. */
      int i;

      for (i = 0; i < n_files; i++)
	{
	  char *dst_path;
	  int parent_exists = 1; /* True if dir_name (dst_path) exists. */
	  struct dir_attr *attr_list;
	  char *arg_in_concat = NULL;
	  char *arg = file[i];

	  /* Trailing slashes are meaningful (i.e., maybe worth preserving)
	     only in the source file names.  */
	  if (remove_trailing_slashes)
	    strip_trailing_slashes (arg);

	  if (flag_path)
	    {
	      char *arg_no_trailing_slash;

	      /* Use `arg' without trailing slashes in constructing destination
		 file names.  Otherwise, we can end up trying to create a
		 directory via `mkdir ("dst/foo/"...', which is not portable.
		 It fails, due to the trailing slash, on at least
		 NetBSD 1.[34] systems.  */
	      ASSIGN_STRDUPA (arg_no_trailing_slash, arg);
	      strip_trailing_slashes (arg_no_trailing_slash);

	      /* Append all of `arg' (minus any trailing slash) to `dest'.  */
	      dst_path = path_concat (dest, arg_no_trailing_slash,
				      &arg_in_concat);
	      if (dst_path == NULL)
		xalloc_die ();

	      /* For --parents, we have to make sure that the directory
	         dir_name (dst_path) exists.  We may have to create a few
	         leading directories. */
	      parent_exists = !make_path_private (dst_path,
						  arg_in_concat - dst_path,
						  (x->verbose
						   ? "%s -> %s\n" : NULL),
						  &attr_list, &new_dst, x);
	    }
  	  else
  	    {
	      char *arg_base;
	      /* Append the last component of `arg' to `dest'.  */

	      ASSIGN_BASENAME_STRDUPA (arg_base, arg);
	      /* For `cp -R source/.. dest', don't copy into `dest/..'. */
	      dst_path = (STREQ (arg_base, "..")
			  ? xstrdup (dest)
			  : path_concat (dest, arg_base, NULL));
	    }

	  if (!parent_exists)
	    {
	      /* make_path_private failed, so don't even attempt the copy. */
	      ret = 1;
  	    }
	  else
	    {
	      int copy_into_self;
	      ret |= copy (arg, dst_path, new_dst, x, &copy_into_self, NULL);

	      if (flag_path)
		{
		  ret |= re_protect (dst_path, arg_in_concat - dst_path,
				     attr_list, x);
		}
	    }

	  free (dst_path);
	}
      return ret;
    }
  else /* if (n_files == 1) */
    {
      char *new_dest;
      char *source;
      int unused;
      struct stat source_stats;

      if (flag_path)
	{
	  error (0, 0,
	       _("when preserving paths, the destination must be a directory"));
	  usage (EXIT_FAILURE);
	}

      source = file[0];

      /* When the force and backup options have been specified and
	 the source and destination are the same name for an existing
	 regular file, convert the user's command, e.g.,
	 `cp --force --backup foo foo' to `cp --force foo fooSUFFIX'
	 where SUFFIX is determined by any version control options used.  */

      if (x->unlink_dest_after_failed_open
	  && x->backup_type != none
	  && STREQ (source, dest)
	  && !new_dst && S_ISREG (sb.st_mode))
	{
	  static struct cp_options x_tmp;

	  new_dest = find_backup_file_name (dest, x->backup_type);
	  /* Set x->backup_type to `none' so that the normal backup
	     mechanism is not used when performing the actual copy.
	     backup_type must be set to `none' only *after* the above
	     call to find_backup_file_name -- that function uses
	     backup_type to determine the suffix it applies.  */
	  x_tmp = *x;
	  x_tmp.backup_type = none;
	  x = &x_tmp;

	  if (new_dest == NULL)
	    xalloc_die ();
	}

      /* When the destination is specified with a trailing slash and the
	 source exists but is not a directory, convert the user's command
	 `cp source dest/' to `cp source dest/basename(source)'.  Doing

⌨️ 快捷键说明

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