summaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2014-01-11 13:18:12 -0600
committerGregory Nutt <gnutt@nuttx.org>2014-01-11 13:18:12 -0600
commit0eb975dd496c6c9726e40999fcf132b2e6189d86 (patch)
tree3b72b40118d0069c060e2ac3593a6e091d037108 /apps
parenta31974f5f25547a6f4e4aadf5056602905b72b87 (diff)
downloadnuttx-0eb975dd496c6c9726e40999fcf132b2e6189d86.tar.gz
nuttx-0eb975dd496c6c9726e40999fcf132b2e6189d86.tar.bz2
nuttx-0eb975dd496c6c9726e40999fcf132b2e6189d86.zip
NSH can not handle command arguments that are concatenations of constant strings, command output, application program output, and environment varaible values.
Diffstat (limited to 'apps')
-rw-r--r--apps/ChangeLog.txt3
-rw-r--r--apps/nshlib/nsh_parse.c471
2 files changed, 370 insertions, 104 deletions
diff --git a/apps/ChangeLog.txt b/apps/ChangeLog.txt
index 03dd38a3a..d2711e456 100644
--- a/apps/ChangeLog.txt
+++ b/apps/ChangeLog.txt
@@ -771,4 +771,7 @@
on initial commit (2014-1-10).
* Logic to support commands enclosed in back quotes is functional
but not thoroughly tested (2014-1-11).
+ * apps/nshlib/nsh_parse.c: Can now handle arguments that are
+ concatenations of constant strings, command return data, application
+ return data, and environment variables (2014-1-11).
diff --git a/apps/nshlib/nsh_parse.c b/apps/nshlib/nsh_parse.c
index 97c87a7a1..4fdeb422a 100644
--- a/apps/nshlib/nsh_parse.c
+++ b/apps/nshlib/nsh_parse.c
@@ -121,7 +121,7 @@ struct nsh_memlist_s
#ifdef HAVE_MEMLIST
static void nsh_memlist_add(FAR struct nsh_memlist_s *memlist,
- FAR char *address);
+ FAR char *allocation);
static void nsh_memlist_free(FAR struct nsh_memlist_s *memlist);
#endif
@@ -144,8 +144,13 @@ static FAR char *nsh_cmdparm(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
FAR char **allocation);
#endif
#ifdef CONFIG_NSH_ARGCAT
-static int nsh_strcat(FAR char *s1, FAR const char *s2);
+static FAR char *nsh_strcat(FAR struct nsh_vtbl_s *vtbl, FAR char *s1,
+ FAR const char *s2);
#endif
+static FAR char *nsh_envexpand(FAR struct nsh_vtbl_s *vtbl,
+ FAR char *varname);
+static FAR char *nsh_argexpand(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
+ FAR char **allocation);
static FAR char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, char **saveptr,
FAR NSH_MEMLIST_TYPE *memlist);
@@ -171,12 +176,18 @@ static int nsh_parse_command(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline);
****************************************************************************/
static const char g_token_separator[] = " \t\n";
+#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_redirect1[] = ">";
static const char g_redirect2[] = ">>";
-static const char g_exitstatus[] = "$?";
+static const char g_exitstatus[] = "?";
static const char g_success[] = "0";
static const char g_failure[] = "1";
+static const char g_nullstring[] = "";
/****************************************************************************
* Public Data
@@ -240,14 +251,14 @@ const char g_fmtsignalrecvd[] = "nsh: %s: Interrupted by signal\n";
#ifdef HAVE_MEMLIST
static void nsh_memlist_add(FAR struct nsh_memlist_s *memlist,
- FAR char *address)
+ FAR char *allocation)
{
- if (memlist)
+ if (memlist && allocation)
{
int index = memlist->nallocs;
if (index < CONFIG_NSH_MAXALLOCS)
{
- memlist->allocations[index] = address;
+ memlist->allocations[index] = allocation;
memlist->nallocs = index + 1;
}
}
@@ -722,9 +733,9 @@ static FAR char *nsh_filecat(FAR struct nsh_vtbl_s *vtbl, FAR char *s1,
for (index = s1size; index < allocsize - 1; )
{
/* Loop until we successfully read something , we encounter the
- * end-of-file, or until a read error occurs
+ * end-of-file, or until a read error occurs
*/
-
+
do
{
nbytesread = read(fd, &argument[index], IOBUFFERSIZE);
@@ -791,7 +802,7 @@ static FAR char *nsh_cmdparm(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
if (!allocation)
{
- return NULL;
+ return (FAR char *)g_nullstring;
}
/* Create a unique file name using the task ID */
@@ -801,7 +812,7 @@ static FAR char *nsh_cmdparm(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
if (ret < 0 || !tmpfile)
{
nsh_output(vtbl, g_fmtcmdoutofmemory, "``");
- return NULL;
+ return (FAR char *)g_nullstring;
}
/* Execute the command that will re-direct the output of the command to
@@ -816,7 +827,7 @@ static FAR char *nsh_cmdparm(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
nsh_output(vtbl, g_fmtcmdfailed, "``", "exec", NSH_ERRNO);
free(tmpfile);
- return NULL;
+ return (FAR char *)g_nullstring;
}
/* Concatenate the file contents with the current allocation */
@@ -842,8 +853,312 @@ static FAR char *nsh_cmdparm(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
****************************************************************************/
#ifdef CONFIG_NSH_ARGCAT
-static int nsh_strcat(FAR char *s1, FAR const char *s2)
+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;
+
+ /* Get the size of the first string... it might be NULL */
+
+ if (s1)
+ {
+ s1size = strlen(s1);
+ }
+
+ /* Then reallocate the first string so that it is large enough to hold
+ * both (including the NUL terminator).
+ */
+
+ allocsize = s1size + strlen(s2) + 1;
+ argument = (FAR char *)realloc(s1, allocsize);
+ if (!argument)
+ {
+ nsh_output(vtbl, g_fmtcmdoutofmemory, "$");
+ argument = s1;
+ }
+ else
+ {
+ argument[s1size] = '\0'; /* (In case s1 was NULL) */
+ strcat(argument, s2);
+ }
+
+ return argument;
+}
+#endif
+
+/****************************************************************************
+ * Name: nsh_envexpand
+ ****************************************************************************/
+
+#ifndef CONFIG_DISABLE_ENVIRON
+static FAR char *nsh_envexpand(FAR struct nsh_vtbl_s *vtbl,
+ FAR char *varname)
+{
+ /* Check for built-in variables */
+
+ if (strcmp(varname, g_exitstatus) == 0)
+ {
+ if (vtbl->np.np_fail)
+ {
+ return (FAR char *)g_failure;
+ }
+ else
+ {
+ return (FAR char *)g_success;
+ }
+ }
+
+ /* Not a built-in? Return the value of the environment variable with this
+ * name.
+ */
+
+ else
+ {
+ FAR char *value = getenv(varname);
+ if (value)
+ {
+ return value;
+ }
+ else
+ {
+ return (FAR char *)g_nullstring;
+ }
+ }
+}
+#endif
+
+/****************************************************************************
+ * 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 char *working = cmdline;
+ FAR char *argument = NULL;
+ FAR char *ptr;
+ size_t len;
+
+ /* Loop until all of the commands on the command line have been processed */
+
+ for (;;)
+ {
+ /* Look for interesting things within the command string. */
+
+ len = strcspn(working, g_arg_separator);
+ ptr = working + len;
+
+ /* If ptr points to the NUL terminator, then there is nothing else
+ * interesting in the argument.
+ */
+
+ if (*ptr == '\0')
+ {
+ /* Was anything previously concatenated? */
+
+ if (argument)
+ {
+ /* Yes, then we probably need to add the last part of the argument
+ * beginning at the last working pointer to the concatenated
+ * argument.
+ *
+ * On failures to allocation memory, nsh_strcat will just return
+ * value value of argument
+ */
+
+ argument = nsh_strcat(vtbl, argument, working);
+ *allocation = argument;
+ return argument;
+ }
+ else
+ {
+ /* No.. just return the original string from the command line. */
+
+ return cmdline;
+ }
+ }
+ else
+
+#ifdef CONFIG_NSH_CMDPARMS
+ /* Check for a backquoted command embedded within the argument string. */
+
+ if (*ptr == '`')
+ {
+ FAR char *tmpalloc;
+ FAR char *result;
+ FAR char *rptr;
+
+ /* Replace the backqutote with a NUL terminator and add the
+ * intervening character to the concatenated string.
+ */
+
+ *ptr++ = '\0';
+ argument = nsh_strcat(vtbl, argument, working);
+ *allocation = argument;
+
+ /* Find the closing backquote */
+
+ rptr = strchr(ptr, '`');
+ if (!rptr)
+ {
+ nsh_output(vtbl, g_fmtnomatching, "`", "`");
+ return (FAR char *)g_nullstring;
+ }
+
+ /* Replace the final backquote with a NUL terminator */
+
+ *rptr = '\0';
+
+ /* Then execute the command to get the sub-string value. On
+ * error, nsh_cmdparm may return g_nullstring but never NULL.
+ */
+
+ result = nsh_cmdparm(vtbl, ptr, &tmpalloc);
+
+ /* Concatenate the result of the operation with the accumulated
+ * string. On failures to allocation memory, nsh_strcat will
+ * just return value value of argument
+ */
+
+ argument = nsh_strcat(vtbl, argument, result);
+ *allocation = argument;
+ working = rptr + 1;
+
+ /* And free any temporary allocations */
+
+ if (tmpalloc)
+ {
+ free(tmpalloc);
+ }
+ }
+ else
+#endif
+
+#ifndef CONFIG_DISABLE_ENVIRON
+ /* Check if we encountered a reference to an environment variable */
+
+ if (*ptr == '$')
+ {
+ FAR char *envstr;
+ FAR char *rptr;
+
+ /* Replace the dollar sign with a NUL terminator and add the
+ * intervening character to the concatenated string.
+ */
+
+ *ptr++ = '\0';
+ argument = nsh_strcat(vtbl, argument, working);
+ *allocation = argument;
+
+ /* Find the end of the environment variable reference. If the
+ * dollar sign ('$') is followed by a right bracket ('{') then the
+ * variable name is terminated with the left bracket character
+ * ('}'). Otherwise, the variable name goes to the end of the
+ * argument.
+ */
+
+ if (*ptr == '{')
+ {
+ /* Skip over the left bracket */
+
+ ptr++;
+
+ /* Find the closing right bracket */
+
+ rptr = strchr(ptr, '}');
+ if (!rptr)
+ {
+ nsh_output(vtbl, g_fmtnomatching, "${", "}");
+ return (FAR char *)g_nullstring;
+ }
+
+ /* Replace the right bracket with a NUL terminator and set the
+ * working pointer to the character after the bracket.
+ */
+
+ *rptr = '\0';
+ working = rptr + 1;
+ }
+ else
+ {
+ /* Set working to the NUL terminator at the end of the string */
+
+ working = ptr + strlen(ptr);
+ }
+
+ /* Then get the value of the environment variable. On errors,
+ * nsh_envexpand will return the NULL string.
+ */
+
+ envstr = nsh_envexpand(vtbl, ptr);
+
+ /* Concatenate the result of the operation with the accumulated
+ * string. On failures to allocation memory, nsh_strcat will
+ * just return value value of argument
+ */
+
+ argument = nsh_strcat(vtbl, argument, envstr);
+ *allocation = argument;
+ }
+#endif
+ }
+}
+
+#else
+static FAR char *nsh_argexpand(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
+ FAR char **allocation)
{
+ FAR char *argument = (FAR char *)g_nullstring;
+
+#ifdef CONFIG_NSH_CMDPARMS
+ /* Are we being asked to use the output from another command or program
+ * as an input parameters for this command?
+ */
+
+ if (*cmdline == '`')
+ {
+ /* Verify that the final character is also a backquote */
+
+ FAR char *rptr = strchr(cmdline + 1, '`');
+ if (!rptr || rptr[1] != '\0')
+ {
+ nsh_output(vtbl, g_fmtnomatching, "`", "`");
+ return (FAR char *)g_nullstring;
+ }
+
+ /* Replace the final backquote with a NUL terminator */
+
+ *rptr = '\0';
+
+ /* Then execute the command to get the paramter value */
+
+ argument = nsh_cmdparm(vtbl, cmdline + 1, allocation);
+ }
+ else
+#endif
+
+#ifndef CONFIG_DISABLE_ENVIRON
+ /* Check for references to environment variables */
+
+ if (*cmdline == '$')
+ {
+ argument = nsh_envexpand(vtbl, cmdline + 1);
+ }
+ else
+#endif
+
+ {
+ /* The argument to be returned is simply the beginning of the
+ * delimited string.
+ */
+
+ argument = cmdline;
+ }
+
+ return argument;
}
#endif
@@ -859,8 +1174,8 @@ static FAR char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, FAR char **saveptr,
FAR char *allocation = NULL;
FAR char *argument = NULL;
FAR const char *term;
-#ifndef CONFIG_DISABLE_ENVIRON
- bool quoted = false;
+#ifdef CONFIG_NSH_CMDPARMS
+ bool backquote;
#endif
/* Find the beginning of the next token */
@@ -906,41 +1221,7 @@ static FAR char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, FAR char **saveptr,
pbegin = NULL;
}
-#ifdef CONFIG_NSH_CMDPARMS
- /* Are we being asked to use the output from another command or program
- * as an input parameters for this command?
- */
-
- else if (*pbegin == '`')
- {
- /* Yes, find the terminating 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;
-
- /* Then execute the command to get the paramter value */
-
- argument = nsh_cmdparm(vtbl, pbegin, &allocation);
- }
-#endif
-
- /* Otherwise, it is a normal string and we have to parse using the normal
+ /* Otherwise, it is a normal argument and we have to parse using the normal
* rules to find the end of the argument.
*/
@@ -958,9 +1239,6 @@ static FAR char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, FAR char **saveptr,
pbegin++;
term = "\"";
-#ifndef CONFIG_DISABLE_ENVIRON
- quoted = true;
-#endif
}
else
{
@@ -971,9 +1249,37 @@ static FAR char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, FAR char **saveptr,
/* Find the end of the string */
- for (pend = pbegin + 1;
- *pend && strchr(term, *pend) == NULL;
- pend++);
+#ifdef CONFIG_NSH_CMDPARMS
+ /* Some special care must be exercised to make sure that we do not break up
+ * any backquote delimited substrings. NOTE that the absence of a closing
+ * backquote is not detected; That case should be detected later.
+ */
+
+ for (backquote = false, pend = pbegin; *pend; pend++)
+ {
+ /* Toggle the backquote flag when one is encountered? */
+
+ if (*pend == '`')
+ {
+ backquote = !backquote;
+ }
+
+ /* Check for a delimiting character only if we are not in a
+ * backquoted sub-string.
+ */
+
+ else if (!backquote && strchr(term, *pend) != NULL)
+ {
+ /* We found a delimiter outside of anybackqouted substring.
+ * Now we can break out of the loop.
+ */
+
+ break;
+ }
+ }
+#else
+ for (pend = pbegin + 1; *pend && strchr(term, *pend) == NULL; pend++);
+#endif
/* pend either points to the end of the string or to the first
* delimiter after the string.
@@ -990,60 +1296,17 @@ static FAR char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, FAR char **saveptr,
*saveptr = pend;
-#ifndef CONFIG_DISABLE_ENVIRON
- /* Check for references to environment variables */
-
- if (pbegin[0] == '$' && !quoted)
- {
- /* Check for built-in variables */
-
- if (strcmp(pbegin, g_exitstatus) == 0)
- {
- if (vtbl->np.np_fail)
- {
- return (char*)g_failure;
- }
- else
- {
- return (char*)g_success;
- }
- }
-
- /* Not a built-in? Return the value of the environment variable
- * with this name.
- */
-
- else
- {
- char *value = getenv(pbegin+1);
- if (value)
- {
- return value;
- }
- else
- {
- return (char*)"";
- }
- }
- }
-#endif
- /* The argument to be returned is simply the beginning of the
- * delimited string.
- */
+ /* Perform expansions as necessary for the argument */
- argument = pbegin;
+ argument = nsh_argexpand(vtbl, pbegin, &allocation);
}
-
/* If any memory was allocated for this argument, make sure that it is
* added to the list of memory to be freed at the end of commend
* processing.
*/
- if (allocation)
- {
- NSH_MEMLIST_ADD(memlist, allocation);
- }
+ NSH_MEMLIST_ADD(memlist, allocation);
/* Return the parsed argument. */
@@ -1651,18 +1914,18 @@ int nsh_parse(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline)
{
/* Find the closing quotation mark */
- FAR char *tmp = strchr(ptr, '"');
+ FAR char *tmp = strchr(ptr + 1, '"');
if (!tmp)
{
/* No closing quotation mark! */
- nsh_output(vtbl, g_fmtnomatching, "[\"]", "[\"]");
+ nsh_output(vtbl, g_fmtnomatching, "\"", "\"");
return ERROR;
}
/* Otherwise, continue parsing after the closing quotation mark */
- working = tmp++;
+ working = ++tmp;
}
}
#endif