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.
*/
Shell::Shell()
: maxHistory(0)
, curStart(0)
: curStart(0)
, curLen(0)
, curMax(sizeof(buffer))
, history(0)
, historyWrite(0)
, historyPosn(0)
, historyRead(0)
, historySize(0)
, prom("$ ")
, isClient(false)
, lineMode(LINEMODE_NORMAL | LINEMODE_ECHO)
@ -165,6 +165,11 @@ Shell::~Shell()
* shell.begin(Serial);
* \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()
*/
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
* 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()
*/
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.
bool ok = true;
this->maxHistory = maxHistory;
delete [] history;
historySize = sizeof(buffer) * maxHistory;
if (maxHistory) {
history = new char [sizeof(buffer) * maxHistory];
history = new char [historySize];
if (history) {
memset(history, 0, sizeof(buffer) * maxHistory);
memset(history, 0, historySize);
} else {
this->maxHistory = 0;
maxHistory = 0;
historySize = 0;
ok = false;
}
} else {
@ -231,7 +242,7 @@ bool Shell::beginShell(Stream &stream, size_t maxHistory, Terminal::Mode mode)
curLen = 0;
curMax = sizeof(buffer);
historyWrite = 0;
historyPosn = 0;
historyRead = 0;
// Begins the login session.
beginSession();
@ -251,13 +262,13 @@ void Shell::end()
Terminal::end();
clearHistory();
delete [] history;
maxHistory = 0;
curStart = 0;
curLen = 0;
curMax = sizeof(buffer);
history = 0;
historyWrite = 0;
historyPosn = 0;
historyRead = 0;
historySize = 0;
isClient = false;
lineMode = LINEMODE_NORMAL | LINEMODE_ECHO;
}
@ -334,18 +345,16 @@ void Shell::loop()
case KEY_UP_ARROW:
// Go back one item in the command history.
if ((lineMode & LINEMODE_NORMAL) != 0 &&
history && historyPosn < maxHistory) {
++historyPosn;
changeHistory();
history && historyRead > 0) {
changeHistory(true);
}
break;
case KEY_DOWN_ARROW:
// Go forward one item in the command history.
if ((lineMode & LINEMODE_NORMAL) != 0 &&
history && historyPosn > 0) {
--historyPosn;
changeHistory();
history && historyRead < historyWrite) {
changeHistory(false);
}
break;
@ -582,22 +591,45 @@ void Shell::execute()
// If we have a history stack and the new command is different from
// the previous command, then copy the command into the stack.
if (history && curLen > curStart) {
char *hist = history + sizeof(buffer) * historyWrite;
if (strcmp(hist, buffer) != 0) {
historyWrite = (historyWrite + 1) % maxHistory;
hist = history + sizeof(buffer) * historyWrite;
strcpy(hist, buffer + curStart);
char *prevCmd;
bool newCmd = true;
if (historyWrite > 0) {
prevCmd = (char *)memrchr(history, '\0', historyWrite - 1);
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.
historyPosn = 0;
historyRead = historyWrite;
// Break the command up into arguments and populate the argument array.
ShellArguments argv(buffer + curStart, curLen - curStart);
// Clear the line buffer.
curLen = 0;
curLen = curStart;
// Execute the command.
if (argv.count() > 0) {
@ -709,36 +741,34 @@ void Shell::clearCharacters(size_t len)
/**
* \brief Changes the current command to reflect a different position
* 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.
// A historyPosn of 1 is the top of the history stack and a
// historyPosn of maxHistory is the bottom of the history stack.
// A historyPosn of 0 means that the down arrow has navigated
// off the history stack, so clear the command only.
if (historyPosn) {
size_t posn = (historyWrite + maxHistory - (historyPosn - 1)) % maxHistory;
char *hist = history + sizeof(buffer) * posn;
if (*hist != '\0') {
// Copy the line from the history into the command buffer.
clearCharacters(curLen);
curLen = strlen(hist);
if (curLen > (curMax - curStart))
curLen = curMax - curStart;
memcpy(buffer + curStart, hist, curLen);
if (lineMode & LINEMODE_ECHO)
write((uint8_t *)hist, curLen);
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;
}
char *cmd;
if (up) {
cmd = (char *)memrchr(history, '\0', historyRead - 1);
if (cmd)
historyRead = (size_t)(cmd - history + 1);
else
historyRead = 0;
} else {
// We've navigated off the history stack.
clearCharacters(curLen);
cmd = (char *)memchr(history + historyRead, '\0', historyWrite - historyRead);
if (cmd)
historyRead = (size_t)(cmd - history + 1);
else
historyRead = historyWrite;
}
clearCharacters(curLen);
if (historyRead < historyWrite) {
cmd = history + historyRead;
curLen = strlen(cmd);
if (curLen > (curMax - curStart))
curLen = curMax - curStart;
memcpy(buffer + curStart, cmd, curLen);
write((uint8_t *)cmd, curLen);
curLen += curStart;
}
}
@ -751,10 +781,9 @@ void Shell::changeHistory()
void Shell::clearHistory()
{
if (history)
memset(history, 0, sizeof(buffer) * maxHistory);
historyPosn = 0;
memset(history, 0, historySize);
historyRead = 0;
historyWrite = 0;
clearCharacters(curLen);
memset(buffer, 0, sizeof(buffer));
}

View File

@ -86,13 +86,13 @@ protected:
private:
char buffer[SHELL_MAX_CMD_LEN];
size_t maxHistory;
size_t curStart;
size_t curLen;
size_t curMax;
char *history;
size_t historyWrite;
size_t historyPosn;
size_t historyRead;
size_t historySize;
const char *prom;
bool isClient;
uint8_t lineMode;
@ -106,7 +106,7 @@ private:
bool execute(const ShellArguments &argv);
void executeBuiltin(const char *cmd);
void clearCharacters(size_t len);
void changeHistory();
void changeHistory(bool up);
void clearHistory();
friend class LoginShell;