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:
Fri Dec 21 20:02:56 2018 +0000
Revision:
18:2b5ed529ab37
Parent:
17:0739cb2f1930
Child:
19:bf5f5ea4e762
bugfixes to ls

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