The CommandProcessor is the interface to install a run-time menu into an embedded system.
Dependents: A_CANAdapter USB2I2C
Diff: CommandProcessor.c
- Revision:
- 15:5f30da93e3e2
- Parent:
- 14:7971c8bd3f11
- Child:
- 16:4ce4f55213ac
diff -r 7971c8bd3f11 -r 5f30da93e3e2 CommandProcessor.c --- 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, ¶ms)) { + 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, ¶ms); + 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, ¶ms)) + 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, ¶ms)) { - 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, ¶ms); - 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, ¶ms)) - 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; }