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:
Gregory Nutt 2018-08-06 13:28:31 -06:00
parent af30085f9c
commit cb556d6236
2 changed files with 287 additions and 27 deletions

View File

@ -106,6 +106,17 @@ config NSH_DISABLE_SEMICOLON
command separated by a semicolon. You can disable this feature to command separated by a semicolon. You can disable this feature to
save a little memory on FLASH challenged platforms. 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 config NSH_CMDPARMS
bool "Enable commands as parameters" bool "Enable commands as parameters"
default n if DEFAULT_SMALL default n if DEFAULT_SMALL

View File

@ -59,6 +59,7 @@
/**************************************************************************** /****************************************************************************
* Pre-processor Definitions * Pre-processor Definitions
****************************************************************************/ ****************************************************************************/
/* If CONFIG_NSH_CMDPARMS or CONFIG_NSH_ARGCAT is enabled, then we will need /* 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 * retain a list of memory allocations to be freed at the completion of
* command processing. * command processing.
@ -151,11 +152,23 @@ static FAR char *nsh_strcat(FAR struct nsh_vtbl_s *vtbl, FAR char *s1,
FAR const char *s2); FAR const char *s2);
#endif #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 #ifndef CONFIG_DISABLE_ENVIRON
static FAR char *nsh_envexpand(FAR struct nsh_vtbl_s *vtbl, static FAR char *nsh_envexpand(FAR struct nsh_vtbl_s *vtbl,
FAR char *varname); FAR char *varname);
#endif #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, static FAR char *nsh_argexpand(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
FAR char **allocation); FAR char **allocation);
static FAR char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, char **saveptr, 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 #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 * Name: nsh_envexpand
****************************************************************************/ ****************************************************************************/
@ -1004,6 +1049,60 @@ static FAR char *nsh_envexpand(FAR struct nsh_vtbl_s *vtbl,
} }
#endif #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 * 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 */ /* Loop until all of the commands on the command line have been processed */
for (;;) for (; ; )
{ {
/* Look for interesting things within the command string. */ /* Look for interesting things within the command string. */
len = strcspn(working, g_arg_separator); len = strcspn(working, g_arg_separator);
ptr = working + len; 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 /* If ptr points to the NUL terminator, then there is nothing else
* interesting in the argument. * interesting in the argument.
@ -1041,24 +1174,29 @@ static FAR char *nsh_argexpand(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
* argument. * argument.
* *
* On failures to allocation memory, nsh_strcat will just return * On failures to allocation memory, nsh_strcat will just return
* value value of argument * old value of argument
*/ */
argument = nsh_strcat(vtbl, argument, working); argument = nsh_strcat(vtbl, argument, working);
*allocation = argument; *allocation = argument;
/* De-quote the returned string */
nsh_dequote(argument);
return argument; return argument;
} }
else else
{ {
/* No.. just return the original string from the command line. */ /* No.. just return the original string from the command line. */
nsh_dequote(cmdline);
return cmdline; return cmdline;
} }
} }
else else
#ifdef CONFIG_NSH_CMDPARMS #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 == '`') 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 *result;
FAR char *rptr; 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. * 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); argument = nsh_strcat(vtbl, argument, working);
*allocation = argument; *allocation = argument;
/* Find the closing backquote */ /* Find the closing back-quote (must be unquoted) */
rptr = strchr(ptr, '`'); rptr = nsh_strchr(ptr, '`');
if (!rptr) if (!rptr)
{ {
nsh_output(vtbl, g_fmtnomatching, "`", "`"); nsh_output(vtbl, g_fmtnomatching, "`", "`");
return (FAR char *)g_nullstring; return (FAR char *)g_nullstring;
} }
/* Replace the final backquote with a NUL terminator */ /* Replace the final back-quote with a NUL terminator */
*rptr = '\0'; *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 /* Concatenate the result of the operation with the accumulated
* string. On failures to allocation memory, nsh_strcat will * 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); 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 */ /* Find the closing right bracket */
rptr = strchr(ptr, '}'); rptr = nsh_strchr(ptr, '}');
if (!rptr) if (!rptr)
{ {
nsh_output(vtbl, g_fmtnomatching, "${", "}"); 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 /* Concatenate the result of the operation with the accumulated
* string. On failures to allocation memory, nsh_strcat will * 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); 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 **allocation)
{ {
FAR char *argument = (FAR char *)g_nullstring; 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 #ifdef CONFIG_NSH_CMDPARMS
/* Are we being asked to use the output from another command or program /* 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 == '`') 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') if (!rptr || rptr[1] != '\0')
{ {
nsh_output(vtbl, g_fmtnomatching, "`", "`"); nsh_output(vtbl, g_fmtnomatching, "`", "`");
return (FAR char *)g_nullstring; return (FAR char *)g_nullstring;
} }
/* Replace the final backquote with a NUL terminator */ /* Replace the final back-quote with a NUL terminator */
*rptr = '\0'; *rptr = '\0';
@ -1239,7 +1391,6 @@ static FAR char *nsh_argexpand(FAR struct nsh_vtbl_s *vtbl, FAR char *cmdline,
} }
else else
#endif #endif
{ {
/* The argument to be returned is simply the beginning of the /* The argument to be returned is simply the beginning of the
* delimited string. * 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 *allocation = NULL;
FAR char *argument = NULL; FAR char *argument = NULL;
FAR const char *term; FAR const char *term;
#ifdef CONFIG_NSH_QUOTE
FAR char *prev;
bool quoted;
#endif
#ifdef CONFIG_NSH_CMDPARMS #ifdef CONFIG_NSH_CMDPARMS
bool backquote; bool backquote;
#endif #endif
@ -1345,13 +1500,38 @@ static FAR char *nsh_argument(FAR struct nsh_vtbl_s *vtbl, FAR char **saveptr,
#ifdef CONFIG_NSH_CMDPARMS #ifdef CONFIG_NSH_CMDPARMS
/* Some special care must be exercised to make sure that we do not break up /* 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 * any back-quote delimited substrings. NOTE that the absence of a closing
* backquote is not detected; That case should be detected later. * 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 == '`') 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 /* 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. * 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 #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). * 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; for (pend = pbegin;
*pend != '\0' && strchr(term, *pend) == NULL; *pend != '\0' && nsh_strchr(term, *pend) == NULL;
pend++) pend++)
{ {
} }
#endif
#endif /* CONFIG_NSH_QUOTE */
#endif /* CONFIG_NSH_CMDPARMS */
/* pend either points to the end of the string or to the first /* pend either points to the end of the string or to the first
* delimiter after the string. * 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 */ /* Find the closing quotation mark */
FAR char *tmp = strchr(ptr + 1, '"'); FAR char *tmp = nsh_strchr(ptr + 1, '"');
if (!tmp) if (!tmp)
{ {
/* No closing quotation mark! */ /* No closing quotation mark! */