in nshlib/nsh_parse.c [2442:2855]
static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline)
{
struct nsh_param_s param =
{
.fd_in = -1,
.fd_out = -1,
.oflags_in = 0,
.oflags_out = 0,
.file_in = NULL,
.file_out = NULL
};
#ifdef CONFIG_NSH_PIPELINE
int pipefd[2] =
{
-1, -1
};
#endif
NSH_MEMLIST_TYPE memlist;
NSH_ALIASLIST_TYPE alist;
FAR char *argv[MAX_ARGV_ENTRIES];
FAR char *saveptr;
FAR char *cmd;
int argc;
int ret;
bool redirect_out_save = false;
bool redirect_in_save = false;
#ifdef CONFIG_NSH_PIPELINE
bool bg_save = false;
#endif
#ifdef CONFIG_SCHED_INSTRUMENTATION_DUMP
char tracebuf[LINE_MAX + 1];
strlcpy(tracebuf, cmdline, sizeof(tracebuf));
sched_note_beginex(NOTE_TAG_APP, tracebuf);
#endif
/* Initialize parser state */
memset(argv, 0, MAX_ARGV_ENTRIES*sizeof(FAR char *));
NSH_MEMLIST_INIT(memlist);
NSH_ALIASLIST_INIT(alist);
#ifndef CONFIG_NSH_DISABLEBG
vtbl->np.np_bg = false;
#endif
vtbl->np.np_redir_out = false;
vtbl->np.np_redir_in = false;
/* Parse out the command at the beginning of the line */
saveptr = cmdline;
cmd = nsh_argument(vtbl, &saveptr, &memlist, &alist, NULL);
#ifndef CONFIG_NSH_DISABLESCRIPT
#ifndef CONFIG_NSH_DISABLE_LOOPS
/* Handle while-do-done and until-do-done loops */
if (nsh_loop(vtbl, &cmd, &saveptr, &memlist, &alist) != 0)
{
ret = nsh_saveresult(vtbl, true);
goto dynlist_free;
}
#endif
#ifndef CONFIG_NSH_DISABLE_ITEF
/* Handle if-then-else-fi */
if (nsh_itef(vtbl, &cmd, &saveptr, &memlist, &alist) != 0)
{
ret = nsh_saveresult(vtbl, true);
goto dynlist_free;
}
#endif
#endif
/* Handle nice */
#ifndef CONFIG_NSH_DISABLEBG
if (nsh_nice(vtbl, &cmd, &saveptr, &memlist, &alist) != 0)
{
ret = nsh_saveresult(vtbl, true);
goto dynlist_free;
}
#endif
/* Check if any command was provided -OR- if command processing is
* currently disabled.
*/
#ifndef CONFIG_NSH_DISABLESCRIPT
if (!cmd || !nsh_cmdenabled(vtbl))
#else
if (!cmd)
#endif
{
/* An empty line is not an error and an unprocessed command cannot
* generate an error, but neither should it change the last command
* status.
*/
ret = OK;
goto dynlist_free;
}
/* Parse all of the arguments following the command name. The form
* of argv is:
*
* argv[0]: The command name.
* argv[1]: The beginning of argument (up to
* CONFIG_NSH_MAXARGUMENTS)
* argv[argc]: NULL terminating pointer
*
* Maximum size is CONFIG_NSH_MAXARGUMENTS+5
*/
argv[0] = cmd;
for (argc = 1; argc < MAX_ARGV_ENTRIES - 1; )
{
int isenvvar = 0; /* flag for if an environment variable gets expanded */
argv[argc] = nsh_argument(vtbl, &saveptr, &memlist, NULL, &isenvvar);
if (!argv[argc])
{
break;
}
if (isenvvar != 0)
{
while (argc < MAX_ARGV_ENTRIES - 1)
{
FAR char *pbegin = argv[argc];
/* Find the end of the current token */
for (; *pbegin && !strchr(g_token_separator, *pbegin);
pbegin++)
{
}
/* If end of string, we've processed the last token and we're
* done.
*/
if ('\0' == *pbegin)
{
break;
}
/* Terminate the token to complete the argv variable */
*pbegin = '\0';
/* We've inserted an extra parameter, so bump the count */
argc++;
/* Move to the next character in the string of tokens */
pbegin++;
/* Throw away any extra separator chars between tokens */
for (; *pbegin && strchr(g_token_separator, *pbegin) != NULL;
pbegin++)
{
}
/* Prepare to loop again on the next argument token */
argv[argc] = pbegin;
}
}
if (!strncmp(argv[argc], g_redirect_out2, g_redirect_out2_len))
{
FAR char *arg;
if (argv[argc][g_redirect_out2_len])
{
arg = &argv[argc][g_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;
param.oflags_out = O_WRONLY | O_CREAT | O_APPEND;
param.file_out = nsh_getfullpath(vtbl, arg);
}
else if (!strncmp(argv[argc], g_redirect_out1, g_redirect_out1_len))
{
FAR char *arg;
if (argv[argc][g_redirect_out1_len])
{
arg = &argv[argc][g_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;
param.oflags_out = O_WRONLY | O_CREAT | O_TRUNC;
param.file_out = nsh_getfullpath(vtbl, arg);
}
else if (!strncmp(argv[argc], g_redirect_in1, g_redirect_in1_len))
{
FAR char *arg;
if (argv[argc][g_redirect_in1_len])
{
arg = &argv[argc][g_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;
param.oflags_in = O_RDONLY;
param.file_in = nsh_getfullpath(vtbl, arg);
}
#ifdef CONFIG_NSH_PIPELINE
else if (!strncmp(argv[argc], g_pipeline1, g_pipeline1_len))
{
FAR char *arg;
FAR char *sh_argv[4];
FAR char *sh_arg2;
if (argv[argc][g_pipeline1_len])
{
arg = &argv[argc][g_pipeline1_len];
}
else
{
arg = nsh_argument(vtbl, &saveptr, &memlist, NULL, &isenvvar);
}
if (!arg)
{
nsh_error(vtbl, g_fmtarginvalid, cmd);
ret = ERROR;
goto dynlist_free;
}
sh_arg2 = lib_get_tempbuffer(LINE_MAX);
if (sh_arg2 == NULL)
{
nsh_error(vtbl, g_fmtcmdoutofmemory, cmd);
ret = -errno;
goto dynlist_free;
}
sh_arg2[0] = '\0';
for (ret = 0; ret < argc; ret++)
{
strlcat(sh_arg2, argv[ret], LINE_MAX);
if (ret < argc - 1)
{
strcat(sh_arg2, " ");
}
}
sh_argv[0] = "sh";
sh_argv[1] = "-c";
sh_argv[2] = sh_arg2;
sh_argv[3] = NULL;
ret = pipe2(pipefd, 0);
if (ret < 0)
{
lib_put_tempbuffer(sh_arg2);
ret = -errno;
goto dynlist_free;
}
redirect_out_save = vtbl->np.np_redir_out;
vtbl->np.np_redir_out = true;
param.fd_out = pipefd[1];
bg_save = vtbl->np.np_bg;
vtbl->np.np_bg = true;
ret = nsh_execute(vtbl, 4, sh_argv, ¶m);
lib_put_tempbuffer(sh_arg2);
vtbl->np.np_bg = bg_save;
if (param.fd_in != -1)
{
close(param.fd_in);
vtbl->np.np_redir_in = redirect_in_save;
}
close(param.fd_out);
param.fd_out = -1;
vtbl->np.np_redir_out = redirect_out_save;
redirect_in_save = vtbl->np.np_redir_in;
vtbl->np.np_redir_in = true;
param.fd_in = pipefd[0];
argv[0] = arg;
argc = 1;
if (ret == -1)
{
goto dynlist_free;
}
}
#endif
else
{
argc++;
}
}
/* Check if the command should run in background */
#ifndef CONFIG_NSH_DISABLEBG
if (argc > 1 && strcmp(argv[argc - 1], "&") == 0)
{
vtbl->np.np_bg = true;
argc--;
}
#endif
/* Last argument vector must be empty */
argv[argc] = NULL;
/* Check if the maximum number of arguments was exceeded */
if (argc > CONFIG_NSH_MAXARGUMENTS)
{
nsh_error(vtbl, g_fmttoomanyargs, cmd);
}
/* Then execute the command */
ret = nsh_execute(vtbl, argc, argv, ¶m);
dynlist_free:
/* Free any allocated resources */
/* Free the redirected output file path */
if (param.file_out)
{
nsh_freefullpath((char *)param.file_out);
vtbl->np.np_redir_out = redirect_out_save;
}
#ifdef CONFIG_NSH_PIPELINE
else if (param.fd_out != -1)
{
close(param.fd_out);
vtbl->np.np_redir_out = redirect_out_save;
}
#endif
/* Free the redirected input file path */
if (param.file_in)
{
nsh_freefullpath((char *)param.file_in);
vtbl->np.np_redir_in = redirect_in_save;
}
#ifdef CONFIG_NSH_PIPELINE
else if (param.fd_in != -1)
{
close(param.fd_in);
vtbl->np.np_redir_in = redirect_in_save;
}
#endif
NSH_ALIASLIST_FREE(vtbl, &alist);
NSH_MEMLIST_FREE(&memlist);
#ifdef CONFIG_SCHED_INSTRUMENTATION_DUMP
sched_note_endex(NOTE_TAG_APP, tracebuf);
#endif
return ret;
}