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:
Sat Dec 22 20:27:54 2018 +0000
Revision:
19:bf5f5ea4e762
Parent:
18:2b5ed529ab37
Child:
20:53f0b5dc30f9
Added rm, touch

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