Simple embedded shell with runtime pluggable commands.

Dependents:   DataBus2018

Implements a simple unix-like shell for embedded systems with a pluggable command architecture.

Revision:
28:753db82debb1
Parent:
24:004e33abce37
Child:
29:8d4132274445
diff -r 51120f1cec44 -r 753db82debb1 SimpleShell.cpp
--- a/SimpleShell.cpp	Mon Dec 24 20:15:29 2018 +0000
+++ b/SimpleShell.cpp	Wed Dec 26 15:51:42 2018 +0000
@@ -1,6 +1,7 @@
 #include "SimpleShell.h"
 #include <ctype.h>
 #include <string>
+#include <list>
 
 #define ESC     0x1b
 #define UP      0x41
@@ -15,16 +16,77 @@
 #define TAIL    0x7e
 
 
+SimpleShell::SimpleShell()
+{
+    lookupEnd = 0;
+
+    // Built-in shell commands
+    command(callback(this, &SimpleShell::help), "help");
+    command(callback(this, &SimpleShell::pwd), "pwd");
+    command(callback(this, &SimpleShell::cat), "cat");
+    command(callback(this, &SimpleShell::cd), "cd");
+    command(callback(this, &SimpleShell::rm), "rm");
+    command(callback(this, &SimpleShell::touch), "touch");
+    command(callback(this, &SimpleShell::ls), "ls");
+    command(callback(this, &SimpleShell::send), "send");
+}
+
+
+/// Path stack representation
+typedef std::list<char*> path_t;
+
 char *SimpleShell::canon(char *path) {
-    static char result[MAXBUF];
+    path_t pstack;
+    static char tmp[MAXBUF*2];
+    char *e;
 
+    // if we're passed empty/null string, just send back cwd so nothing breaks
+    if (path == NULL || strlen(path) == 0) {
+        strcpy(tmp, _cwd);
+        return tmp;
+    }
+
+    // relative path? add current working directory to path stack
     if (path[0] != '/') {
-        strncpy(result, _cwd, MAXBUF);
-        strcat(result, "/");
+        strcpy(tmp, _cwd);
+        strcat(tmp, "/");
+        strcat(tmp, path);
+    } else {
+        strcpy(tmp, path);
     }
-    strcat(result, path);
+
+    // now canonicalize the path spec
+    e = strtok(tmp+1, "/");
+    while (e) {
+        //printf("e = <%s>\n", e);
+        if (strcmp("..", e) == 0) {
+            // pop most recent directory
+            if (!pstack.empty())
+                pstack.pop_back();
+        } else if (strcmp(".", e) != 0) {
+            // push this dir onto path
+            if (strlen(e) > 0)
+                pstack.push_back(e);
+        }
+        e = strtok(NULL, "/");
+    }
 
-    return result;
+    static std::string result;
+    result = "";
+
+    for (path_t::iterator it = pstack.begin(); it != pstack.end(); it++) {
+      result.append("/");
+      result.append(*it);
+    }
+
+    // if empty, add a /
+    if (result.size() < 2) {
+      result.append("/");
+    }
+
+    strcpy(tmp, result.c_str());
+
+    return tmp;
 }
 
 
@@ -43,23 +105,6 @@
 }
 
 
-
-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");
-    attach(callback(this, &SimpleShell::send), "send");
-}
-
-
 void SimpleShell::help(int argc, char **argv)
 {
     printf("Available commands: ");
@@ -73,7 +118,7 @@
 void SimpleShell::cd(int argc, char **argv)
 {
     if (argc == 2) {
-        strncpy(_cwd, argv[1], MAXBUF);
+        strncpy(_cwd, canon(argv[1]), MAXBUF);
     } else {
         puts("usage: cd directory");
     }
@@ -97,7 +142,7 @@
     if (argc == 1) {
         path = _cwd;
     } else if (argc == 2) {
-        path = argv[1];
+        path = canon(argv[1]);
     } else {
         puts("usage: ls [directory]");
         return;
@@ -259,7 +304,7 @@
 }
 
 
-void SimpleShell::attach(callback_t cb, char *command)
+void SimpleShell::command(callback_t cb, char *command)
 {
     if (lookupEnd < MAXLOOKUP) {
         lookup[lookupEnd].cb = cb;