Simple embedded shell with runtime pluggable commands.

Dependents:   DataBus2018

Implements a simple unix-like shell for embedded systems with a pluggable command architecture.

Committer:
shimniok
Date:
Thu Dec 13 09:19:51 2018 +0000
Revision:
12:ecf3fc049bca
Parent:
11:23f61057d877
Child:
13:a29fb89018e1
tweaked esc sequence parsing

Who changed what in which revision?

UserRevisionLine numberNew contents of line
shimniok 0:49820d5a38c9 1 #include "SimpleShell.h"
shimniok 5:8f486f4d29d3 2 #include <ctype.h>
shimniok 0:49820d5a38c9 3
shimniok 12:ecf3fc049bca 4 #define ESC 0x1b
shimniok 12:ecf3fc049bca 5 #define UP 0x41
shimniok 12:ecf3fc049bca 6 #define DOWN 0x42
shimniok 12:ecf3fc049bca 7 #define RIGHT 0x43
shimniok 12:ecf3fc049bca 8 #define LEFT 0x44
shimniok 12:ecf3fc049bca 9 #define HOME 0x31
shimniok 12:ecf3fc049bca 10 #define INS 0x32
shimniok 12:ecf3fc049bca 11 #define DEL 0x33
shimniok 12:ecf3fc049bca 12 #define PGUP 0x35
shimniok 12:ecf3fc049bca 13 #define PGDN 0x36
shimniok 12:ecf3fc049bca 14 #define TAIL 0x7e
shimniok 12:ecf3fc049bca 15
shimniok 10:c3faa7ffd23b 16
shimniok 0:49820d5a38c9 17 SimpleShell::SimpleShell()
shimniok 0:49820d5a38c9 18 {
shimniok 2:4f0affdb7db9 19 lookupEnd = 0;
shimniok 8:41b7274a9753 20 // Built-in shell commands
shimniok 8:41b7274a9753 21 attach(callback(this, &SimpleShell::help), "help");
shimniok 8:41b7274a9753 22 // TODO: cd
shimniok 0:49820d5a38c9 23 }
shimniok 0:49820d5a38c9 24
shimniok 8:41b7274a9753 25
shimniok 8:41b7274a9753 26 void SimpleShell::help()
shimniok 8:41b7274a9753 27 {
shimniok 8:41b7274a9753 28 printf("Available commands: ");
shimniok 8:41b7274a9753 29 for (int i=0; i < lookupEnd; i++) {
shimniok 8:41b7274a9753 30 printf("%s ", lookup[i].command);
shimniok 8:41b7274a9753 31 }
shimniok 8:41b7274a9753 32 printf("\n\n");
shimniok 8:41b7274a9753 33 }
shimniok 8:41b7274a9753 34
shimniok 8:41b7274a9753 35
shimniok 0:49820d5a38c9 36 void SimpleShell::run()
shimniok 0:49820d5a38c9 37 {
shimniok 0:49820d5a38c9 38 bool done=false;
shimniok 4:8b8fa59d0015 39 Callback<void()> cb;
shimniok 12:ecf3fc049bca 40 //int status; // TODO implement command status return
shimniok 4:8b8fa59d0015 41
shimniok 0:49820d5a38c9 42 strcpy(_cwd, "/log");
shimniok 0:49820d5a38c9 43
shimniok 0:49820d5a38c9 44 printf("Type help for assistance.\n");
shimniok 8:41b7274a9753 45 help();
shimniok 0:49820d5a38c9 46 while (!done) {
shimniok 0:49820d5a38c9 47 printPrompt();
shimniok 0:49820d5a38c9 48 readCommand();
shimniok 5:8f486f4d29d3 49 if (cmd[0]) { // skip blank command
shimniok 5:8f486f4d29d3 50 if (cb = findCommand()) {
shimniok 5:8f486f4d29d3 51 cb.call();
shimniok 5:8f486f4d29d3 52 } else {
shimniok 5:8f486f4d29d3 53 printf("command <%s> not found\n", cmd);
shimniok 5:8f486f4d29d3 54 }
shimniok 4:8b8fa59d0015 55 }
shimniok 0:49820d5a38c9 56 }
shimniok 0:49820d5a38c9 57 puts("exiting shell\n");
shimniok 0:49820d5a38c9 58
shimniok 0:49820d5a38c9 59 return;
shimniok 0:49820d5a38c9 60 }
shimniok 0:49820d5a38c9 61
shimniok 0:49820d5a38c9 62
shimniok 2:4f0affdb7db9 63 void SimpleShell::attach(Callback<void()> cb, char *command)
shimniok 2:4f0affdb7db9 64 {
shimniok 2:4f0affdb7db9 65 if (lookupEnd < MAXLOOKUP) {
shimniok 2:4f0affdb7db9 66 lookup[lookupEnd].cb = cb;
shimniok 2:4f0affdb7db9 67 lookup[lookupEnd].command = command;
shimniok 2:4f0affdb7db9 68 lookupEnd++;
shimniok 2:4f0affdb7db9 69 }
shimniok 2:4f0affdb7db9 70
shimniok 2:4f0affdb7db9 71 return;
shimniok 1:998a7ed04f10 72 }
shimniok 1:998a7ed04f10 73
shimniok 1:998a7ed04f10 74
shimniok 3:ebb4893f033d 75 Callback<void()> SimpleShell::findCommand()
shimniok 3:ebb4893f033d 76 {
shimniok 3:ebb4893f033d 77 Callback<void()> cb=NULL;
shimniok 3:ebb4893f033d 78
shimniok 4:8b8fa59d0015 79 for (int i=0; i < lookupEnd; i++) {
shimniok 4:8b8fa59d0015 80 if (strncmp(cmd, lookup[i].command, MAXBUF) == 0) {
shimniok 4:8b8fa59d0015 81 cb = lookup[i].cb;
shimniok 4:8b8fa59d0015 82 break;
shimniok 4:8b8fa59d0015 83 }
shimniok 4:8b8fa59d0015 84 }
shimniok 4:8b8fa59d0015 85
shimniok 3:ebb4893f033d 86 return cb;
shimniok 3:ebb4893f033d 87 }
shimniok 3:ebb4893f033d 88
shimniok 3:ebb4893f033d 89
shimniok 0:49820d5a38c9 90 void SimpleShell::printPrompt()
shimniok 0:49820d5a38c9 91 {
shimniok 0:49820d5a38c9 92 fputc('(', stdout);
shimniok 0:49820d5a38c9 93 fputs(_cwd, stdout);
shimniok 0:49820d5a38c9 94 fputs(")# ", stdout);
shimniok 2:4f0affdb7db9 95
shimniok 2:4f0affdb7db9 96 return;
shimniok 0:49820d5a38c9 97 }
shimniok 0:49820d5a38c9 98
shimniok 0:49820d5a38c9 99
shimniok 0:49820d5a38c9 100 void SimpleShell::readCommand()
shimniok 0:49820d5a38c9 101 {
shimniok 0:49820d5a38c9 102 int i=0;
shimniok 0:49820d5a38c9 103 char c;
shimniok 0:49820d5a38c9 104 bool done = false;
shimniok 0:49820d5a38c9 105
shimniok 0:49820d5a38c9 106 memset(cmd, 0, MAXBUF);
shimniok 0:49820d5a38c9 107 do {
shimniok 0:49820d5a38c9 108 cmd[i] = 0;
shimniok 0:49820d5a38c9 109 c = fgetc(stdin);
shimniok 0:49820d5a38c9 110 if (c == '\r') { // if return is hit, we're done, don't add \r to cmd
shimniok 0:49820d5a38c9 111 done = true;
shimniok 10:c3faa7ffd23b 112 } else if (c == ESC) { // keyboard escape codes (arrow keys, etc)
shimniok 12:ecf3fc049bca 113 int c2 = getchar();
shimniok 12:ecf3fc049bca 114 int c3 = getchar();
shimniok 10:c3faa7ffd23b 115
shimniok 10:c3faa7ffd23b 116 if (c2 == 0x4f && c3 == 0x46) {
shimniok 10:c3faa7ffd23b 117 printf("<END>");
shimniok 10:c3faa7ffd23b 118 } else if (c2 == 0x5b) {
shimniok 12:ecf3fc049bca 119
shimniok 12:ecf3fc049bca 120 if (c3 == UP) {
shimniok 10:c3faa7ffd23b 121 printf("<UP>");
shimniok 12:ecf3fc049bca 122 } else if (c3 == DOWN) {
shimniok 10:c3faa7ffd23b 123 printf("<DOWN>");
shimniok 12:ecf3fc049bca 124 } else if (c3 == RIGHT) {
shimniok 10:c3faa7ffd23b 125 printf("<RIGHT>");
shimniok 12:ecf3fc049bca 126 } else if (c3 == LEFT) {
shimniok 10:c3faa7ffd23b 127 printf("<LEFT>");
shimniok 12:ecf3fc049bca 128 } else if (c3 == HOME || c3 == INS || c3 == DEL ||
shimniok 12:ecf3fc049bca 129 c3 == PGUP || c3 == PGDN) {
shimniok 10:c3faa7ffd23b 130 char c4 = getchar();
shimniok 12:ecf3fc049bca 131 if (c4 == TAIL) {
shimniok 12:ecf3fc049bca 132 if (c4 == HOME) {
shimniok 10:c3faa7ffd23b 133 printf("<HOME>");
shimniok 12:ecf3fc049bca 134 } else if (c4 == INS) {
shimniok 10:c3faa7ffd23b 135 printf("<INS>");
shimniok 12:ecf3fc049bca 136 } else if (c4 == DEL) {
shimniok 10:c3faa7ffd23b 137 printf("<DEL>");
shimniok 12:ecf3fc049bca 138 } else if (c4 == PGUP) {
shimniok 10:c3faa7ffd23b 139 printf("<PGUP>");
shimniok 12:ecf3fc049bca 140 } else if (c4 == PGDN) {
shimniok 10:c3faa7ffd23b 141 printf("<PGDN>");
shimniok 12:ecf3fc049bca 142 }//if
shimniok 10:c3faa7ffd23b 143 }//if
shimniok 12:ecf3fc049bca 144 }//if
shimniok 10:c3faa7ffd23b 145 }//if
shimniok 10:c3faa7ffd23b 146 //printf("\n");
shimniok 0:49820d5a38c9 147 } else if (i < MAXBUF-1) {
shimniok 0:49820d5a38c9 148 if (c == 0x7f || c == '\b') { // backspace or delete
shimniok 0:49820d5a38c9 149 if (i > 0) { // if we're at the beginning, do nothing
shimniok 0:49820d5a38c9 150 i--;
shimniok 0:49820d5a38c9 151 fputs("\b \b", stdout);
shimniok 0:49820d5a38c9 152 }
shimniok 0:49820d5a38c9 153 } else {
shimniok 10:c3faa7ffd23b 154 if (isprint(c))
shimniok 10:c3faa7ffd23b 155 fputc(c, stdout);
shimniok 0:49820d5a38c9 156 cmd[i++] = c;
shimniok 0:49820d5a38c9 157 }
shimniok 0:49820d5a38c9 158 }
shimniok 10:c3faa7ffd23b 159
shimniok 0:49820d5a38c9 160 } while (!done);
shimniok 0:49820d5a38c9 161 fputc('\n', stdout);
shimniok 0:49820d5a38c9 162
shimniok 5:8f486f4d29d3 163 // chomp
shimniok 5:8f486f4d29d3 164 for (int j=i; j >= 0; j--) {
shimniok 5:8f486f4d29d3 165 if (isspace(cmd[j])) {
shimniok 5:8f486f4d29d3 166 cmd[j] = '\0';
shimniok 5:8f486f4d29d3 167 }
shimniok 5:8f486f4d29d3 168 }
shimniok 5:8f486f4d29d3 169
shimniok 2:4f0affdb7db9 170 return;
shimniok 0:49820d5a38c9 171 }