From 96100b30f2b883057ea6b153dbfcf6691439c50e Mon Sep 17 00:00:00 2001 From: Marco Casaroli Date: Tue, 6 Aug 2024 23:29:50 +0200 Subject: [PATCH] feat(nsh): input (stdin) redirection This adds support for `<` to redirect input on nsh commands. --- builtin/exec_builtin.c | 38 ++++-- include/builtin/builtin.h | 19 +-- nshlib/nsh.h | 14 +- nshlib/nsh_altconsole.c | 4 + nshlib/nsh_builtin.c | 5 +- nshlib/nsh_console.c | 24 +++- nshlib/nsh_console.h | 26 ++-- nshlib/nsh_parse.c | 246 +++++++++++++++++++++++++---------- nshlib/nsh_script.c | 2 +- nshlib/nsh_timcmds.c | 9 +- testing/cmocka/cmocka_main.c | 2 +- 11 files changed, 269 insertions(+), 120 deletions(-) diff --git a/builtin/exec_builtin.c b/builtin/exec_builtin.c index 3f4c9b38b..4c2920b09 100644 --- a/builtin/exec_builtin.c +++ b/builtin/exec_builtin.c @@ -43,13 +43,15 @@ * New application is run in a separate task context (and thread). * * Input Parameter: - * filename - Name of the linked-in binary to be started. - * argv - Argument list - * redirfile - If output is redirected, this parameter will be non-NULL - * and will provide the full path to the file. - * oflags - If output is redirected, this parameter will provide the - * open flags to use. This will support file replacement - * of appending to an existing file. + * filename - Name of the linked-in binary to be started. + * argv - Argument list + * redirfile_in - If input is redirected, this parameter will be non-NULL + * and will provide the full path to the file. + * redirfile_out - If output is redirected, this parameter will be non-NULL + * and will provide the full path to the file. + * oflags - If output is redirected, this parameter will provide the + * open flags to use. This will support file replacement + * of appending to an existing file. * * Returned Value: * This is an end-user function, so it follows the normal convention: @@ -59,7 +61,8 @@ ****************************************************************************/ int exec_builtin(FAR const char *appname, FAR char * const *argv, - FAR const char *redirfile, int oflags) + FAR const char *redirfile_in, FAR const char *redirfile_out, + int oflags) { FAR const struct builtin_s *builtin; posix_spawnattr_t attr; @@ -144,14 +147,29 @@ int exec_builtin(FAR const char *appname, FAR char * const *argv, #endif + /* Is input being redirected? */ + + if (redirfile_in) + { + /* Set up to close open redirfile and set to stdin (0) */ + + ret = posix_spawn_file_actions_addopen(&file_actions, 0, + redirfile_in, O_RDONLY, 0); + if (ret != 0) + { + serr("ERROR: posix_spawn_file_actions_addopen failed: %d\n", ret); + goto errout_with_actions; + } + } + /* Is output being redirected? */ - if (redirfile) + if (redirfile_out) { /* Set up to close open redirfile and set to stdout (1) */ ret = posix_spawn_file_actions_addopen(&file_actions, 1, - redirfile, oflags, 0644); + redirfile_out, oflags, 0644); if (ret != 0) { serr("ERROR: posix_spawn_file_actions_addopen failed: %d\n", ret); diff --git a/include/builtin/builtin.h b/include/builtin/builtin.h index e6662127f..cc589db2f 100644 --- a/include/builtin/builtin.h +++ b/include/builtin/builtin.h @@ -64,13 +64,15 @@ extern "C" * New application is run in a separate task context (and thread). * * Input Parameter: - * filename - Name of the linked-in binary to be started. - * argv - Argument list - * redirfile - If output is redirected, this parameter will be non-NULL - * and will provide the full path to the file. - * oflags - If output is redirected, this parameter will provide the - * open flags to use. This will support file replacement - * of appending to an existing file. + * filename - Name of the linked-in binary to be started. + * argv - Argument list + * redirfile_in - If input is redirected, this parameter will be non-NULL + * and will provide the full path to the file. + * redirfile_out - If output is redirected, this parameter will be non-NULL + * and will provide the full path to the file. + * oflags - If output is redirected, this parameter will provide the + * open flags to use. This will support file replacement + * of appending to an existing file. * * Returned Value: * This is an end-user function, so it follows the normal convention: @@ -80,7 +82,8 @@ extern "C" ****************************************************************************/ int exec_builtin(FAR const char *appname, FAR char * const *argv, - FAR const char *redirfile, int oflags); + FAR const char *redirfile_in, FAR const char *redirfile_out, + int oflags); #undef EXTERN #if defined(__cplusplus) diff --git a/nshlib/nsh.h b/nshlib/nsh.h index 574150247..8d0cb8d9c 100644 --- a/nshlib/nsh.h +++ b/nshlib/nsh.h @@ -654,15 +654,16 @@ enum nsh_npflags_e struct nsh_parser_s { #ifndef CONFIG_NSH_DISABLEBG - bool np_bg; /* true: The last command executed in background */ + bool np_bg; /* true: The last command executed in background */ #endif - bool np_redirect; /* true: Output from the last command was re-directed */ - bool np_fail; /* true: The last command failed */ + bool np_redir_out; /* true: Output from the last command was re-directed */ + bool np_redir_in; /* true: Input from the last command was re-directed */ + bool np_fail; /* true: The last command failed */ #ifndef CONFIG_NSH_DISABLESCRIPT - uint8_t np_flags; /* See nsh_npflags_e above */ + uint8_t np_flags; /* See nsh_npflags_e above */ #endif #ifndef CONFIG_NSH_DISABLEBG - int np_nice; /* "nice" value applied to last background cmd */ + int np_nice; /* "nice" value applied to last background cmd */ #endif #ifndef CONFIG_NSH_DISABLESCRIPT @@ -852,7 +853,8 @@ int nsh_command(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char *argv[]); #ifdef CONFIG_NSH_BUILTIN_APPS int nsh_builtin(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd, - FAR char **argv, FAR const char *redirfile, int oflags); + FAR char **argv, FAR const char *redirfile_in, + FAR const char *redirfile_out, int oflags); #endif #ifdef CONFIG_NSH_FILE_APPS diff --git a/nshlib/nsh_altconsole.c b/nshlib/nsh_altconsole.c index 4f6cb5bd6..3e2c9335b 100644 --- a/nshlib/nsh_altconsole.c +++ b/nshlib/nsh_altconsole.c @@ -98,6 +98,10 @@ static int nsh_clone_console(FAR struct console_stdio_s *pstate) OUTFD(pstate) = 1; + /* Setup stdin */ + + INFD(pstate) = 0; + return OK; } diff --git a/nshlib/nsh_builtin.c b/nshlib/nsh_builtin.c index 49c0fd5d9..ea80c0cc7 100644 --- a/nshlib/nsh_builtin.c +++ b/nshlib/nsh_builtin.c @@ -69,7 +69,8 @@ ****************************************************************************/ int nsh_builtin(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd, - FAR char **argv, FAR const char *redirfile, int oflags) + FAR char **argv, FAR const char *redirfile_in, + FAR const char *redirfile_out, int oflags) { #if !defined(CONFIG_NSH_DISABLEBG) && defined(CONFIG_SCHED_CHILD_STATUS) struct sigaction act; @@ -101,7 +102,7 @@ int nsh_builtin(FAR struct nsh_vtbl_s *vtbl, FAR const char *cmd, * applications. */ - ret = exec_builtin(cmd, argv, redirfile, oflags); + ret = exec_builtin(cmd, argv, redirfile_in, redirfile_out, oflags); if (ret >= 0) { /* The application was successfully started with pre-emption disabled. diff --git a/nshlib/nsh_console.c b/nshlib/nsh_console.c index 2fd1c9c12..444a65421 100644 --- a/nshlib/nsh_console.c +++ b/nshlib/nsh_console.c @@ -47,6 +47,7 @@ struct serialsave_s { int cn_errfd; /* Re-directed error output file descriptor */ int cn_outfd; /* Re-directed output file descriptor */ + int cn_infd; /* Re-directed input file descriptor */ }; /**************************************************************************** @@ -68,8 +69,8 @@ static int nsh_erroroutput(FAR struct nsh_vtbl_s *vtbl, FAR const char *fmt, ...) printf_like(2, 3); #endif static FAR char *nsh_consolelinebuffer(FAR struct nsh_vtbl_s *vtbl); -static void nsh_consoleredirect(FAR struct nsh_vtbl_s *vtbl, int fd, - FAR uint8_t *save); +static void nsh_consoleredirect(FAR struct nsh_vtbl_s *vtbl, int fd_in, + int fd_out, FAR uint8_t *save); static void nsh_consoleundirect(FAR struct nsh_vtbl_s *vtbl, FAR uint8_t *save); static void nsh_consoleexit(FAR struct nsh_vtbl_s *vtbl, @@ -100,8 +101,14 @@ static void nsh_closeifnotclosed(struct console_stdio_s *pstate) close(ERRFD(pstate)); } + if (INFD(pstate) >= 0 && INFD(pstate) != STDIN_FILENO) + { + close(INFD(pstate)); + } + ERRFD(pstate) = -1; OUTFD(pstate) = -1; + INFD(pstate) = -1; } /**************************************************************************** @@ -290,8 +297,8 @@ static void nsh_consolerelease(FAR struct nsh_vtbl_s *vtbl) * ****************************************************************************/ -static void nsh_consoleredirect(FAR struct nsh_vtbl_s *vtbl, int fd, - FAR uint8_t *save) +static void nsh_consoleredirect(FAR struct nsh_vtbl_s *vtbl, int fd_in, + int fd_out, FAR uint8_t *save) { FAR struct console_stdio_s *pstate = (FAR struct console_stdio_s *)vtbl; FAR struct serialsave_s *ssave = (FAR struct serialsave_s *)save; @@ -306,11 +313,13 @@ static void nsh_consoleredirect(FAR struct nsh_vtbl_s *vtbl, int fd, ERRFD(ssave) = ERRFD(pstate); OUTFD(ssave) = OUTFD(pstate); + INFD(ssave) = INFD(pstate); } /* Set the fd of the new. */ - OUTFD(pstate) = fd; + OUTFD(pstate) = fd_out; + INFD(pstate) = fd_in; } /**************************************************************************** @@ -330,6 +339,7 @@ static void nsh_consoleundirect(FAR struct nsh_vtbl_s *vtbl, nsh_closeifnotclosed(pstate); ERRFD(pstate) = ERRFD(ssave); OUTFD(pstate) = OUTFD(ssave); + INFD(pstate) = INFD(ssave); } /**************************************************************************** @@ -395,6 +405,10 @@ FAR struct console_stdio_s *nsh_newconsole(bool isctty) /* Initialize the output stream */ OUTFD(pstate) = STDOUT_FILENO; + + /* Initialize the input stream */ + + INFD(pstate) = STDIN_FILENO; } return pstate; diff --git a/nshlib/nsh_console.h b/nshlib/nsh_console.h index adea4b455..7f950460a 100644 --- a/nshlib/nsh_console.h +++ b/nshlib/nsh_console.h @@ -41,18 +41,18 @@ /* Method access macros */ -#define nsh_clone(v) (v)->clone(v) -#define nsh_release(v) (v)->release(v) -#define nsh_write(v,b,n) (v)->write(v,b,n) -#define nsh_ioctl(v,c,a) (v)->ioctl(v,c,a) -#define nsh_linebuffer(v) (v)->linebuffer(v) -#define nsh_redirect(v,f,s) (v)->redirect(v,f,s) -#define nsh_undirect(v,s) (v)->undirect(v,s) -#define nsh_exit(v,s) (v)->exit(v,s) +#define nsh_clone(v) (v)->clone(v) +#define nsh_release(v) (v)->release(v) +#define nsh_write(v,b,n) (v)->write(v,b,n) +#define nsh_ioctl(v,c,a) (v)->ioctl(v,c,a) +#define nsh_linebuffer(v) (v)->linebuffer(v) +#define nsh_redirect(v,fi,fo,s) (v)->redirect(v,fi,fo,s) +#define nsh_undirect(v,s) (v)->undirect(v,s) +#define nsh_exit(v,s) (v)->exit(v,s) #ifdef CONFIG_CPP_HAVE_VARARGS -# define nsh_error(v, ...) (v)->error(v, ##__VA_ARGS__) -# define nsh_output(v, ...) (v)->output(v, ##__VA_ARGS__) +# define nsh_error(v, ...) (v)->error(v, ##__VA_ARGS__) +# define nsh_output(v, ...) (v)->output(v, ##__VA_ARGS__) # define nsh_none(v, ...) \ do { if (0) nsh_output_none(v, ##__VA_ARGS__); } while (0) #else @@ -85,7 +85,7 @@ #else -# define INFD(p) 0 +# define INFD(p) ((p)->cn_infd) #endif @@ -121,7 +121,8 @@ struct nsh_vtbl_s int (*output)(FAR struct nsh_vtbl_s *vtbl, FAR const char *fmt, ...) printf_like(2, 3); FAR char *(*linebuffer)(FAR struct nsh_vtbl_s *vtbl); - void (*redirect)(FAR struct nsh_vtbl_s *vtbl, int fd, FAR uint8_t *save); + void (*redirect)(FAR struct nsh_vtbl_s *vtbl, int fd_in, int fd_out, + FAR uint8_t *save); void (*undirect)(FAR struct nsh_vtbl_s *vtbl, FAR uint8_t *save); void (*exit)(FAR struct nsh_vtbl_s *vtbl, int status) noreturn_function; @@ -163,6 +164,7 @@ struct console_stdio_s #ifdef CONFIG_NSH_ALTCONDEV int cn_confd; /* Console I/O file descriptor */ #endif + int cn_infd; /* Input file descriptor (possibly redirected) */ int cn_outfd; /* Output file descriptor (possibly redirected) */ int cn_errfd; /* Error Output file descriptor (possibly redirected) */ diff --git a/nshlib/nsh_parse.c b/nshlib/nsh_parse.c index 4709bd901..b4fa64be7 100644 --- a/nshlib/nsh_parse.c +++ b/nshlib/nsh_parse.c @@ -131,7 +131,8 @@ struct cmdarg_s { FAR struct nsh_vtbl_s *vtbl; /* For front-end interaction */ - int fd; /* FD for output redirection */ + int fd_in; /* FD for output redirection */ + int fd_out; /* FD for output redirection */ int argc; /* Number of arguments in argv */ FAR char *argv[MAX_ARGV_ENTRIES]; /* Argument list */ }; @@ -176,13 +177,13 @@ static void nsh_alist_free(FAR struct nsh_vtbl_s *vtbl, static void nsh_releaseargs(struct cmdarg_s *arg); static pthread_addr_t nsh_child(pthread_addr_t arg); static struct cmdarg_s *nsh_cloneargs(FAR struct nsh_vtbl_s *vtbl, - int fd, int argc, FAR char *argv[]); + int fd_in, int fd_out, int argc, FAR char *argv[]); #endif static int nsh_saveresult(FAR struct nsh_vtbl_s *vtbl, bool result); static int nsh_execute(FAR struct nsh_vtbl_s *vtbl, - int argc, FAR char *argv[], FAR const char *redirfile, - int oflags); + int argc, FAR char *argv[], FAR const char *redirfile_in, + FAR const char *redirfile_out, int oflags); #ifdef CONFIG_NSH_CMDPARMS static FAR char *nsh_filecat(FAR struct nsh_vtbl_s *vtbl, FAR char *s1, @@ -258,7 +259,7 @@ static int nsh_nice(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd, #ifdef CONFIG_NSH_CMDPARMS static int nsh_parse_cmdparm(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline, - FAR const char *redirfile); + FAR const char *redirfile_out); #endif static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline); @@ -275,8 +276,9 @@ static const char g_line_separator[] = "\"'#;\n"; #ifdef CONFIG_NSH_ARGCAT static const char g_arg_separator[] = "`$"; #endif -static const char g_redirect1[] = ">"; -static const char g_redirect2[] = ">>"; +static const char g_redirect_out1[] = ">"; +static const char g_redirect_out2[] = ">>"; +static const char g_redirect_in1[] = "<"; #ifdef NSH_HAVE_VARS static const char g_exitstatus[] = "?"; static const char g_success[] = "0"; @@ -454,9 +456,16 @@ static void nsh_releaseargs(struct cmdarg_s *arg) * the file descriptor */ - if (vtbl->np.np_redirect) + if (vtbl->np.np_redir_out) { - close(arg->fd); + close(arg->fd_out); + } + + /* Same for the input */ + + if (vtbl->np.np_redir_in) + { + close(arg->fd_in); } /* Released the cloned vtbl instance */ @@ -504,7 +513,8 @@ static pthread_addr_t nsh_child(pthread_addr_t arg) #ifndef CONFIG_NSH_DISABLEBG static struct cmdarg_s *nsh_cloneargs(FAR struct nsh_vtbl_s *vtbl, - int fd, int argc, FAR char *argv[]) + int fd_in, int fd_out, int argc, + FAR char *argv[]) { struct cmdarg_s *ret = (struct cmdarg_s *)zalloc(sizeof(struct cmdarg_s)); int i; @@ -512,7 +522,8 @@ static struct cmdarg_s *nsh_cloneargs(FAR struct nsh_vtbl_s *vtbl, if (ret) { ret->vtbl = vtbl; - ret->fd = fd; + ret->fd_in = fd_in; + ret->fd_out = fd_out; ret->argc = argc; for (i = 0; i < argc; i++) @@ -594,9 +605,11 @@ static int nsh_saveresult(FAR struct nsh_vtbl_s *vtbl, bool result) static int nsh_execute(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char *argv[], - FAR const char *redirfile, int oflags) + FAR const char *redirfile_in, + FAR const char *redirfile_out, int oflags) { - int fd = -1; + int fd_in = STDIN_FILENO; + int fd_out = STDOUT_FILENO; int ret; /* DO NOT CHANGE THE ORDERING OF THE FOLLOWING STEPS @@ -634,7 +647,8 @@ static int nsh_execute(FAR struct nsh_vtbl_s *vtbl, */ #ifdef CONFIG_NSH_BUILTIN_APPS - ret = nsh_builtin(vtbl, argv[0], argv, redirfile, oflags); + ret = nsh_builtin(vtbl, argv[0], argv, redirfile_in, redirfile_out, + oflags); if (ret >= 0) { /* nsh_builtin() returned 0 or 1. This means that the built-in @@ -671,7 +685,7 @@ static int nsh_execute(FAR struct nsh_vtbl_s *vtbl, */ #ifdef CONFIG_NSH_FILE_APPS - ret = nsh_fileapp(vtbl, argv[0], argv, redirfile, oflags); + ret = nsh_fileapp(vtbl, argv[0], argv, redirfile_out, oflags); if (ret >= 0) { /* nsh_fileapp() returned 0 or 1. This means that the built-in @@ -692,7 +706,7 @@ static int nsh_execute(FAR struct nsh_vtbl_s *vtbl, /* Redirected output? */ - if (vtbl->np.np_redirect) + if (vtbl->np.np_redir_out) { /* Open the redirection file. This file will eventually * be closed by a call to either nsh_release (if the command @@ -700,8 +714,26 @@ static int nsh_execute(FAR struct nsh_vtbl_s *vtbl, * command is executed in the foreground. */ - fd = open(redirfile, oflags, 0666); - if (fd < 0) + fd_out = open(redirfile_out, oflags, 0666); + if (fd_out < 0) + { + nsh_error(vtbl, g_fmtcmdfailed, argv[0], "open", NSH_ERRNO); + goto errout; + } + } + + /* Redirected input? */ + + if (vtbl->np.np_redir_in) + { + /* Open the redirection file. This file will eventually + * be closed by a call to either nsh_release (if the command + * is executed in the background) or by nsh_undirect if the + * command is executed in the foreground. + */ + + fd_in = open(redirfile_in, O_RDONLY, 0); + if (fd_in < 0) { nsh_error(vtbl, g_fmtcmdfailed, argv[0], "open", NSH_ERRNO); goto errout; @@ -735,7 +767,7 @@ static int nsh_execute(FAR struct nsh_vtbl_s *vtbl, /* Create a container for the command arguments */ - args = nsh_cloneargs(bkgvtbl, fd, argc, argv); + args = nsh_cloneargs(bkgvtbl, fd_in, fd_out, argc, argv); if (!args) { nsh_release(bkgvtbl); @@ -744,9 +776,9 @@ static int nsh_execute(FAR struct nsh_vtbl_s *vtbl, /* Handle redirection of output via a file descriptor */ - if (vtbl->np.np_redirect) + if (vtbl->np.np_redir_out || vtbl->np.np_redir_in) { - nsh_redirect(bkgvtbl, fd, NULL); + nsh_redirect(bkgvtbl, fd_in, fd_out, NULL); } /* Get the execution priority of this task */ @@ -824,11 +856,11 @@ static int nsh_execute(FAR struct nsh_vtbl_s *vtbl, { uint8_t save[SAVE_SIZE]; - /* Handle redirection of output via a file descriptor */ + /* Handle redirection of stdin/stdout file descriptor */ - if (vtbl->np.np_redirect) + if (vtbl->np.np_redir_out || vtbl->np.np_redir_in) { - nsh_redirect(vtbl, fd, save); + nsh_redirect(vtbl, fd_in, fd_out, save); } /* Then execute the command in "foreground" -- i.e., while the user @@ -844,7 +876,7 @@ static int nsh_execute(FAR struct nsh_vtbl_s *vtbl, * file descriptor. */ - if (vtbl->np.np_redirect) + if (vtbl->np.np_redir_out || vtbl->np.np_redir_in) { nsh_undirect(vtbl, save); } @@ -867,9 +899,14 @@ static int nsh_execute(FAR struct nsh_vtbl_s *vtbl, #ifndef CONFIG_NSH_DISABLEBG errout_with_redirect: - if (vtbl->np.np_redirect) + if (vtbl->np.np_redir_out) { - close(fd); + close(fd_out); + } + + if (vtbl->np.np_redir_in) + { + close(fd_in); } #endif @@ -1719,15 +1756,23 @@ static FAR char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, if (*(pbegin + 1) == '>') { *saveptr = pbegin + 2; - argument = (FAR char *)g_redirect2; + argument = (FAR char *)g_redirect_out2; } else { *saveptr = pbegin + 1; - argument = (FAR char *)g_redirect1; + argument = (FAR char *)g_redirect_out1; } } + /* Does the token begin with '<' -- redirection of input? */ + + if (*pbegin == '<') + { + *saveptr = pbegin + 1; + argument = (FAR char *)g_redirect_in1; + } + /* Does the token begin with '#' -- comment */ else if (*pbegin == '#') @@ -2411,7 +2456,7 @@ static int nsh_nice(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd, #ifdef CONFIG_NSH_CMDPARMS static int nsh_parse_cmdparm(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline, - FAR const char *redirfile) + FAR const char *redirfile_out) { NSH_MEMLIST_TYPE memlist; NSH_ALIASLIST_TYPE alist; @@ -2447,8 +2492,8 @@ static int nsh_parse_cmdparm(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline, /* Output is always redirected. Remember the current redirection state */ - redirsave = vtbl->np.np_redirect; - vtbl->np.np_redirect = true; + redirsave = vtbl->np.np_redir_out; + vtbl->np.np_redir_out = true; /* Parse out the command at the beginning of the line */ @@ -2506,7 +2551,7 @@ static int nsh_parse_cmdparm(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline, /* Then execute the command */ - ret = nsh_execute(vtbl, argc, argv, redirfile, + ret = nsh_execute(vtbl, argc, argv, NULL, redirfile_out, O_WRONLY | O_CREAT | O_TRUNC); /* Restore the backgrounding and redirection state */ @@ -2515,7 +2560,7 @@ exit: #ifndef CONFIG_NSH_DISABLEBG vtbl->np.np_bg = bgsave; #endif - vtbl->np.np_redirect = redirsave; + vtbl->np.np_redir_out = redirsave; NSH_ALIASLIST_FREE(vtbl, &alist); NSH_MEMLIST_FREE(&memlist); @@ -2538,11 +2583,16 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline) FAR char *argv[MAX_ARGV_ENTRIES]; FAR char *saveptr; FAR char *cmd; - FAR char *redirfile = NULL; + FAR char *redirfile_out = NULL; + FAR char *redirfile_in = NULL; int oflags = 0; int argc; int ret; - bool redirect_save = false; + bool redirect_out_save = false; + bool redirect_in_save = false; + size_t redirect_out1_len = strlen(g_redirect_out1); + size_t redirect_out2_len = strlen(g_redirect_out2); + size_t redirect_in1_len = strlen(g_redirect_in1); /* Initialize parser state */ @@ -2554,7 +2604,8 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline) vtbl->np.np_bg = false; #endif - vtbl->np.np_redirect = false; + vtbl->np.np_redir_out = false; + vtbl->np.np_redir_in = false; /* Parse out the command at the beginning of the line */ @@ -2619,16 +2670,13 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline) * argv[0]: The command name. * argv[1]: The beginning of argument (up to * CONFIG_NSH_MAXARGUMENTS) - * argv[argc-3]: Possibly '>' or '>>' - * argv[argc-2]: Possibly - * argv[argc-1]: Possibly '&' * argv[argc]: NULL terminating pointer * * Maximum size is CONFIG_NSH_MAXARGUMENTS+5 */ argv[0] = cmd; - for (argc = 1; argc < MAX_ARGV_ENTRIES - 1; argc++) + for (argc = 1; argc < MAX_ARGV_ENTRIES - 1; ) { int isenvvar = 0; /* flag for if an environment variable gets expanded */ @@ -2685,6 +2733,81 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline) argv[argc] = pbegin; } } + else if (!strncmp(argv[argc], g_redirect_out2, redirect_out2_len)) + { + FAR char *arg; + if (argv[argc][redirect_out2_len]) + { + arg = &argv[argc][redirect_out2_len]; + } + else + { + arg = nsh_argument(vtbl, &saveptr, &memlist, NULL, &isenvvar); + } + + if (!arg) + { + nsh_error(vtbl, g_fmtarginvalid, cmd); + ret = ERROR; + goto dynlist_free; + } + + redirect_out_save = vtbl->np.np_redir_out; + vtbl->np.np_redir_out = true; + oflags = O_WRONLY | O_CREAT | O_APPEND; + redirfile_out = nsh_getfullpath(vtbl, arg); + } + else if (!strncmp(argv[argc], g_redirect_out1, redirect_out1_len)) + { + FAR char *arg; + if (argv[argc][redirect_out1_len]) + { + arg = &argv[argc][redirect_out1_len]; + } + else + { + arg = nsh_argument(vtbl, &saveptr, &memlist, NULL, &isenvvar); + } + + if (!arg) + { + nsh_error(vtbl, g_fmtarginvalid, cmd); + ret = ERROR; + goto dynlist_free; + } + + redirect_out_save = vtbl->np.np_redir_out; + vtbl->np.np_redir_out = true; + oflags = O_WRONLY | O_CREAT | O_TRUNC; + redirfile_out = nsh_getfullpath(vtbl, arg); + } + else if (!strncmp(argv[argc], g_redirect_in1, redirect_in1_len)) + { + FAR char *arg; + if (argv[argc][redirect_in1_len]) + { + arg = &argv[argc][redirect_in1_len]; + } + else + { + arg = nsh_argument(vtbl, &saveptr, &memlist, NULL, &isenvvar); + } + + if (!arg) + { + nsh_error(vtbl, g_fmtarginvalid, cmd); + ret = ERROR; + goto dynlist_free; + } + + redirect_in_save = vtbl->np.np_redir_in; + vtbl->np.np_redir_in = true; + redirfile_in = nsh_getfullpath(vtbl, arg); + } + else + { + argc++; + } } /* Check if the command should run in background */ @@ -2697,33 +2820,6 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline) } #endif - /* Check if the output was re-directed using > or >> */ - - if (argc > 2) - { - /* Check for redirection to a new file */ - - if (strcmp(argv[argc - 2], g_redirect1) == 0) - { - redirect_save = vtbl->np.np_redirect; - vtbl->np.np_redirect = true; - oflags = O_WRONLY | O_CREAT | O_TRUNC; - redirfile = nsh_getfullpath(vtbl, argv[argc - 1]); - argc -= 2; - } - - /* Check for redirection by appending to an existing file */ - - else if (strcmp(argv[argc - 2], g_redirect2) == 0) - { - redirect_save = vtbl->np.np_redirect; - vtbl->np.np_redirect = true; - oflags = O_WRONLY | O_CREAT | O_APPEND; - redirfile = nsh_getfullpath(vtbl, argv[argc - 1]); - argc -= 2; - } - } - /* Last argument vector must be empty */ argv[argc] = NULL; @@ -2737,16 +2833,22 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline) /* Then execute the command */ - ret = nsh_execute(vtbl, argc, argv, redirfile, oflags); + ret = nsh_execute(vtbl, argc, argv, redirfile_in, redirfile_out, oflags); /* Free any allocated resources */ /* Free the redirected output file path */ - if (redirfile) + if (redirfile_out) { - nsh_freefullpath(redirfile); - vtbl->np.np_redirect = redirect_save; + nsh_freefullpath(redirfile_out); + vtbl->np.np_redir_out = redirect_out_save; + } + + if (redirfile_in) + { + nsh_freefullpath(redirfile_in); + vtbl->np.np_redir_out = redirect_in_save; } dynlist_free: diff --git a/nshlib/nsh_script.c b/nshlib/nsh_script.c index e49ac1951..e9a89af5b 100644 --- a/nshlib/nsh_script.c +++ b/nshlib/nsh_script.c @@ -54,7 +54,7 @@ static int nsh_script_redirect(FAR struct nsh_vtbl_s *vtbl, fd = open(CONFIG_NSH_SCRIPT_REDIRECT_PATH, 0666); if (fd > 0) { - nsh_redirect(vtbl, fd, save); + nsh_redirect(vtbl, 0, fd, save); } } diff --git a/nshlib/nsh_timcmds.c b/nshlib/nsh_timcmds.c index 08d9716d4..a806e97a9 100644 --- a/nshlib/nsh_timcmds.c +++ b/nshlib/nsh_timcmds.c @@ -298,7 +298,8 @@ int cmd_time(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) #ifndef CONFIG_NSH_DISABLEBG bool bgsave; #endif - bool redirsave; + bool redirsave_out; + bool redirsave_in; int ret; /* Get the current time */ @@ -315,7 +316,8 @@ int cmd_time(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) #ifndef CONFIG_NSH_DISABLEBG bgsave = vtbl->np.np_bg; #endif - redirsave = vtbl->np.np_redirect; + redirsave_out = vtbl->np.np_redir_out; + redirsave_in = vtbl->np.np_redir_in; /* Execute the command */ @@ -354,7 +356,8 @@ int cmd_time(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv) #ifndef CONFIG_NSH_DISABLEBG vtbl->np.np_bg = bgsave; #endif - vtbl->np.np_redirect = redirsave; + vtbl->np.np_redir_out = redirsave_out; + vtbl->np.np_redir_out = redirsave_in; return ret; } diff --git a/testing/cmocka/cmocka_main.c b/testing/cmocka/cmocka_main.c index 40c8b45d9..c5d171efb 100644 --- a/testing/cmocka/cmocka_main.c +++ b/testing/cmocka/cmocka_main.c @@ -182,7 +182,7 @@ int main(int argc, FAR char *argv[]) } bypass[0] = (FAR char *)builtin->name; - ret = exec_builtin(builtin->name, bypass, NULL, 0); + ret = exec_builtin(builtin->name, bypass, NULL, NULL, 0); if (ret >= 0) { waitpid(ret, &ret, WUNTRACED);