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 18:42:25 2018 +0000
Revision:
16:f2b9b7b2c71e
Parent:
15:242626d8d104
Child:
17:0739cb2f1930
added cat built-in

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