summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2014-01-10 18:42:24 -0600
committerGregory Nutt <gnutt@nuttx.org>2014-01-10 18:42:24 -0600
commit9a3db48a553a6d62d9954d6b20327e95505578cc (patch)
tree878aaee60f14cf39ebc08abc354b1ab2c5546958 /apps
parentaddae32212c5c7650148aadd422478bbffa66d7c (diff)
downloadnuttx-9a3db48a553a6d62d9954d6b20327e95505578cc.tar.gz
nuttx-9a3db48a553a6d62d9954d6b20327e95505578cc.tar.bz2
nuttx-9a3db48a553a6d62d9954d6b20327e95505578cc.zip
Add support for backquoated commands as command arguments
Diffstat (limited to 'apps')
-rw-r--r--apps/ChangeLog.txt3
-rw-r--r--apps/nshlib/Kconfig44
-rw-r--r--apps/nshlib/nsh.h9
-rw-r--r--apps/nshlib/nsh_parse.c269
4 files changed, 307 insertions, 18 deletions
diff --git a/apps/ChangeLog.txt b/apps/ChangeLog.txt
index 57cc12c48..0b377bf91 100644
--- a/apps/ChangeLog.txt
+++ b/apps/ChangeLog.txt
@@ -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).
diff --git a/apps/nshlib/Kconfig b/apps/nshlib/Kconfig
index 2a96b851b..7d12d4d65 100644
--- a/apps/nshlib/Kconfig
+++ b/apps/nshlib/Kconfig
@@ -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
diff --git a/apps/nshlib/nsh.h b/apps/nshlib/nsh.h
index fe85f34ce..1466b7187 100644
--- a/apps/nshlib/nsh.h
+++ b/apps/nshlib/nsh.h
@@ -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 */
diff --git a/apps/nshlib/nsh_parse.c b/apps/nshlib/nsh_parse.c
index 3a944261a..10ac3a258 100644
--- a/apps/nshlib/nsh_parse.c
+++ b/apps/nshlib/nsh_parse.c
@@ -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;
@@ -897,6 +1013,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
*
* Description:
@@ -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