mirror of
https://github.com/espressif/openthread.git
synced 2026-09-14 13:10:08 +00:00
[cli] allow to escape separators (#4608)
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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')
|
||||||
|
|||||||
@@ -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 '
|
||||||
|
|||||||
Reference in New Issue
Block a user