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:
Mon Dec 24 17:42:43 2018 +0000
Revision:
21:5d7ac1f0b842
Parent:
20:53f0b5dc30f9
Child:
23:b1e49cfcaef6
Restored blanked-out file

Who changed what in which revision?

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