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:
chenrun1 2024-01-16 20:42:08 +08:00 committed by Xiang Xiao
parent 9e71031a09
commit e8470f7d0e
2 changed files with 254 additions and 151 deletions

View File

@ -169,7 +169,7 @@ static const struct cmdmap_s g_cmdmap[] =
#endif
#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
#ifndef CONFIG_NSH_DISABLE_CMP

View File

@ -96,6 +96,235 @@
* 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
****************************************************************************/
@ -618,79 +847,38 @@ int cmd_dmesg(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv)
#ifndef CONFIG_NSH_DISABLE_CP
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 *destpath = NULL;
FAR char *allocpath = NULL;
int oflags = O_WRONLY | O_CREAT | O_TRUNC;
int rdfd;
int wrfd;
bool recursive = false;
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 */
srcpath = nsh_getfullpath(vtbl, argv[1]);
srcpath = nsh_getfullpath(vtbl, argv[optind]);
if (srcpath == NULL)
{
nsh_error(vtbl, g_fmtcmdoutofmemory, argv[0]);
goto errout;
}
/* Open the source file for reading */
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]);
destpath = nsh_getfullpath(vtbl, argv[optind + 1]);
if (destpath == NULL)
{
nsh_error(vtbl, g_fmtcmdoutofmemory, argv[0]);
goto errout_with_rdfd;
}
/* 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;
}
goto errout_with_srcpath;
}
/* 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)
{
nsh_error(vtbl, g_fmtsyntax, argv[0]);
goto errout_with_allocpath;
goto errout_with_destpath;
}
/* Now open the destination */
wrfd = open(destpath, oflags, 0666);
if (wrfd < 0)
if (recursive)
{
nsh_error(vtbl, g_fmtcmdfailed, argv[0], "open", NSH_ERRNO);
goto errout_with_allocpath;
ret = cp_recursive(vtbl, srcpath, destpath);
}
/* Now copy the file */
for (; ; )
else
{
int nbytesread;
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);
ret = cp_handler(vtbl, srcpath, destpath);
}
errout_with_destpath:
if (destpath && !allocpath)
{
nsh_freefullpath(destpath);
}
errout_with_rdfd:
close(rdfd);
nsh_freefullpath(destpath);
errout_with_srcpath:
if (srcpath)
{
nsh_freefullpath(srcpath);
}
nsh_freefullpath(srcpath);
errout:
return ret;