From e8470f7d0e8fa1b10d3434c50d768f26c4d8379d Mon Sep 17 00:00:00 2001 From: chenrun1 Date: Tue, 16 Jan 2024 20:42:08 +0800 Subject: [PATCH] cp:support -r recursive copies The cp command supports recursive copying. Use "cp -r" to copy a folder. Signed-off-by: chenrun1 --- nshlib/nsh_command.c | 2 +- nshlib/nsh_fscmds.c | 403 +++++++++++++++++++++++++++---------------- 2 files changed, 254 insertions(+), 151 deletions(-) diff --git a/nshlib/nsh_command.c b/nshlib/nsh_command.c index 24d3e828d..70cd6ca8d 100644 --- a/nshlib/nsh_command.c +++ b/nshlib/nsh_command.c @@ -169,7 +169,7 @@ static const struct cmdmap_s g_cmdmap[] = #endif #ifndef CONFIG_NSH_DISABLE_CP - CMD_MAP("cp", cmd_cp, 3, 3, " "), + CMD_MAP("cp", cmd_cp, 3, 4, "[-r] "), #endif #ifndef CONFIG_NSH_DISABLE_CMP diff --git a/nshlib/nsh_fscmds.c b/nshlib/nsh_fscmds.c index 04636a7ed..3d45a288b 100644 --- a/nshlib/nsh_fscmds.c +++ b/nshlib/nsh_fscmds.c @@ -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;