1
0
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:
Rhys Weatherley 2016-03-11 20:46:13 +10:00
parent 57fb8f2fe3
commit c24e67c63d
4 changed files with 154 additions and 55 deletions

View File

@ -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;
}
}

View File

@ -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)

View File

@ -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);

View File

@ -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();
} }