mirror of
https://github.com/taigrr/arduinolibs
synced 2025-01-18 04:33:12 -08:00
Memory-efficient encapsulation of command arguments
This commit is contained in:
parent
57fb8f2fe3
commit
c24e67c63d
@ -452,8 +452,6 @@ void Shell::help()
|
|||||||
*/
|
*/
|
||||||
void Shell::execute()
|
void Shell::execute()
|
||||||
{
|
{
|
||||||
size_t posn = 0;
|
|
||||||
|
|
||||||
// Terminate the current line.
|
// Terminate the current line.
|
||||||
println();
|
println();
|
||||||
|
|
||||||
@ -474,58 +472,24 @@ void Shell::execute()
|
|||||||
// Reset the history read position to the top of the stack.
|
// Reset the history read position to the top of the stack.
|
||||||
historyPosn = 0;
|
historyPosn = 0;
|
||||||
|
|
||||||
// Skip white space at the start of the line.
|
// Break the command up into arguments and populate the argument array.
|
||||||
while (posn < curLen && buffer[posn] == ' ')
|
ShellArguments argv(buffer, curLen);
|
||||||
++posn;
|
|
||||||
|
|
||||||
// Break the command up into arguments and populate the argv array.
|
|
||||||
int argc = 0;
|
|
||||||
size_t outposn = 0;
|
|
||||||
char quote = 0;
|
|
||||||
while (posn < curLen) {
|
|
||||||
char ch = buffer[posn];
|
|
||||||
if (ch == ' ') {
|
|
||||||
++posn;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
argv[argc++] = buffer + outposn;
|
|
||||||
do {
|
|
||||||
ch = buffer[posn];
|
|
||||||
if (ch == '"' || ch == '\'') {
|
|
||||||
if (quote == ch) {
|
|
||||||
quote = 0;
|
|
||||||
++posn;
|
|
||||||
continue;
|
|
||||||
} else if (!quote) {
|
|
||||||
quote = ch;
|
|
||||||
++posn;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else if (!quote && ch == ' ') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
buffer[outposn++] = ch;
|
|
||||||
++posn;
|
|
||||||
} while (posn < curLen);
|
|
||||||
buffer[outposn++] = '\0';
|
|
||||||
if (posn < curLen)
|
|
||||||
++posn;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear the line buffer.
|
// Clear the line buffer.
|
||||||
curLen = 0;
|
curLen = 0;
|
||||||
|
|
||||||
// Execute the command.
|
// Execute the command.
|
||||||
if (argc > 0) {
|
if (argv.count() > 0) {
|
||||||
if (!execute(argc, argv)) {
|
if (!execute(argv)) {
|
||||||
// Could not find a matching command, try the builtin "help".
|
// Could not find a matching command, try the builtin "help".
|
||||||
if (!strcmp_P(argv[0], builtin_cmd_help) ||
|
const char *argv0 = argv[0];
|
||||||
!strcmp_P(argv[0], builtin_cmd_help_alt)) {
|
if (!strcmp_P(argv0, builtin_cmd_help) ||
|
||||||
|
!strcmp_P(argv0, builtin_cmd_help_alt)) {
|
||||||
help();
|
help();
|
||||||
} else {
|
} else {
|
||||||
static char const unknown_cmd[] PROGMEM = "Unknown command: ";
|
static char const unknown_cmd[] PROGMEM = "Unknown command: ";
|
||||||
writeProgMem(unknown_cmd);
|
writeProgMem(unknown_cmd);
|
||||||
print(argv[0]);
|
print(argv0);
|
||||||
println();
|
println();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -539,18 +503,18 @@ void Shell::execute()
|
|||||||
/**
|
/**
|
||||||
* \brief Executes a command that has been parsed into arguments.
|
* \brief Executes a command that has been parsed into arguments.
|
||||||
*
|
*
|
||||||
* \param argc The number of elements in \a argv.
|
|
||||||
* \param argv The arguments.
|
* \param argv The arguments.
|
||||||
*
|
*
|
||||||
* \return Returns true if the command was found; false if not found.
|
* \return Returns true if the command was found; false if not found.
|
||||||
*/
|
*/
|
||||||
bool Shell::execute(int argc, char **argv)
|
bool Shell::execute(const ShellArguments &argv)
|
||||||
{
|
{
|
||||||
|
const char *argv0 = argv[0];
|
||||||
ShellCommandRegister *current = firstCmd;
|
ShellCommandRegister *current = firstCmd;
|
||||||
while (current != 0) {
|
while (current != 0) {
|
||||||
if (!strcmp_P(argv[0], readInfoName(current->info))) {
|
if (!strcmp_P(argv0, readInfoName(current->info))) {
|
||||||
ShellCommandFunc func = readInfoFunc(current->info);
|
ShellCommandFunc func = readInfoFunc(current->info);
|
||||||
(*func)(*this, argc, argv);
|
(*func)(*this, argv.count(), argv);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
current = current->next;
|
current = current->next;
|
||||||
@ -664,7 +628,7 @@ void Shell::changeHistory()
|
|||||||
* be placed into program memory.
|
* be placed into program memory.
|
||||||
*
|
*
|
||||||
* \code
|
* \code
|
||||||
* void cmdMotor(Shell &shell, int argc, char *argv[])
|
* void cmdMotor(Shell &shell, int argc, const ShellArguments &argv)
|
||||||
* {
|
* {
|
||||||
* ...
|
* ...
|
||||||
* }
|
* }
|
||||||
@ -677,3 +641,115 @@ void Shell::changeHistory()
|
|||||||
*
|
*
|
||||||
* \relates Shell
|
* \relates Shell
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Constructs a new argument array.
|
||||||
|
*
|
||||||
|
* \param buffer Points to the command buffer to parse into arguments.
|
||||||
|
* \param len The length of the command buffer in bytes, excluding the
|
||||||
|
* terminating NUL.
|
||||||
|
*/
|
||||||
|
ShellArguments::ShellArguments(char *buffer, size_t len)
|
||||||
|
: line(buffer)
|
||||||
|
, size(0)
|
||||||
|
, argc(0)
|
||||||
|
, currentIndex(0)
|
||||||
|
, currentPosn(0)
|
||||||
|
{
|
||||||
|
// Break the command up into arguments and add NUL terminators.
|
||||||
|
size_t posn = 0;
|
||||||
|
size_t outposn = 0;
|
||||||
|
char quote = 0;
|
||||||
|
while (posn < len) {
|
||||||
|
char ch = buffer[posn];
|
||||||
|
if (ch == ' ') {
|
||||||
|
++posn;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
++argc;
|
||||||
|
do {
|
||||||
|
ch = buffer[posn];
|
||||||
|
if (ch == '"' || ch == '\'') {
|
||||||
|
if (quote == ch) {
|
||||||
|
quote = 0;
|
||||||
|
++posn;
|
||||||
|
continue;
|
||||||
|
} else if (!quote) {
|
||||||
|
quote = ch;
|
||||||
|
++posn;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else if (!quote && ch == ' ') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buffer[outposn++] = ch;
|
||||||
|
++posn;
|
||||||
|
} while (posn < len);
|
||||||
|
buffer[outposn++] = '\0';
|
||||||
|
if (posn < len)
|
||||||
|
++posn;
|
||||||
|
}
|
||||||
|
size = outposn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \class ShellArguments Shell.h <Shell.h>
|
||||||
|
* \brief Convenience class that encapsulates an array of shell
|
||||||
|
* command arguments.
|
||||||
|
*
|
||||||
|
* \sa Shell
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn ShellArguments::~ShellArguments()
|
||||||
|
* \brief Destroys this argument array.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \fn int ShellArguments::count() const
|
||||||
|
* \brief Returns the number of arguments, including the name of the command.
|
||||||
|
*
|
||||||
|
* \sa operator[]
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Gets a specific argument for the command.
|
||||||
|
*
|
||||||
|
* \param index The argument index between 0 and count() - 1.
|
||||||
|
* \return The argument, or NULL if \a index is out of range.
|
||||||
|
*
|
||||||
|
* The name of the command is argument 0. The command's remaining
|
||||||
|
* arguments are numbered 1 to count() - 1.
|
||||||
|
*
|
||||||
|
* \sa count()
|
||||||
|
*/
|
||||||
|
const char *ShellArguments::operator[](int index) const
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= argc) {
|
||||||
|
// Argument index is out of range.
|
||||||
|
return 0;
|
||||||
|
} else if (index == currentIndex) {
|
||||||
|
// We already found this argument last time.
|
||||||
|
return line + currentPosn;
|
||||||
|
} else {
|
||||||
|
// Search forwards or backwards for the next argument.
|
||||||
|
const char *temp;
|
||||||
|
while (index > currentIndex) {
|
||||||
|
temp = (const char *)memchr
|
||||||
|
(line + currentPosn, '\0', size - currentPosn);
|
||||||
|
if (!temp)
|
||||||
|
return 0;
|
||||||
|
currentPosn = ((size_t)(temp - line)) + 1;
|
||||||
|
++currentIndex;
|
||||||
|
}
|
||||||
|
while (index < currentIndex) {
|
||||||
|
temp = (const char *)memrchr(line, '\0', currentPosn - 1);
|
||||||
|
if (temp)
|
||||||
|
currentPosn = ((size_t)(temp - line)) + 1;
|
||||||
|
else
|
||||||
|
currentPosn = 0;
|
||||||
|
--currentIndex;
|
||||||
|
}
|
||||||
|
return line + currentPosn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "Terminal.h"
|
#include "Terminal.h"
|
||||||
|
|
||||||
class Shell;
|
class Shell;
|
||||||
|
class ShellArguments;
|
||||||
|
|
||||||
#if defined(__arm__)
|
#if defined(__arm__)
|
||||||
#define SHELL_MAX_CMD_LEN 256
|
#define SHELL_MAX_CMD_LEN 256
|
||||||
@ -33,7 +34,7 @@ class Shell;
|
|||||||
#define SHELL_MAX_CMD_LEN 64
|
#define SHELL_MAX_CMD_LEN 64
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef void (*ShellCommandFunc)(Shell &shell, int argc, char *argv[]);
|
typedef void (*ShellCommandFunc)(Shell &shell, int argc, const ShellArguments &argv);
|
||||||
typedef bool (*ShellPasswordCheckFunc)(const char *userid, const char *password);
|
typedef bool (*ShellPasswordCheckFunc)(const char *userid, const char *password);
|
||||||
|
|
||||||
/** @cond */
|
/** @cond */
|
||||||
@ -80,7 +81,6 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
char buffer[SHELL_MAX_CMD_LEN];
|
char buffer[SHELL_MAX_CMD_LEN];
|
||||||
char *argv[SHELL_MAX_CMD_LEN / 2];
|
|
||||||
size_t maxHistory;
|
size_t maxHistory;
|
||||||
size_t curLen;
|
size_t curLen;
|
||||||
char *history;
|
char *history;
|
||||||
@ -94,12 +94,35 @@ private:
|
|||||||
Shell &operator=(const Shell &) { return *this; }
|
Shell &operator=(const Shell &) { return *this; }
|
||||||
|
|
||||||
void execute();
|
void execute();
|
||||||
bool execute(int argc, char **argv);
|
bool execute(const ShellArguments &argv);
|
||||||
void executeBuiltin(const char *cmd);
|
void executeBuiltin(const char *cmd);
|
||||||
void clearCharacters(size_t len);
|
void clearCharacters(size_t len);
|
||||||
void changeHistory();
|
void changeHistory();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ShellArguments
|
||||||
|
{
|
||||||
|
friend class Shell;
|
||||||
|
private:
|
||||||
|
ShellArguments(char *buffer, size_t len);
|
||||||
|
~ShellArguments() {}
|
||||||
|
public:
|
||||||
|
|
||||||
|
int count() const { return argc; }
|
||||||
|
const char *operator[](int index) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const char *line;
|
||||||
|
size_t size;
|
||||||
|
int argc;
|
||||||
|
mutable int currentIndex;
|
||||||
|
mutable size_t currentPosn;
|
||||||
|
|
||||||
|
// Disable copy constructor and operator=().
|
||||||
|
ShellArguments(const ShellArguments &other) {}
|
||||||
|
ShellArguments &operator=(const ShellArguments &) { return *this; }
|
||||||
|
};
|
||||||
|
|
||||||
/** @cond */
|
/** @cond */
|
||||||
|
|
||||||
inline ShellCommandRegister::ShellCommandRegister(const ShellCommandInfo *_info)
|
inline ShellCommandRegister::ShellCommandRegister(const ShellCommandInfo *_info)
|
||||||
|
@ -10,7 +10,7 @@ int ledPin = 13;
|
|||||||
|
|
||||||
Shell shell;
|
Shell shell;
|
||||||
|
|
||||||
void cmdLed(Shell &shell, int argc, char *argv[])
|
void cmdLed(Shell &shell, int argc, const ShellArguments &argv)
|
||||||
{
|
{
|
||||||
if (argc > 1 && !strcmp(argv[1], "on"))
|
if (argc > 1 && !strcmp(argv[1], "on"))
|
||||||
digitalWrite(ledPin, HIGH);
|
digitalWrite(ledPin, HIGH);
|
||||||
|
@ -21,7 +21,7 @@ bool haveClient = false;
|
|||||||
|
|
||||||
Shell shell;
|
Shell shell;
|
||||||
|
|
||||||
void cmdLed(Shell &shell, int argc, char *argv[])
|
void cmdLed(Shell &shell, int argc, const ShellArguments &argv)
|
||||||
{
|
{
|
||||||
if (argc > 1 && !strcmp(argv[1], "on"))
|
if (argc > 1 && !strcmp(argv[1], "on"))
|
||||||
digitalWrite(ledPin, HIGH);
|
digitalWrite(ledPin, HIGH);
|
||||||
@ -29,7 +29,7 @@ void cmdLed(Shell &shell, int argc, char *argv[])
|
|||||||
digitalWrite(ledPin, LOW);
|
digitalWrite(ledPin, LOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
void cmdExit(Shell &shell, int argc, char *argv[])
|
void cmdExit(Shell &shell, int argc, const ShellArguments &argv)
|
||||||
{
|
{
|
||||||
client.stop();
|
client.stop();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user