1
0
mirror of https://github.com/taigrr/arduinolibs synced 2025-01-18 04:33:12 -08:00

More compact representation of command history

This commit is contained in:
Rhys Weatherley 2016-03-12 15:14:27 +10:00
parent fcf875f9fd
commit 07af69c612
2 changed files with 85 additions and 56 deletions

View File

@ -120,13 +120,13 @@
* the underlying I/O stream. * the underlying I/O stream.
*/ */
Shell::Shell() Shell::Shell()
: maxHistory(0) : curStart(0)
, curStart(0)
, curLen(0) , curLen(0)
, curMax(sizeof(buffer)) , curMax(sizeof(buffer))
, history(0) , history(0)
, historyWrite(0) , historyWrite(0)
, historyPosn(0) , historyRead(0)
, historySize(0)
, prom("$ ") , prom("$ ")
, isClient(false) , isClient(false)
, lineMode(LINEMODE_NORMAL | LINEMODE_ECHO) , lineMode(LINEMODE_NORMAL | LINEMODE_ECHO)
@ -165,6 +165,11 @@ Shell::~Shell()
* shell.begin(Serial); * shell.begin(Serial);
* \endcode * \endcode
* *
* The \a maxHistory parameter indicates the number of commands of
* maximum length that can be stored in the history. If the actual
* entered commands are shorter, then more commands can be stored in
* the history.
*
* \sa end(), setPrompt() * \sa end(), setPrompt()
*/ */
bool Shell::begin(Stream &stream, size_t maxHistory, Terminal::Mode mode) bool Shell::begin(Stream &stream, size_t maxHistory, Terminal::Mode mode)
@ -192,6 +197,11 @@ bool Shell::begin(Stream &stream, size_t maxHistory, Terminal::Mode mode)
* builtin "exit" command to forcibly stop the TCP connection rather * builtin "exit" command to forcibly stop the TCP connection rather
* than returning to the login prompt. * than returning to the login prompt.
* *
* The \a maxHistory parameter indicates the number of commands of
* maximum length that can be stored in the history. If the actual
* entered commands are shorter, then more commands can be stored in
* the history.
*
* \sa end(), setPrompt() * \sa end(), setPrompt()
*/ */
bool Shell::begin(Client &client, size_t maxHistory, Terminal::Mode mode) bool Shell::begin(Client &client, size_t maxHistory, Terminal::Mode mode)
@ -212,14 +222,15 @@ bool Shell::beginShell(Stream &stream, size_t maxHistory, Terminal::Mode mode)
// Create the history buffer. // Create the history buffer.
bool ok = true; bool ok = true;
this->maxHistory = maxHistory;
delete [] history; delete [] history;
historySize = sizeof(buffer) * maxHistory;
if (maxHistory) { if (maxHistory) {
history = new char [sizeof(buffer) * maxHistory]; history = new char [historySize];
if (history) { if (history) {
memset(history, 0, sizeof(buffer) * maxHistory); memset(history, 0, historySize);
} else { } else {
this->maxHistory = 0; maxHistory = 0;
historySize = 0;
ok = false; ok = false;
} }
} else { } else {
@ -231,7 +242,7 @@ bool Shell::beginShell(Stream &stream, size_t maxHistory, Terminal::Mode mode)
curLen = 0; curLen = 0;
curMax = sizeof(buffer); curMax = sizeof(buffer);
historyWrite = 0; historyWrite = 0;
historyPosn = 0; historyRead = 0;
// Begins the login session. // Begins the login session.
beginSession(); beginSession();
@ -251,13 +262,13 @@ void Shell::end()
Terminal::end(); Terminal::end();
clearHistory(); clearHistory();
delete [] history; delete [] history;
maxHistory = 0;
curStart = 0; curStart = 0;
curLen = 0; curLen = 0;
curMax = sizeof(buffer); curMax = sizeof(buffer);
history = 0; history = 0;
historyWrite = 0; historyWrite = 0;
historyPosn = 0; historyRead = 0;
historySize = 0;
isClient = false; isClient = false;
lineMode = LINEMODE_NORMAL | LINEMODE_ECHO; lineMode = LINEMODE_NORMAL | LINEMODE_ECHO;
} }
@ -334,18 +345,16 @@ void Shell::loop()
case KEY_UP_ARROW: case KEY_UP_ARROW:
// Go back one item in the command history. // Go back one item in the command history.
if ((lineMode & LINEMODE_NORMAL) != 0 && if ((lineMode & LINEMODE_NORMAL) != 0 &&
history && historyPosn < maxHistory) { history && historyRead > 0) {
++historyPosn; changeHistory(true);
changeHistory();
} }
break; break;
case KEY_DOWN_ARROW: case KEY_DOWN_ARROW:
// Go forward one item in the command history. // Go forward one item in the command history.
if ((lineMode & LINEMODE_NORMAL) != 0 && if ((lineMode & LINEMODE_NORMAL) != 0 &&
history && historyPosn > 0) { history && historyRead < historyWrite) {
--historyPosn; changeHistory(false);
changeHistory();
} }
break; break;
@ -582,22 +591,45 @@ void Shell::execute()
// If we have a history stack and the new command is different from // If we have a history stack and the new command is different from
// the previous command, then copy the command into the stack. // the previous command, then copy the command into the stack.
if (history && curLen > curStart) { if (history && curLen > curStart) {
char *hist = history + sizeof(buffer) * historyWrite; char *prevCmd;
if (strcmp(hist, buffer) != 0) { bool newCmd = true;
historyWrite = (historyWrite + 1) % maxHistory; if (historyWrite > 0) {
hist = history + sizeof(buffer) * historyWrite; prevCmd = (char *)memrchr(history, '\0', historyWrite - 1);
strcpy(hist, buffer + curStart); if (prevCmd)
++prevCmd;
else
prevCmd = history;
if (strcmp(prevCmd, buffer + curStart) == 0)
newCmd = false;
}
if (newCmd) {
size_t len = curLen - curStart;
while ((len + 1) > (historySize - historyWrite)) {
// History stack is full. Pop older entries to get some room.
prevCmd = (char *)memchr(history, '\0', historyWrite);
if (prevCmd) {
size_t histLen = historyWrite - ((prevCmd + 1) - history);
memmove(history, prevCmd + 1, histLen);
historyWrite = histLen;
} else {
historyWrite = 0;
break;
}
}
memcpy(history + historyWrite, buffer + curStart, len);
historyWrite += len;
history[historyWrite++] = '\0';
} }
} }
// Reset the history read position to the top of the stack. // Reset the history read position to the top of the stack.
historyPosn = 0; historyRead = historyWrite;
// Break the command up into arguments and populate the argument array. // Break the command up into arguments and populate the argument array.
ShellArguments argv(buffer + curStart, curLen - curStart); ShellArguments argv(buffer + curStart, curLen - curStart);
// Clear the line buffer. // Clear the line buffer.
curLen = 0; curLen = curStart;
// Execute the command. // Execute the command.
if (argv.count() > 0) { if (argv.count() > 0) {
@ -709,36 +741,34 @@ void Shell::clearCharacters(size_t len)
/** /**
* \brief Changes the current command to reflect a different position * \brief Changes the current command to reflect a different position
* in the history stack. * in the history stack.
*
* \param up Set to true to go up in the history, false to go down.
*/ */
void Shell::changeHistory() void Shell::changeHistory(bool up)
{ {
// Replace the command with the historyPosn item from the stack. char *cmd;
// A historyPosn of 1 is the top of the history stack and a if (up) {
// historyPosn of maxHistory is the bottom of the history stack. cmd = (char *)memrchr(history, '\0', historyRead - 1);
// A historyPosn of 0 means that the down arrow has navigated if (cmd)
// off the history stack, so clear the command only. historyRead = (size_t)(cmd - history + 1);
if (historyPosn) { else
size_t posn = (historyWrite + maxHistory - (historyPosn - 1)) % maxHistory; historyRead = 0;
char *hist = history + sizeof(buffer) * posn; } else {
if (*hist != '\0') { cmd = (char *)memchr(history + historyRead, '\0', historyWrite - historyRead);
// Copy the line from the history into the command buffer. if (cmd)
historyRead = (size_t)(cmd - history + 1);
else
historyRead = historyWrite;
}
clearCharacters(curLen); clearCharacters(curLen);
curLen = strlen(hist); if (historyRead < historyWrite) {
cmd = history + historyRead;
curLen = strlen(cmd);
if (curLen > (curMax - curStart)) if (curLen > (curMax - curStart))
curLen = curMax - curStart; curLen = curMax - curStart;
memcpy(buffer + curStart, hist, curLen); memcpy(buffer + curStart, cmd, curLen);
if (lineMode & LINEMODE_ECHO) write((uint8_t *)cmd, curLen);
write((uint8_t *)hist, curLen);
curLen += curStart; curLen += curStart;
} else {
// We've gone too far - the history is still smaller
// than maxHistory in size. So reset the position and
// don't go any further.
--historyPosn;
}
} else {
// We've navigated off the history stack.
clearCharacters(curLen);
} }
} }
@ -751,10 +781,9 @@ void Shell::changeHistory()
void Shell::clearHistory() void Shell::clearHistory()
{ {
if (history) if (history)
memset(history, 0, sizeof(buffer) * maxHistory); memset(history, 0, historySize);
historyPosn = 0; historyRead = 0;
historyWrite = 0; historyWrite = 0;
clearCharacters(curLen);
memset(buffer, 0, sizeof(buffer)); memset(buffer, 0, sizeof(buffer));
} }

View File

@ -86,13 +86,13 @@ protected:
private: private:
char buffer[SHELL_MAX_CMD_LEN]; char buffer[SHELL_MAX_CMD_LEN];
size_t maxHistory;
size_t curStart; size_t curStart;
size_t curLen; size_t curLen;
size_t curMax; size_t curMax;
char *history; char *history;
size_t historyWrite; size_t historyWrite;
size_t historyPosn; size_t historyRead;
size_t historySize;
const char *prom; const char *prom;
bool isClient; bool isClient;
uint8_t lineMode; uint8_t lineMode;
@ -106,7 +106,7 @@ private:
bool execute(const ShellArguments &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(bool up);
void clearHistory(); void clearHistory();
friend class LoginShell; friend class LoginShell;