[cli] allow to escape separators (#4608)

This commit is contained in:
Eduardo Montoya
2020-03-02 08:48:31 -08:00
committed by GitHub
parent f4775fe13a
commit 15f6808206
5 changed files with 74 additions and 18 deletions
+21
View File
@@ -5,6 +5,27 @@ command line interface. Use the CLI to play with OpenThread, which
can also be used with additional application code. The can also be used with additional application code. The
OpenThread test scripts use the CLI to execute test cases. OpenThread test scripts use the CLI to execute test cases.
## Separator and escaping characters
The whitespace character (`' '`) is used to delimit the command name
and the different arguments, together with tab (`'\t'`) and new line
characters (`'\r'`, `'\n'`).
Some arguments might require to accept whitespaces on them. For those
cases the backslash character (`'\'`) can be used to escape separators
or the backslash itself.
Example:
```bash
> networkname Test\ Network
Done
> networkname
Test Network
Done
>
```
## OpenThread Command List ## OpenThread Command List
* [bufferinfo](#bufferinfo) * [bufferinfo](#bufferinfo)
+17 -12
View File
@@ -31,6 +31,8 @@
* This file implements the command line parser. * This file implements the command line parser.
*/ */
#include <string.h>
#include "parse_cmdline.hpp" #include "parse_cmdline.hpp"
#include "common/code_utils.hpp" #include "common/code_utils.hpp"
@@ -38,11 +40,16 @@
namespace ot { namespace ot {
namespace Utils { namespace Utils {
static bool IsSpaceOrNewLine(char aChar) static bool IsSeparator(char aChar)
{ {
return (aChar == ' ') || (aChar == '\t') || (aChar == '\r') || (aChar == '\n'); return (aChar == ' ') || (aChar == '\t') || (aChar == '\r') || (aChar == '\n');
} }
static bool IsEscapable(char aChar)
{
return IsSeparator(aChar) || (aChar == '\\');
}
otError CmdLineParser::ParseCmd(char *aString, uint8_t &aArgc, char *aArgv[], uint8_t aArgcMax) otError CmdLineParser::ParseCmd(char *aString, uint8_t &aArgc, char *aArgv[], uint8_t aArgcMax)
{ {
otError error = OT_ERROR_NONE; otError error = OT_ERROR_NONE;
@@ -50,21 +57,19 @@ otError CmdLineParser::ParseCmd(char *aString, uint8_t &aArgc, char *aArgv[], ui
aArgc = 0; aArgc = 0;
for (cmd = aString; IsSpaceOrNewLine(*cmd) && *cmd; cmd++) for (cmd = aString; *cmd; cmd++)
;
if (*cmd)
{ {
aArgv[aArgc++] = cmd++; // the first argument if ((*cmd == '\\') && IsEscapable(*(cmd + 1)))
} {
// include the null terminator: strlen(cmd) = strlen(cmd + 1) + 1
for (; *cmd; cmd++) memmove(cmd, cmd + 1, strlen(cmd));
{ }
if (IsSpaceOrNewLine(*cmd)) else if (IsSeparator(*cmd))
{ {
*cmd = '\0'; *cmd = '\0';
} }
else if (*(cmd - 1) == '\0')
if ((*cmd != '\0') && ((aArgc == 0) || (*(cmd - 1) == '\0')))
{ {
VerifyOrExit(aArgc < aArgcMax, error = OT_ERROR_INVALID_ARGS); VerifyOrExit(aArgc < aArgcMax, error = OT_ERROR_INVALID_ARGS);
aArgv[aArgc++] = cmd; aArgv[aArgc++] = cmd;
+2 -1
View File
@@ -60,7 +60,8 @@ public:
* This function parses the command line. * This function parses the command line.
* *
* Note: this method may change the input @p aString, it will put a '\0' by the end of each argument, * Note: this method may change the input @p aString, it will put a '\0' by the end of each argument,
* and @p aArgv will point to the arguments in the input @p aString. * and @p aArgv will point to the arguments in the input @p aString. Backslash ('\') can be used
* to escape separators (' ', '\t', '\r', '\n') and the backslash itself.
* *
* @param[in] aString A NULL-terminated input string. * @param[in] aString A NULL-terminated input string.
* @param[out] aArgc The argument counter of the command line. * @param[out] aArgc The argument counter of the command line.
+17 -3
View File
@@ -446,6 +446,20 @@ class Node:
self.send_command(cmd) self.send_command(cmd)
self._expect('Done') self._expect('Done')
def _escape_escapable(self, string):
"""Escape CLI escapable characters in the given string.
Args:
string (str): UTF-8 input string.
Returns:
[str]: The modified string with escaped characters.
"""
escapable_chars = '\\ \t\r\n'
for char in escapable_chars:
string = string.replace(char, '\\%s' % char)
return string
def get_network_name(self): def get_network_name(self):
self.send_command('networkname') self.send_command('networkname')
while True: while True:
@@ -457,7 +471,7 @@ class Node:
return network_name return network_name
def set_network_name(self, network_name): def set_network_name(self, network_name):
cmd = 'networkname %s' % network_name cmd = 'networkname %s' % self._escape_escapable(network_name)
self.send_command(cmd) self.send_command(cmd)
self._expect('Done') self._expect('Done')
@@ -969,7 +983,7 @@ class Node:
cmd += 'localprefix %s ' % mesh_local cmd += 'localprefix %s ' % mesh_local
if network_name is not None: if network_name is not None:
cmd += 'networkname %s ' % network_name cmd += 'networkname %s ' % self._escape_escapable(network_name)
if binary is not None: if binary is not None:
cmd += 'binary %s ' % binary cmd += 'binary %s ' % binary
@@ -1011,7 +1025,7 @@ class Node:
cmd += 'localprefix %s ' % mesh_local cmd += 'localprefix %s ' % mesh_local
if network_name is not None: if network_name is not None:
cmd += 'networkname %s ' % network_name cmd += 'networkname %s ' % self._escape_escapable(network_name)
self.send_command(cmd) self.send_command(cmd)
self._expect('Done') self._expect('Done')
+17 -2
View File
@@ -626,6 +626,20 @@ class OpenThread(IThci):
print('%s call getCommissionerSessionId' % self.port) print('%s call getCommissionerSessionId' % self.port)
return self.__sendCommand('commissioner sessionid')[0] return self.__sendCommand('commissioner sessionid')[0]
def __escapeEscapable(self, string):
"""Escape CLI escapable characters in the given string.
Args:
string (str): UTF-8 input string.
Returns:
[str]: The modified string with escaped characters.
"""
escapable_chars = '\\ \t\r\n'
for char in escapable_chars:
string = string.replace(char, '\\%s' % char)
return string
def _connect(self): def _connect(self):
print('My port is %s' % self.port) print('My port is %s' % self.port)
if self.port.startswith('COM'): if self.port.startswith('COM'):
@@ -688,6 +702,7 @@ class OpenThread(IThci):
""" """
print('%s call setNetworkName' % self.port) print('%s call setNetworkName' % self.port)
print(networkName) print(networkName)
networkName = self.__escapeEscapable(networkName)
try: try:
cmd = 'networkname %s' % networkName cmd = 'networkname %s' % networkName
datasetCmd = 'dataset networkname %s' % networkName datasetCmd = 'dataset networkname %s' % networkName
@@ -2393,7 +2408,7 @@ class OpenThread(IThci):
if sNetworkName is not None: if sNetworkName is not None:
cmd += ' networkname ' cmd += ' networkname '
cmd += str(sNetworkName) cmd += self.__escapeEscapable(str(sNetworkName))
if xChannel is not None: if xChannel is not None:
cmd += ' channel ' cmd += ' channel '
@@ -2594,7 +2609,7 @@ class OpenThread(IThci):
if sNetworkName is not None: if sNetworkName is not None:
cmd += ' networkname ' cmd += ' networkname '
cmd += str(sNetworkName) cmd += self.__escapeEscapable(str(sNetworkName))
if xCommissionerSessionId is not None: if xCommissionerSessionId is not None:
cmd += ' binary ' cmd += ' binary '