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:
Wed Dec 19 19:02:17 2018 +0000
Revision:
17:0739cb2f1930
Parent:
16:f2b9b7b2c71e
Child:
18:2b5ed529ab37
implemented built-in change dir (cd)

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 13:a29fb89018e1 3 #include <string>
shimniok 0:49820d5a38c9 4
shimniok 12:ecf3fc049bca 5 #define ESC 0x1b
shimniok 12:ecf3fc049bca 6 #define UP 0x41
shimniok 12:ecf3fc049bca 7 #define DOWN 0x42
shimniok 12:ecf3fc049bca 8 #define RIGHT 0x43
shimniok 12:ecf3fc049bca 9 #define LEFT 0x44
shimniok 12:ecf3fc049bca 10 #define HOME 0x31
shimniok 12:ecf3fc049bca 11 #define INS 0x32
shimniok 12:ecf3fc049bca 12 #define DEL 0x33
shimniok 12:ecf3fc049bca 13 #define PGUP 0x35
shimniok 12:ecf3fc049bca 14 #define PGDN 0x36
shimniok 12:ecf3fc049bca 15 #define TAIL 0x7e
shimniok 12:ecf3fc049bca 16
shimniok 10:c3faa7ffd23b 17
shimniok 0:49820d5a38c9 18 SimpleShell::SimpleShell()
shimniok 0:49820d5a38c9 19 {
shimniok 2:4f0affdb7db9 20 lookupEnd = 0;
shimniok 13:a29fb89018e1 21
shimniok 8:41b7274a9753 22 // Built-in shell commands
shimniok 8:41b7274a9753 23 attach(callback(this, &SimpleShell::help), "help");
shimniok 13:a29fb89018e1 24 attach(callback(this, &SimpleShell::pwd), "pwd");
shimniok 16:f2b9b7b2c71e 25 attach(callback(this, &SimpleShell::cat), "cat");
shimniok 17:0739cb2f1930 26 attach(callback(this, &SimpleShell::cd), "cd");
shimniok 0:49820d5a38c9 27 }
shimniok 0:49820d5a38c9 28
shimniok 8:41b7274a9753 29
shimniok 15:242626d8d104 30 void SimpleShell::help(int argc, char **argv)
shimniok 8:41b7274a9753 31 {
shimniok 8:41b7274a9753 32 printf("Available commands: ");
shimniok 8:41b7274a9753 33 for (int i=0; i < lookupEnd; i++) {
shimniok 8:41b7274a9753 34 printf("%s ", lookup[i].command);
shimniok 8:41b7274a9753 35 }
shimniok 8:41b7274a9753 36 printf("\n\n");
shimniok 8:41b7274a9753 37 }
shimniok 8:41b7274a9753 38
shimniok 8:41b7274a9753 39
shimniok 17:0739cb2f1930 40 void SimpleShell::cd(int argc, char **argv)
shimniok 17:0739cb2f1930 41 {
shimniok 17:0739cb2f1930 42 if (argc == 2) {
shimniok 17:0739cb2f1930 43 strncpy(_cwd, argv[1], MAXBUF);
shimniok 17:0739cb2f1930 44 } else {
shimniok 17:0739cb2f1930 45 puts("usage: cd <directory>");
shimniok 17:0739cb2f1930 46 }
shimniok 17:0739cb2f1930 47 return;
shimniok 17:0739cb2f1930 48 }
shimniok 17:0739cb2f1930 49
shimniok 17:0739cb2f1930 50
shimniok 15:242626d8d104 51 void SimpleShell::pwd(int argc, char **argv)
shimniok 13:a29fb89018e1 52 {
shimniok 13:a29fb89018e1 53 puts(_cwd);
shimniok 13:a29fb89018e1 54 return;
shimniok 13:a29fb89018e1 55 }
shimniok 13:a29fb89018e1 56
shimniok 13:a29fb89018e1 57
shimniok 16:f2b9b7b2c71e 58 void SimpleShell::cat(int argc, char **argv)
shimniok 16:f2b9b7b2c71e 59 {
shimniok 16:f2b9b7b2c71e 60 FILE *fp;
shimniok 16:f2b9b7b2c71e 61 //int status=0;
shimniok 16:f2b9b7b2c71e 62 char *buf = new char[MAXBUF];
shimniok 16:f2b9b7b2c71e 63
shimniok 16:f2b9b7b2c71e 64 for (int i=1; i < argc; i++) {
shimniok 16:f2b9b7b2c71e 65 //resolveDirectory(path, arg);
shimniok 16:f2b9b7b2c71e 66 if ((fp = fopen(argv[i], "r")) != NULL) {
shimniok 16:f2b9b7b2c71e 67 while (!feof(fp)) {
shimniok 16:f2b9b7b2c71e 68 fgets(buf, MAXBUF-1, fp);
shimniok 16:f2b9b7b2c71e 69 fputs(buf, stdout);
shimniok 16:f2b9b7b2c71e 70 }
shimniok 16:f2b9b7b2c71e 71 fclose(fp);
shimniok 16:f2b9b7b2c71e 72 } else {
shimniok 16:f2b9b7b2c71e 73 fputs(argv[i], stdout);
shimniok 16:f2b9b7b2c71e 74 fputs(": No such file\n", stdout);
shimniok 16:f2b9b7b2c71e 75 //status = 1;
shimniok 16:f2b9b7b2c71e 76 }
shimniok 16:f2b9b7b2c71e 77 }
shimniok 16:f2b9b7b2c71e 78 delete[] buf;
shimniok 16:f2b9b7b2c71e 79
shimniok 16:f2b9b7b2c71e 80 return;
shimniok 16:f2b9b7b2c71e 81 }
shimniok 16:f2b9b7b2c71e 82
shimniok 16:f2b9b7b2c71e 83
shimniok 0:49820d5a38c9 84 void SimpleShell::run()
shimniok 0:49820d5a38c9 85 {
shimniok 0:49820d5a38c9 86 bool done=false;
shimniok 15:242626d8d104 87 callback_t cb;
shimniok 12:ecf3fc049bca 88 //int status; // TODO implement command status return
shimniok 13:a29fb89018e1 89 std::string x;
shimniok 4:8b8fa59d0015 90
shimniok 13:a29fb89018e1 91 // Set current working directory
shimniok 13:a29fb89018e1 92 strncpy(_cwd, "/log", MAXBUF);
shimniok 0:49820d5a38c9 93
shimniok 0:49820d5a38c9 94 printf("Type help for assistance.\n");
shimniok 15:242626d8d104 95 help(0, NULL);
shimniok 0:49820d5a38c9 96 while (!done) {
shimniok 0:49820d5a38c9 97 printPrompt();
shimniok 0:49820d5a38c9 98 readCommand();
shimniok 14:75b5918090ae 99 if (argv[0]) { // skip blank command
shimniok 5:8f486f4d29d3 100 if (cb = findCommand()) {
shimniok 15:242626d8d104 101 cb.call(argc, argv);
shimniok 5:8f486f4d29d3 102 } else {
shimniok 14:75b5918090ae 103 printf("command <%s> not found\n", argv[0]);
shimniok 5:8f486f4d29d3 104 }
shimniok 4:8b8fa59d0015 105 }
shimniok 0:49820d5a38c9 106 }
shimniok 0:49820d5a38c9 107 puts("exiting shell\n");
shimniok 0:49820d5a38c9 108
shimniok 0:49820d5a38c9 109 return;
shimniok 0:49820d5a38c9 110 }
shimniok 0:49820d5a38c9 111
shimniok 0:49820d5a38c9 112
shimniok 15:242626d8d104 113 void SimpleShell::attach(callback_t cb, char *command)
shimniok 2:4f0affdb7db9 114 {
shimniok 2:4f0affdb7db9 115 if (lookupEnd < MAXLOOKUP) {
shimniok 2:4f0affdb7db9 116 lookup[lookupEnd].cb = cb;
shimniok 2:4f0affdb7db9 117 lookup[lookupEnd].command = command;
shimniok 2:4f0affdb7db9 118 lookupEnd++;
shimniok 2:4f0affdb7db9 119 }
shimniok 2:4f0affdb7db9 120
shimniok 2:4f0affdb7db9 121 return;
shimniok 1:998a7ed04f10 122 }
shimniok 1:998a7ed04f10 123
shimniok 1:998a7ed04f10 124
shimniok 15:242626d8d104 125 SimpleShell::callback_t SimpleShell::findCommand()
shimniok 3:ebb4893f033d 126 {
shimniok 15:242626d8d104 127 SimpleShell::callback_t cb=NULL;
shimniok 3:ebb4893f033d 128
shimniok 4:8b8fa59d0015 129 for (int i=0; i < lookupEnd; i++) {
shimniok 14:75b5918090ae 130 if (strncmp(argv[0], lookup[i].command, MAXBUF) == 0) {
shimniok 4:8b8fa59d0015 131 cb = lookup[i].cb;
shimniok 4:8b8fa59d0015 132 break;
shimniok 4:8b8fa59d0015 133 }
shimniok 4:8b8fa59d0015 134 }
shimniok 4:8b8fa59d0015 135
shimniok 3:ebb4893f033d 136 return cb;
shimniok 3:ebb4893f033d 137 }
shimniok 3:ebb4893f033d 138
shimniok 3:ebb4893f033d 139
shimniok 0:49820d5a38c9 140 void SimpleShell::printPrompt()
shimniok 0:49820d5a38c9 141 {
shimniok 0:49820d5a38c9 142 fputc('(', stdout);
shimniok 0:49820d5a38c9 143 fputs(_cwd, stdout);
shimniok 0:49820d5a38c9 144 fputs(")# ", stdout);
shimniok 2:4f0affdb7db9 145
shimniok 2:4f0affdb7db9 146 return;
shimniok 0:49820d5a38c9 147 }
shimniok 0:49820d5a38c9 148
shimniok 0:49820d5a38c9 149
shimniok 0:49820d5a38c9 150 void SimpleShell::readCommand()
shimniok 0:49820d5a38c9 151 {
shimniok 0:49820d5a38c9 152 int i=0;
shimniok 0:49820d5a38c9 153 char c;
shimniok 0:49820d5a38c9 154 bool done = false;
shimniok 14:75b5918090ae 155 static char cmd[MAXBUF];
shimniok 0:49820d5a38c9 156
shimniok 0:49820d5a38c9 157 memset(cmd, 0, MAXBUF);
shimniok 0:49820d5a38c9 158 do {
shimniok 0:49820d5a38c9 159 cmd[i] = 0;
shimniok 0:49820d5a38c9 160 c = fgetc(stdin);
shimniok 0:49820d5a38c9 161 if (c == '\r') { // if return is hit, we're done, don't add \r to cmd
shimniok 0:49820d5a38c9 162 done = true;
shimniok 10:c3faa7ffd23b 163 } else if (c == ESC) { // keyboard escape codes (arrow keys, etc)
shimniok 12:ecf3fc049bca 164 int c2 = getchar();
shimniok 12:ecf3fc049bca 165 int c3 = getchar();
shimniok 10:c3faa7ffd23b 166
shimniok 10:c3faa7ffd23b 167 if (c2 == 0x4f && c3 == 0x46) {
shimniok 10:c3faa7ffd23b 168 printf("<END>");
shimniok 10:c3faa7ffd23b 169 } else if (c2 == 0x5b) {
shimniok 12:ecf3fc049bca 170
shimniok 12:ecf3fc049bca 171 if (c3 == UP) {
shimniok 10:c3faa7ffd23b 172 printf("<UP>");
shimniok 12:ecf3fc049bca 173 } else if (c3 == DOWN) {
shimniok 10:c3faa7ffd23b 174 printf("<DOWN>");
shimniok 12:ecf3fc049bca 175 } else if (c3 == RIGHT) {
shimniok 10:c3faa7ffd23b 176 printf("<RIGHT>");
shimniok 12:ecf3fc049bca 177 } else if (c3 == LEFT) {
shimniok 10:c3faa7ffd23b 178 printf("<LEFT>");
shimniok 12:ecf3fc049bca 179 } else if (c3 == HOME || c3 == INS || c3 == DEL ||
shimniok 12:ecf3fc049bca 180 c3 == PGUP || c3 == PGDN) {
shimniok 10:c3faa7ffd23b 181 char c4 = getchar();
shimniok 12:ecf3fc049bca 182 if (c4 == TAIL) {
shimniok 12:ecf3fc049bca 183 if (c4 == HOME) {
shimniok 10:c3faa7ffd23b 184 printf("<HOME>");
shimniok 12:ecf3fc049bca 185 } else if (c4 == INS) {
shimniok 10:c3faa7ffd23b 186 printf("<INS>");
shimniok 12:ecf3fc049bca 187 } else if (c4 == DEL) {
shimniok 10:c3faa7ffd23b 188 printf("<DEL>");
shimniok 12:ecf3fc049bca 189 } else if (c4 == PGUP) {
shimniok 10:c3faa7ffd23b 190 printf("<PGUP>");
shimniok 12:ecf3fc049bca 191 } else if (c4 == PGDN) {
shimniok 10:c3faa7ffd23b 192 printf("<PGDN>");
shimniok 12:ecf3fc049bca 193 }//if
shimniok 10:c3faa7ffd23b 194 }//if
shimniok 12:ecf3fc049bca 195 }//if
shimniok 10:c3faa7ffd23b 196 }//if
shimniok 10:c3faa7ffd23b 197 //printf("\n");
shimniok 0:49820d5a38c9 198 } else if (i < MAXBUF-1) {
shimniok 0:49820d5a38c9 199 if (c == 0x7f || c == '\b') { // backspace or delete
shimniok 0:49820d5a38c9 200 if (i > 0) { // if we're at the beginning, do nothing
shimniok 0:49820d5a38c9 201 i--;
shimniok 0:49820d5a38c9 202 fputs("\b \b", stdout);
shimniok 0:49820d5a38c9 203 }
shimniok 0:49820d5a38c9 204 } else {
shimniok 10:c3faa7ffd23b 205 if (isprint(c))
shimniok 10:c3faa7ffd23b 206 fputc(c, stdout);
shimniok 0:49820d5a38c9 207 cmd[i++] = c;
shimniok 0:49820d5a38c9 208 }
shimniok 0:49820d5a38c9 209 }
shimniok 10:c3faa7ffd23b 210
shimniok 0:49820d5a38c9 211 } while (!done);
shimniok 0:49820d5a38c9 212 fputc('\n', stdout);
shimniok 0:49820d5a38c9 213
shimniok 14:75b5918090ae 214 // remove leading/trailing whitespace
shimniok 14:75b5918090ae 215 char *s = cmd;
shimniok 14:75b5918090ae 216 while (isspace(*s)) {
shimniok 14:75b5918090ae 217 s++;
shimniok 14:75b5918090ae 218 }
shimniok 14:75b5918090ae 219 for (int j=i; j >= 0 && isspace(cmd[j]); j--) {
shimniok 14:75b5918090ae 220 cmd[j] = '\0';
shimniok 14:75b5918090ae 221 }
shimniok 14:75b5918090ae 222
shimniok 14:75b5918090ae 223 // split into command and arguments
shimniok 14:75b5918090ae 224 argc = 0;
shimniok 14:75b5918090ae 225 char *t;
shimniok 14:75b5918090ae 226
shimniok 14:75b5918090ae 227 for (int i=0; i < MAXARGS; i++) {
shimniok 14:75b5918090ae 228 argv[i] = NULL;
shimniok 14:75b5918090ae 229 }
shimniok 14:75b5918090ae 230 t = strtok(s, " ");
shimniok 14:75b5918090ae 231 while (t && argc < 10) {
shimniok 14:75b5918090ae 232 argv[argc++] = t;
shimniok 14:75b5918090ae 233 t = strtok(NULL, " ");
shimniok 14:75b5918090ae 234 }
shimniok 5:8f486f4d29d3 235
shimniok 2:4f0affdb7db9 236 return;
shimniok 0:49820d5a38c9 237 }