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 00:29:05 2018 +0000
Revision:
14:75b5918090ae
Parent:
13:a29fb89018e1
Child:
15:242626d8d104
parse command line into command and arguments

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