cp:support -r recursive copies
The cp command supports recursive copying. Use "cp -r" to copy a folder. Signed-off-by: chenrun1 <chenrun1@xiaomi.com>
This commit is contained in:
parent
9e71031a09
commit
e8470f7d0e
@ -169,7 +169,7 @@ static const struct cmdmap_s g_cmdmap[] =
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef CONFIG_NSH_DISABLE_CP
|
#ifndef CONFIG_NSH_DISABLE_CP
|
||||||
CMD_MAP("cp", cmd_cp, 3, 3, "<source-path> <dest-path>"),
|
CMD_MAP("cp", cmd_cp, 3, 4, "[-r] <source-path> <dest-path>"),
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef CONFIG_NSH_DISABLE_CMP
|
#ifndef CONFIG_NSH_DISABLE_CMP
|
||||||
|
@ -96,6 +96,235 @@
|
|||||||
* Private Functions
|
* Private Functions
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: cp_handler
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef CONFIG_NSH_DISABLE_CP
|
||||||
|
static int cp_handler(FAR struct nsh_vtbl_s *vtbl, FAR const char *srcpath,
|
||||||
|
FAR const char *destpath)
|
||||||
|
{
|
||||||
|
struct stat buf;
|
||||||
|
FAR char *allocpath = NULL;
|
||||||
|
int oflags = O_WRONLY | O_CREAT | O_TRUNC;
|
||||||
|
int rdfd;
|
||||||
|
int wrfd;
|
||||||
|
int ret = ERROR;
|
||||||
|
|
||||||
|
rdfd = open(srcpath, O_RDONLY);
|
||||||
|
if (rdfd < 0)
|
||||||
|
{
|
||||||
|
nsh_error(vtbl, g_fmtcmdfailed, "cp", "open_rdfd", NSH_ERRNO);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check if the destination is a directory */
|
||||||
|
|
||||||
|
if (stat(destpath, &buf) == 0)
|
||||||
|
{
|
||||||
|
/* Something exists here... is it a directory? */
|
||||||
|
|
||||||
|
if (S_ISDIR(buf.st_mode))
|
||||||
|
{
|
||||||
|
/* Yes, it is a directory.
|
||||||
|
* Remove any trailing '/' characters from the path
|
||||||
|
*/
|
||||||
|
|
||||||
|
nsh_trimdir((FAR char *)destpath);
|
||||||
|
|
||||||
|
/* Construct the full path to the new file */
|
||||||
|
|
||||||
|
allocpath = nsh_getdirpath(vtbl, destpath,
|
||||||
|
basename((FAR char *)srcpath));
|
||||||
|
if (!allocpath)
|
||||||
|
{
|
||||||
|
nsh_error(vtbl, g_fmtcmdoutofmemory, "cp");
|
||||||
|
goto errout_with_rdfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Open then dest for writing */
|
||||||
|
|
||||||
|
destpath = allocpath;
|
||||||
|
}
|
||||||
|
else if (!S_ISREG(buf.st_mode))
|
||||||
|
{
|
||||||
|
/* Maybe it is a driver? */
|
||||||
|
|
||||||
|
oflags = O_WRONLY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wrfd = open(destpath, oflags, 0666);
|
||||||
|
if (wrfd < 0)
|
||||||
|
{
|
||||||
|
nsh_error(vtbl, g_fmtcmdfailed, "cp", "open_wrfd", NSH_ERRNO);
|
||||||
|
goto errout_with_allocpath;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; ; )
|
||||||
|
{
|
||||||
|
int nbytesread;
|
||||||
|
int nbyteswritten;
|
||||||
|
FAR char *iobuffer = vtbl->iobuffer;
|
||||||
|
|
||||||
|
nbytesread = read(rdfd, iobuffer, IOBUFFERSIZE);
|
||||||
|
if (nbytesread == 0)
|
||||||
|
{
|
||||||
|
/* End of file */
|
||||||
|
|
||||||
|
ret = OK;
|
||||||
|
goto errout_with_wrfd;
|
||||||
|
}
|
||||||
|
else if (nbytesread < 0)
|
||||||
|
{
|
||||||
|
/* EINTR is not an error (but will still stop the copy) */
|
||||||
|
|
||||||
|
if (errno == EINTR)
|
||||||
|
{
|
||||||
|
nsh_error(vtbl, g_fmtsignalrecvd, "cp");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Read error */
|
||||||
|
|
||||||
|
nsh_error(vtbl, g_fmtcmdfailed, "cp", "read",
|
||||||
|
NSH_ERRNO);
|
||||||
|
}
|
||||||
|
|
||||||
|
goto errout_with_wrfd;
|
||||||
|
}
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
nbyteswritten = write(wrfd, iobuffer, nbytesread);
|
||||||
|
if (nbyteswritten >= 0)
|
||||||
|
{
|
||||||
|
nbytesread -= nbyteswritten;
|
||||||
|
iobuffer += nbyteswritten;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* EINTR is not an error (but will still stop the copy) */
|
||||||
|
|
||||||
|
if (errno == EINTR)
|
||||||
|
{
|
||||||
|
nsh_error(vtbl, g_fmtsignalrecvd, "cp");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Read error */
|
||||||
|
|
||||||
|
nsh_error(vtbl, g_fmtcmdfailed, "cp", "write",
|
||||||
|
NSH_ERRNO);
|
||||||
|
}
|
||||||
|
|
||||||
|
goto errout_with_wrfd;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (nbytesread > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
errout_with_wrfd:
|
||||||
|
close(wrfd);
|
||||||
|
|
||||||
|
errout_with_allocpath:
|
||||||
|
free(allocpath);
|
||||||
|
|
||||||
|
errout_with_rdfd:
|
||||||
|
close(rdfd);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Name: cp_recursive
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef CONFIG_NSH_DISABLE_CP
|
||||||
|
static int cp_recursive(FAR struct nsh_vtbl_s *vtbl, FAR const char *srcpath,
|
||||||
|
FAR const char *destpath)
|
||||||
|
{
|
||||||
|
FAR struct dirent *entry;
|
||||||
|
FAR char *allocdestpath;
|
||||||
|
FAR char *allocsrcpath;
|
||||||
|
struct stat buf;
|
||||||
|
int ret = OK;
|
||||||
|
DIR *dp;
|
||||||
|
|
||||||
|
dp = opendir(srcpath);
|
||||||
|
if (dp == NULL)
|
||||||
|
{
|
||||||
|
nsh_error(vtbl, g_fmtcmdfailed, "cp", "opendir", NSH_ERRNO);
|
||||||
|
return ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((entry = readdir(dp)) != NULL && ret == OK)
|
||||||
|
{
|
||||||
|
if (strcmp(entry->d_name, ".") == 0 ||
|
||||||
|
strcmp(entry->d_name, "..") == 0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
allocsrcpath = nsh_getdirpath(vtbl, srcpath, entry->d_name);
|
||||||
|
if (allocsrcpath == NULL)
|
||||||
|
{
|
||||||
|
ret = ERROR;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = stat(allocsrcpath, &buf);
|
||||||
|
if (ret != OK)
|
||||||
|
{
|
||||||
|
nsh_error(vtbl, g_fmtcmdfailed, "cp", "stat", NSH_ERRNO);
|
||||||
|
goto errout_with_allocsrcpath;
|
||||||
|
}
|
||||||
|
|
||||||
|
allocdestpath = nsh_getdirpath(vtbl, destpath, entry->d_name);
|
||||||
|
if (allocdestpath == NULL)
|
||||||
|
{
|
||||||
|
ret = ERROR;
|
||||||
|
goto errout_with_allocsrcpath;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (S_ISDIR(buf.st_mode))
|
||||||
|
{
|
||||||
|
ret = mkdir(allocdestpath, S_IRWXU | S_IRWXG | S_IROTH |
|
||||||
|
S_IXOTH);
|
||||||
|
if (ret != OK)
|
||||||
|
{
|
||||||
|
nsh_error(vtbl, g_fmtcmdfailed, "cp", "mkdir", NSH_ERRNO);
|
||||||
|
goto errout_with_allocdestpath;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = cp_recursive(vtbl, allocsrcpath, allocdestpath);
|
||||||
|
if (ret != OK)
|
||||||
|
{
|
||||||
|
goto errout_with_allocdestpath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ret = cp_handler(vtbl, allocsrcpath, allocdestpath);
|
||||||
|
if (ret != OK)
|
||||||
|
{
|
||||||
|
goto errout_with_allocdestpath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errout_with_allocdestpath:
|
||||||
|
free(allocdestpath);
|
||||||
|
|
||||||
|
errout_with_allocsrcpath:
|
||||||
|
free(allocsrcpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
closedir(dp);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/****************************************************************************
|
/****************************************************************************
|
||||||
* Name: ls_specialdir
|
* Name: ls_specialdir
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
@ -618,79 +847,38 @@ int cmd_dmesg(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv)
|
|||||||
#ifndef CONFIG_NSH_DISABLE_CP
|
#ifndef CONFIG_NSH_DISABLE_CP
|
||||||
int cmd_cp(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv)
|
int cmd_cp(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv)
|
||||||
{
|
{
|
||||||
UNUSED(argc);
|
|
||||||
|
|
||||||
struct stat buf;
|
|
||||||
FAR char *srcpath = NULL;
|
FAR char *srcpath = NULL;
|
||||||
FAR char *destpath = NULL;
|
FAR char *destpath = NULL;
|
||||||
FAR char *allocpath = NULL;
|
bool recursive = false;
|
||||||
int oflags = O_WRONLY | O_CREAT | O_TRUNC;
|
|
||||||
int rdfd;
|
|
||||||
int wrfd;
|
|
||||||
int ret = ERROR;
|
int ret = ERROR;
|
||||||
|
int option;
|
||||||
|
|
||||||
|
/* Get the cp flags */
|
||||||
|
|
||||||
|
while ((option = getopt(argc, argv, "r")) != ERROR)
|
||||||
|
{
|
||||||
|
switch (option)
|
||||||
|
{
|
||||||
|
case 'r':
|
||||||
|
recursive = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Get the full path to the source file */
|
/* Get the full path to the source file */
|
||||||
|
|
||||||
srcpath = nsh_getfullpath(vtbl, argv[1]);
|
srcpath = nsh_getfullpath(vtbl, argv[optind]);
|
||||||
if (srcpath == NULL)
|
if (srcpath == NULL)
|
||||||
{
|
{
|
||||||
nsh_error(vtbl, g_fmtcmdoutofmemory, argv[0]);
|
nsh_error(vtbl, g_fmtcmdoutofmemory, argv[0]);
|
||||||
goto errout;
|
goto errout;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Open the source file for reading */
|
destpath = nsh_getfullpath(vtbl, argv[optind + 1]);
|
||||||
|
|
||||||
rdfd = open(srcpath, O_RDONLY);
|
|
||||||
if (rdfd < 0)
|
|
||||||
{
|
|
||||||
nsh_error(vtbl, g_fmtcmdfailed, argv[0], "open", NSH_ERRNO);
|
|
||||||
goto errout_with_srcpath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Get the full path to the destination file or directory */
|
|
||||||
|
|
||||||
destpath = nsh_getfullpath(vtbl, argv[2]);
|
|
||||||
if (destpath == NULL)
|
if (destpath == NULL)
|
||||||
{
|
{
|
||||||
nsh_error(vtbl, g_fmtcmdoutofmemory, argv[0]);
|
nsh_error(vtbl, g_fmtcmdoutofmemory, argv[0]);
|
||||||
goto errout_with_rdfd;
|
goto errout_with_srcpath;
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if the destination is a directory */
|
|
||||||
|
|
||||||
ret = stat(destpath, &buf);
|
|
||||||
if (ret == 0)
|
|
||||||
{
|
|
||||||
/* Something exists here... is it a directory? */
|
|
||||||
|
|
||||||
if (S_ISDIR(buf.st_mode))
|
|
||||||
{
|
|
||||||
/* Yes, it is a directory.
|
|
||||||
* Remove any trailing '/' characters from the path
|
|
||||||
*/
|
|
||||||
|
|
||||||
nsh_trimdir(destpath);
|
|
||||||
|
|
||||||
/* Construct the full path to the new file */
|
|
||||||
|
|
||||||
allocpath = nsh_getdirpath(vtbl, destpath, basename(argv[1]));
|
|
||||||
if (!allocpath)
|
|
||||||
{
|
|
||||||
nsh_error(vtbl, g_fmtcmdoutofmemory, argv[0]);
|
|
||||||
goto errout_with_destpath;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Open then dest for writing */
|
|
||||||
|
|
||||||
nsh_freefullpath(destpath);
|
|
||||||
destpath = allocpath;
|
|
||||||
}
|
|
||||||
else if (!S_ISREG(buf.st_mode))
|
|
||||||
{
|
|
||||||
/* Maybe it is a driver? */
|
|
||||||
|
|
||||||
oflags = O_WRONLY;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if the destination does not match the source */
|
/* Check if the destination does not match the source */
|
||||||
@ -698,110 +886,25 @@ int cmd_cp(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv)
|
|||||||
if (strcmp(destpath, srcpath) == 0)
|
if (strcmp(destpath, srcpath) == 0)
|
||||||
{
|
{
|
||||||
nsh_error(vtbl, g_fmtsyntax, argv[0]);
|
nsh_error(vtbl, g_fmtsyntax, argv[0]);
|
||||||
goto errout_with_allocpath;
|
goto errout_with_destpath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now open the destination */
|
/* Now open the destination */
|
||||||
|
|
||||||
wrfd = open(destpath, oflags, 0666);
|
if (recursive)
|
||||||
if (wrfd < 0)
|
|
||||||
{
|
{
|
||||||
nsh_error(vtbl, g_fmtcmdfailed, argv[0], "open", NSH_ERRNO);
|
ret = cp_recursive(vtbl, srcpath, destpath);
|
||||||
goto errout_with_allocpath;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
/* Now copy the file */
|
|
||||||
|
|
||||||
for (; ; )
|
|
||||||
{
|
{
|
||||||
int nbytesread;
|
ret = cp_handler(vtbl, srcpath, destpath);
|
||||||
int nbyteswritten;
|
|
||||||
FAR char *iobuffer = vtbl->iobuffer;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
nbytesread = read(rdfd, iobuffer, IOBUFFERSIZE);
|
|
||||||
if (nbytesread == 0)
|
|
||||||
{
|
|
||||||
/* End of file */
|
|
||||||
|
|
||||||
ret = OK;
|
|
||||||
goto errout_with_wrfd;
|
|
||||||
}
|
|
||||||
else if (nbytesread < 0)
|
|
||||||
{
|
|
||||||
/* EINTR is not an error (but will still stop the copy) */
|
|
||||||
|
|
||||||
if (errno == EINTR)
|
|
||||||
{
|
|
||||||
nsh_error(vtbl, g_fmtsignalrecvd, argv[0]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Read error */
|
|
||||||
|
|
||||||
nsh_error(vtbl, g_fmtcmdfailed, argv[0], "read",
|
|
||||||
NSH_ERRNO);
|
|
||||||
}
|
|
||||||
|
|
||||||
goto errout_with_wrfd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (nbytesread <= 0);
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
nbyteswritten = write(wrfd, iobuffer, nbytesread);
|
|
||||||
if (nbyteswritten >= 0)
|
|
||||||
{
|
|
||||||
nbytesread -= nbyteswritten;
|
|
||||||
iobuffer += nbyteswritten;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* EINTR is not an error (but will still stop the copy) */
|
|
||||||
|
|
||||||
if (errno == EINTR)
|
|
||||||
{
|
|
||||||
nsh_error(vtbl, g_fmtsignalrecvd, argv[0]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Read error */
|
|
||||||
|
|
||||||
nsh_error(vtbl, g_fmtcmdfailed, argv[0], "write",
|
|
||||||
NSH_ERRNO);
|
|
||||||
}
|
|
||||||
|
|
||||||
goto errout_with_wrfd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (nbytesread > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
errout_with_wrfd:
|
|
||||||
close(wrfd);
|
|
||||||
|
|
||||||
errout_with_allocpath:
|
|
||||||
if (allocpath)
|
|
||||||
{
|
|
||||||
free(allocpath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
errout_with_destpath:
|
errout_with_destpath:
|
||||||
if (destpath && !allocpath)
|
nsh_freefullpath(destpath);
|
||||||
{
|
|
||||||
nsh_freefullpath(destpath);
|
|
||||||
}
|
|
||||||
|
|
||||||
errout_with_rdfd:
|
|
||||||
close(rdfd);
|
|
||||||
|
|
||||||
errout_with_srcpath:
|
errout_with_srcpath:
|
||||||
if (srcpath)
|
nsh_freefullpath(srcpath);
|
||||||
{
|
|
||||||
nsh_freefullpath(srcpath);
|
|
||||||
}
|
|
||||||
|
|
||||||
errout:
|
errout:
|
||||||
return ret;
|
return ret;
|
||||||
|
Loading…
Reference in New Issue
Block a user