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

Dependents:   A_CANAdapter USB2I2C

Files at this revision

API Documentation at this revision

Comitter:
WiredHome
Date:
Sun Apr 03 21:36:22 2011 +0000
Parent:
6:1a0512faa75d
Child:
8:25581f24f7f9
Commit message:
Now sorts the commands when adding them

Changed in this revision

CommandProcessor.c Show annotated file Show diff for this revision Revisions of this file
CommandProcessor.h Show annotated file Show diff for this revision Revisions of this file
--- a/CommandProcessor.c	Sat Apr 02 17:12:39 2011 +0000
+++ b/CommandProcessor.c	Sun Apr 03 21:36:22 2011 +0000
@@ -1,12 +1,12 @@
-/// @file CommandProcessor.c is a simple interface to an interactive 
-///            command set of user defined commands.
+/// @file CommandProcessor.c is a simple interface to an interactive
+///         command set of user defined commands.
 ///
-///    With this, you can create functions that are exposed to a console
-///    interface. Each command may have 0 or more parameters.
-///    Typing the command, or at least the set of characters that make
-///    it unique from all other commands is enough to activate the command.
-///    
-///    Even though it is a c interface, it is somewhat object oriented.
+/// With this, you can create functions that are exposed to a console
+/// interface. Each command may have 0 or more parameters.
+/// Typing the command, or at least the set of characters that make
+/// it unique from all other commands is enough to activate the command.
+///
+/// Even though it is a c interface, it is somewhat object oriented.
 ///
 /// @version 1.0
 ///
@@ -26,9 +26,17 @@
 #include "CommandProcessor.h"
 
 /// This holds the single linked list of commands
+/// +-- Head->next
+/// v
+/// +-- p       +-- n
+/// v           v
+/// |menu|-------------------------------->|"Command"  |
+/// |next|->0   |menu|---->|"Help"         |"Help"     |
+///             |next|->0  |"..."          |*(callback)|
+///                        |*(callback)    |visible    |
+///                        |visible
 ///
-typedef struct CMDLINK_T
-{
+typedef struct CMDLINK_T {
     CMD_T * menu;                // handle to the menu item
     struct CMDLINK_T * next;    // handle to the next link
 } CMDLINK_T;
@@ -37,8 +45,7 @@
 
 static char *buffer;        // buffer space must be allocated based on the longest command
 
-static struct
-{
+static struct {
     int caseinsensitive;    // FALSE=casesensitive, TRUE=insensitive
     int echo;               // TRUE=echo on, FALSE=echo off
     int bufferSize;         // size of the buffer
@@ -52,19 +59,24 @@
     int defaultMenu,
     int caseinsensitive,
     int echo,
-    int maxCmdLen, 
+    int maxCmdLen,
     int (*kbhit)(void),
     int (*getch)(void),
     int (*putch)(int ch),
     int (*puts)(const char * s)
-    );
+);
 static ADDRESULT_T CommandProcessor_Add(CMD_T *m);
 static RUNRESULT_T CommandProcessor_Run(void);
 static RUNRESULT_T CommandProcessor_End(void);
 static RUNRESULT_T CommandProcessor_Echo(int echo);
 
-static CMDP_T CommandProcessor = 
-{
+// helper functions
+static int myisprint(int c);
+static void mystrcat(char *dst, char *src);
+static char mytolower(char a);
+int mystrnicmp(const char *l, const char *r, size_t n);
+
+static CMDP_T CommandProcessor = {
     CommandProcessor_Init,
     CommandProcessor_Add,
     CommandProcessor_Run,
@@ -85,27 +97,43 @@
 
 /// Gets a handle to the CommandProcessor
 ///
-///    This returns a handle to the CommandProcessor, which then permits
-///    access to the CommandProcessor functions.
+/// This returns a handle to the CommandProcessor, which then permits
+/// access to the CommandProcessor functions.
 ///
-///    @returns handle to the CommandProcessor
+/// @returns handle to the CommandProcessor
 ///
-CMDP_T * GetCommandProcessor(void)
-{
+CMDP_T * GetCommandProcessor(void) {
     return &CommandProcessor;
 }
 
 
-static RUNRESULT_T About(char *p)
-{
+/// Information about this command processor
+///
+/// @param p is a pointer to command line arguments, of which there is
+///        none for this function.
+/// @returns runok
+///
+static RUNRESULT_T About(char *p) {
     cfg.puts("\r\n About this CommandProcessor:\r\n"
-        "    This CommandProcessor provides easy facility for creating a\r\n"
-        "      runtime CommandProcessor in an embedded systems.\r\n"
-        "    author: David Smart, Smartware Computing\r\n"
-        );
+             "    This CommandProcessor provides an easy facility for creating a\r\n"
+             "      runtime interactive 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;
 }
 
+/// Turns command prompt echo on and off
+///
+/// This command is used to turn the command prompt on and off. When
+/// running in an interactive mode, it is best to have this one.
+/// When driven by another program, off may be the best choice.
+///
+/// This command also displays the current state of the echo mode.
+///
+/// @param p is a pointer to a string "on" | "1" | "off" | "0"
+/// @returns runok
+///
 static RUNRESULT_T Echo(char *p) {
     if (*p) {
         if (*p == '1' || mystrnicmp(p, "on", 2) == 0)
@@ -120,28 +148,21 @@
     return runok;
 }
 
-static RUNRESULT_T Exit(char *p)
-{
+static RUNRESULT_T Exit(char *p) {
     (void)p;
-
     cfg.puts("\r\nbye.");
     return runexit;
 }
 
-static RUNRESULT_T Help(char *p)
-{
+static RUNRESULT_T Help(char *p) {
     CMDLINK_T *link = head;
     char buffer[100];
-
     cfg.puts("\r\n");
     sprintf(buffer, " %-10s: %s", "Command", "Description");
     cfg.puts(buffer);
-    while (link && link->menu)
-    {
-        if (link->menu->visible)
-        {
-            if (strlen(link->menu->command) + strlen(link->menu->helptext) + 5 < sizeof(buffer))
-            {
+    while (link && link->menu) {
+        if (link->menu->visible) {
+            if (strlen(link->menu->command) + strlen(link->menu->helptext) + 5 < sizeof(buffer)) {
                 sprintf(buffer, " %-10s: %s", link->menu->command, link->menu->helptext);
                 cfg.puts(buffer);
             }
@@ -149,34 +170,326 @@
         link = link->next;
     }
     cfg.puts("");
-    if (*p == '?')
-    {
+    if (*p == '?') {
         cfg.puts("\r\n Extended Help\r\n"
-            "    The general form for entering commands is:\r\n"
-            "      >command option1 option2 ...\r\n"
-            "      [note that note all commands support optional parameters]\r\n"
-            "    * Abbreviations of commands may be entered so long as there\r\n"
-            "      is exactly one match in the list of commands. For example,\r\n"
-            "      'he' is the same as 'help', if there is no other command \r\n"
-            "       that starts with 'he'.\r\n"
-            "    * <bs> can be used to erase previously entered information.\r\n"
-            "    * <esc> can be used to cancel a command.\r\n"
-            "    * <tab> can be used to complete the entry of a partial command.\r\n"
-            "");
+                 "    The general form for entering commands is:\r\n"
+                 "      >command option1 option2 ...\r\n"
+                 "      [note that note all commands support optional parameters]\r\n"
+                 "    * Abbreviations of commands may be entered so long as there\r\n"
+                 "      is exactly one match in the list of commands. For example,\r\n"
+                 "      'he' is the same as 'help', if there is no other command \r\n"
+                 "       that starts with 'he'.\r\n"
+                 "    * <bs> can be used to erase previously entered information.\r\n"
+                 "    * <esc> can be used to cancel a command.\r\n"
+                 "    * <tab> can be used to complete the entry of a partial command.\r\n"
+                 "");
     }
     return runok;
 }
 
+/// CommandMatches is the function that determines if the user is entering a valid
+/// command
+///
+/// This function gets called whenever the user types a printable character or when
+/// they press \<enter\>.
+/// The buffer of user input is evaluated to determine if the command is legitimate.
+/// It also determines if they want to execute the command, or simply evaluate it.
+/// Finally, it identifies writes to user provided pointers, the menu item that
+/// matches and a pointer to any parameters that the user entered.
+///
+/// @param buffer is the buffer that the user has entered commands into
+/// @param exec indicates the intention to execute the command, if found
+/// @param menu is a pointer to a pointer to a menu structure, which is updated based on the command
+/// @param params is a pointer to a pointer to the user entered parameters
+/// @returns the number of menu picks that match the user entered command
+///
+static int CommandMatches(char * buffer, int exec, CMD_T **menu, char **params) {
+    char *space;
+    int compareLength;
+    int foundCount = 0;
+    CMDLINK_T *link = head;
+
+    if (strlen(buffer)) {  // simple sanity check
+        // Try to process the buffer. A command could be "Help", or it could be "Test1 123 abc"
+        space = strchr(buffer, ' ');        // if the command has parameters, find the delimiter
+        if (space) {
+            compareLength = space - buffer;
+            space++;        // char after the space
+        } else {
+            compareLength = strlen(buffer);
+            space = buffer + compareLength;
+        }
+        while (link && link->menu) {
+            int cmpResult;
+
+            if (cfg.caseinsensitive) {
+                cmpResult = mystrnicmp(buffer, link->menu->command, compareLength);
+            } else
+                cmpResult = strncmp(buffer, link->menu->command, compareLength);
+            if (0 == cmpResult) {  // A match to what they typed
+                if (menu) {  // yes, we have a callback
+                    *menu = link->menu;    // accessor to the command they want to execute
+                    *params = space;
+                }
+                foundCount++;    // how many command match what they typed so far
+            }
+            link = link->next;    // follow the link to the next command
+        }
+        if (foundCount == 1) {
+            // If we found exactly one and they expressed an intent to execute that command
+            // then we'll rewrite the command to be fully qualified
+            if (exec) {  // command wants to execute it, not just validate the command syntax
+                // If they type "He 1234 5678", we backup and rewrite as "Help 1234 5678"
+                int diff = strlen((*menu)->command) - compareLength;    // e.g. 5 - 3
+
+                if (diff > 0) {
+                    int back = 0;
+                    char *p;
+                    if (strlen(space))
+                        back = strlen(space) + 1;
+
+                    while (back--)
+                        cfg.putch(0x08);
+                    p = (*menu)->command + compareLength;
+                    while (*p)
+                        cfg.putch(*p++);
+                    cfg.putch(' ');
+                    p = space;
+                    while (*p)
+                        cfg.putch(*p++);
+                    //cfg.printf("%s %s", link->menu->command + compareLength, space);
+                }
+            }
+        }
+    }
+    return foundCount;
+}
+
+/// Initialize the CommandProcessor
+///
+/// This initializes the CommandProcessor by adding the built-in menus
+///
+/// @param addDefaultMenu configures it to add the Help, About, and Exit menus
+/// @param bufSize configures the size of the longest command, which must be
+///         greater than 6 (the size of "About\0").
+/// @returns initok if it successfully initialized the CommandProcessor
+/// @returns initfailed if it could not allocate memory
+///
+INITRESULT_T CommandProcessor_Init(
+    int addDefaultMenu,
+    int caseinsensitive,
+    int echo,
+    int maxCmdLen,
+    int (*kbhit)(void),
+    int (*getch)(void),
+    int (*putch)(int ch),
+    int (*puts)(const char * s)
+) {
+    if (maxCmdLen < 6)
+        maxCmdLen = 6;
+    buffer = (char *)malloc(maxCmdLen+1);
+    cfg.bufferSize = maxCmdLen;
+    if (buffer) {
+        if (addDefaultMenu & 0x0008)
+            CommandProcessor.Add(&QuestionMenu);
+        if (addDefaultMenu & 0x0008)
+            CommandProcessor.Add(&HelpMenu);
+        if (addDefaultMenu & 0x0004)
+            CommandProcessor.Add(&EchoMenu);
+        if (addDefaultMenu & 0x0002)
+            CommandProcessor.Add(&AboutMenu);
+        if (addDefaultMenu & 0x0001)
+            CommandProcessor.Add(&ExitMenu);
+        cfg.caseinsensitive = caseinsensitive;
+        cfg.echo = echo;
+        cfg.kbhit = kbhit;
+        cfg.getch = getch;
+        cfg.putch = putch;
+        cfg.puts = puts;
+        return initok;
+    } else
+        return initfailed;
+}
+
+/// Add a command to the CommandProcessor
+///
+/// This adds a command to the CommandProcessor. A command has several components
+/// to it, including the command name, a brief description, the function to
+/// activate when the command is entered, and a flag indicating if the command
+/// should show up in the built-in help.
+///
+/// @param menu is the menu to add to the CommandProcessor
+/// @returns addok if the command was added
+/// @returns addfail if the command could not be added (failure to allocate memory for the linked list)
+///
+ADDRESULT_T CommandProcessor_Add(CMD_T * menu) {
+    CMDLINK_T *ptr;
+    CMDLINK_T *prev;
+    CMDLINK_T *temp;
+
+    // Allocate the storage for this menu item
+    temp = (CMDLINK_T *)malloc(sizeof(CMDLINK_T));
+    if (!temp)
+        return addfailed;			// something went really bad
+    temp->menu = menu;
+    temp->next = NULL;
+
+    prev = ptr = head;
+    if (!ptr) {
+        head = temp;			// This installs the very first item
+        return addok;
+    }
+    // Search alphabetically for the insertion point
+    while (ptr && mystrnicmp(ptr->menu->command, menu->command, strlen(menu->command)) < 0) {
+        prev = ptr;
+        ptr = ptr->next;
+    }
+    prev->next = temp;
+    prev = temp;
+    prev->next = ptr;
+    return addok;
+}
+
+/// Run the CommandProcessor
+///
+/// This will peek to see if there is a keystroke ready. It will pull that into a
+/// buffer if it is part of a valid command in the command set. You may then enter
+/// arguments to the command to be run.
+///
+/// Primitive editing is permitted with <bs>.
+///
+/// When you press <enter> it will evaluate the command and execute the command
+/// passing it the parameter string.
+///
+/// @returns runok if the command that was run allows continuation of the CommandProcessor
+/// @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?
+    RUNRESULT_T val = runok;            // return true when happy, false to exit the prog
+    CMD_T *cbk = NULL;
+    char * params = NULL;
+
+    if (showPrompt && cfg.echo) {
+        cfg.putch('>');
+        showPrompt = FALSE;
+    }
+    if (cfg.kbhit()) {
+        int c = cfg.getch();
+
+        switch (c) {
+            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);
+                        while (*p)
+                            cfg.putch(*p++);
+                        //cfg.printf("%s", p);
+                    }
+                }
+                break;
+            case 0x1b:    // <ESC> to empty the command buffer
+                while (keycount--) {
+                    cfg.putch(0x08);    // <bs>
+                    cfg.putch(' ');
+                    cfg.putch(0x08);
+                }
+                keycount = 0;
+                buffer[keycount] = '\0';
+                break;
+            case '\x08':    // <bs>
+                if (keycount) {
+                    buffer[--keycount] = '\0';
+                    cfg.putch(0x08);
+                    cfg.putch(' ');
+                    cfg.putch(0x08);
+                } else
+                    cfg.putch(0x07);    // bell
+                break;
+            case '\r':
+            case '\n': {
+                int foundCount = 0;
+
+                if (strlen(buffer)) {
+                    foundCount = CommandMatches(buffer, TRUE, &cbk, &params);
+                    if (foundCount == 1) {
+                        val = (*cbk->callback)(params);        // Execute the command
+                    } 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++] = 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;
+}
+
+
+static RUNRESULT_T CommandProcessor_Echo(int echo) {
+    cfg.echo = echo;
+    return runok;
+}
+
+/// End the CommandProcessor by freeing all the memory that was allocated
+///
+/// @returns runok
+///
+RUNRESULT_T CommandProcessor_End(void) {
+    CMDLINK_T *p = head;
+    CMDLINK_T *n;
+
+    do {
+        n = p->next;
+        free(p);            // free each of the allocated links to menu items.
+        p = n;
+    } while (n);
+    free(buffer);            // finally, free the command buffer
+    buffer = NULL;            // flag it as deallocated
+    return runok;
+}
+
+
+// Helper functions follow. These functions may exist in some environments and
+// not in other combinations of libraries and compilers, so private versions
+// are here to ensure consistent behavior.
+
 /// mytolower exists because not all compiler libraries have this function
 ///
 /// This takes a character and if it is upper-case, it converts it to
 /// lower-case and returns it.
 ///
-///    @param a is the character to convert
+/// @param a is the character to convert
 /// @returns the lower case equivalent to a
 ///
-char mytolower(char a)
-{
+static char mytolower(char a) {
     if (a >= 'A' && a <= 'Z')
         return (a - 'A' + 'a');
     else
@@ -188,19 +501,17 @@
 /// Some have strnicmp, others _strnicmp, and others have C++ methods, which
 /// is outside the scope of this C-portable set of functions.
 ///
-///    @param l is a pointer to the string on the left
+/// @param l is a pointer to the string on the left
 /// @param r is a pointer to the string on the right
 /// @param n is the number of characters to compare
 /// @returns -1 if l < r
 /// @returns 0 if l == r
 /// @returns +1 if l > r
 ///
-int mystrnicmp(const char *l, const char *r, size_t n)
-{
+int mystrnicmp(const char *l, const char *r, size_t n) {
     int result = 0;
 
-    if (n != 0)
-    {
+    if (n != 0) {
         do {
             result = mytolower(*l++) - mytolower(*r++);
         } while ((result == 0) && (*l != '\0') && (--n > 0));
@@ -214,21 +525,20 @@
 
 /// mystrcat exists because not all compiler libraries have this function
 ///
-/// This function concatinates one string onto another. It is generally 
+/// This function concatinates one string onto another. It is generally
 /// considered unsafe, because of the potential for buffer overflow.
 /// Some libraries offer a strcat_s as the safe version, and others may have
 /// _strcat. Because this is needed only internal to the CommandProcessor,
 /// this version was created.
 ///
-///    @param dst is a pointer to the destination string
+/// @param dst is a pointer to the destination string
 /// @param src is a pointer to the source string
 /// @returns nothing
 ///
-void mystrcat(char *dst, char *src)
-{
+static void mystrcat(char *dst, char *src) {
     while (*dst)
         dst++;
-    do 
+    do
         *dst++ = *src;
     while (*src++);
 }
@@ -242,337 +552,10 @@
 /// @returns TRUE if the character is printable
 /// @returns FALSE if the character is not printable
 ///
-static int myisprint(int c)
-{
+static int myisprint(int c) {
     if (c >= ' ' && c <= '~')
         return TRUE;
     else
         return FALSE;
 }
 
-
-/// CommandMatches is the function that determines if the user is entering a valid
-/// command
-///
-/// This function gets called whenever the user types a printable character or when
-/// they press \<enter\>.
-/// The buffer of user input is evaluated to determine if the command is legitimate.
-/// It also determines if they want to execute the command, or simply evaluate it.
-/// Finally, it identifies writes to user provided pointers, the menu item that 
-/// matches and a pointer to any parameters that the user entered.
-///
-///    @param buffer is the buffer that the user has entered commands into
-///    @param exec indicates the intention to execute the command, if found
-/// @param menu is a pointer to a pointer to a menu structure, which is updated based on the command
-/// @param params is a pointer to a pointer to the user entered parameters
-/// @returns the number of menu picks that match the user entered command
-///
-static int CommandMatches(char * buffer, int exec, CMD_T **menu, char ** params)
-{
-    char *space;
-    int compareLength;
-    int foundCount = 0;
-    CMDLINK_T *link = head;
-
-    if (strlen(buffer))    // simple sanity check
-    {
-        // Try to process the buffer. A command could be "Help", or it could be "Test1 123 abc"
-        space = strchr(buffer, ' ');        // if the command has parameters, find the delimiter
-        if (space)
-        {
-            compareLength = space - buffer;
-            space++;        // char after the space
-        }
-        else
-        {
-            compareLength = strlen(buffer);
-            space = buffer + compareLength;
-        }
-        while (link && link->menu)
-        {
-            int cmpResult;
-
-            if (cfg.caseinsensitive)
-            {
-                cmpResult = mystrnicmp(buffer, link->menu->command, compareLength);
-            }
-            else
-                cmpResult = strncmp(buffer, link->menu->command, compareLength);
-            if (0 == cmpResult)    // A match to what they typed
-            {
-                if (menu)    // yes, we have a callback
-                {
-                    if (exec)    // command wants to execute it, not just validate the command syntax
-                    {
-                        // If they type "He 1234 5678", we backup and rewrite as "Help 1234 5678"
-                        int diff = strlen(link->menu->command) - compareLength;    // e.g. 5 - 3
-                        
-                        if (diff > 0)
-                        {
-                            int back = 0;
-                            char *p;
-                            if (strlen(space))
-                                back = strlen(space) + 1;
-
-                            while (back--)
-                                cfg.putch(0x08);
-                            p = link->menu->command + compareLength;
-                            while (*p)
-                                cfg.putch(*p++);
-                            cfg.putch(' ');
-                            p = space;
-                            while (*p)
-                                cfg.putch(*p++);
-                            //cfg.printf("%s %s", link->menu->command + compareLength, space);
-                        }
-                    }
-                    *menu = link->menu;    // accessor to the command they want to execute
-                    *params = space;
-                }
-                foundCount++;    // how many command match what they typed so far
-            }
-            link = link->next;    // follow the link to the next command
-        }
-    }
-    return foundCount;
-}
-
-/// Initialize the CommandProcessor
-///
-///    This initializes the CommandProcessor by adding the built-in menus
-///
-///    @param addDefaultMenu configures it to add the Help, About, and Exit menus
-///    @param bufSize configures the size of the longest command, which must be 
-///            greater than 6 (the size of "About\0").
-///    @returns initok if it successfully initialized the CommandProcessor
-///    @returns initfailed if it could not allocate memory
-///
-INITRESULT_T CommandProcessor_Init(
-    int addDefaultMenu, 
-    int caseinsensitive,
-    int echo,
-    int maxCmdLen, 
-    int (*kbhit)(void),
-    int (*getch)(void),
-    int (*putch)(int ch),
-    int (*puts)(const char * s)
-    )
-{
-    if (maxCmdLen < 6)
-        maxCmdLen = 6;
-    buffer = (char *)malloc(maxCmdLen+1);
-    cfg.bufferSize = maxCmdLen;
-    if (buffer)
-    {
-        if (addDefaultMenu & 0x0008)
-            CommandProcessor.Add(&QuestionMenu);
-        if (addDefaultMenu & 0x0008)
-            CommandProcessor.Add(&HelpMenu);
-        if (addDefaultMenu & 0x0004)
-            CommandProcessor.Add(&EchoMenu);            
-        if (addDefaultMenu & 0x0002)
-            CommandProcessor.Add(&AboutMenu);
-        if (addDefaultMenu & 0x0001)
-            CommandProcessor.Add(&ExitMenu);
-        cfg.caseinsensitive = caseinsensitive;
-        cfg.echo = echo;
-        cfg.kbhit = kbhit;
-        cfg.getch = getch;
-        cfg.putch = putch;
-        cfg.puts = puts;
-        return initok;
-    }
-    else
-        return initfailed;
-}
-
-/// Add a command to the CommandProcessor
-///
-///    This adds a command to the CommandProcessor. A command has several components
-///    to it, including the command name, a brief description, the function to 
-///    activate when the command is entered, and a flag indicating if the command
-///    should show up in the built-in help.
-///
-///    @todo sort them when adding a menu item
-///
-///    @param m is the menu to add to the CommandProcessor
-///    @returns addok if the command was added
-///    @returns addfail if the command could not be added (failure to allocate memory for the linked list)
-///
-ADDRESULT_T CommandProcessor_Add(CMD_T * m)
-{
-    CMDLINK_T *p;
-
-    if (head == NULL)
-    {
-        head = (CMDLINK_T *)malloc(sizeof(CMDLINK_T));
-        if (!head)
-            return addfailed;
-        head->menu = NULL;
-        head->next = NULL;
-    }
-    p = head;
-    while (p->next)
-        p = p->next;
-    if (p->menu == NULL)
-    {
-        p->menu = m;
-        return addok;
-    }
-    else if (p->next == NULL)
-    {
-        p->next = (CMDLINK_T *)malloc(sizeof(CMDLINK_T));
-        if (!p->next)
-            return addfailed;
-        p = p->next;
-        p->menu = m;
-        p->next = NULL;
-        return addok;
-    }
-    return addfailed;
-}
-
-/// Run the CommandProcessor
-///
-///    This will peek to see if there is a keystroke ready. It will pull that into a
-///    buffer if it is part of a valid command in the command set. You may then enter
-/// arguments to the command to be run.
-///
-/// Primitive editing is permitted with <bs>.
-///    
-///    When you press <enter> it will evaluate the command and execute the command
-///    passing it the parameter string.
-///
-///    @returns runok if the command that was run allows continuation of the CommandProcessor
-///    @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?
-    RUNRESULT_T val = runok;            // return true when happy, false to exit the prog
-    CMD_T *cbk = NULL;
-    char * params = NULL;
-
-    if (showPrompt && cfg.echo)
-    {
-        cfg.putch('>');
-        showPrompt = FALSE;
-    }
-    if (cfg.kbhit())
-    {
-        int c = cfg.getch();
-
-        switch (c)
-        {
-        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);
-                    while (*p)
-                        cfg.putch(*p++);
-                    //cfg.printf("%s", p);
-                }
-            }
-            break;
-        case 0x1b:    // <ESC> to empty the command buffer
-            while (keycount--)
-            {
-                cfg.putch(0x08);    // <bs>
-                cfg.putch(' ');
-                cfg.putch(0x08);
-            }
-            keycount = 0;
-            buffer[keycount] = '\0';
-            break;
-        case '\x08':    // <bs>
-            if (keycount)
-            {
-                buffer[--keycount] = '\0';
-                cfg.putch(0x08);
-                cfg.putch(' ');
-                cfg.putch(0x08);
-            }
-            else
-                cfg.putch(0x07);    // bell
-            break;
-        case '\r':
-        case '\n':
-            {
-                int foundCount = 0;
-
-                if (strlen(buffer))
-                {
-                    foundCount = CommandMatches(buffer, TRUE, &cbk, &params);
-                    if (foundCount == 1)
-                    {
-                        val = (*cbk->callback)(params);        // Execute the command
-                    }
-                    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++] = 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;
-}
-
-
-static RUNRESULT_T CommandProcessor_Echo(int echo) {
-    cfg.echo = echo;
-    return runok;
-}
-
-/// End the CommandProcessor by freeing all the memory that was allocated
-///
-///    returns runok
-///
-RUNRESULT_T CommandProcessor_End(void)
-{
-    CMDLINK_T *p = head;
-    CMDLINK_T *n;
-
-    do
-    {
-        n = p->next;
-        free(p);            // free each of the allocated links to menu items.
-        p = n;
-    } while (n);
-    free(buffer);            // finally, free the command buffer
-    buffer = NULL;            // flag it as deallocated
-    return runok;
-}
--- a/CommandProcessor.h	Sat Apr 02 17:12:39 2011 +0000
+++ b/CommandProcessor.h	Sun Apr 03 21:36:22 2011 +0000
@@ -44,7 +44,11 @@
 /// }
 /// 
 /// RUNRESULT_T Who(char *p);
-/// const CMD_T WhoCmd = {"who", "Shows who is logged on, or 'who id' for specifics", Who, visible};
+/// const CMD_T WhoCmd = {
+///       "who", 
+///       "Shows who is logged on, or 'who id' for specifics", 
+///       Who, 
+///       visible};
 /// 
 /// RUNRESULT_T Who(char *p)
 /// {