Simple embedded shell with runtime pluggable commands.
Implements a simple unix-like shell for embedded systems with a pluggable command architecture.
Diff: SimpleShell.cpp
- Revision:
- 21:5d7ac1f0b842
- Parent:
- 20:53f0b5dc30f9
- Child:
- 23:b1e49cfcaef6
--- a/SimpleShell.cpp Mon Dec 24 04:31:43 2018 +0000 +++ b/SimpleShell.cpp Mon Dec 24 17:42:43 2018 +0000 @@ -0,0 +1,324 @@ +#include "SimpleShell.h" +#include <ctype.h> +#include <string> + +#define ESC 0x1b +#define UP 0x41 +#define DOWN 0x42 +#define RIGHT 0x43 +#define LEFT 0x44 +#define HOME 0x31 +#define INS 0x32 +#define DEL 0x33 +#define PGUP 0x35 +#define PGDN 0x36 +#define TAIL 0x7e + + +char *SimpleShell::canon(char *path) { + static char result[MAXBUF]; + + if (path[0] != '/') { + strncpy(result, _cwd, MAXBUF); + strcat(result, "/"); + } + strcat(result, path); + + return result; +} + + +SimpleShell::SimpleShell() +{ + lookupEnd = 0; + + // Built-in shell commands + attach(callback(this, &SimpleShell::help), "help"); + attach(callback(this, &SimpleShell::pwd), "pwd"); + attach(callback(this, &SimpleShell::cat), "cat"); + attach(callback(this, &SimpleShell::cd), "cd"); + attach(callback(this, &SimpleShell::rm), "rm"); + attach(callback(this, &SimpleShell::touch), "touch"); + attach(callback(this, &SimpleShell::ls), "ls"); +} + + +void SimpleShell::help(int argc, char **argv) +{ + printf("Available commands: "); + for (int i=0; i < lookupEnd; i++) { + printf("%s ", lookup[i].command); + } + printf("\n\n"); +} + + +void SimpleShell::cd(int argc, char **argv) +{ + if (argc == 2) { + strncpy(_cwd, argv[1], MAXBUF); + } else { + puts("usage: cd directory"); + } + return; +} + + +void SimpleShell::pwd(int argc, char **argv) +{ + puts(_cwd); + return; +} + + +void SimpleShell::ls(int argc, char **argv) +{ + DIR *d; + struct dirent *p; + char *path; + + if (argc == 1) { + path = _cwd; + } else if (argc == 2) { + path = argv[1]; + } else { + puts("usage: ls [directory]"); + return; + } + + int cols=0; + if ((d = opendir(path)) != NULL) { + while ((p = readdir(d)) != NULL) { + if (p->d_name && p->d_name[0] != 0xff) { + if (cols++ > 3) { + putc('\n', stdout); + cols = 0; + } + printf("%-15s ", p->d_name); + } + } + putc('\n', stdout); + if (cols < 3) + putc('\n', stdout); + closedir(d); + } else { + puts(path); + puts(": No such directory\n"); + } + + return; +} + + +void SimpleShell::rm(int argc, char **argv) +{ + if (argc >= 2) { + for (int i=1; i < argc; i++) { + if (remove(canon(argv[i]))) { + printf("%s: cannot remove\n", argv[i]); + } + } + } else { + puts("usage: rm [file1 [file2 ...]]"); + } +} + + +void SimpleShell::touch(int argc, char **argv) +{ + FILE *fp; + + if (argc >= 2) { + for (int i=1; i < argc; i++) { + if ((fp = fopen(canon(argv[i]), "w")) != NULL) { + fclose(fp); + } else { + printf("%s: cannot touch\n", argv[1]); + } + } + } else { + puts("usage: touch [file1 [file2 ...]]"); + } +} + + +void SimpleShell::cat(int argc, char **argv) +{ + FILE *fp; + //int status=0; + char *buf = new char[MAXBUF]; + + for (int i=1; i < argc; i++) { + //resolveDirectory(path, arg); + if ((fp = fopen(canon(argv[i]), "r")) != NULL) { + while (!feof(fp)) { + fgets(buf, MAXBUF-1, fp); + fputs(buf, stdout); + } + fclose(fp); + } else { + fputs(argv[i], stdout); + fputs(": No such file\n", stdout); + //status = 1; + } + } + delete[] buf; + + return; +} + + +void SimpleShell::run() +{ + bool done=false; + callback_t cb; + //int status; // TODO implement command status return + std::string x; + + // Set current working directory + strncpy(_cwd, "/etc", MAXBUF); + + printf("Type help for assistance.\n"); + help(0, NULL); + while (!done) { + printPrompt(); + readCommand(); + if (argv[0]) { // skip blank command + if (cb = findCommand()) { + cb.call(argc, argv); + } else { + printf("command <%s> not found\n", argv[0]); + } + } + } + puts("exiting shell\n"); + + return; +} + + +void SimpleShell::attach(callback_t cb, char *command) +{ + if (lookupEnd < MAXLOOKUP) { + lookup[lookupEnd].cb = cb; + lookup[lookupEnd].command = command; + lookupEnd++; + } + + return; +} + + +SimpleShell::callback_t SimpleShell::findCommand() +{ + SimpleShell::callback_t cb=NULL; + + for (int i=0; i < lookupEnd; i++) { + if (strncmp(argv[0], lookup[i].command, MAXBUF) == 0) { + cb = lookup[i].cb; + break; + } + } + + return cb; +} + + +void SimpleShell::printPrompt() +{ + fputc('(', stdout); + fputs(_cwd, stdout); + fputs(")# ", stdout); + + return; +} + + +void SimpleShell::readCommand() +{ + int i=0; + char c; + bool done = false; + static char cmd[MAXBUF]; + + memset(cmd, 0, MAXBUF); + do { + cmd[i] = 0; + c = fgetc(stdin); + if (c == '\r') { // if return is hit, we're done, don't add \r to cmd + done = true; + } else if (c == ESC) { // keyboard escape codes (arrow keys, etc) + int c2 = getchar(); + int c3 = getchar(); + + if (c2 == 0x4f && c3 == 0x46) { + printf("<END>"); + } else if (c2 == 0x5b) { + + if (c3 == UP) { + printf("<UP>"); + } else if (c3 == DOWN) { + printf("<DOWN>"); + } else if (c3 == RIGHT) { + printf("<RIGHT>"); + } else if (c3 == LEFT) { + printf("<LEFT>"); + } else if (c3 == HOME || c3 == INS || c3 == DEL || + c3 == PGUP || c3 == PGDN) { + char c4 = getchar(); + if (c4 == TAIL) { + if (c4 == HOME) { + printf("<HOME>"); + } else if (c4 == INS) { + printf("<INS>"); + } else if (c4 == DEL) { + printf("<DEL>"); + } else if (c4 == PGUP) { + printf("<PGUP>"); + } else if (c4 == PGDN) { + printf("<PGDN>"); + }//if + }//if + }//if + }//if + //printf("\n"); + } else if (i < MAXBUF-1) { + if (c == 0x7f || c == '\b') { // backspace or delete + if (i > 0) { // if we're at the beginning, do nothing + i--; + fputs("\b \b", stdout); + } + } else { + if (isprint(c)) + fputc(c, stdout); + cmd[i++] = c; + } + } + + } while (!done); + fputc('\n', stdout); + + // remove leading/trailing whitespace + char *s = cmd; + while (isspace(*s)) { + s++; + } + for (int j=i; j >= 0 && isspace(cmd[j]); j--) { + cmd[j] = '\0'; + } + + // split into command and arguments + argc = 0; + char *t; + + for (int i=0; i < MAXARGS; i++) { + argv[i] = NULL; + } + t = strtok(s, " "); + while (t && argc < 10) { + argv[argc++] = t; + t = strtok(NULL, " "); + } + + return; +} \ No newline at end of file