* apps/nshlib/nsh_parse.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <debug.h>
#include <sched.h>
#include <unistd.h>
#include <assert.h>
#ifdef CONFIG_NSH_CMDPARMS
# include <sys/stat.h>
#endif
#include <nuttx/version.h>
#include <nuttx/trace.h>
#include "nsh.h"
#include "nsh_console.h"
#include "nshlib/nshlib.h"
* Pre-processor Definitions
****************************************************************************/
* retain a list of memory allocations to be freed at the completion of
* command processing.
*/
#undef HAVE_MEMLIST
#if defined(CONFIG_NSH_CMDPARMS) || defined(CONFIG_NSH_ALIAS) || \
defined(CONFIG_NSH_ARGCAT)
# define HAVE_MEMLIST 1
#endif
* memory, in case the alias has arguments and is set like:
*
* $ alias ls='ls -l'
*
* In this case the command verb and the arguments need to be separated, much
* like the argument separation is done with environment variable expansion.
*
* This needs a new working buffer in order to keep the original alias string
* intact.
*/
#ifdef CONFIG_NSH_ALIAS
# define ALIAS_ALLOCS 2
#else
# define ALIAS_ALLOCS 0
#endif
#if defined(HAVE_MEMLIST) && !defined(CONFIG_NSH_MAXALLOCS)
# ifdef CONFIG_NSH_ARGCAT
# define CONFIG_NSH_MAXALLOCS ((2*CONFIG_NSH_MAXARGUMENTS) + ALIAS_ALLOCS)
# else
# define CONFIG_NSH_MAXALLOCS (CONFIG_NSH_MAXARGUMENTS + ALIAS_ALLOCS)
# endif
#endif
#ifdef HAVE_MEMLIST
# define NSH_MEMLIST_TYPE struct nsh_memlist_s
# define NSH_MEMLIST_INIT(m) memset(&(m), 0, sizeof(struct nsh_memlist_s));
# define NSH_MEMLIST_ADD(m,a) nsh_memlist_add(m,a)
# define NSH_MEMLIST_FREE(m) nsh_memlist_free(m)
#else
# define NSH_MEMLIST_TYPE uint8_t
# define NSH_MEMLIST_INIT(m) do { (m) = 0; } while (0)
# define NSH_MEMLIST_ADD(m,a)
# define NSH_MEMLIST_FREE(m)
#endif
#undef NEED_NULLSTRING
#if defined(NSH_HAVE_VARS) || defined(CONFIG_NSH_CMDPARMS)
# define NEED_NULLSTRING 1
#elif !defined(CONFIG_NSH_ARGCAT) || !defined(HAVE_MEMLIST)
# define NEED_NULLSTRING 1
#endif
#ifdef CONFIG_NSH_ALIAS
# define NSH_ALIASLIST_TYPE struct nsh_alist_s
# define NSH_ALIASLIST_INIT(l) memset(&(l), 0, sizeof(struct nsh_alist_s))
# define NSH_ALIASLIST_ADD(l, a) nsh_alist_add((l), (a))
# define NSH_ALIASLIST_FREE(v, l) nsh_alist_free((v), (l))
#else
# define NSH_ALIASLIST_TYPE uint8_t
# define NSH_ALIASLIST_INIT(l) do { (l) = 0; } while (0)
# define NSH_ALIASLIST_FREE(v, l)
#endif
* Private Types
****************************************************************************/
#ifdef HAVE_MEMLIST
struct nsh_memlist_s
{
int nallocs;
FAR char *allocations[CONFIG_NSH_MAXALLOCS];
};
#endif
#ifdef CONFIG_NSH_ALIAS
struct nsh_alist_s
{
int nallocs;
FAR struct nsh_alias_s *allocs[CONFIG_NSH_ALIAS_MAX_AMOUNT];
};
#endif
* Private Function Prototypes
****************************************************************************/
#ifdef HAVE_MEMLIST
static void nsh_memlist_add(FAR struct nsh_memlist_s *memlist,
FAR char *allocation);
static void nsh_memlist_free(FAR struct nsh_memlist_s *memlist);
#endif
#ifdef CONFIG_NSH_ALIAS
static void nsh_alist_add(FAR struct nsh_alist_s *alist,
FAR struct nsh_alias_s *alias);
static void nsh_alist_free(FAR struct nsh_vtbl_s *vtbl,
FAR struct nsh_alist_s *alist);
#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 struct nsh_param_s *param);
#ifdef CONFIG_NSH_CMDPARMS
static FAR char *nsh_filecat(FAR struct nsh_vtbl_s *vtbl, FAR char *s1,
FAR const char *filename);
static FAR char *nsh_cmdparm(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
FAR char **allocation);
#endif
#if defined(CONFIG_NSH_ARGCAT) || defined(CONFIG_NSH_ALIAS)
static FAR char *nsh_strcat(FAR struct nsh_vtbl_s *vtbl, FAR char *s1,
FAR const char *s2);
#endif
#if defined(CONFIG_NSH_QUOTE) && defined(CONFIG_NSH_ARGCAT)
static FAR char *nsh_strchr(FAR const char *str, int ch);
#else
# define nsh_strchr(s,c) strchr(s,c)
#endif
#ifdef CONFIG_NSH_ALIAS
static FAR char *nsh_aliasexpand(FAR struct nsh_vtbl_s *vtbl,
FAR char *cmdline, FAR char **saveptr,
FAR NSH_MEMLIST_TYPE *memlist,
FAR NSH_ALIASLIST_TYPE *alist);
#endif
#ifdef NSH_HAVE_VARS
static FAR char *nsh_envexpand(FAR struct nsh_vtbl_s *vtbl,
FAR char *varname);
#endif
#if defined(CONFIG_NSH_QUOTE)
static void nsh_dequote(FAR char *cmdline);
#else
# define nsh_dequote(c)
#endif
static FAR char *nsh_rmquotes(FAR char *qbegin, FAR char *qend);
static FAR char *nsh_argexpand(FAR struct nsh_vtbl_s *vtbl,
FAR char *cmdline, FAR char **allocation, FAR int *isenvvar);
static FAR char *nsh_argument(FAR struct nsh_vtbl_s *vtbl,
FAR char **saveptr,
FAR NSH_MEMLIST_TYPE *memlist,
FAR NSH_ALIASLIST_TYPE *alist,
FAR int *isenvvar);
#ifndef CONFIG_NSH_DISABLESCRIPT
#ifndef CONFIG_NSH_DISABLE_LOOPS
static bool nsh_loop_enabled(FAR struct nsh_vtbl_s *vtbl);
#endif
#ifndef CONFIG_NSH_DISABLE_ITEF
static bool nsh_itef_enabled(FAR struct nsh_vtbl_s *vtbl);
#endif
static bool nsh_cmdenabled(FAR struct nsh_vtbl_s *vtbl);
#ifndef CONFIG_NSH_DISABLE_LOOPS
static int nsh_loop(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist,
FAR NSH_ALIASLIST_TYPE *alist);
#endif
#ifndef CONFIG_NSH_DISABLE_ITEF
static int nsh_itef(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist,
FAR NSH_ALIASLIST_TYPE *alist);
#endif
#endif
#ifndef CONFIG_NSH_DISABLEBG
static int nsh_nice(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist,
FAR NSH_ALIASLIST_TYPE *alist);
#endif
#ifndef CONFIG_NSH_DISABLE_PRLIMIT
static int nsh_prlimit(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist,
FAR NSH_ALIASLIST_TYPE *alist,
FAR struct nsh_param_s *param);
#endif
#ifdef CONFIG_NSH_CMDPARMS
static int nsh_parse_cmdparm(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
FAR const struct nsh_param_s *param);
#endif
static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
FAR struct nsh_param_s *param);
* Private Data
****************************************************************************/
static const char g_token_separator[] = " \t\n";
static const char g_quote_separator[] = "'\"`";
#ifndef NSH_DISABLE_SEMICOLON
static const char g_line_separator[] = "\"'#;\n";
#endif
#ifdef CONFIG_NSH_ARGCAT
static const char g_arg_separator[] = "`$";
#endif
static const char g_redirect_out1[] = ">";
static const size_t g_redirect_out1_len = sizeof(g_redirect_out1) - 1;
static const char g_redirect_out2[] = ">>";
static const size_t g_redirect_out2_len = sizeof(g_redirect_out2) - 1;
static const char g_redirect_in1[] = "<";
static const size_t g_redirect_in1_len = sizeof(g_redirect_in1) - 1;
static const char g_redirect_err1[] = "2>";
static const size_t g_redirect_err1_len = sizeof(g_redirect_err1) - 1;
static const char g_redirect_err2[] = "2>>";
static const size_t g_redirect_err2_len = sizeof(g_redirect_err2) - 1;
static const char g_redirect_err3[] = "2>&1";
static const size_t g_redirect_err3_len = sizeof(g_redirect_err3) - 1;
#ifdef CONFIG_NSH_PIPELINE
static const char g_pipeline1[] = "|";
static const size_t g_pipeline1_len = sizeof(g_pipeline1) - 1;
#endif
#ifdef NSH_HAVE_VARS
static const char g_exitstatus[] = "?";
static const char g_lastpid[] = "!";
static const char g_success[] = "0";
static const char g_failure[] = "1";
#endif
#ifdef NEED_NULLSTRING
static const char g_nullstring[] = "";
#endif
* Public Data
****************************************************************************/
* in the NSH greeting.
*/
#if CONFIG_VERSION_MAJOR != 0 || CONFIG_VERSION_MINOR != 0
const char g_nshgreeting[] =
"\nNuttShell (NSH) NuttX-" CONFIG_VERSION_STRING "\n";
#else
const char g_nshgreeting[] = "\nNuttShell (NSH)\n";
#endif
#if defined(CONFIG_NSH_MOTD) && !defined(CONFIG_NSH_PLATFORM_MOTD)
const char g_nshmotd[] = CONFIG_NSH_MOTD_STRING;
#endif
#ifdef CONFIG_NSH_LOGIN
#if defined(CONFIG_NSH_TELNET_LOGIN) && defined(CONFIG_NSH_TELNET)
const char g_telnetgreeting[] =
"\nWelcome to NuttShell(NSH) Telnet Server...\n";
#endif
const char g_userprompt[] = "login: ";
const char g_passwordprompt[] = "password: ";
const char g_loginsuccess[] = "\nUser Logged-in!\n";
const char g_badcredentials[] = "\nInvalid username or password\n";
const char g_loginfailure[] = "Login failed!\n";
#endif
const char g_fmtsyntax[] = "nsh: %s: syntax error\n";
const char g_fmtargrequired[] = "nsh: %s: missing required argument(s)\n";
const char g_fmtnomatching[] = "nsh: %s: no matching %s\n";
const char g_fmtarginvalid[] = "nsh: %s: argument invalid\n";
const char g_fmtargrange[] = "nsh: %s: value out of range\n";
const char g_fmtcmdnotfound[] = "nsh: %s: command not found\n";
const char g_fmtnosuch[] = "nsh: %s: no such %s: %s\n";
const char g_fmttoomanyargs[] = "nsh: %s: too many arguments\n";
const char g_fmtdeepnesting[] = "nsh: %s: nesting too deep\n";
const char g_fmtcontext[] = "nsh: %s: not valid in this context\n";
#ifdef CONFIG_NSH_STRERROR
const char g_fmtcmdfailed[] = "nsh: %s: %s failed: %s\n";
#else
const char g_fmtcmdfailed[] = "nsh: %s: %s failed: %d\n";
#endif
const char g_fmtcmdoutofmemory[] = "nsh: %s: out of memory\n";
const char g_fmtinternalerror[] = "nsh: %s: Internal error\n";
const char g_fmtsignalrecvd[] = "nsh: %s: Interrupted by signal\n";
* Private Functions
****************************************************************************/
* Name: nsh_memlist_add
****************************************************************************/
#ifdef HAVE_MEMLIST
static void nsh_memlist_add(FAR struct nsh_memlist_s *memlist,
FAR char *allocation)
{
if (memlist && allocation)
{
int index = memlist->nallocs;
if (index < CONFIG_NSH_MAXALLOCS)
{
memlist->allocations[index] = allocation;
memlist->nallocs = index + 1;
}
}
}
#endif
* Name: nsh_memlist_free
****************************************************************************/
#ifdef HAVE_MEMLIST
static void nsh_memlist_free(FAR struct nsh_memlist_s *memlist)
{
if (memlist)
{
int index;
for (index = 0; index < memlist->nallocs; index++)
{
free(memlist->allocations[index]);
memlist->allocations[index] = NULL;
}
memlist->nallocs = 0;
}
}
#endif
* Name: nsh_alist_add
****************************************************************************/
#ifdef CONFIG_NSH_ALIAS
static void nsh_alist_add(FAR struct nsh_alist_s *alist,
FAR struct nsh_alias_s *alias)
{
if (alist && alias)
{
int index = alist->nallocs;
if (index < CONFIG_NSH_ALIAS_MAX_AMOUNT)
{
alias->exp = 1;
alist->allocs[index] = alias;
alist->nallocs = index + 1;
}
}
}
#endif
* Name: nsh_alist_free
****************************************************************************/
#ifdef CONFIG_NSH_ALIAS
static void nsh_alist_free(FAR struct nsh_vtbl_s *vtbl,
FAR struct nsh_alist_s *alist)
{
if (vtbl && alist)
{
FAR struct nsh_alias_s *alias;
int index;
for (index = 0; index < alist->nallocs; index++)
{
alias = alist->allocs[index];
alias->exp = 0;
if (alias->rem == 1)
{
nsh_aliasfree(vtbl, alias);
}
alist->allocs[index] = NULL;
}
alist->nallocs = 0;
}
}
#endif
* Name: nsh_saveresult
****************************************************************************/
static int nsh_saveresult(FAR struct nsh_vtbl_s *vtbl, bool result)
{
struct nsh_parser_s *np = &vtbl->np;
#ifndef CONFIG_NSH_DISABLESCRIPT
#ifndef CONFIG_NSH_DISABLE_LOOPS
* token.
*
* while <test-cmd>; do <cmd-sequence>; done
*
* Execute <cmd-sequence> as long as <test-cmd> has an exit status of
* zero.
*/
if (np->np_lpstate[np->np_lpndx].lp_state == NSH_LOOP_WHILE)
{
np->np_fail = false;
np->np_lpstate[np->np_lpndx].lp_enable = (result == OK);
return OK;
}
* token.
*
* until <test-cmd>; do <cmd-sequence>; done
*
* Execute <cmd-sequence> as long as <test-cmd> has a non-zero exit
* status.
*/
else if (np->np_lpstate[np->np_lpndx].lp_state == NSH_LOOP_UNTIL)
{
np->np_fail = false;
np->np_lpstate[np->np_lpndx].lp_enable = (result != OK);
return OK;
}
else
#endif
#ifndef CONFIG_NSH_DISABLE_ITEF
if (np->np_iestate[np->np_iendx].ie_state == NSH_ITEF_IF)
{
np->np_fail = false;
np->np_iestate[np->np_iendx].ie_ifcond =
np->np_iestate[np->np_iendx].ie_inverted ^ result;
return OK;
}
else
#endif
#endif
{
np->np_fail = result;
return result ? ERROR : OK;
}
}
* Name: nsh_execute
****************************************************************************/
static int nsh_execute(FAR struct nsh_vtbl_s *vtbl,
int argc, FAR char *argv[],
FAR const struct nsh_param_s *param)
{
int ret;
*
* They must work as follows:
*
* 1. Load a file from file system if possible. An external command on a
* file system with the provided name (and on the defined PATH) takes
* precedence over any other source of a command by that name. This
* allows the user to replace a built-in command with a command on a`
* file system
*
* 2. If not, run a built-in application of that name if possible. A
* built-in application will take precedence over any NSH command.
*
* 3. If not, run an NSH command line command if possible.
*
* 4. If not, report that the command was not found.
*/
* nsh_fileapp() returns:
*
* -1 (ERROR) if the application task corresponding to 'argv[0]' could
* not be started (possibly because it doesn not exist).
* 0 (OK) if the application task corresponding to 'argv[0]' was
* and successfully started. If CONFIG_SCHED_WAITPID is
* defined, this return value also indicates that the
* application returned successful status (EXIT_SUCCESS)
* 1 If CONFIG_SCHED_WAITPID is defined, then this return value
* indicates that the application task was spawned
* successfully but returned failure exit status.
*
* Note the priority is not effected by nice-ness.
*/
#if defined(CONFIG_NSH_FILE_APPS) || \
(defined(CONFIG_NSH_BUILTIN_APPS) && !defined(CONFIG_NSH_BUILTIN_AS_COMMAND))
ret = nsh_fileapp(vtbl, argv[0], argv, param);
if (ret >= 0)
{
* command was successfully started (although it may not have ran
* successfully). So certainly it is not an NSH command.
*/
return nsh_saveresult(vtbl, ret != OK);
}
* program of that name). Treat it like an NSH command.
*/
#endif
* However is app is to be started as built-in new process will
* be created anyway, so skip this step.
*/
#ifndef CONFIG_NSH_DISABLEBG
if (vtbl->np.np_bg)
{
FAR char *sh_argv[4];
FAR char *sh_cmd = "sh";
FAR char *sh_arg2;
sh_arg2 = lib_get_tempbuffer(CONFIG_NSH_LINELEN);
if (sh_arg2 == NULL)
{
nsh_error(vtbl, g_fmtcmdoutofmemory, sh_cmd);
return nsh_saveresult(vtbl, false);
}
DEBUGASSERT(strncmp(argv[0], sh_cmd, 3) != 0);
sh_arg2[0] = '\0';
for (ret = 0; ret < argc; ret++)
{
strlcat(sh_arg2, argv[ret], CONFIG_NSH_LINELEN);
if (ret < argc - 1)
{
strcat(sh_arg2, " ");
}
}
sh_argv[0] = sh_cmd;
sh_argv[1] = "-c";
sh_argv[2] = sh_arg2;
sh_argv[3] = NULL;
* dispatch the background by sh -c ""
*/
ret = nsh_execute(vtbl, 4, sh_argv, param);
lib_put_tempbuffer(sh_arg2);
return ret;
}
else
#endif
{
uint8_t save[SAVE_SIZE];
int fd_in = STDIN_FILENO;
int fd_out = STDOUT_FILENO;
int fd_err = STDERR_FILENO;
if (vtbl->np.np_redir_out)
{
if (param->file_out)
{
* 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_out = open(param->file_out, param->oflags_out, 0666);
if (fd_out < 0)
{
nsh_error(vtbl, g_fmtcmdfailed, argv[0], "open",
NSH_ERRNO);
return nsh_saveresult(vtbl, true);
}
}
else
{
fd_out = param->fd_out;
}
}
if (vtbl->np.np_redir_in)
{
if (param->file_in)
{
* 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(param->file_in, param->oflags_in, 0);
if (fd_in < 0)
{
nsh_error(vtbl, g_fmtcmdfailed, argv[0], "open",
NSH_ERRNO);
return nsh_saveresult(vtbl, true);
}
}
else
{
fd_in = param->fd_in;
}
}
if (vtbl->np.np_redir_err)
{
if (param->file_err)
{
fd_err = open(param->file_err, param->oflags_err, 0666);
if (fd_err < 0)
{
nsh_error(vtbl, g_fmtcmdfailed, argv[0], "open",
NSH_ERRNO);
return nsh_saveresult(vtbl, true);
}
}
else
{
fd_err = fd_out;
}
}
if (vtbl->np.np_redir_out || vtbl->np.np_redir_in ||
vtbl->np.np_redir_err)
{
nsh_redirect(vtbl, fd_in, fd_out, fd_err, save);
}
* waits for the next prompt. nsh_command will return:
*
* -1 (ERROR) if the command was unsuccessful
* 0 (OK) if the command was successful
*/
ret = nsh_command(vtbl, argc, argv);
* file descriptor.
*/
if (vtbl->np.np_redir_out || vtbl->np.np_redir_in ||
vtbl->np.np_redir_err)
{
nsh_undirect(vtbl, save);
fd_out = -1;
fd_in = -1;
}
* values in nsh scripts.
*/
if (ret < 0)
{
return nsh_saveresult(vtbl, true);
}
}
* command task succeeded).
*/
return nsh_saveresult(vtbl, false);
}
* Name: nsh_filecat
****************************************************************************/
#ifdef CONFIG_NSH_CMDPARMS
static FAR char *nsh_filecat(FAR struct nsh_vtbl_s *vtbl, FAR char *s1,
FAR const char *filename)
{
struct stat buf;
size_t s1size = 0;
size_t allocsize;
ssize_t nbytesread;
FAR char *argument;
unsigned index;
int fd;
int ret;
if (s1)
{
s1size = (size_t)strlen(s1);
}
ret = stat(filename, &buf);
if (ret != 0)
{
nsh_error(vtbl, g_fmtcmdfailed, "``", "stat", NSH_ERRNO);
return NULL;
}
allocsize = s1size + (size_t)buf.st_size + 1;
argument = (FAR char *)realloc(s1, allocsize);
if (!argument)
{
nsh_error(vtbl, g_fmtcmdoutofmemory, "``");
return NULL;
}
fd = open(filename, O_RDONLY);
if (fd < 0)
{
nsh_error(vtbl, g_fmtcmdfailed, "``", "open", NSH_ERRNO);
goto errout_with_alloc;
}
* the allocated string (after the original contents of s1size bytes.
*/
for (index = s1size; index < allocsize - 1; )
{
* end-of-file, or until a read error occurs
*/
do
{
nbytesread = read(fd, &argument[index], allocsize - index);
if (nbytesread == 0)
{
break;
}
else if (nbytesread < 0)
{
if (errno == EINTR)
{
nsh_error(vtbl, g_fmtsignalrecvd, "``");
}
else
{
nsh_error(vtbl, g_fmtcmdfailed, "``", "read", NSH_ERRNO);
}
goto errout_with_fd;
}
}
while (nbytesread <= 0);
index += nbytesread;
}
for (;
index > s1size &&
strchr(g_token_separator, argument[index - 1]) != NULL;
index--);
argument[index] = '\0';
close (fd);
return argument;
errout_with_fd:
close(fd);
errout_with_alloc:
free(argument);
return NULL;
}
#endif
* Name: nsh_cmdparm
****************************************************************************/
#ifdef CONFIG_NSH_CMDPARMS
static FAR char *nsh_cmdparm(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
FAR char **allocation)
{
struct nsh_param_s param =
{
.fd_in = -1,
.fd_out = -1,
.fd_err = -1,
.oflags_in = 0,
.oflags_out = O_WRONLY | O_CREAT | O_TRUNC,
.oflags_err = O_WRONLY | O_CREAT | O_TRUNC,
.file_in = NULL,
.file_out = NULL,
.file_err = NULL
};
FAR char *tmpfile;
FAR char *argument;
int ret;
* pointer.
*/
if (!allocation)
{
return (FAR char *)g_nullstring;
}
tmpfile = NULL;
ret = asprintf(&tmpfile, "%s/TMP%d.dat", CONFIG_LIBC_TMPDIR, getpid());
if (ret < 0 || !tmpfile)
{
nsh_error(vtbl, g_fmtcmdoutofmemory, "``");
return (FAR char *)g_nullstring;
}
* the temporary file. This is a simple command that can't handle most
* options.
*/
param.file_out = tmpfile;
ret = nsh_parse_cmdparm(vtbl, cmdline, ¶m);
if (ret != OK)
{
nsh_error(vtbl, g_fmtcmdfailed, "``", "exec", NSH_ERRNO);
free(tmpfile);
return (FAR char *)g_nullstring;
}
argument = nsh_filecat(vtbl, *allocation, tmpfile);
if (argument == NULL)
{
argument = (FAR char *)g_nullstring;
}
else
{
*allocation = argument;
}
ret = unlink(tmpfile);
if (ret < 0)
{
nsh_error(vtbl, g_fmtcmdfailed, "``", "unlink", NSH_ERRNO);
}
free(tmpfile);
return argument;
}
#endif
* Name: nsh_strcat
****************************************************************************/
#if defined(CONFIG_NSH_ARGCAT) || defined(CONFIG_NSH_ALIAS)
static FAR char *nsh_strcat(FAR struct nsh_vtbl_s *vtbl, FAR char *s1,
FAR const char *s2)
{
FAR char *argument;
int s1size = 0;
int allocsize;
if (s1)
{
s1size = strlen(s1);
}
* both (including the NUL terminator).
*/
allocsize = s1size + strlen(s2) + 1;
argument = (FAR char *)realloc(s1, allocsize);
if (!argument)
{
nsh_error(vtbl, g_fmtcmdoutofmemory, "$");
argument = s1;
}
else
{
argument[s1size] = '\0';
strlcat(argument, s2, allocsize);
}
return argument;
}
#endif
* Name: nsh_strchr
****************************************************************************/
#if defined(CONFIG_NSH_QUOTE) && defined(CONFIG_NSH_ARGCAT)
static FAR char *nsh_strchr(FAR const char *str, int ch)
{
FAR const char *ptr;
bool quoted = false;
for (ptr = str; ; ptr++)
{
if (*ptr == '\\' && !quoted)
{
quoted = true;
}
else if ((int)*ptr == ch && !quoted)
{
return (FAR char *)ptr;
}
else if (*ptr == '\0')
{
return NULL;
}
else
{
quoted = false;
}
}
}
#endif
* Name: nsh_aliasexpand
****************************************************************************/
#ifdef CONFIG_NSH_ALIAS
static FAR char *nsh_aliasexpand(FAR struct nsh_vtbl_s *vtbl,
FAR char *cmdline, FAR char **saveptr,
FAR NSH_MEMLIST_TYPE *memlist,
FAR NSH_ALIASLIST_TYPE *alist)
{
FAR struct nsh_alias_s *alias;
alias = nsh_aliasfind(vtbl, cmdline);
if (alias)
{
FAR char *ptr;
size_t len;
cmdline = alias->value;
NSH_ALIASLIST_ADD(alist, alias);
len = strcspn(cmdline, g_token_separator);
ptr = cmdline + len;
if (*ptr != '\0')
{
if ((ptr = strdup(alias->value)) != NULL)
{
ptr = nsh_strcat(vtbl, ptr, " ");
ptr = nsh_strcat(vtbl, ptr, *saveptr);
NSH_MEMLIST_ADD(memlist, ptr);
cmdline = ptr;
ptr = cmdline + len;
*ptr++ = '\0';
*saveptr = ptr;
}
}
}
return cmdline;
}
#endif
* Name: nsh_envexpand
****************************************************************************/
#ifdef NSH_HAVE_VARS
static FAR char *nsh_envexpand(FAR struct nsh_vtbl_s *vtbl,
FAR char *varname)
{
if (strcmp(varname, g_exitstatus) == 0)
{
if (vtbl->np.np_fail)
{
return (FAR char *)g_failure;
}
else
{
return (FAR char *)g_success;
}
}
else if (strcmp(varname, g_lastpid) == 0)
{
itoa(vtbl->np.np_lastpid, vtbl->np.np_pids, 10);
return vtbl->np.np_pids;
}
else
{
FAR char *value;
#ifdef CONFIG_NSH_VARS
* name.
*/
value = nsh_getvar(vtbl, varname);
if (value != NULL)
{
return value;
}
#endif
#ifndef CONFIG_DISABLE_ENVIRON
* environment variable with this name.
*/
value = getenv(varname);
if (value != NULL)
{
return value;
}
#endif
return (FAR char *)g_nullstring;
}
}
#endif
* Name: nsh_dequote
****************************************************************************/
#if defined(CONFIG_NSH_QUOTE)
static void nsh_dequote(FAR char *cmdline)
{
FAR char *ptr;
bool quoted;
quoted = false;
for (ptr = cmdline; *ptr != '\0'; )
{
if (*ptr == '\\' && !quoted)
{
FAR char *dest = ptr;
FAR const char *src = ptr + 1;
char ch;
do
{
ch = *src++;
*dest++ = ch;
}
while (ch != '\0');
* another back-slash character).
*/
quoted = true;
continue;
}
else
{
* preceded by a back-slash, or (2) it was preceded by a quoted
* back-slash.
*/
quoted = false;
ptr++;
}
}
*ptr = '\0';
}
#endif
* Name: nsh_rmquotes
****************************************************************************/
static FAR char *nsh_rmquotes(FAR char *qbegin, FAR char *qend)
{
FAR char *dst;
FAR char *ptr;
char ch;
dst = qbegin;
ptr = qbegin + 1;
do
{
if (ptr == qend)
{
ptr++;
}
ch = *ptr++;
*dst++ = ch;
}
while (ch != '\0');
return qend - 2;
}
* Name: nsh_argexpand
****************************************************************************/
#if defined(CONFIG_NSH_ARGCAT) && defined(HAVE_MEMLIST)
static FAR char *nsh_argexpand(FAR struct nsh_vtbl_s *vtbl,
FAR char *cmdline, FAR char **allocation,
FAR int *isenvvar)
{
FAR char *working = cmdline;
#ifdef CONFIG_NSH_QUOTE
FAR char *nextwork;
#endif
FAR char *argument = NULL;
FAR char *ptr;
size_t len;
for (; ; )
{
len = strcspn(working, g_arg_separator);
ptr = working + len;
#ifdef CONFIG_NSH_QUOTE
nextwork = ptr + 1;
while (len > 0 && *ptr != '\0')
{
FAR char *prev = working + len - 1;
unsigned bcount;
bool quoted;
for (bcount = 0, quoted = false;
bcount < len && *prev == '\\';
bcount++, prev--)
{
quoted ^= true;
}
if (quoted)
{
len += strcspn(ptr + 1, g_arg_separator) + 1;
ptr = working + len;
nextwork = ptr + 1;
}
else
{
break;
}
}
#endif
* interesting in the argument.
*/
if (*ptr == '\0')
{
if (argument)
{
* argument beginning at the last working pointer to the
* concatenated argument.
*
* On failures to allocation memory, nsh_strcat will just
* return old value of argument
*/
argument = nsh_strcat(vtbl, argument, working);
*allocation = argument;
nsh_dequote(argument);
return argument;
}
else
{
* line.
*/
nsh_dequote(cmdline);
return cmdline;
}
}
else
#ifdef CONFIG_NSH_CMDPARMS
* string.
*/
if (*ptr == '`')
{
FAR char *tmpalloc = NULL;
FAR char *result;
FAR char *rptr;
* intervening character to the concatenated string.
*/
*ptr++ = '\0';
argument = nsh_strcat(vtbl, argument, working);
*allocation = argument;
rptr = nsh_strchr(ptr, '`');
if (!rptr)
{
nsh_error(vtbl, g_fmtnomatching, "`", "`");
return (FAR char *)g_nullstring;
}
*rptr = '\0';
* error, nsh_cmdparm may return g_nullstring but never NULL.
*/
result = nsh_cmdparm(vtbl, ptr, &tmpalloc);
* string. On failures to allocation memory, nsh_strcat will
* just return old value of argument
*/
argument = nsh_strcat(vtbl, argument, result);
*allocation = argument;
working = rptr + 1;
if (tmpalloc)
{
free(tmpalloc);
}
}
else
#endif
#ifdef NSH_HAVE_VARS
if (*ptr == '$')
{
FAR const char *envstr;
FAR char *rptr;
* intervening character to the concatenated string.
*/
*ptr++ = '\0';
argument = nsh_strcat(vtbl, argument, working);
*allocation = argument;
* dollar sign ('$') is followed by a left bracket ('{') then the
* variable name is terminated with the right bracket character
* ('}'). Otherwise, the variable name goes to the end of the
* argument.
*/
if (*ptr == '{')
{
ptr++;
rptr = nsh_strchr(ptr, '}');
if (!rptr)
{
nsh_error(vtbl, g_fmtnomatching, "${", "}");
return (FAR char *)g_nullstring;
}
* working pointer to the character after the bracket.
*/
*rptr = '\0';
working = rptr + 1;
}
else
{
*
* REVISIT: Needs logic to get the size of the variable name
* based on parsing the name string which must be of the form
* [a-zA-Z_]+[a-zA-Z0-9_]*
*/
working = ptr + strlen(ptr);
}
* nsh_envexpand will return the NULL string.
*/
if (isenvvar != NULL)
{
*isenvvar = 1;
}
envstr = nsh_envexpand(vtbl, ptr);
#ifndef CONFIG_NSH_DISABLESCRIPT
if ((vtbl->np.np_flags & NSH_PFLAG_SILENT) == 0)
#endif
{
nsh_output(vtbl, " %s=%s\n", ptr, envstr ? envstr : "(null)");
}
* string. On failures to allocation memory, nsh_strcat will
* just return old value of argument
*/
argument = nsh_strcat(vtbl, argument, envstr);
*allocation = argument;
}
else
#endif
{
* cmdline.
*/
#ifdef CONFIG_NSH_QUOTE
working = nextwork;
#else
working++;
#endif
}
}
}
#else
static FAR char *nsh_argexpand(FAR struct nsh_vtbl_s *vtbl,
FAR char *cmdline, FAR char **allocation,
FAR int *isenvvar)
{
FAR char *argument = (FAR char *)g_nullstring;
#ifdef CONFIG_NSH_QUOTE
char ch = *cmdline;
* more.
*/
nsh_dequote(cmdline);
if (ch == '\\')
{
argument = cmdline;
}
else
#endif
#ifdef CONFIG_NSH_CMDPARMS
* as an input parameters for this command?
*/
if (*cmdline == '`')
{
FAR char *rptr = nsh_strchr(cmdline + 1, '`');
if (!rptr || rptr[1] != '\0')
{
nsh_error(vtbl, g_fmtnomatching, "`", "`");
return (FAR char *)g_nullstring;
}
*rptr = '\0';
argument = nsh_cmdparm(vtbl, cmdline + 1, allocation);
}
else
#endif
#ifdef NSH_HAVE_VARS
if (*cmdline == '$')
{
if (isenvvar != NULL)
{
*isenvvar = 1;
}
argument = nsh_envexpand(vtbl, cmdline + 1);
}
else
#endif
{
* delimited string.
*/
argument = cmdline;
}
return argument;
}
#endif
* Name: nsh_argument
****************************************************************************/
static FAR char *nsh_argument(FAR struct nsh_vtbl_s *vtbl,
FAR char **saveptr,
FAR NSH_MEMLIST_TYPE *memlist,
FAR NSH_ALIASLIST_TYPE *alist,
FAR int *isenvvar)
{
FAR char *pbegin = *saveptr;
FAR char *pend = NULL;
FAR char *allocation = NULL;
FAR char *argument = NULL;
#ifdef CONFIG_NSH_QUOTE
FAR char *prev;
bool escaped = false;
#endif
bool squote = false;
#ifdef CONFIG_NSH_ALIAS
bool quoted = false;
#endif
for (;
*pbegin && strchr(g_token_separator, *pbegin) != NULL;
pbegin++);
* then return NULL, meaning that there are no further arguments on the
* line.
*/
if (!*pbegin)
{
return NULL;
}
if (*pbegin == '>')
{
if (*(pbegin + 1) == '>')
{
*saveptr = pbegin + 2;
argument = (FAR char *)g_redirect_out2;
}
else
{
*saveptr = pbegin + 1;
argument = (FAR char *)g_redirect_out1;
}
}
if (*pbegin == '<')
{
*saveptr = pbegin + 1;
argument = (FAR char *)g_redirect_in1;
}
#ifdef CONFIG_NSH_PIPELINE
if (*pbegin == '|')
{
*saveptr = pbegin + 1;
argument = (FAR char *)g_pipeline1;
}
#endif
else if (*pbegin == '#')
{
*saveptr = pbegin;
argument = NULL;
}
* rules to find the end of the argument.
*/
else
{
* make sure that we do not break up any quoted substrings.
*/
#ifdef CONFIG_NSH_QUOTE
for (prev = NULL, pend = pbegin; *pend != '\0'; prev = pend, pend++)
#else
for (pend = pbegin; *pend != '\0'; pend++)
#endif
{
#ifdef CONFIG_NSH_QUOTE
if (prev != NULL && *prev == '\\' && !escaped)
{
escaped = true;
continue;
}
escaped = false;
if (*pend == '\\' && !escaped)
{
continue;
}
#endif
#ifdef CONFIG_NSH_ALIAS
if ((quoted = (nsh_strchr(g_quote_separator, *pend) != NULL)))
#else
if (nsh_strchr(g_quote_separator, *pend) != NULL)
#endif
{
FAR char *qend = nsh_strchr(pend + 1, *pend);
if (!qend)
{
#ifndef CONFIG_NSH_DISABLE_ERROR_PRINT
char qterm[2];
qterm[0] = *pend;
qterm[1] = '\0';
nsh_error(vtbl, g_fmtnomatching, qterm, qterm);
#endif
return NULL;
}
if (*pend == '\'')
{
squote = true;
}
if (*pend == '"')
{
isenvvar = NULL;
}
if (*pend == '`')
{
pend = qend;
}
else
{
pend = nsh_rmquotes(pend, qend);
}
}
* sub-string.
*/
else if (nsh_strchr(g_token_separator, *pend) != NULL)
{
* Now we can break out of the loop.
*/
break;
}
}
* delimiter after the string.
*/
if (*pend)
{
*pend++ = '\0';
}
*saveptr = pend;
#ifdef CONFIG_NSH_ALIAS
if (alist && !quoted)
{
pbegin = nsh_aliasexpand(vtbl, pbegin, saveptr, memlist, alist);
}
#endif
if (squote)
{
argument = pbegin;
}
else
{
argument = nsh_argexpand(vtbl, pbegin, &allocation, isenvvar);
}
}
* added to the list of memory to be freed at the end of command
* processing.
*/
NSH_MEMLIST_ADD(memlist, allocation);
return argument;
}
* Name: nsh_loop_enabled
****************************************************************************/
#if !defined(CONFIG_NSH_DISABLESCRIPT) && !defined(CONFIG_NSH_DISABLE_LOOPS)
static bool nsh_loop_enabled(FAR struct nsh_vtbl_s *vtbl)
{
FAR struct nsh_parser_s *np = &vtbl->np;
* all data until we next get to the 'done' token at the end of the
* loop.
*/
if (np->np_lpstate[np->np_lpndx].lp_state == NSH_LOOP_DO)
{
return (bool)np->np_lpstate[np->np_lpndx].lp_enable;
}
return true;
}
#else
# define nsh_loop_enabled(vtbl) true
#endif
* Name: nsh_itef_enabled
****************************************************************************/
#if !defined(CONFIG_NSH_DISABLESCRIPT) && !defined(CONFIG_NSH_DISABLE_ITEF)
static bool nsh_itef_enabled(FAR struct nsh_vtbl_s *vtbl)
{
FAR struct nsh_parser_s *np = &vtbl->np;
bool ret = !np->np_iestate[np->np_iendx].ie_disabled;
if (ret)
{
switch (np->np_iestate[np->np_iendx].ie_state)
{
case NSH_ITEF_NORMAL:
case NSH_ITEF_IF:
default:
break;
case NSH_ITEF_THEN:
ret = !np->np_iestate[np->np_iendx].ie_ifcond;
break;
case NSH_ITEF_ELSE:
ret = np->np_iestate[np->np_iendx].ie_ifcond;
break;
}
}
return ret;
}
#else
# define nsh_itef_enabled(vtbl) true
#endif
* Name: nsh_cmdenabled
****************************************************************************/
#ifndef CONFIG_NSH_DISABLESCRIPT
static bool nsh_cmdenabled(FAR struct nsh_vtbl_s *vtbl)
{
* loop AND if command processing is enabled in this part of the if-then-
* else-fi sequence.
*/
return (nsh_loop_enabled(vtbl) && nsh_itef_enabled(vtbl));
}
#endif
* Name: nsh_loop
****************************************************************************/
#if !defined(CONFIG_NSH_DISABLESCRIPT) && !defined(CONFIG_NSH_DISABLE_LOOPS)
static int nsh_loop(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist,
FAR NSH_ALIASLIST_TYPE *alist)
{
FAR struct nsh_parser_s *np = &vtbl->np;
FAR char *cmd = *ppcmd;
long offset;
bool whilematch;
bool untilmatch;
bool enable;
int ret;
if (cmd != NULL)
{
whilematch = strcmp(cmd, "while") == 0;
untilmatch = strcmp(cmd, "until") == 0;
if (whilematch || untilmatch)
{
uint8_t state;
*ppcmd = nsh_argument(vtbl, saveptr, memlist, alist, 0);
if (*ppcmd == NULL || **ppcmd == '\0')
{
nsh_error(vtbl, g_fmtarginvalid, cmd);
goto errout;
}
if (
#ifndef CONFIG_NSH_DISABLE_ITEF
np->np_iestate[np->np_iendx].ie_state == NSH_ITEF_IF ||
#endif
np->np_lpstate[np->np_lpndx].lp_state == NSH_LOOP_WHILE ||
np->np_lpstate[np->np_lpndx].lp_state == NSH_LOOP_UNTIL ||
np->np_fd < 0 || np->np_foffs < 0)
{
nsh_error(vtbl, g_fmtcontext, cmd);
goto errout;
}
if (np->np_lpndx >= CONFIG_NSH_NESTDEPTH - 1)
{
nsh_error(vtbl, g_fmtdeepnesting, cmd);
goto errout;
}
state = whilematch ? NSH_LOOP_WHILE : NSH_LOOP_UNTIL;
enable = nsh_cmdenabled(vtbl);
#ifdef NSH_DISABLE_SEMICOLON
offset = np->np_foffs;
#else
offset = np->np_foffs + np->np_loffs;
#endif
#ifndef NSH_DISABLE_SEMICOLON
np->np_jump = false;
#endif
np->np_lpndx++;
np->np_lpstate[np->np_lpndx].lp_state = state;
np->np_lpstate[np->np_lpndx].lp_enable = enable;
#ifndef CONFIG_NSH_DISABLE_ITEF
np->np_lpstate[np->np_lpndx].lp_iendx = np->np_iendx;
#endif
np->np_lpstate[np->np_lpndx].lp_topoffs = offset;
}
else if (strcmp(cmd, "do") == 0)
{
*ppcmd = nsh_argument(vtbl, saveptr, memlist, alist, NULL);
if (np->np_lpstate[np->np_lpndx].lp_state != NSH_LOOP_WHILE &&
np->np_lpstate[np->np_lpndx].lp_state != NSH_LOOP_UNTIL)
{
nsh_error(vtbl, g_fmtcontext, "do");
goto errout;
}
np->np_lpstate[np->np_lpndx].lp_state = NSH_LOOP_DO;
}
else if (strcmp(cmd, "done") == 0)
{
*ppcmd = nsh_argument(vtbl, saveptr, memlist, alist, NULL);
if (*ppcmd)
{
nsh_error(vtbl, g_fmtarginvalid, "done");
goto errout;
}
if (np->np_lpstate[np->np_lpndx].lp_state != NSH_LOOP_DO)
{
nsh_error(vtbl, g_fmtcontext, "done");
goto errout;
}
if (np->np_lpndx < 1)
{
nsh_error(vtbl, g_fmtinternalerror, "done");
goto errout;
}
* loop (if lp_enable == true) or continue past the end of the
* loop (if lp_enable == false)
*/
if (np->np_lpstate[np->np_lpndx].lp_enable)
{
ret = lseek(np->np_fd,
np->np_lpstate[np->np_lpndx].lp_topoffs,
SEEK_SET);
if (ret < 0)
{
nsh_error(vtbl, g_fmtcmdfailed, "done", "lseek",
NSH_ERRNO);
}
#ifndef NSH_DISABLE_SEMICOLON
* current line and jump back to the top of the loop.
*/
np->np_jump = true;
#endif
}
else
{
np->np_lpstate[np->np_lpndx].lp_enable = true;
}
* decided to do
*/
np->np_lpstate[np->np_lpndx].lp_state = NSH_LOOP_NORMAL;
np->np_lpndx--;
}
* other than "do"
*/
else if (np->np_lpstate[np->np_lpndx].lp_state == NSH_LOOP_WHILE ||
np->np_lpstate[np->np_lpndx].lp_state == NSH_LOOP_UNTIL)
{
nsh_error(vtbl, g_fmtcontext, cmd);
goto errout;
}
}
return OK;
errout:
#ifndef NSH_DISABLE_SEMICOLON
np->np_jump = false;
#endif
np->np_lpndx = 0;
np->np_lpstate[0].lp_state = NSH_LOOP_NORMAL;
np->np_lpstate[0].lp_enable = true;
np->np_lpstate[0].lp_topoffs = 0;
return ERROR;
}
#endif
* Name: nsh_itef
****************************************************************************/
#if !defined(CONFIG_NSH_DISABLESCRIPT) && !defined(CONFIG_NSH_DISABLE_ITEF)
static int nsh_itef(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist,
FAR NSH_ALIASLIST_TYPE *alist)
{
FAR struct nsh_parser_s *np = &vtbl->np;
FAR char *cmd = *ppcmd;
bool disabled;
bool inverted = false;
if (cmd != NULL)
{
if (strcmp(cmd, "if") == 0)
{
*ppcmd = nsh_argument(vtbl, saveptr, memlist, alist, NULL);
if (*ppcmd == NULL || **ppcmd == '\0')
{
nsh_error(vtbl, g_fmtarginvalid, "if");
goto errout;
}
if (strcmp(*ppcmd, "!") == 0)
{
inverted = true;
*ppcmd = nsh_argument(vtbl, saveptr, memlist, alist, 0);
if (*ppcmd == NULL || **ppcmd == '\0')
{
nsh_error(vtbl, g_fmtarginvalid, "if");
goto errout;
}
}
if (np->np_iestate[np->np_iendx].ie_state == NSH_ITEF_IF)
{
nsh_error(vtbl, g_fmtcontext, "if");
goto errout;
}
if (np->np_iendx >= CONFIG_NSH_NESTDEPTH - 1)
{
nsh_error(vtbl, g_fmtdeepnesting, "if");
goto errout;
}
disabled = !nsh_cmdenabled(vtbl);
np->np_iendx++;
np->np_iestate[np->np_iendx].ie_state = NSH_ITEF_IF;
np->np_iestate[np->np_iendx].ie_disabled = disabled;
np->np_iestate[np->np_iendx].ie_ifcond = false;
np->np_iestate[np->np_iendx].ie_inverted = inverted;
}
else if (strcmp(cmd, "then") == 0)
{
* one.
*/
*ppcmd = nsh_argument(vtbl, saveptr, memlist, alist, NULL);
if (np->np_iestate[np->np_iendx].ie_state != NSH_ITEF_IF)
{
nsh_error(vtbl, g_fmtcontext, "then");
goto errout;
}
np->np_iestate[np->np_iendx].ie_state = NSH_ITEF_THEN;
}
else if (strcmp(cmd, "else") == 0)
{
* one.
*/
*ppcmd = nsh_argument(vtbl, saveptr, memlist, alist, NULL);
if (np->np_iestate[np->np_iendx].ie_state != NSH_ITEF_THEN)
{
nsh_error(vtbl, g_fmtcontext, "else");
goto errout;
}
np->np_iestate[np->np_iendx].ie_state = NSH_ITEF_ELSE;
}
else if (strcmp(cmd, "fi") == 0)
{
*ppcmd = nsh_argument(vtbl, saveptr, memlist, alist, NULL);
if (*ppcmd)
{
nsh_error(vtbl, g_fmtarginvalid, "fi");
goto errout;
}
if (np->np_iestate[np->np_iendx].ie_state != NSH_ITEF_THEN &&
np->np_iestate[np->np_iendx].ie_state != NSH_ITEF_ELSE)
{
nsh_error(vtbl, g_fmtcontext, "fi");
goto errout;
}
if (np->np_iendx < 1)
{
nsh_error(vtbl, g_fmtinternalerror, "if");
goto errout;
}
np->np_iendx--;
}
* "then".
*/
else if (np->np_iestate[np->np_iendx].ie_state == NSH_ITEF_IF)
{
nsh_error(vtbl, g_fmtcontext, cmd);
goto errout;
}
}
return OK;
errout:
np->np_iendx = 0;
np->np_iestate[0].ie_state = NSH_ITEF_NORMAL;
np->np_iestate[0].ie_disabled = false;
np->np_iestate[0].ie_ifcond = false;
np->np_iestate[0].ie_inverted = false;
return ERROR;
}
#endif
* Name: nsh_nice
****************************************************************************/
#ifndef CONFIG_NSH_DISABLEBG
static int nsh_nice(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist,
FAR NSH_ALIASLIST_TYPE *alist)
{
FAR char *cmd = *ppcmd;
vtbl->np.np_nice = 0;
if (cmd)
{
if (strcmp(cmd, "nice") == 0)
{
* (least favorable). Default is 10.
*/
vtbl->np.np_nice = 10;
cmd = nsh_argument(vtbl, saveptr, memlist, alist, NULL);
if (cmd && strcmp(cmd, "-d") == 0)
{
FAR char *val = nsh_argument(vtbl, saveptr, memlist, alist,
NULL);
if (val)
{
FAR char *endptr;
vtbl->np.np_nice = (int)strtol(val, &endptr, 0);
if (vtbl->np.np_nice > 19 || vtbl->np.np_nice < -20 ||
endptr == val || *endptr != '\0')
{
nsh_error(vtbl, g_fmtarginvalid, "nice");
return ERROR;
}
cmd = nsh_argument(vtbl, saveptr, memlist, alist, NULL);
}
}
*ppcmd = cmd;
}
}
return OK;
}
#endif
* Name: nsh_prlimit
*
* Returned Value:
* Zero (OK) on success; a negated errno value on failure.
*
****************************************************************************/
#ifndef CONFIG_NSH_DISABLE_PRLIMIT
static int nsh_prlimit(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
FAR char **saveptr, FAR NSH_MEMLIST_TYPE *memlist,
FAR NSH_ALIASLIST_TYPE *alist,
FAR struct nsh_param_s *param)
{
FAR char *cmd = *ppcmd;
FAR char *arg;
FAR char *endptr;
if (cmd && strcmp(cmd, "prlimit") == 0)
{
while ((arg = nsh_argument(vtbl, saveptr, memlist, alist, NULL))
!= NULL)
{
if (strcmp(arg, "-p") == 0)
{
arg = nsh_argument(vtbl, saveptr, memlist, alist, NULL);
if (!arg)
{
nsh_error(vtbl, g_fmtarginvalid, "prlimit");
return ERROR;
}
param->prlimit.priority = (int)strtol(arg, &endptr, 0);
if (endptr == arg || *endptr != '\0')
{
nsh_error(vtbl, g_fmtarginvalid, "prlimit");
return ERROR;
}
param->prlimit.used = 1;
}
else if (strcmp(arg, "-s") == 0)
{
arg = nsh_argument(vtbl, saveptr, memlist, alist, NULL);
if (!arg)
{
nsh_error(vtbl, g_fmtarginvalid, "prlimit");
return ERROR;
}
param->prlimit.stacksize = (size_t)strtoul(arg, &endptr, 0);
if (endptr == arg || *endptr != '\0')
{
nsh_error(vtbl, g_fmtarginvalid, "prlimit");
return ERROR;
}
param->prlimit.used = 1;
}
else if (strcmp(arg, "-h") == 0)
{
arg = nsh_argument(vtbl, saveptr, memlist, alist, NULL);
if (!arg)
{
nsh_error(vtbl, g_fmtarginvalid, "prlimit");
return ERROR;
}
param->prlimit.heapsize = (size_t)strtoul(arg, &endptr, 0);
if (endptr == arg || *endptr != '\0')
{
nsh_error(vtbl, g_fmtarginvalid, "prlimit");
return ERROR;
}
param->prlimit.used = 1;
}
else
{
break;
}
}
*ppcmd = arg;
}
return OK;
}
#endif
* Name: nsh_parse_cmdparm
*
* Description:
* This function parses and executes a simple NSH command. Output is
* always redirected. This function supports command parameters like
*
* set FOO `hello`
*
* which would set the environment variable FOO to the output from
* the hello program
*
****************************************************************************/
#ifdef CONFIG_NSH_CMDPARMS
static int nsh_parse_cmdparm(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
FAR const struct nsh_param_s *param)
{
NSH_MEMLIST_TYPE memlist;
NSH_ALIASLIST_TYPE alist;
FAR char *argv[MAX_ARGV_ENTRIES];
FAR char *saveptr;
FAR char *cmd;
#ifndef CONFIG_NSH_DISABLEBG
bool bgsave;
#endif
bool redirsave;
int argc;
int ret;
memset(argv, 0, MAX_ARGV_ENTRIES*sizeof(FAR char *));
NSH_MEMLIST_INIT(memlist);
NSH_ALIASLIST_INIT(alist);
* these will not be recognized and will just be passed through as
* normal, invalid commands or parameters.
*/
#ifndef CONFIG_NSH_DISABLEBG
* state
*/
bgsave = vtbl->np.np_bg;
vtbl->np.np_bg = false;
#endif
redirsave = vtbl->np.np_redir_out;
vtbl->np.np_redir_out = true;
saveptr = cmdline;
cmd = nsh_argument(vtbl, &saveptr, &memlist, &alist, NULL);
* currently disabled.
*/
#ifndef CONFIG_NSH_DISABLESCRIPT
if (!cmd || !nsh_cmdenabled(vtbl))
#else
if (!cmd)
#endif
{
* generate an error, but neither should it change the last command
* status.
*/
ret = 0;
goto exit;
}
* 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+1
*/
argv[0] = cmd;
for (argc = 1; argc < MAX_ARGV_ENTRIES - 1; argc++)
{
argv[argc] = nsh_argument(vtbl, &saveptr, &memlist, NULL, NULL);
if (!argv[argc])
{
break;
}
}
argv[argc] = NULL;
if (argc > CONFIG_NSH_MAXARGUMENTS)
{
nsh_error(vtbl, g_fmttoomanyargs, cmd);
}
ret = nsh_execute(vtbl, argc, argv, param);
exit:
#ifndef CONFIG_NSH_DISABLEBG
vtbl->np.np_bg = bgsave;
#endif
vtbl->np.np_redir_out = redirsave;
NSH_ALIASLIST_FREE(vtbl, &alist);
NSH_MEMLIST_FREE(&memlist);
return ret;
}
#endif
* Name: nsh_parse_command
*
* Description:
* This function parses and executes one NSH command from the command line.
*
****************************************************************************/
static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
FAR struct nsh_param_s *param)
{
struct nsh_param_s tmp =
{
.fd_in = -1,
.fd_out = -1,
.fd_err = -1,
.oflags_in = 0,
.oflags_out = 0,
.oflags_err = 0,
.file_in = NULL,
.file_out = NULL,
.file_err = 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;
bool redirect_err_save = false;
#ifdef CONFIG_NSH_PIPELINE
bool bg_save = false;
#endif
#ifdef CONFIG_TRACE_APP
char tracebuf[CONFIG_NSH_LINELEN + 1];
strlcpy(tracebuf, cmdline, sizeof(tracebuf));
app_trace_beginex(tracebuf);
#endif
memset(argv, 0, MAX_ARGV_ENTRIES*sizeof(FAR char *));
NSH_MEMLIST_INIT(memlist);
NSH_ALIASLIST_INIT(alist);
if (param == NULL)
{
param = &tmp;
}
#ifndef CONFIG_NSH_DISABLEBG
vtbl->np.np_bg = false;
#endif
vtbl->np.np_redir_out = false;
vtbl->np.np_redir_in = false;
vtbl->np.np_redir_err = false;
saveptr = cmdline;
cmd = nsh_argument(vtbl, &saveptr, &memlist, &alist, NULL);
#ifndef CONFIG_NSH_DISABLESCRIPT
#ifndef CONFIG_NSH_DISABLE_LOOPS
if (nsh_loop(vtbl, &cmd, &saveptr, &memlist, &alist) != 0)
{
ret = nsh_saveresult(vtbl, true);
goto dynlist_free;
}
#endif
#ifndef CONFIG_NSH_DISABLE_ITEF
if (nsh_itef(vtbl, &cmd, &saveptr, &memlist, &alist) != 0)
{
ret = nsh_saveresult(vtbl, true);
goto dynlist_free;
}
#endif
#endif
#ifndef CONFIG_NSH_DISABLEBG
if (nsh_nice(vtbl, &cmd, &saveptr, &memlist, &alist) != 0)
{
ret = nsh_saveresult(vtbl, true);
goto dynlist_free;
}
#endif
#ifndef CONFIG_NSH_DISABLE_PRLIMIT
if (nsh_prlimit(vtbl, &cmd, &saveptr, &memlist, &alist, param) != 0)
{
ret = nsh_saveresult(vtbl, true);
goto dynlist_free;
}
#endif
* currently disabled.
*/
#ifndef CONFIG_NSH_DISABLESCRIPT
if (!cmd || !nsh_cmdenabled(vtbl))
#else
if (!cmd)
#endif
{
* generate an error, but neither should it change the last command
* status.
*/
ret = OK;
goto dynlist_free;
}
* 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;
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];
for (; *pbegin && !strchr(g_token_separator, *pbegin);
pbegin++)
{
}
* done.
*/
if ('\0' == *pbegin)
{
break;
}
*pbegin = '\0';
argc++;
pbegin++;
for (; *pbegin && strchr(g_token_separator, *pbegin) != NULL;
pbegin++)
{
}
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);
}
else if (!strncmp(argv[argc], g_redirect_err3, g_redirect_err3_len))
{
redirect_err_save = vtbl->np.np_redir_err;
vtbl->np.np_redir_err = true;
param->fd_err = STDOUT_FILENO;
}
else if (!strncmp(argv[argc], g_redirect_err2, g_redirect_err2_len))
{
FAR char *arg;
if (argv[argc][g_redirect_err2_len])
{
arg = &argv[argc][g_redirect_err2_len];
}
else
{
arg = nsh_argument(vtbl, &saveptr, &memlist, NULL, &isenvvar);
}
if (!arg)
{
nsh_error(vtbl, g_fmtarginvalid, cmd);
ret = ERROR;
goto dynlist_free;
}
redirect_err_save = vtbl->np.np_redir_err;
vtbl->np.np_redir_err = true;
param->oflags_err = O_WRONLY | O_CREAT | O_APPEND;
param->file_err = nsh_getfullpath(vtbl, arg);
}
else if (!strncmp(argv[argc], g_redirect_err1, g_redirect_err1_len))
{
FAR char *arg;
if (argv[argc][g_redirect_err1_len])
{
arg = &argv[argc][g_redirect_err1_len];
}
else
{
arg = nsh_argument(vtbl, &saveptr, &memlist, NULL, &isenvvar);
}
if (!arg)
{
nsh_error(vtbl, g_fmtarginvalid, cmd);
ret = ERROR;
goto dynlist_free;
}
redirect_err_save = vtbl->np.np_redir_err;
vtbl->np.np_redir_err = true;
param->oflags_err = O_WRONLY | O_CREAT | O_TRUNC;
param->file_err = 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;
sh_arg2 = lib_get_tempbuffer(CONFIG_NSH_LINELEN);
if (sh_arg2 == NULL)
{
nsh_error(vtbl, g_fmtcmdoutofmemory, cmd);
ret = -ENOMEM;
goto dynlist_free;
}
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);
lib_put_tempbuffer(sh_arg2);
ret = ERROR;
goto dynlist_free;
}
sh_arg2[0] = '\0';
for (ret = 0; ret < argc; ret++)
{
strlcat(sh_arg2, argv[ret], CONFIG_NSH_LINELEN);
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, param);
lib_put_tempbuffer(sh_arg2);
vtbl->np.np_bg = bg_save;
if (param->fd_in != -1)
{
close(param->fd_in);
param->fd_in = -1;
vtbl->np.np_redir_in = redirect_in_save;
}
if (param->fd_out != -1)
{
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++;
}
}
#ifndef CONFIG_NSH_DISABLEBG
if (argc > 1 && strcmp(argv[argc - 1], "&") == 0)
{
vtbl->np.np_bg = true;
argc--;
}
#endif
argv[argc] = NULL;
if (argc > CONFIG_NSH_MAXARGUMENTS)
{
nsh_error(vtbl, g_fmttoomanyargs, cmd);
}
ret = nsh_execute(vtbl, argc, argv, param);
dynlist_free:
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
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
if (param->file_err)
{
nsh_freefullpath((char *)param->file_err);
}
if (vtbl->np.np_redir_err)
{
vtbl->np.np_redir_err = redirect_err_save;
}
NSH_ALIASLIST_FREE(vtbl, &alist);
NSH_MEMLIST_FREE(&memlist);
#ifdef CONFIG_TRACE_APP
app_trace_endex(tracebuf);
#endif
return ret;
}
* Public Functions
****************************************************************************/
* Name: nsh_parse
*
* Description:
* This function parses and executes the line of text received from the
* user. This may consist of one or more NSH commands. Multiple NSH
* commands are separated by semi-colons.
*
****************************************************************************/
int nsh_parse(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline)
{
#ifdef NSH_DISABLE_SEMICOLON
return nsh_parse_command(vtbl, cmdline, NULL);
#else
#if !defined(CONFIG_NSH_DISABLESCRIPT) && !defined(CONFIG_NSH_DISABLE_LOOPS)
FAR struct nsh_parser_s *np = &vtbl->np;
#endif
FAR char *start = cmdline;
FAR char *working = cmdline;
FAR char *ptr;
size_t len;
int ret;
* OR until the end-of-loop has been encountered and we need to reload the
* line at the top of the loop.
*/
#if !defined(CONFIG_NSH_DISABLESCRIPT) && !defined(CONFIG_NSH_DISABLE_LOOPS)
for (np->np_jump = false; !np->np_jump; )
#else
for (; ; )
#endif
{
#if !defined(CONFIG_NSH_DISABLESCRIPT) && !defined(CONFIG_NSH_DISABLE_LOOPS)
np->np_loffs = (uint16_t)(working - cmdline);
#endif
* line, a semicolon, or a '#' character. NOTE that the set of
* delimiting characters includes the quotation mark. We need to
* handle quotation marks here because any other delimiter within a
* quoted string must be treated as normal text.
*/
len = strcspn(working, g_line_separator);
ptr = working + len;
* of the delimiting characters was found or that the newline or '#'
* character was found. Anything after the newline or '#' character
* is ignored (there should not be anything after a newline, of
* course).
*/
if (*ptr == '\0' || *ptr == '\n' || *ptr == '#')
{
return nsh_parse_command(vtbl, start, NULL);
}
* command on the command line after this one.
*/
else if (*ptr == ';')
{
*ptr++ = '\0';
ret = nsh_parse_command(vtbl, start, NULL);
if (ret != OK)
{
* command failed or we failed to start the command application
* or (2) 1 meaning that the application task was spawned
* successfully but returned failure exit status.
*/
return ret;
}
start = ptr;
working = ptr;
}
else
{
FAR char *prev = ptr - 1;
if (prev >= start && *prev == '\\')
{
working++;
}
else
{
FAR char *tmp = nsh_strchr(ptr + 1, *ptr);
if (!tmp)
{
#ifndef CONFIG_NSH_DISABLE_ERROR_PRINT
char qterm[2];
qterm[0] = *ptr;
qterm[1] = '\0';
nsh_error(vtbl, g_fmtnomatching, qterm, qterm);
#endif
return ERROR;
}
* mark
*/
working = ++tmp;
}
}
}
#ifndef CONFIG_NSH_DISABLESCRIPT
return OK;
#endif
#endif
}
* Name: cmd_break
****************************************************************************/
#if !defined(CONFIG_NSH_DISABLESCRIPT) && !defined(CONFIG_NSH_DISABLE_LOOPS)
int cmd_break(FAR struct nsh_vtbl_s *vtbl, int argc, FAR char **argv)
{
UNUSED(argc);
UNUSED(argv);
FAR struct nsh_parser_s *np = &vtbl->np;
if (np->np_lpstate[np->np_lpndx].lp_state == NSH_LOOP_DO)
{
#ifndef CONFIG_NSH_DISABLE_ITEF
np->np_iendx = np->np_lpstate[np->np_lpndx].lp_iendx;
#endif
np->np_lpstate[np->np_lpndx].lp_enable = false;
}
* the supported context.
*/
return OK;
}
#endif