📄 cp.c
字号:
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, ©_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 + -