24 #include "LoginShell.h"
106 #define LINEMODE_NORMAL 0x01
107 #define LINEMODE_ECHO 0x02
108 #define LINEMODE_USERNAME 0x04
109 #define LINEMODE_PASSWORD 0x08
110 #define LINEMODE_PROMPT 0x10
111 #define LINEMODE_DELAY 0x20
114 #define LOGIN_SHELL_DELAY 3000
125 , curMax(sizeof(buffer))
132 , lineMode(LINEMODE_NORMAL | LINEMODE_ECHO)
178 if (!beginShell(stream, maxHistory, mode))
210 if (!beginShell(client, maxHistory, mode))
219 bool Shell::beginShell(Stream &stream,
size_t maxHistory,
Terminal::Mode mode)
227 historySize =
sizeof(buffer) * maxHistory;
229 history =
new char [historySize];
231 memset(history, 0, historySize);
244 curMax =
sizeof(buffer);
269 curMax =
sizeof(buffer);
275 lineMode = LINEMODE_NORMAL | LINEMODE_ECHO;
282 static char const builtin_cmd_exit[] PROGMEM =
"exit";
283 static char const builtin_cmd_help[] PROGMEM =
"help";
284 static char const builtin_cmd_help_alt[] PROGMEM =
"?";
297 if (isClient && !((Client *)
stream())->connected()) {
303 if (lineMode & LINEMODE_DELAY) {
304 if ((millis() - timer) >= LOGIN_SHELL_DELAY) {
305 lineMode &= ~LINEMODE_DELAY;
314 if (lineMode & LINEMODE_PROMPT)
339 clearCharacters(curLen);
344 if (lineMode & LINEMODE_NORMAL)
345 executeBuiltin(builtin_cmd_exit);
350 if ((lineMode & LINEMODE_NORMAL) != 0 &&
351 history && historyRead > 0) {
358 if ((lineMode & LINEMODE_NORMAL) != 0 &&
359 history && historyRead < historyWrite) {
360 changeHistory(
false);
366 if (lineMode & LINEMODE_NORMAL)
367 executeBuiltin(builtin_cmd_help);
374 if (size && (curLen + size) < (curMax - 1)) {
376 if (lineMode & LINEMODE_ECHO)
377 write((uint8_t *)(buffer + curLen), size);
383 if (key >= 0x20 && key <= 0x7E) {
385 if (curLen < (curMax - 1)) {
386 if (lineMode & LINEMODE_ECHO)
388 buffer[curLen++] = (char)key;
398 static int progmem_strcmp(
const char *str1,
const char *str2)
402 ch1 = pgm_read_byte((
const uint8_t *)str1);
403 ch2 = pgm_read_byte((
const uint8_t *)str2);
411 }
else if (ch1 != ch2) {
412 return ((
int)ch1) - ((int)ch2);
422 #define progmem_strcmp(str1,str2) (strcmp((str1), (str2)))
427 static const char *readInfoName(
const ShellCommandInfo *info)
430 return (
const char *)pgm_read_word
431 (((
const uint8_t *)info) + offsetof(ShellCommandInfo, name));
438 static const char *readInfoHelp(
const ShellCommandInfo *info)
441 return (
const char *)pgm_read_word
442 (((
const uint8_t *)info) + offsetof(ShellCommandInfo, help));
454 (((
const uint8_t *)info) + offsetof(ShellCommandInfo, func));
457 (((
const uint8_t *)info) + offsetof(ShellCommandInfo, func));
464 static ShellCommandRegister *firstCmd = 0;
476 ShellCommandRegister *prev = 0;
477 ShellCommandRegister *current = firstCmd;
478 while (current != 0) {
479 if (progmem_strcmp(readInfoName(cmd->info), readInfoName(current->info)) < 0)
482 current = current->next;
545 ShellCommandRegister *current = firstCmd;
548 while (current != 0) {
549 len = strlen_P(readInfoName(current->info));
552 current = current->next;
558 while (current != 0) {
560 len = maxLen - strlen_P(readInfoName(current->info));
567 current = current->next;
579 Stream *stream = this->
stream();
583 ((Client *)stream)->stop();
597 lineMode = LINEMODE_NORMAL | LINEMODE_ECHO | LINEMODE_PROMPT;
607 lineMode &= ~LINEMODE_PROMPT;
619 buffer[curLen] =
'\0';
623 if (history && curLen > curStart) {
626 if (historyWrite > 0) {
627 prevCmd = (
char *)memrchr(history,
'\0', historyWrite - 1);
632 if (strcmp(prevCmd, buffer + curStart) == 0)
636 size_t len = curLen - curStart;
637 while ((len + 1) > (historySize - historyWrite)) {
639 prevCmd = (
char *)memchr(history,
'\0', historyWrite);
641 size_t histLen = historyWrite - ((prevCmd + 1) - history);
642 memmove(history, prevCmd + 1, histLen);
643 historyWrite = histLen;
649 memcpy(history + historyWrite, buffer + curStart, len);
651 history[historyWrite++] =
'\0';
656 historyRead = historyWrite;
665 if (argv.
count() > 0) {
668 const char *argv0 = argv[0];
669 if (!strcmp_P(argv0, builtin_cmd_help) ||
670 !strcmp_P(argv0, builtin_cmd_help_alt)) {
672 }
else if (!strcmp_P(argv0, builtin_cmd_exit)) {
675 static char const unknown_cmd[] PROGMEM =
"Unknown command: ";
684 lineMode |= LINEMODE_PROMPT;
696 const char *argv0 = argv[0];
697 ShellCommandRegister *current = firstCmd;
698 while (current != 0) {
699 if (!strcmp_P(argv0, readInfoName(current->info))) {
701 (*func)(*
this, argv.
count(), argv);
704 current = current->next;
714 void Shell::executeBuiltin(
const char *cmd)
716 clearCharacters(curLen);
717 curLen = strlen_P(cmd);
718 strncpy_P(buffer + curStart, cmd, curLen);
719 write((
const uint8_t *)(buffer + curStart), curLen);
729 void Shell::clearCharacters(
size_t len)
732 if (!(lineMode & LINEMODE_ECHO))
736 while (len > 0 && curLen > curStart) {
737 uint8_t ch = (uint8_t)(buffer[curLen - 1]);
743 long code = (ch & 0x3F);
747 ch = (uint8_t)(buffer[curLen - 1]);
748 if ((ch & 0xC0) != 0x80)
750 code |= ((long)(ch & 0x3F)) << shift;
753 if ((ch & 0xE0) == 0xC0)
755 else if ((ch & 0xF0) == 0xE0)
759 code |= ((long)ch) << shift;
777 void Shell::changeHistory(
bool up)
781 cmd = (
char *)memrchr(history,
'\0', historyRead - 1);
783 historyRead = (size_t)(cmd - history + 1);
787 cmd = (
char *)memchr(history + historyRead,
'\0', historyWrite - historyRead);
789 historyRead = (size_t)(cmd - history + 1);
791 historyRead = historyWrite;
793 clearCharacters(curLen);
794 if (historyRead < historyWrite) {
795 cmd = history + historyRead;
796 curLen = strlen(cmd);
797 if (curLen > (curMax - curStart))
798 curLen = curMax - curStart;
799 memcpy(buffer + curStart, cmd, curLen);
800 write((uint8_t *)cmd, curLen);
811 void Shell::clearHistory()
814 memset(history, 0, historySize);
817 memset(buffer, 0,
sizeof(buffer));
853 ShellArguments::ShellArguments(
char *buffer,
size_t len)
865 char ch = buffer[posn];
873 if (ch ==
'"' || ch ==
'\'') {
883 }
else if (!quote && ch ==
' ') {
886 buffer[outposn++] = ch;
888 }
while (posn < len);
889 buffer[outposn++] =
'\0';
929 if (index < 0 || index >= argc) {
932 }
else if (index == currentIndex) {
934 return line + currentPosn;
938 while (index > currentIndex) {
939 temp = (
const char *)memchr
940 (line + currentPosn,
'\0', size - currentPosn);
943 currentPosn = ((size_t)(temp - line)) + 1;
946 while (index < currentIndex) {
947 temp = (
const char *)memrchr(line,
'\0', currentPosn - 1);
949 currentPosn = ((size_t)(temp - line)) + 1;
954 return line + currentPosn;
960 lineMode = LINEMODE_USERNAME | LINEMODE_ECHO | LINEMODE_PROMPT;
963 curMax =
sizeof(buffer) / 2;
1009 static char const loginString[] PROGMEM =
"login: ";
1010 static char const passwordString[] PROGMEM =
"Password: ";
1011 if (lineMode & LINEMODE_NORMAL) {
1019 curMax =
sizeof(buffer);
1020 }
else if (lineMode & LINEMODE_USERNAME) {
1024 write((uint8_t)
' ');
1031 curMax =
sizeof(buffer) / 2;
1032 }
else if (lineMode & LINEMODE_PASSWORD) {
1037 curStart =
sizeof(buffer) / 2;
1039 curMax =
sizeof(buffer);
1041 lineMode &= ~LINEMODE_PROMPT;
1045 static int defaultPasswordCheckFunc(
const char *username,
const char *password)
1047 static char const defaultUsername[] PROGMEM =
"root";
1048 static char const defaultPassword[] PROGMEM =
"arduino";
1049 if (!strcmp_P(username, defaultUsername) &&
1050 !strcmp_P(password, defaultPassword)) {
1059 if (lineMode & LINEMODE_NORMAL) {
1062 }
else if (lineMode & LINEMODE_USERNAME) {
1064 buffer[curLen] =
'\0';
1065 lineMode = LINEMODE_PASSWORD | LINEMODE_PROMPT;
1067 }
else if (lineMode & LINEMODE_PASSWORD) {
1069 buffer[curLen] =
'\0';
1075 userid = checkFunc(buffer, buffer +
sizeof(buffer) / 2);
1077 userid = defaultPasswordCheckFunc(buffer, buffer +
sizeof(buffer) / 2);
1080 memset(buffer, 0,
sizeof(buffer));
1085 lineMode = LINEMODE_NORMAL | LINEMODE_ECHO | LINEMODE_PROMPT;
1087 lineMode = LINEMODE_USERNAME | LINEMODE_ECHO |
1088 LINEMODE_PROMPT | LINEMODE_DELAY;
const char * operator[](int index) const
Gets a specific argument for the command.
virtual void beginSession()
Begins a login session.
void help()
Displays help for all supported commands.
long unicodeKey() const
Gets the Unicode version of the last key returned by readKey().
bool begin(Stream &stream, size_t maxHistory=0, Terminal::Mode mode=Serial)
Begin shell handling on an underlying character stream.
virtual size_t write(uint8_t c)
Writes a single byte to the underlying stream.
virtual void printPrompt()
Prints the current prompt string.
static void registerCommand(ShellCommandRegister *cmd)
Registers a command with the shell.
void begin(Stream &stream, Mode mode=Serial)
Begins terminal operations on an underlying stream.
void exit()
Exit from the shell back to the login prompt.
void backspace()
Backspaces over the last character.
virtual void printPrompt()
Prints the current prompt string.
void loop()
Performs regular activities on the shell.
Shell()
Constructs a new Shell instance.
Mode
Mode to operate in, Serial or Telnet.
Stream * stream() const
Returns a pointer to the underlying Stream, or NULL if the stream has not been set with begin() yet...
virtual void beginSession()
Begins a login session.
ShellCommandFunc
Type of functions that provide shell command handlers.
int count() const
Returns the number of arguments, including the name of the command.
void writeProgMem(const char *str)
Writes a static string that is stored in program memory.
virtual void execute()
Executes the command in the buffer.
static size_t utf8Format(uint8_t *buffer, long code)
Formats a Unicode code point in a buffer in the UTF-8 encoding.
static size_t utf8Length(long code)
Determines the length of a Unicode code point in the UTF-8 encoding.
virtual void execute()
Executes the command in the buffer.
void end()
Ends terminal operations on an underlying stream.
int readKey()
Reads the next key that was typed on this terminal.
Convenience class that encapsulates an array of shell command arguments.
void end()
Ends shell processing on the underlying stream.
static bool isWideCharacter(long code)
Determine if a Unicode character is wide.
virtual ~Shell()
Destroys this Shell object.
int userid() const
Gets the user identifier for the currently logged in user, or -1 if there is no user logged in curren...