The CommandProcessor is the interface to install a run-time menu into an embedded system.

Dependents:   A_CANAdapter USB2I2C

Revision:
15:5f30da93e3e2
Parent:
14:7971c8bd3f11
Child:
16:4ce4f55213ac
--- a/CommandProcessor.c	Sat Oct 01 20:01:44 2011 +0000
+++ b/CommandProcessor.c	Sun Oct 30 19:57:39 2011 +0000
@@ -8,8 +8,6 @@
 ///
 /// Even though it is a c interface, it is somewhat object oriented.
 ///
-/// @version 1.04
-///
 /// @note Copyright &copr; 2011 by Smartware Computing, all rights reserved.
 ///     Individuals may use this application for evaluation or non-commercial
 ///     purposes. Within this restriction, changes may be made to this application
@@ -53,14 +51,14 @@
 static CMDLINK_T * head = NULL;
 
 static char *buffer;        // buffer space must be allocated based on the longest command
-static char *historyBuffer;		// keeps the history of commands for recall
-static int historyCount = 0;	// and the count of
+static char *historyBuffer;        // keeps the history of commands for recall
+static int historyCount = 0;    // and the count of
 static int historyDepth = 0;
 static size_t longestCommand = 0;
 
 static struct {
-	CMD_T *SignOnBanner;
-	int showSignOnBanner;		// Shows the sign-on banner at startup
+    CMD_T *SignOnBanner;
+    int showSignOnBanner;        // Shows the sign-on banner at startup
     int caseinsensitive;    // FALSE=casesensitive, TRUE=insensitive
     int echo;               // TRUE=echo on, FALSE=echo off
     int bufferSize;         // size of the command buffer
@@ -72,14 +70,22 @@
 
 static INITRESULT_T CommandProcessor_Init(
     CMD_T *SignOnBanner,
-	CONFIG_T config,
+    CONFIG_T config,
     int maxCmdLen,
-	int historyCount,
+    int historyCount,
     int (*kbhit)(void),
     int (*getch)(void),
     int (*putch)(int ch),
     int (*puts)(const char * s)
 );
+
+// Used when processing characters
+static int keycount = 0;    // how full?
+static int leadinChar = 0;
+static int whereInHistory = 0;        // navigates history
+static int showPrompt = TRUE;
+
+
 static ADDRESULT_T CommandProcessor_Add(CMD_T *m);
 static RUNRESULT_T CommandProcessor_Run(void);
 static RUNRESULT_T CommandProcessor_End(void);
@@ -131,17 +137,17 @@
 /// @returns runok
 ///
 static RUNRESULT_T History(char *p) {
-	int whereInHistory = 0;
-	char buf[100];
+    int whereInHistory = 0;
+    char buf[100];
 
-	cfg.puts("");
-	for (whereInHistory = 0; whereInHistory < historyCount; whereInHistory++) {
-		sprintf(buf, "  %2i: %s", whereInHistory - historyCount, &historyBuffer[whereInHistory * cfg.bufferSize]);
-		cfg.puts(buf);
-	}
-	sprintf(buf, "  %2i: %s", 0, buffer);
-	cfg.puts(buf);
-	return runok;
+    cfg.puts("");
+    for (whereInHistory = 0; whereInHistory < historyCount; whereInHistory++) {
+        sprintf(buf, "  %2i: %s", whereInHistory - historyCount, &historyBuffer[whereInHistory * cfg.bufferSize]);
+        cfg.puts(buf);
+    }
+    sprintf(buf, "  %2i: %s", 0, buffer);
+    cfg.puts(buf);
+    return runok;
 }
 
 /// Turns command prompt echo on and off
@@ -204,11 +210,11 @@
                  "    * <esc> can be used to cancel a command.\r\n"
                  "    * <tab> can be used to complete the entry of a partial command.\r\n"
                  "");
-		cfg.puts("\r\n About this CommandProcessor:\r\n"
-				 "    This CommandProcessor provides an easy facility for creating an\r\n"
-				 "      interactive runtime interpreter in an embedded system.\r\n"
-				 "    Copyright (c) 2011 by Smartware Computing, all rights reserved.\r\n"
-				 "    Author: David Smart, Smartware Computing\r\n");
+        cfg.puts("\r\n About this CommandProcessor:                               (v" VERSION ")\r\n"
+                 "    This CommandProcessor provides an easy facility for creating an\r\n"
+                 "      interactive runtime interpreter in an embedded system.\r\n"
+                 "    Copyright (c) 2011 by Smartware Computing, all rights reserved.\r\n"
+                 "    Author: David Smart, Smartware Computing\r\n");
     }
     return runok;
 }
@@ -235,7 +241,7 @@
     int compareLength;
     int foundCount = 0;
     CMDLINK_T *link = head;
-	char * alternateBuffer;
+    char * alternateBuffer;
 
     if (strlen(buffer)) {  // simple sanity check
         // Try to process the buffer. A command could be "Help", or it could be "Test1 123 abc"
@@ -270,17 +276,17 @@
                 // If they type "He 1234 5678", we backup and rewrite as "Help 1234 5678"
                 int diff = strlen((*menu)->command) - compareLength;    // e.g. 5 - 3
 
-				// or if they entered it in a case that doesn't match the command exactly
+                // or if they entered it in a case that doesn't match the command exactly
                 if (diff > 0 || 0 != strncmp(buffer, (*menu)->command, compareLength)) {
                     char *p = buffer;
-					alternateBuffer = (char *)malloc(cfg.bufferSize);
-					strcpy(alternateBuffer, (*menu)->command);
-					strcat(alternateBuffer, " ");
-					strcat(alternateBuffer, space);
-					EraseChars(strlen(buffer));
-					strcpy(buffer, alternateBuffer);
-					free(alternateBuffer);
-					EchoString(p);
+                    alternateBuffer = (char *)malloc(cfg.bufferSize);
+                    strcpy(alternateBuffer, (*menu)->command);
+                    strcat(alternateBuffer, " ");
+                    strcat(alternateBuffer, space);
+                    EraseChars(strlen(buffer));
+                    strcpy(buffer, alternateBuffer);
+                    free(alternateBuffer);
+                    EchoString(p);
                 }
             }
         }
@@ -291,7 +297,7 @@
 
 /// Init is the first function to call to configure the CommandProcessor.
 ///
-/// This function has a number of parameters, which make the CommandProcessor 
+/// This function has a number of parameters, which make the CommandProcessor
 /// quite flexible.
 ///
 /// @param SignOnBanner function, which is used as a signon banner
@@ -314,31 +320,30 @@
     CMD_T (*SignOnBanner),
     CONFIG_T config,
     int maxCmdLen,
-	int numInHistory,
+    int numInHistory,
     int (*kbhit)(void),
     int (*getch)(void),
     int (*putch)(int ch),
     int (*puts)(const char * s)
 ) {
-	if (SignOnBanner) {
-		CommandProcessor.Add(SignOnBanner);
-		cfg.SignOnBanner = SignOnBanner;
-		cfg.showSignOnBanner = 1;
-	}
+    if (SignOnBanner) {
+        CommandProcessor.Add(SignOnBanner);
+        cfg.SignOnBanner = SignOnBanner;
+        cfg.showSignOnBanner = 1;
+    }
     if (maxCmdLen < 6)
         maxCmdLen = 6;
-	buffer = (char *)malloc(maxCmdLen);			// users often error by one, so we'll be generous
-	historyDepth = numInHistory;
-	historyBuffer = (char *)malloc(historyDepth * maxCmdLen);
+    buffer = (char *)malloc(maxCmdLen);            // users often error by one, so we'll be generous
+    historyDepth = numInHistory;
+    historyBuffer = (char *)malloc(historyDepth * maxCmdLen);
     cfg.bufferSize = maxCmdLen;
     if (buffer && historyBuffer) {
-        if (config & CFG_ENABLE_SYSTEM)
-		{
+        if (config & CFG_ENABLE_SYSTEM) {
             CommandProcessor.Add(&QuestionMenu);
             CommandProcessor.Add(&HelpMenu);
-			CommandProcessor.Add(&HistoryMenu);
+            CommandProcessor.Add(&HistoryMenu);
             CommandProcessor.Add(&EchoMenu);
-		}
+        }
         if (config & CFG_ENABLE_TERMINATE)
             CommandProcessor.Add(&ExitMenu);
         //if (addDefaultMenu & 0x0002)
@@ -373,7 +378,7 @@
 
     if (strlen(menu->command) > longestCommand)
         longestCommand = strlen(menu->command);
-    
+
     // Allocate the storage for this menu item
     temp = (CMDLINK_T *)malloc(sizeof(CMDLINK_T));
     if (!temp)
@@ -391,20 +396,20 @@
         prev = ptr;
         ptr = ptr->next;
     }
-	if (prev == head) {
-		head = temp;
-		head->next = prev;
-	} else {
-		prev->next = temp;
-		prev = temp;
-		prev->next = ptr;
-	}
+    if (prev == head) {
+        head = temp;
+        head->next = prev;
+    } else {
+        prev->next = temp;
+        prev = temp;
+        prev->next = ptr;
+    }
     return addok;
 }
 
 static void EchoString(char *p) {
-	while (*p)
-		cfg.putch(*p++);
+    while (*p)
+        cfg.putch(*p++);
 }
 
 static void EraseChars(int keycount) {
@@ -416,6 +421,170 @@
 }
 
 
+static int ProcessComplexSequence(int c) {
+    switch (c) {
+        case 0x42:
+        case 0x50:    // down arrow - toward the newest (forward in time)
+            // if there is anything in the history, copy it out
+            if (historyCount && whereInHistory < historyCount) {
+                char *p;
+
+                EraseChars(keycount);
+                p = strcpy(buffer, &historyBuffer[whereInHistory * cfg.bufferSize]);
+                EchoString(p);
+                keycount = strlen(buffer);
+                whereInHistory++;
+            }
+            c = 0;
+            break;
+        case 0x41:
+        case 0x48:    // up arrow - from newest to oldest (backward in time)
+            // same as escape
+            if (historyCount && --whereInHistory >= 0) {
+                char *p;
+
+                EraseChars(keycount);
+                p = strcpy(buffer, &historyBuffer[whereInHistory * cfg.bufferSize]);
+                EchoString(p);
+                keycount = strlen(buffer);
+                c = 0;
+            } else {
+                whereInHistory = 0;
+                c = 0x1B;
+            }
+            break;
+        default:
+            // ignore this char
+            c = 0;
+            break;
+    }
+    leadinChar = 0;
+    return c;
+}
+
+
+static RUNRESULT_T ProcessStandardSequence(int c) {
+    int foundCount = 0;
+    CMD_T *cbk = NULL;
+    char * params = NULL;
+    RUNRESULT_T val = runok;
+
+    // Process Character
+    switch (c) {
+        case 0:
+            // null - do nothing
+            break;
+        case 0x5B:
+            // ANSI (VT100) sequence
+            // <ESC>[A is up
+            // <ESC>[B is down
+            // <ESC>[C is right
+            // <ESC>[D is left
+            leadinChar = 1;
+            break;
+        case 0xE0:
+            // Windows Command Shell (DOS box)
+            // Lead-in char
+            // 0xE0 0x48 is up arrow
+            // 0xE0 0x50 is down arrow
+            // 0xE0 0x4B is left arrow
+            // 0xE0 0x4D is right arrow
+            leadinChar = 1;
+            break;
+        case 0x09:    // <TAB> to request command completion
+            if (1 == CommandMatches(buffer, FALSE, &cbk, &params)) {
+                size_t n;
+                char *p = strchr(buffer, ' ');
+                if (p)
+                    n = p - buffer;
+                else
+                    n = strlen(buffer);
+                if (n < strlen(cbk->command)) {
+                    p = cbk->command + strlen(buffer);
+                    mystrcat(buffer, p);
+                    keycount = strlen(buffer);
+                    EchoString(p);
+                    //cfg.printf("%s", p);
+                }
+            }
+            break;
+        case 0x1b:    // <ESC> to empty the command buffer
+            EraseChars(keycount);
+            keycount = 0;
+            buffer[keycount] = '\0';
+            break;
+        case '\x08':    // <bs>
+            if (keycount) {
+                buffer[--keycount] = '\0';
+                EraseChars(1);
+            } else
+                cfg.putch(0x07);    // bell
+            break;
+        case '\r':
+        case '\n':
+            if (strlen(buffer)) {
+                foundCount = CommandMatches(buffer, TRUE, &cbk, &params);
+                if (foundCount == 1) {
+                    val = (*cbk->callback)(params);        // Execute the command
+                    if (mystrnicmp(buffer, (const char *)&historyBuffer[(historyCount-1) * cfg.bufferSize], strlen(&historyBuffer[(historyCount-1) * cfg.bufferSize])) != 0) {
+                        // not repeating the last command, so enter into the history
+                        if (historyCount == historyDepth) {
+                            int i;
+                            historyCount--;
+                            for (i=0; i<historyCount; i++)
+                                strcpy(&historyBuffer[i * cfg.bufferSize], &historyBuffer[(i+1) * cfg.bufferSize]);
+                        }
+                        strcpy(&historyBuffer[historyCount * cfg.bufferSize], buffer);
+                        whereInHistory = historyCount;
+                        historyCount++;
+                    }
+                } else if (foundCount > 1)
+                    cfg.puts(" *** non-unique command ignored      try 'Help' ***");
+                else if (foundCount == 0)
+                    cfg.puts(" *** huh?                            try 'Help' ***");
+            } else
+                cfg.puts("");
+            keycount = 0;
+            buffer[keycount] = '\0';
+            showPrompt = TRUE;        // forces the prompt
+            break;
+        default:
+            // any other character is assumed to be part of the command
+            if (myisprint(c) && keycount < cfg.bufferSize) {
+                buffer[keycount++] = (char)c;
+                buffer[keycount] = '\0';
+                if (CommandMatches(buffer, FALSE, &cbk, &params))
+                    cfg.putch(c);
+                else {
+                    buffer[--keycount] = '\0';
+                    cfg.putch(0x07);    // bell
+                }
+            } else
+                cfg.putch(0x07);    // bell
+            break;
+    }
+    return val;
+}
+
+
+#if 0
+static void PutCharToHex(int c) {
+    int upper = c >> 4;
+    int lower = c & 0x0F;
+
+    cfg.putch('[');
+    if (upper >= 10)
+        cfg.putch(upper - 10 + 'A');
+    else
+        cfg.putch(upper + '0');
+    if (lower >= 10)
+        cfg.putch(lower - 10 + 'A');
+    else
+        cfg.putch(lower + '0');
+    cfg.putch(']');
+}
+#endif
+
 /// Run the CommandProcessor
 ///
 /// This will peek to see if there is a keystroke ready. It will pull that into a
@@ -431,146 +600,25 @@
 /// @returns runfail if the command that was run is asking the CommandProcessor to exit
 ///
 RUNRESULT_T CommandProcessor_Run(void) {
-    static int showPrompt = TRUE;
-    static int keycount = 0;    // how full?
-	static int leadinChar = 0;
-	static int whereInHistory = 0;		// navigates history
-    int foundCount = 0;
     RUNRESULT_T val = runok;            // return true when happy, false to exit the prog
-    CMD_T *cbk = NULL;
-    char * params = NULL;
 
-	if (cfg.showSignOnBanner) {
-		cfg.SignOnBanner->callback("");
-		cfg.showSignOnBanner = 0;
-	}
+    if (cfg.showSignOnBanner) {
+        cfg.SignOnBanner->callback("");
+        cfg.showSignOnBanner = 0;
+    }
     if (showPrompt && cfg.echo) {
         cfg.putch('>');
         showPrompt = FALSE;
     }
     if (cfg.kbhit()) {
         int c = cfg.getch();
-
-		if (leadinChar) {
-        switch (c) {
-			case 0x50:	// down arrow - toward the newest (forward in time)
-				// if there is anything in the history, copy it out
-				if (historyCount && whereInHistory < historyCount) {
-					char *p;
-
-					EraseChars(keycount);
-					p = strcpy(buffer, &historyBuffer[whereInHistory * cfg.bufferSize]);
-					EchoString(p);
-					keycount = strlen(buffer);
-					whereInHistory++;
-				}
-				c = 0;
-				break;
-			case 0x48:	// up arrow - from newest to oldest (backward in time)
-				// same as escape
-				if (historyCount && --whereInHistory >= 0) {
-					char *p;
-
-					EraseChars(keycount);
-					p = strcpy(buffer, &historyBuffer[whereInHistory * cfg.bufferSize]);
-					EchoString(p);
-					keycount = strlen(buffer);
-					c = 0;
-				} else {
-					whereInHistory = 0;
-					c = 0x1B;
-				}
-				break;
-			default:
-				// ignore this char
-				c = 0;
-				break;
-			}
-			leadinChar = 0;
-		}
-		switch (c) {
-			case 0:
-				// null - do nothing
-				break;
-			case 0xE0:
-				// Lead-in char
-				// 0xE0 0x48 is up arrow
-				// 0xE0 0x50 is down arrow
-				// 0xE0 0x4B is left arrow
-				// 0xE0 0x4D is right arrow
-				leadinChar = 1;
-				break;
-            case 0x09:    // <TAB> to request command completion
-                if (1 == CommandMatches(buffer, FALSE, &cbk, &params)) {
-                    size_t n;
-                    char *p = strchr(buffer, ' ');
-                    if (p)
-                        n = p - buffer;
-                    else
-                        n = strlen(buffer);
-                    if (n < strlen(cbk->command)) {
-                        p = cbk->command + strlen(buffer);
-                        mystrcat(buffer, p);
-                        keycount = strlen(buffer);
-                        EchoString(p);
-						//cfg.printf("%s", p);
-                    }
-                }
-                break;
-            case 0x1b:    // <ESC> to empty the command buffer
-				EraseChars(keycount);
-                keycount = 0;
-                buffer[keycount] = '\0';
-                break;
-            case '\x08':    // <bs>
-                if (keycount) {
-                    buffer[--keycount] = '\0';
-                    EraseChars(1);
-                } else
-                    cfg.putch(0x07);    // bell
-                break;
-            case '\r':
-            case '\n': 
-                if (strlen(buffer)) {
-                    foundCount = CommandMatches(buffer, TRUE, &cbk, &params);
-                    if (foundCount == 1) {
-                        val = (*cbk->callback)(params);        // Execute the command
-						if (mystrnicmp(buffer, (const char *)&historyBuffer[(historyCount-1) * cfg.bufferSize], strlen(&historyBuffer[(historyCount-1) * cfg.bufferSize])) != 0) {
-							// not repeating the last command, so enter into the history
-							if (historyCount == historyDepth) {
-								int i;
-								historyCount--;
-								for (i=0; i<historyCount; i++)
-									strcpy(&historyBuffer[i * cfg.bufferSize], &historyBuffer[(i+1) * cfg.bufferSize]);
-							}
-							strcpy(&historyBuffer[historyCount * cfg.bufferSize], buffer);
-							whereInHistory = historyCount;
-							historyCount++;
-						}
-                    } else if (foundCount > 1)
-                        cfg.puts(" *** non-unique command ignored      try 'Help' ***");
-                    else if (foundCount == 0)
-                        cfg.puts(" *** huh?                            try 'Help' ***");
-                } else
-                    cfg.puts("");
-                keycount = 0;
-                buffer[keycount] = '\0';
-                showPrompt = TRUE;        // forces the prompt
-            break;
-            default:
-                if (myisprint(c) && keycount < cfg.bufferSize) {
-                    buffer[keycount++] = (char)c;
-                    buffer[keycount] = '\0';
-                    if (CommandMatches(buffer, FALSE, &cbk, &params))
-                        cfg.putch(c);
-                    else {
-                        buffer[--keycount] = '\0';
-                        cfg.putch(0x07);    // bell
-                    }
-                } else
-                    cfg.putch(0x07);    // bell
-                break;
+        //PutCharToHex(c);      // a debug utility
+        if (leadinChar) {
+            // some previous character was a lead-in to a more complex sequence
+            // to be processed
+            c = ProcessComplexSequence(c);
         }
+        ProcessStandardSequence(c);
     }
     return val;
 }