mirror of
https://github.com/apache/nuttx-apps.git
synced 2026-08-01 20:29:00 +00:00
Add support for backquoated commands as command arguments
This commit is contained in:
parent
09d0aa815d
commit
d499f287ad
4 changed files with 307 additions and 18 deletions
|
|
@ -766,4 +766,7 @@
|
|||
* apps/nshlib/nsh_parse.c: Put the logic that executes a command
|
||||
after parsing parameters into a separate file so that it can be
|
||||
reused (2014-1-10).
|
||||
* apps/nshlib/nsh_parse.c: Add initial support of commands enclosed
|
||||
in back quotes as command arguments. Functionality still incomplete
|
||||
on initial commit (2014-1-10).
|
||||
|
||||
|
|
|
|||
|
|
@ -277,6 +277,34 @@ config NSH_DISABLE_SEMICOLON
|
|||
command separated by a semicolon. You can disable this feature to
|
||||
save a little memory on FLASH challenged platforms.
|
||||
|
||||
config NSH_FUNCPARMS
|
||||
bool "Enable function parameters"
|
||||
default n
|
||||
---help---
|
||||
If selected, then the output from cmds, from file applications, and
|
||||
from NSH built-in commands can be used as arguments to other
|
||||
commands. The entity to be executed is identified by enclosing the
|
||||
command line in back quotes. For example,
|
||||
|
||||
set FOO `myprogram $BAR`
|
||||
|
||||
Will execute the program named myprogram passing it the value of the
|
||||
environment variable BAR. The value of the environment variable FOO
|
||||
is then set output of myprogram on stdout.
|
||||
|
||||
This feature is disabled by default.
|
||||
|
||||
config NSH_TMPDIR
|
||||
string "Temporary file directory"
|
||||
default "/tmp"
|
||||
depends on NSH_FUNCPARMS
|
||||
---help---
|
||||
If NSH_FUNCPARMS is selected, then function output will be retained
|
||||
in a temporary file. In that case, this string must be provided to
|
||||
specify the full path to a directory where temporary files can be
|
||||
created. This would be a good application of RAM disk: To provide
|
||||
temporary storage for function output.
|
||||
|
||||
config NSH_MAXARGUMENTS
|
||||
int "Maximum number of command arguments"
|
||||
default 6
|
||||
|
|
@ -284,6 +312,22 @@ config NSH_MAXARGUMENTS
|
|||
The maximum number of NSH command arguments.
|
||||
Default: 6
|
||||
|
||||
config NSH_ENVCAT
|
||||
bool "Concatenation of environment variables"
|
||||
default n
|
||||
---help---
|
||||
Support concatenation of strings with environment variables. For
|
||||
example:
|
||||
|
||||
set FOO XYZ
|
||||
set BAR 123
|
||||
set FOOBAR ABC_${FOO}_${BAR}
|
||||
|
||||
would set the environment variable FOO to XYZ, BAR to 123 and FOOBAR
|
||||
to ABC_XYZ_123. If NSH_ENV, then a slightly small FLASH footprint
|
||||
results but then also only simple environment variables like $FOO
|
||||
can be used on the command line.
|
||||
|
||||
config NSH_NESTDEPTH
|
||||
int "Maximum command nesting"
|
||||
default 3
|
||||
|
|
|
|||
|
|
@ -72,6 +72,15 @@
|
|||
# undef CONFIG_NSH_TELNET
|
||||
# undef CONFIG_NSH_FILE_APPS
|
||||
# undef CONFIG_NSH_TELNET
|
||||
# undef CONFIG_NSH_FUNCPARMS
|
||||
#endif
|
||||
|
||||
/* If CONFIG_NSH_FUNCPARMS is selected, then the path to a directory to
|
||||
* hold temporary files must be provided.
|
||||
*/
|
||||
|
||||
#if defined(CONFIG_NSH_FUNCPARMS) && !defined(CONFIG_NSH_TMPDIR)
|
||||
# define CONFIG_NSH_TMPDIR "/tmp"
|
||||
#endif
|
||||
|
||||
/* Telnetd requires networking support */
|
||||
|
|
|
|||
|
|
@ -74,6 +74,36 @@ struct cmdarg_s
|
|||
* Private Function Prototypes
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef CONFIG_NSH_DISABLEBG
|
||||
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, 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);
|
||||
static char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, char **saveptr);
|
||||
|
||||
#ifndef CONFIG_NSH_DISABLESCRIPT
|
||||
static bool nsh_cmdenabled(FAR struct nsh_vtbl_s *vtbl);
|
||||
static int nsh_ifthenelse(FAR struct nsh_vtbl_s *vtbl,
|
||||
FAR char **ppcmd, FAR char **saveptr);
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_NSH_DISABLEBG
|
||||
static int nsh_nice(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
|
||||
FAR char **saveptr);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NSH_FUNCPARMS
|
||||
static int nsh_parse_funcparm(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
|
||||
FAR const char *redirfile);
|
||||
#endif
|
||||
static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline);
|
||||
|
||||
/****************************************************************************
|
||||
* Private Data
|
||||
****************************************************************************/
|
||||
|
|
@ -207,8 +237,8 @@ static pthread_addr_t nsh_child(pthread_addr_t arg)
|
|||
****************************************************************************/
|
||||
|
||||
#ifndef CONFIG_NSH_DISABLEBG
|
||||
static inline struct cmdarg_s *nsh_cloneargs(FAR struct nsh_vtbl_s *vtbl,
|
||||
int fd, int argc, char *argv[])
|
||||
static struct cmdarg_s *nsh_cloneargs(FAR struct nsh_vtbl_s *vtbl,
|
||||
int fd, int argc, char *argv[])
|
||||
{
|
||||
struct cmdarg_s *ret = (struct cmdarg_s *)zalloc(sizeof(struct cmdarg_s));
|
||||
int i;
|
||||
|
|
@ -253,7 +283,7 @@ static int nsh_saveresult(FAR struct nsh_vtbl_s *vtbl, bool result)
|
|||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nsh_argument
|
||||
* Name: nsh_execute
|
||||
****************************************************************************/
|
||||
|
||||
static int nsh_execute(FAR struct nsh_vtbl_s *vtbl,
|
||||
|
|
@ -361,7 +391,7 @@ static int nsh_execute(FAR struct nsh_vtbl_s *vtbl,
|
|||
#endif
|
||||
|
||||
/* Handle the case where the command is executed in background.
|
||||
* However is app is to be started as builtin new process will
|
||||
* However is app is to be started as built-in new process will
|
||||
* be created anyway, so skip this step.
|
||||
*/
|
||||
|
||||
|
|
@ -532,10 +562,10 @@ errout:
|
|||
* Name: nsh_argument
|
||||
****************************************************************************/
|
||||
|
||||
static char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, char **saveptr)
|
||||
static char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, FAR char **saveptr)
|
||||
{
|
||||
char *pbegin = *saveptr;
|
||||
char *pend = NULL;
|
||||
FAR char *pbegin = *saveptr;
|
||||
FAR char *pend = NULL;
|
||||
const char *term;
|
||||
#ifndef CONFIG_DISABLE_ENVIRON
|
||||
bool quoted = false;
|
||||
|
|
@ -583,15 +613,101 @@ static char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, char **saveptr)
|
|||
*saveptr = pbegin;
|
||||
pbegin = NULL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NSH_FUNCPARMS
|
||||
/* Are we being asked to use the output from another command or program
|
||||
* as an input parameters for this command?
|
||||
*/
|
||||
|
||||
else if (*pbegin == '`')
|
||||
{
|
||||
FAR char *tmpfile;
|
||||
int ret;
|
||||
|
||||
/* Yes, find the terminated backquote */
|
||||
|
||||
for (++pbegin, pend = pbegin; *pend && *pend != '`'; pend++);
|
||||
|
||||
/* pend either points to the end of the string or to the closing
|
||||
* backquote.
|
||||
*/
|
||||
|
||||
if (!*pend)
|
||||
{
|
||||
nsh_output(vtbl, g_nshsyntax, "``");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Turn the closing backquote into a NUL terminator and save the
|
||||
* pointer where we left off.
|
||||
*/
|
||||
|
||||
*pend++ = '\0';
|
||||
*saveptr = pend;
|
||||
|
||||
/* Create a unique file name using the task ID */
|
||||
|
||||
tmpfile = NULL;
|
||||
ret = asprintf(&tmpfile, "%s/TMP%d.dat", CONFIG_NSH_TMPDIR, getpid());
|
||||
if (ret < 0 || !tmpfile)
|
||||
{
|
||||
nsh_output(vtbl, g_fmtcmdoutofmemory, "``");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Execute the command that will re-direct the output of the command
|
||||
* to the temporary file. This is a simple command that can't handle
|
||||
* most options.
|
||||
*/
|
||||
|
||||
ret = nsh_parse_funcparm(vtbl, pbegin, tmpfile);
|
||||
if (ret != OK)
|
||||
{
|
||||
/* Report the failure */
|
||||
#warning Logic Missing
|
||||
|
||||
free(tmpfile);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* mmap() the file contain the output from the command so that
|
||||
* we can refer to it like any string.
|
||||
*/
|
||||
#warning Logic Missing
|
||||
|
||||
/* setup so that we can unmap the memory region and close the
|
||||
* file after the command completes.
|
||||
*/
|
||||
#warning Logic Missing
|
||||
|
||||
/* We can not unlink the tmpfile and free the tmpfile string */
|
||||
|
||||
ret = unlink(tmpfile);
|
||||
if (ret < 0)
|
||||
{
|
||||
nsh_output(vtbl, g_fmtcmdfailed, "``", "unlink", NSH_ERRNO);
|
||||
}
|
||||
|
||||
free(tmpfile);
|
||||
return "OH NO!";
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Otherwise, it is a normal string and we have to parse using the normal
|
||||
* rules to find the end of the argument.
|
||||
*/
|
||||
|
||||
else
|
||||
{
|
||||
/* Otherwise, we are going to have to parse to find the end of
|
||||
* the token. Does the token begin with '"'?
|
||||
/* However, the rules are a little different if the next argument is
|
||||
* a quoted string.
|
||||
*/
|
||||
|
||||
if (*pbegin == '"')
|
||||
{
|
||||
/* Yes.. then only another '"' can terminate the string */
|
||||
/* A quoted string can only be terminated with another quotation
|
||||
* mark.
|
||||
*/
|
||||
|
||||
pbegin++;
|
||||
term = "\"";
|
||||
|
|
@ -612,13 +728,13 @@ static char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, char **saveptr)
|
|||
*pend && strchr(term, *pend) == NULL;
|
||||
pend++);
|
||||
|
||||
/* pend either points to the end of the string or to
|
||||
* the first delimiter after the string.
|
||||
/* pend either points to the end of the string or to the first
|
||||
* delimiter after the string.
|
||||
*/
|
||||
|
||||
if (*pend)
|
||||
{
|
||||
/* Turn the delimiter into a null terminator */
|
||||
/* Turn the delimiter into a NUL terminator */
|
||||
|
||||
*pend++ = '\0';
|
||||
}
|
||||
|
|
@ -676,7 +792,7 @@ static char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, char **saveptr)
|
|||
****************************************************************************/
|
||||
|
||||
#ifndef CONFIG_NSH_DISABLESCRIPT
|
||||
static inline bool nsh_cmdenabled(FAR struct nsh_vtbl_s *vtbl)
|
||||
static bool nsh_cmdenabled(FAR struct nsh_vtbl_s *vtbl)
|
||||
{
|
||||
struct nsh_parser_s *np = &vtbl->np;
|
||||
bool ret = !np->np_st[np->np_ndx].ns_disabled;
|
||||
|
|
@ -708,8 +824,8 @@ static inline bool nsh_cmdenabled(FAR struct nsh_vtbl_s *vtbl)
|
|||
****************************************************************************/
|
||||
|
||||
#ifndef CONFIG_NSH_DISABLESCRIPT
|
||||
static inline int nsh_ifthenelse(FAR struct nsh_vtbl_s *vtbl,
|
||||
FAR char **ppcmd, FAR char **saveptr)
|
||||
static int nsh_ifthenelse(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
|
||||
FAR char **saveptr)
|
||||
{
|
||||
struct nsh_parser_s *np = &vtbl->np;
|
||||
FAR char *cmd = *ppcmd;
|
||||
|
|
@ -848,8 +964,8 @@ errout:
|
|||
****************************************************************************/
|
||||
|
||||
#ifndef CONFIG_NSH_DISABLEBG
|
||||
static inline int nsh_nice(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
|
||||
FAR char **saveptr)
|
||||
static int nsh_nice(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
|
||||
FAR char **saveptr)
|
||||
{
|
||||
FAR char *cmd = *ppcmd;
|
||||
|
||||
|
|
@ -896,6 +1012,121 @@ static inline int nsh_nice(FAR struct nsh_vtbl_s *vtbl, FAR char **ppcmd,
|
|||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nsh_parse_funcparm
|
||||
*
|
||||
* Description:
|
||||
* This function parses and executes a simple NSH command. Output is
|
||||
* always redirected. Function function supports function paramers like
|
||||
*
|
||||
* set FOO `hello`
|
||||
*
|
||||
* which would set the environment variable FOO to the output from
|
||||
* the hello program
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_NSH_FUNCPARMS
|
||||
static int nsh_parse_funcparm(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
|
||||
FAR const char *redirfile)
|
||||
{
|
||||
FAR char *argv[MAX_ARGV_ENTRIES];
|
||||
FAR char *saveptr;
|
||||
FAR char *cmd;
|
||||
bool bgsave;
|
||||
bool redirsave;
|
||||
int argc;
|
||||
int ret;
|
||||
|
||||
/* Initialize parser state */
|
||||
|
||||
memset(argv, 0, MAX_ARGV_ENTRIES*sizeof(FAR char *));
|
||||
|
||||
/* If any options like nice, redirection, or backgrounding are attempted,
|
||||
* these will not be recognized and will just be passed through as
|
||||
* normal, invalid commands or parameters.
|
||||
*/
|
||||
|
||||
#ifndef CONFIG_NSH_DISABLEBG
|
||||
/* The function is never backgrounded . Remember the current backgrouding
|
||||
* state
|
||||
*/
|
||||
|
||||
bgsave = vtbl->np.np_bg;
|
||||
vtbl->np.np_bg = false;
|
||||
#endif
|
||||
|
||||
/* Output is always redirected. Remember the current redirection state */
|
||||
|
||||
redirsave = vtbl->np.np_redirect;
|
||||
vtbl->np.np_redirect = true;
|
||||
|
||||
/* Parse out the command at the beginning of the line */
|
||||
|
||||
saveptr = cmdline;
|
||||
cmd = nsh_argument(vtbl, &saveptr);
|
||||
|
||||
/* 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 they change the last
|
||||
* command status.
|
||||
*/
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/* 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+1
|
||||
*/
|
||||
|
||||
argv[0] = cmd;
|
||||
for (argc = 1; argc < MAX_ARGV_ENTRIES-1; argc++)
|
||||
{
|
||||
argv[argc] = nsh_argument(vtbl, &saveptr);
|
||||
if (!argv[argc])
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
argv[argc] = NULL;
|
||||
|
||||
/* Check if the maximum number of arguments was exceeded */
|
||||
|
||||
if (argc > CONFIG_NSH_MAXARGUMENTS)
|
||||
{
|
||||
nsh_output(vtbl, g_fmttoomanyargs, cmd);
|
||||
}
|
||||
|
||||
/* Then execute the command */
|
||||
|
||||
ret = nsh_execute(vtbl, argc, argv, redirfile, O_WRONLY|O_CREAT|O_TRUNC);
|
||||
|
||||
/* Restore the backgrounding and redirection state */
|
||||
|
||||
#ifndef CONFIG_NSH_DISABLEBG
|
||||
vtbl->np.np_bg = bgsave;
|
||||
#endif
|
||||
vtbl->np.np_redirect = redirsave;
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nsh_parse_command
|
||||
*
|
||||
|
|
@ -919,9 +1150,11 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline)
|
|||
/* Initialize parser state */
|
||||
|
||||
memset(argv, 0, MAX_ARGV_ENTRIES*sizeof(FAR char *));
|
||||
|
||||
#ifndef CONFIG_NSH_DISABLEBG
|
||||
vtbl->np.np_bg = false;
|
||||
#endif
|
||||
|
||||
#if CONFIG_NFILE_STREAMS > 0
|
||||
vtbl->np.np_redirect = false;
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue