apps/nshlib: Add parsing support for back-slash quoted characters. Currenlty only implemented properly if CONFIG_NSH_ARGCAT is also selected. This commit is in response to Bitbucket Issue 11 opened by Maciej Wójcik
This commit is contained in:
parent
af30085f9c
commit
cb556d6236
@ -106,6 +106,17 @@ 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_QUOTE
|
||||
bool "Enable back-slash quoting of characters"
|
||||
default n if DEFAULT_SMALL || !NSH_ARGCAT
|
||||
default y if !DEFAULT_SMALL && NSH_ARGCAT
|
||||
---help---
|
||||
Force special characters like back-quotes, quotation marks, and the
|
||||
back-slash character itself to be treat like normal text.
|
||||
|
||||
This feature is only implemented properly for the case where
|
||||
CONFIG_NSH_ARGCAT is also selected.
|
||||
|
||||
config NSH_CMDPARMS
|
||||
bool "Enable commands as parameters"
|
||||
default n if DEFAULT_SMALL
|
||||
|
@ -59,6 +59,7 @@
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/* If CONFIG_NSH_CMDPARMS or CONFIG_NSH_ARGCAT is enabled, then we will need
|
||||
* retain a list of memory allocations to be freed at the completion of
|
||||
* command processing.
|
||||
@ -151,11 +152,23 @@ 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
|
||||
|
||||
#ifndef CONFIG_DISABLE_ENVIRON
|
||||
static FAR char *nsh_envexpand(FAR struct nsh_vtbl_s *vtbl,
|
||||
FAR char *varname);
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_NSH_QUOTE) && defined(CONFIG_NSH_ARGCAT)
|
||||
static void nsh_dequote(FAR char *cmdline);
|
||||
#else
|
||||
# define nsh_dequote(c)
|
||||
#endif
|
||||
|
||||
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,
|
||||
@ -963,6 +976,38 @@ static FAR char *nsh_strcat(FAR struct nsh_vtbl_s *vtbl, FAR char *s1,
|
||||
}
|
||||
#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_envexpand
|
||||
****************************************************************************/
|
||||
@ -1004,6 +1049,60 @@ static FAR char *nsh_envexpand(FAR struct nsh_vtbl_s *vtbl,
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nsh_dequote
|
||||
****************************************************************************/
|
||||
|
||||
#if defined(CONFIG_NSH_QUOTE) && defined(CONFIG_NSH_ARGCAT)
|
||||
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;
|
||||
|
||||
/* Move the data to eliminate the quote from the command line */
|
||||
|
||||
do
|
||||
{
|
||||
ch = *src++;
|
||||
*dest++ = ch;
|
||||
}
|
||||
while (ch != '\0');
|
||||
|
||||
/* Remember that the next character is quote (in case it is
|
||||
* another back-slash character).
|
||||
*/
|
||||
|
||||
quoted = true;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* The next character is not quoted because either (1) it was not
|
||||
* preceded by a back-slash, or (2) it was preceded by a quoted
|
||||
* back-slash.
|
||||
*/
|
||||
|
||||
quoted = false;
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure that the new, possibly shorted string is NUL terminated */
|
||||
|
||||
*ptr = '\0';
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: nsh_argexpand
|
||||
****************************************************************************/
|
||||
@ -1019,12 +1118,46 @@ static FAR char *nsh_argexpand(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
|
||||
|
||||
/* Loop until all of the commands on the command line have been processed */
|
||||
|
||||
for (;;)
|
||||
for (; ; )
|
||||
{
|
||||
/* Look for interesting things within the command string. */
|
||||
|
||||
len = strcspn(working, g_arg_separator);
|
||||
ptr = working + len;
|
||||
len = strcspn(working, g_arg_separator);
|
||||
ptr = working + len;
|
||||
|
||||
#ifdef CONFIG_NSH_QUOTE
|
||||
/* But ignore these interesting things if they are quoted */
|
||||
|
||||
while (len > 0 && *ptr != '\0')
|
||||
{
|
||||
FAR char *prev = working + len - 1;
|
||||
int bcount;
|
||||
bool quoted;
|
||||
|
||||
/* Check if the current character is quoted */
|
||||
|
||||
for (bcount = 0, quoted = false;
|
||||
bcount < len && *prev == '\\';
|
||||
bcount++, prev--)
|
||||
{
|
||||
quoted ^= true;
|
||||
}
|
||||
|
||||
if (quoted)
|
||||
{
|
||||
/* Yes.. skip over it */
|
||||
|
||||
len += strcspn(ptr + 1, g_arg_separator) + 1;
|
||||
ptr = working + len;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Not quoted.. subject to normal processing */
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* If ptr points to the NUL terminator, then there is nothing else
|
||||
* interesting in the argument.
|
||||
@ -1041,24 +1174,29 @@ static FAR char *nsh_argexpand(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
|
||||
* argument.
|
||||
*
|
||||
* On failures to allocation memory, nsh_strcat will just return
|
||||
* value value of argument
|
||||
* old value of argument
|
||||
*/
|
||||
|
||||
argument = nsh_strcat(vtbl, argument, working);
|
||||
*allocation = argument;
|
||||
|
||||
/* De-quote the returned string */
|
||||
|
||||
nsh_dequote(argument);
|
||||
return argument;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* No.. just return the original string from the command line. */
|
||||
|
||||
nsh_dequote(cmdline);
|
||||
return cmdline;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
#ifdef CONFIG_NSH_CMDPARMS
|
||||
/* Check for a backquoted command embedded within the argument string. */
|
||||
/* Check for a back-quoted command embedded within the argument string. */
|
||||
|
||||
if (*ptr == '`')
|
||||
{
|
||||
@ -1066,7 +1204,7 @@ static FAR char *nsh_argexpand(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
|
||||
FAR char *result;
|
||||
FAR char *rptr;
|
||||
|
||||
/* Replace the backquote with a NUL terminator and add the
|
||||
/* Replace the back-quote with a NUL terminator and add the
|
||||
* intervening character to the concatenated string.
|
||||
*/
|
||||
|
||||
@ -1074,16 +1212,16 @@ static FAR char *nsh_argexpand(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
|
||||
argument = nsh_strcat(vtbl, argument, working);
|
||||
*allocation = argument;
|
||||
|
||||
/* Find the closing backquote */
|
||||
/* Find the closing back-quote (must be unquoted) */
|
||||
|
||||
rptr = strchr(ptr, '`');
|
||||
rptr = nsh_strchr(ptr, '`');
|
||||
if (!rptr)
|
||||
{
|
||||
nsh_output(vtbl, g_fmtnomatching, "`", "`");
|
||||
return (FAR char *)g_nullstring;
|
||||
}
|
||||
|
||||
/* Replace the final backquote with a NUL terminator */
|
||||
/* Replace the final back-quote with a NUL terminator */
|
||||
|
||||
*rptr = '\0';
|
||||
|
||||
@ -1095,7 +1233,7 @@ static FAR char *nsh_argexpand(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
|
||||
|
||||
/* Concatenate the result of the operation with the accumulated
|
||||
* string. On failures to allocation memory, nsh_strcat will
|
||||
* just return value value of argument
|
||||
* just return old value of argument
|
||||
*/
|
||||
|
||||
argument = nsh_strcat(vtbl, argument, result);
|
||||
@ -1143,7 +1281,7 @@ static FAR char *nsh_argexpand(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
|
||||
|
||||
/* Find the closing right bracket */
|
||||
|
||||
rptr = strchr(ptr, '}');
|
||||
rptr = nsh_strchr(ptr, '}');
|
||||
if (!rptr)
|
||||
{
|
||||
nsh_output(vtbl, g_fmtnomatching, "${", "}");
|
||||
@ -1179,7 +1317,7 @@ static FAR char *nsh_argexpand(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
|
||||
|
||||
/* Concatenate the result of the operation with the accumulated
|
||||
* string. On failures to allocation memory, nsh_strcat will
|
||||
* just return value value of argument
|
||||
* just return old value of argument
|
||||
*/
|
||||
|
||||
argument = nsh_strcat(vtbl, argument, envstr);
|
||||
@ -1202,6 +1340,20 @@ 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_QUOTE
|
||||
char ch = *cmdline;
|
||||
|
||||
/* A single backslash at the beginning of the line is support, nothing
|
||||
* more.
|
||||
*/
|
||||
|
||||
nsh_dequote(cmdline);
|
||||
if (ch == '\\')
|
||||
{
|
||||
argument = cmdline;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_NSH_CMDPARMS
|
||||
/* Are we being asked to use the output from another command or program
|
||||
@ -1210,16 +1362,16 @@ static FAR char *nsh_argexpand(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
|
||||
|
||||
if (*cmdline == '`')
|
||||
{
|
||||
/* Verify that the final character is also a backquote */
|
||||
/* Verify that the final character is also a back-quote */
|
||||
|
||||
FAR char *rptr = strchr(cmdline + 1, '`');
|
||||
FAR char *rptr = nsh_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 */
|
||||
/* Replace the final back-quote with a NUL terminator */
|
||||
|
||||
*rptr = '\0';
|
||||
|
||||
@ -1239,7 +1391,6 @@ static FAR char *nsh_argexpand(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
||||
{
|
||||
/* The argument to be returned is simply the beginning of the
|
||||
* delimited string.
|
||||
@ -1264,6 +1415,10 @@ 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;
|
||||
#ifdef CONFIG_NSH_QUOTE
|
||||
FAR char *prev;
|
||||
bool quoted;
|
||||
#endif
|
||||
#ifdef CONFIG_NSH_CMDPARMS
|
||||
bool backquote;
|
||||
#endif
|
||||
@ -1345,13 +1500,38 @@ static FAR char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, FAR char **saveptr,
|
||||
|
||||
#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.
|
||||
* any back-quote delimited substrings. NOTE that the absence of a closing
|
||||
* back-quote is not detected; That case should be detected later.
|
||||
*/
|
||||
|
||||
for (backquote = false, pend = pbegin; *pend; pend++)
|
||||
#ifdef CONFIG_NSH_QUOTE
|
||||
quoted = false;
|
||||
backquote = false;
|
||||
|
||||
for (prev = NULL, pend = pbegin; *pend != '\0'; prev = pend, pend++)
|
||||
{
|
||||
/* Toggle the backquote flag when one is encountered? */
|
||||
/* Check if the current character is quoted */
|
||||
|
||||
if (prev != NULL && *prev == '\\' && !quoted)
|
||||
{
|
||||
/* Do no special checks on the quoted character */
|
||||
|
||||
quoted = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
quoted = false;
|
||||
|
||||
/* Check if the current character is an (unquoted) back-quote */
|
||||
|
||||
if (*pend == '\\' && !quoted)
|
||||
{
|
||||
/* Yes.. Do no special processing on the backspace character */
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Toggle the back-quote flag when one is encountered? */
|
||||
|
||||
if (*pend == '`')
|
||||
{
|
||||
@ -1359,12 +1539,12 @@ static FAR char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, FAR char **saveptr,
|
||||
}
|
||||
|
||||
/* Check for a delimiting character only if we are not in a
|
||||
* backquoted sub-string.
|
||||
* back-quoted sub-string.
|
||||
*/
|
||||
|
||||
else if (!backquote && strchr(term, *pend) != NULL)
|
||||
else if (!backquote && nsh_strchr(term, *pend) != NULL)
|
||||
{
|
||||
/* We found a delimiter outside of anybackqouted substring.
|
||||
/* We found a delimiter outside of any back-quoted substring.
|
||||
* Now we can break out of the loop.
|
||||
*/
|
||||
|
||||
@ -1372,16 +1552,85 @@ static FAR char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, FAR char **saveptr,
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* Search the next occurence of a terminating character (or the end
|
||||
backquote = false;
|
||||
|
||||
for (pend = pbegin; *pend != '\0'; pend++)
|
||||
{
|
||||
/* Toggle the back-quote flag when one is encountered? */
|
||||
|
||||
if (*pend == '`')
|
||||
{
|
||||
backquote = !backquote;
|
||||
}
|
||||
|
||||
/* Check for a delimiting character only if we are not in a
|
||||
* back-quoted sub-string.
|
||||
*/
|
||||
|
||||
else if (!backquote && nsh_strchr(term, *pend) != NULL)
|
||||
{
|
||||
/* We found a delimiter outside of any back-quoted substring.
|
||||
* Now we can break out of the loop.
|
||||
*/
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* CONFIG_NSH_QUOTE */
|
||||
#else /* CONFIG_NSH_CMDPARMS */
|
||||
|
||||
/* Search the next occurrence of a terminating character (or the end
|
||||
* of the line).
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_NSH_QUOTE
|
||||
quoted = false;
|
||||
|
||||
for (prev = NULL, pend = pbegin; *pend != '\0'; prev = pend, pend++)
|
||||
{
|
||||
/* Check if the current character is quoted */
|
||||
|
||||
if (prev != NULL && *prev == '\\' && !quoted)
|
||||
{
|
||||
|
||||
/* Do no special checks on the quoted character */
|
||||
|
||||
quoted = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
quoted = false;
|
||||
|
||||
/* Check if the current character is an (unquoted) back-quote */
|
||||
|
||||
if (*pend == '\\' && !quoted)
|
||||
{
|
||||
/* Yes.. Do no special processing on the backspace character */
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check for a delimiting character */
|
||||
|
||||
if (nsh_strchr(term, *pend) != NULL)
|
||||
{
|
||||
/* We found a delimiter. Now we can break out of the loop. */
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
for (pend = pbegin;
|
||||
*pend != '\0' && strchr(term, *pend) == NULL;
|
||||
*pend != '\0' && nsh_strchr(term, *pend) == NULL;
|
||||
pend++)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* CONFIG_NSH_QUOTE */
|
||||
#endif /* CONFIG_NSH_CMDPARMS */
|
||||
|
||||
/* pend either points to the end of the string or to the first
|
||||
* delimiter after the string.
|
||||
@ -2284,7 +2533,7 @@ int nsh_parse(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline)
|
||||
{
|
||||
/* Find the closing quotation mark */
|
||||
|
||||
FAR char *tmp = strchr(ptr + 1, '"');
|
||||
FAR char *tmp = nsh_strchr(ptr + 1, '"');
|
||||
if (!tmp)
|
||||
{
|
||||
/* No closing quotation mark! */
|
||||
|
Loading…
x
Reference in New Issue
Block a user