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

Dependents:   A_CANAdapter USB2I2C

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers CommandProcessor.c Source File

CommandProcessor.c

Go to the documentation of this file.
00001 /// @file CommandProcessor.c is a simple interface to an interactive
00002 ///         command set of user defined commands.
00003 ///
00004 /// With this, you can create functions that are exposed to a console
00005 /// interface. Each command may have 0 or more parameters.
00006 /// Typing the command, or at least the set of characters that make
00007 /// it unique from all other commands is enough to activate the command.
00008 ///
00009 /// Even though it is a c interface, it is somewhat object oriented.
00010 ///
00011 /// @note Copyright &copr; 2011 by Smartware Computing, all rights reserved.
00012 ///     Individuals may use this application for evaluation or non-commercial
00013 ///     purposes. Within this restriction, changes may be made to this application
00014 ///     as long as this copyright notice is retained. The user shall make
00015 ///     clear that their work is a derived work, and not the original.
00016 ///     Users of this application and sources accept this application "as is" and
00017 ///     shall hold harmless Smartware Computing, for any undesired results while
00018 ///     using this application - whether real or imagined.
00019 ///
00020 /// @author David Smart, Smartware Computing
00021 ///
00022 
00023 #include "stdio.h"
00024 #include "string.h"
00025 #include "stdlib.h"
00026 #ifdef WIN32
00027 #include "windows.h"
00028 #pragma warning (disable: 4996)
00029 #endif
00030 
00031 #include "CommandProcessor.h"
00032 
00033 /// This holds the single linked list of commands
00034 /// @verbatim
00035 /// +-- Head->next
00036 /// v
00037 /// +-- p       +-- n
00038 /// v           v
00039 /// |menu|-------------------------------->|"Command"  |
00040 /// |next|->0   |menu|---->|"Help"         |"Help"     |
00041 ///             |next|->0  |"..."          |*(callback)|
00042 ///                        |*(callback)    |visible    |
00043 ///                        |visible
00044 /// @endverbatim
00045 ///
00046 typedef struct CMDLINK_T {
00047     CMD_T * menu;                // handle to the menu item
00048     struct CMDLINK_T * next;    // handle to the next link
00049 } CMDLINK_T;
00050 
00051 static CMDLINK_T * head = NULL;
00052 
00053 static char *buffer;        // buffer space must be allocated based on the longest command
00054 static char *historyBuffer;        // keeps the history of commands for recall
00055 static int historyCount = 0;    // and the count of
00056 static int historyDepth = 0;
00057 static size_t longestCommand = 0;
00058 
00059 static struct {
00060     CMD_T *SignOnBanner;
00061     int showSignOnBanner;        // Shows the sign-on banner at startup
00062     int caseinsensitive;    // FALSE=casesensitive, TRUE=insensitive
00063     int echo;               // TRUE=echo on, FALSE=echo off
00064     int bufferSize;         // size of the command buffer
00065     int (*kbhit)(void);
00066     int (*getch)(void);
00067     int (*putch)(int ch);
00068     int (*puts)(const char * s);
00069 } cfg;
00070 
00071 static INITRESULT_T CommandProcessor_Init(
00072     CMD_T *SignOnBanner,
00073     CONFIG_T config,
00074     int maxCmdLen,
00075     int historyCount,
00076     int (*kbhit)(void),
00077     int (*getch)(void),
00078     int (*putch)(int ch),
00079     int (*puts)(const char * s)
00080 );
00081 
00082 // Used when processing characters
00083 static int keycount = 0;    // how full?
00084 static int leadinChar = 0;
00085 static int whereInHistory = 0;        // navigates history
00086 static int showPrompt = TRUE;
00087 
00088 
00089 static ADDRESULT_T CommandProcessor_Add(CMD_T *m);
00090 static RUNRESULT_T CommandProcessor_Run(void);
00091 static RUNRESULT_T CommandProcessor_End(void);
00092 static RUNRESULT_T CommandProcessor_Echo(int echo);
00093 static void EraseChars(int keycount);
00094 static void EchoString(char * p);
00095 
00096 // helper functions
00097 static int myisprint(int c);
00098 static void mystrcat(char *dst, char *src);
00099 static char mytolower(char a);
00100 static int mystrnicmp(const char *l, const char *r, size_t n);
00101 
00102 static CMDP_T CommandProcessor = {
00103     CommandProcessor_Init,
00104     CommandProcessor_Add,
00105     CommandProcessor_Run,
00106     CommandProcessor_Echo,
00107     CommandProcessor_End
00108 };
00109 
00110 static RUNRESULT_T Help(char *p);
00111 static RUNRESULT_T History(char *p);
00112 static RUNRESULT_T Echo(char *p);
00113 static RUNRESULT_T Exit(char *p);
00114 //static RUNRESULT_T About(char *p);
00115 
00116 static CMD_T HelpMenu = {"Help", "Help or '?' shows this help, 'Help ?' shows more details.", Help, visible};
00117 static CMD_T QuestionMenu = {"?", "Shows this help, '? ?' shows more details.", Help, invisible};
00118 static CMD_T HistoryMenu = {"History", "Show command history", History, visible};
00119 static CMD_T EchoMenu = {"Echo", "Echo [1|on|0|off] turns echo on or off.", Echo, visible};
00120 static CMD_T ExitMenu = {"Exit", "Exits the program", Exit, visible};
00121 
00122 /// Gets a handle to the CommandProcessor
00123 ///
00124 /// This returns a handle to the CommandProcessor, which then permits
00125 /// access to the CommandProcessor functions.
00126 ///
00127 /// @returns handle to the CommandProcessor
00128 ///
00129 CMDP_T * GetCommandProcessor(void) {
00130     return &CommandProcessor;
00131 }
00132 
00133 
00134 /// History shows the command history
00135 ///
00136 /// @param p is a pointer to a string that is ignored
00137 /// @returns runok
00138 ///
00139 static RUNRESULT_T History(char *p) {
00140     int whereInHistory = 0;
00141     char buf[100];
00142 
00143     cfg.puts("");
00144     for (whereInHistory = 0; whereInHistory < historyCount; whereInHistory++) {
00145         sprintf(buf, "  %2i: %s", whereInHistory - historyCount, &historyBuffer[whereInHistory * cfg.bufferSize]);
00146         cfg.puts(buf);
00147     }
00148     sprintf(buf, "  %2i: %s", 0, buffer);
00149     cfg.puts(buf);
00150     return runok;
00151 }
00152 
00153 /// Turns command prompt echo on and off
00154 ///
00155 /// This command is used to turn the command prompt on and off. When
00156 /// running in an interactive mode, it is best to have this one.
00157 /// When driven by another program, off may be the best choice.
00158 ///
00159 /// This command also displays the current state of the echo mode.
00160 ///
00161 /// @param p is a pointer to a string "on" | "1" | "off" | "0"
00162 /// @returns runok
00163 ///
00164 static RUNRESULT_T Echo(char *p) {
00165     if (*p) {
00166         if (*p == '1' || mystrnicmp(p, "on", 2) == 0)
00167             CommandProcessor_Echo(1);
00168         if (*p == '0' || mystrnicmp(p, "off", 3) == 0)
00169             CommandProcessor_Echo(0);
00170     }
00171     if (cfg.echo)
00172         cfg.puts("\r\nEcho is on");
00173     else
00174         cfg.puts("\r\nEcho is off");
00175     return runok;
00176 }
00177 
00178 static RUNRESULT_T Exit(char *p) {
00179     (void)p;
00180     cfg.puts("\r\nbye.");
00181     return runexit;
00182 }
00183 
00184 static RUNRESULT_T Help(char *p) {
00185     CMDLINK_T *link = head;
00186     char buffer[100];
00187     cfg.puts("\r\n");
00188     //sprintf(buffer, " %-10s: %s", "Command", "Description");
00189     //cfg.puts(buffer);
00190     while (link && link->menu) {
00191         if (link->menu->visible) {
00192             if (strlen(link->menu->command) + strlen(link->menu->helptext) + 5 < sizeof(buffer)) {
00193                 sprintf(buffer, " %-*s: %s", longestCommand, link->menu->command, link->menu->helptext);
00194                 cfg.puts(buffer);
00195             }
00196         }
00197         link = link->next;
00198     }
00199     cfg.puts("");
00200     if (*p == '?') {
00201         cfg.puts("\r\n Extended Help:\r\n"
00202                  "    The general form for entering commands is:\r\n"
00203                  "      >command option1 option2 ...\r\n"
00204                  "      [note that not all commands support optional parameters]\r\n"
00205                  "    * Abbreviations of commands may be entered so long as there\r\n"
00206                  "      is exactly one match in the list of commands. For example,\r\n"
00207                  "      'he' is the same as 'help', if there is no other command \r\n"
00208                  "       that starts with 'he'.\r\n"
00209                  "    * <bs> can be used to erase previously entered information.\r\n"
00210                  "    * <esc> can be used to cancel a command.\r\n"
00211                  "    * <tab> can be used to complete the entry of a partial command.\r\n"
00212                  "");
00213         cfg.puts("\r\n About this CommandProcessor:                               (v" VERSION ")\r\n"
00214                  "    This CommandProcessor provides an easy facility for creating an\r\n"
00215                  "      interactive runtime interpreter in an embedded system.\r\n"
00216                  "    Copyright (c) 2011 by Smartware Computing, all rights reserved.\r\n"
00217                  "    Author: David Smart, Smartware Computing\r\n");
00218     }
00219     return runok;
00220 }
00221 
00222 
00223 /// CommandMatches is the function that determines if the user is entering a valid
00224 /// command
00225 ///
00226 /// This function gets called whenever the user types a printable character or when
00227 /// they press <enter>.
00228 /// The buffer of user input is evaluated to determine if the command is legitimate.
00229 /// It also determines if they want to execute the command, or simply evaluate it.
00230 /// Finally, it identifies writes to user provided pointers, the menu item that
00231 /// matches and a pointer to any parameters that the user entered.
00232 ///
00233 /// @param buffer is the buffer that the user has entered commands into
00234 /// @param exec indicates the intention to execute the command, if found
00235 /// @param menu is a pointer to a pointer to a menu structure, which is updated based on the command
00236 /// @param params is a pointer to a pointer to the user entered parameters
00237 /// @returns the number of menu picks that match the user entered command
00238 ///
00239 static int CommandMatches(char * buffer, int exec, CMD_T **menu, char **params) {
00240     char *space;
00241     int compareLength;
00242     int foundCount = 0;
00243     CMDLINK_T *link = head;
00244     char * alternateBuffer;
00245 
00246     if (strlen(buffer)) {  // simple sanity check
00247         // Try to process the buffer. A command could be "Help", or it could be "Test1 123 abc"
00248         space = strchr(buffer, ' ');        // if the command has parameters, find the delimiter
00249         if (space) {
00250             compareLength = space - buffer;
00251             space++;        // advance to the char after the space (where the params may start)
00252         } else {
00253             compareLength = strlen(buffer);
00254             space = buffer + compareLength; // points to the NULL terminator
00255         }
00256         while (link && link->menu) {
00257             int cmpResult;
00258 
00259             if (cfg.caseinsensitive) {
00260                 cmpResult = mystrnicmp(buffer, link->menu->command, compareLength);
00261             } else
00262                 cmpResult = strncmp(buffer, link->menu->command, compareLength);
00263             if (0 == cmpResult) {  // A match to what they typed
00264                 if (menu) {  // yes, we have a callback
00265                     *menu = link->menu;    // accessor to the command they want to execute
00266                     *params = space;
00267                 }
00268                 foundCount++;    // how many command match what they typed so far
00269             }
00270             link = link->next;    // follow the link to the next command
00271         }
00272         if (foundCount == 1) {
00273             // If we found exactly one and they expressed an intent to execute that command
00274             // then we'll rewrite the command to be fully qualified
00275             if (exec) {  // command wants to execute it, not just validate the command syntax
00276                 // If they type "He 1234 5678", we backup and rewrite as "Help 1234 5678"
00277                 int diff = strlen((*menu)->command) - compareLength;    // e.g. 5 - 3
00278 
00279                 // or if they entered it in a case that doesn't match the command exactly
00280                 if (diff > 0 || 0 != strncmp(buffer, (*menu)->command, compareLength)) {
00281                     char *p = buffer;
00282                     alternateBuffer = (char *)malloc(cfg.bufferSize);
00283                     strcpy(alternateBuffer, (*menu)->command);
00284                     strcat(alternateBuffer, " ");
00285                     strcat(alternateBuffer, space);
00286                     EraseChars(strlen(buffer));
00287                     strcpy(buffer, alternateBuffer);
00288                     free(alternateBuffer);
00289                     EchoString(p);
00290                     *params = strchr(buffer, ' ');        // if the command has parameters, find the delimiter
00291                     if (*params) {
00292                         (*params)++;        // advance to the char after the space (where the params may start)
00293                     } else {
00294                         compareLength = strlen(buffer);
00295                         *params = buffer + compareLength; // points to the NULL terminator
00296                     }
00297                 }
00298             }
00299         }
00300     }
00301     return foundCount;
00302 }
00303 
00304 
00305 /// Init is the first function to call to configure the CommandProcessor.
00306 ///
00307 /// This function has a number of parameters, which make the CommandProcessor
00308 /// quite flexible.
00309 ///
00310 /// @param SignOnBanner function, which is used as a signon banner
00311 /// @param config enables various default menu items, based on the bit values, combine the following:
00312 ///   \li CFG_ENABLE_TERMINATE - enables the Exit command
00313 ///   \li CFG_ENABLE_SYSTEM    - enables system commands Echo, Help, etc.
00314 ///   \li CFG_ECHO_ON          - initialize with echo on
00315 ///   \li CFG_CASE_INSENSITIVE - Command Parser is case insensitive
00316 /// @param maxCmdLen sizes the buffer, and is the maximum number of characters in a single
00317 ///        command, including all command arguments
00318 /// @param kbhit is a user provided function to detect if a character is available for the CommandProcessor,
00319 ///        and when using standard io, you can typically use kbhit, or _kbhit as your system provides.
00320 /// @param getch is a user provided function that provides a single character to the CommandProcessor
00321 /// @param putch is a user provided function that permits the CommandProcessor to output a character
00322 /// @param puts is a user provided function that permits the CommandProcessor to output a string
00323 ///        to which is automatically appended a \\n
00324 /// @returns INITRESULT_T to indicate if the init was successful or failed
00325 ///
00326 INITRESULT_T CommandProcessor_Init(
00327     CMD_T (*SignOnBanner),
00328     CONFIG_T config,
00329     int maxCmdLen,
00330     int numInHistory,
00331     int (*kbhit)(void),
00332     int (*getch)(void),
00333     int (*putch)(int ch),
00334     int (*puts)(const char * s)
00335 ) {
00336     if (SignOnBanner) {
00337         CommandProcessor.Add(SignOnBanner);
00338         cfg.SignOnBanner = SignOnBanner;
00339         cfg.showSignOnBanner = 1;
00340     }
00341     if (maxCmdLen < 6)
00342         maxCmdLen = 6;
00343     buffer = (char *)malloc(maxCmdLen);            // users often error by one, so we'll be generous
00344     historyDepth = numInHistory;
00345     historyBuffer = (char *)malloc(historyDepth * maxCmdLen);
00346     cfg.bufferSize = maxCmdLen;
00347     if (buffer && historyBuffer) {
00348         if (config & CFG_ENABLE_SYSTEM) {
00349             CommandProcessor.Add(&QuestionMenu);
00350             CommandProcessor.Add(&HelpMenu);
00351             CommandProcessor.Add(&HistoryMenu);
00352             CommandProcessor.Add(&EchoMenu);
00353         }
00354         if (config & CFG_ENABLE_TERMINATE)
00355             CommandProcessor.Add(&ExitMenu);
00356         //if (addDefaultMenu & 0x0002)
00357         //    CommandProcessor.Add(&AboutMenu);
00358         cfg.caseinsensitive = (config & CFG_CASE_INSENSITIVE) ? 1 : 0;
00359         cfg.echo = (config & CFG_ECHO_ON) ? 1 : 0;
00360         cfg.kbhit = kbhit;
00361         cfg.getch = getch;
00362         cfg.putch = putch;
00363         cfg.puts = puts;
00364         return initok;
00365     } else
00366         return initfailed;
00367 }
00368 
00369 
00370 /// Add a command to the CommandProcessor
00371 ///
00372 /// This adds a command to the CommandProcessor. A command has several components
00373 /// to it, including the command name, a brief description, the function to
00374 /// activate when the command is entered, and a flag indicating if the command
00375 /// should show up in the built-in help.
00376 ///
00377 /// @param menu is the menu to add to the CommandProcessor
00378 /// @returns addok if the command was added
00379 /// @returns addfail if the command could not be added (failure to allocate memory for the linked list)
00380 ///
00381 ADDRESULT_T CommandProcessor_Add(CMD_T * menu) {
00382     CMDLINK_T *ptr;
00383     CMDLINK_T *prev;
00384     CMDLINK_T *temp;
00385 
00386     if (strlen(menu->command) > longestCommand)
00387         longestCommand = strlen(menu->command);
00388 
00389     // Allocate the storage for this menu item
00390     temp = (CMDLINK_T *)malloc(sizeof(CMDLINK_T));
00391     if (!temp)
00392         return addfailed;            // something went really bad
00393     temp->menu = menu;
00394     temp->next = NULL;
00395 
00396     prev = ptr = head;
00397     if (!ptr) {
00398         head = temp;            // This installs the very first item
00399         return addok;
00400     }
00401     // Search alphabetically for the insertion point
00402     while (ptr && mystrnicmp(ptr->menu->command, menu->command, strlen(menu->command)) < 0) {
00403         prev = ptr;
00404         ptr = ptr->next;
00405     }
00406     if (prev == head) {
00407         head = temp;
00408         head->next = prev;
00409     } else {
00410         prev->next = temp;
00411         prev = temp;
00412         prev->next = ptr;
00413     }
00414     return addok;
00415 }
00416 
00417 static void EchoString(char *p) {
00418     while (*p)
00419         cfg.putch(*p++);
00420 }
00421 
00422 static void EraseChars(int keycount) {
00423     while (keycount--) {
00424         cfg.putch(0x08);    // <bs>
00425         cfg.putch(' ');
00426         cfg.putch(0x08);
00427     }
00428 }
00429 
00430 
00431 static int ProcessComplexSequence(int c) {
00432     switch (c) {
00433         case 0x42:
00434         case 0x50:    // down arrow - toward the newest (forward in time)
00435             // if there is anything in the history, copy it out
00436             if (historyCount && whereInHistory < historyCount) {
00437                 char *p;
00438 
00439                 EraseChars(keycount);
00440                 p = strcpy(buffer, &historyBuffer[whereInHistory * cfg.bufferSize]);
00441                 EchoString(p);
00442                 keycount = strlen(buffer);
00443                 whereInHistory++;
00444             }
00445             c = 0;
00446             break;
00447         case 0x41:
00448         case 0x48:    // up arrow - from newest to oldest (backward in time)
00449             // same as escape
00450             if (historyCount && --whereInHistory >= 0) {
00451                 char *p;
00452 
00453                 EraseChars(keycount);
00454                 p = strcpy(buffer, &historyBuffer[whereInHistory * cfg.bufferSize]);
00455                 EchoString(p);
00456                 keycount = strlen(buffer);
00457                 c = 0;
00458             } else {
00459                 whereInHistory = 0;
00460                 c = 0x1B;
00461             }
00462             break;
00463         default:
00464             // ignore this char
00465             c = 0;
00466             break;
00467     }
00468     leadinChar = 0;
00469     return c;
00470 }
00471 
00472 
00473 static RUNRESULT_T ProcessStandardSequence(int c) {
00474     int foundCount = 0;
00475     CMD_T *cbk = NULL;
00476     char * params = NULL;
00477     RUNRESULT_T val = runok;
00478 
00479     // Process Character
00480     switch (c) {
00481         case 0:
00482             // null - do nothing
00483             break;
00484         case 0x5B:
00485             // ANSI (VT100) sequence
00486             // <ESC>[A is up
00487             // <ESC>[B is down
00488             // <ESC>[C is right
00489             // <ESC>[D is left
00490             leadinChar = 1;
00491             break;
00492         case 0xE0:
00493             // Windows Command Shell (DOS box)
00494             // Lead-in char
00495             // 0xE0 0x48 is up arrow
00496             // 0xE0 0x50 is down arrow
00497             // 0xE0 0x4B is left arrow
00498             // 0xE0 0x4D is right arrow
00499             leadinChar = 1;
00500             break;
00501         case 0x09:    // <TAB> to request command completion
00502             if (1 == CommandMatches(buffer, FALSE, &cbk, &params)) {
00503                 size_t n;
00504                 char *p = strchr(buffer, ' ');
00505                 if (p)
00506                     n = p - buffer;
00507                 else
00508                     n = strlen(buffer);
00509                 if (n < strlen(cbk->command)) {
00510                     p = cbk->command + strlen(buffer);
00511                     mystrcat(buffer, p);
00512                     keycount = strlen(buffer);
00513                     EchoString(p);
00514                     //cfg.printf("%s", p);
00515                 }
00516             }
00517             break;
00518         case 0x1b:    // <ESC> to empty the command buffer
00519             EraseChars(keycount);
00520             keycount = 0;
00521             buffer[keycount] = '\0';
00522             break;
00523         case '\x08':    // <bs>
00524             if (keycount) {
00525                 buffer[--keycount] = '\0';
00526                 EraseChars(1);
00527             } else
00528                 cfg.putch(0x07);    // bell
00529             break;
00530         case '\r':
00531         case '\n':
00532             if (strlen(buffer)) {
00533                 foundCount = CommandMatches(buffer, TRUE, &cbk, &params);
00534                 if (foundCount == 1) {
00535                     val = (*cbk->callback)(params);        // Execute the command
00536                     if (mystrnicmp(buffer, (const char *)&historyBuffer[(historyCount-1) * cfg.bufferSize], strlen(&historyBuffer[(historyCount-1) * cfg.bufferSize])) != 0) {
00537                         // not repeating the last command, so enter into the history
00538                         if (historyCount == historyDepth) {
00539                             int i;
00540                             historyCount--;
00541                             for (i=0; i<historyCount; i++)
00542                                 strcpy(&historyBuffer[i * cfg.bufferSize], &historyBuffer[(i+1) * cfg.bufferSize]);
00543                         }
00544                         strcpy(&historyBuffer[historyCount * cfg.bufferSize], buffer);
00545                         whereInHistory = historyCount;
00546                         historyCount++;
00547                     }
00548                 } else if (foundCount > 1)
00549                     cfg.puts(" *** non-unique command ignored      try 'Help' ***");
00550                 else if (foundCount == 0)
00551                     cfg.puts(" *** huh?                            try 'Help' ***");
00552             } else
00553                 cfg.puts("");
00554             keycount = 0;
00555             buffer[keycount] = '\0';
00556             showPrompt = TRUE;        // forces the prompt
00557             break;
00558         default:
00559             // any other character is assumed to be part of the command
00560             if (myisprint(c) && keycount < cfg.bufferSize) {
00561                 buffer[keycount++] = (char)c;
00562                 buffer[keycount] = '\0';
00563                 if (CommandMatches(buffer, FALSE, &cbk, &params))
00564                     cfg.putch(c);
00565                 else {
00566                     buffer[--keycount] = '\0';
00567                     cfg.putch(0x07);    // bell
00568                 }
00569             } else
00570                 cfg.putch(0x07);    // bell
00571             break;
00572     }
00573     return val;
00574 }
00575 
00576 
00577 #if 0
00578 static void PutCharToHex(int c) {
00579     int upper = c >> 4;
00580     int lower = c & 0x0F;
00581 
00582     cfg.putch('[');
00583     if (upper >= 10)
00584         cfg.putch(upper - 10 + 'A');
00585     else
00586         cfg.putch(upper + '0');
00587     if (lower >= 10)
00588         cfg.putch(lower - 10 + 'A');
00589     else
00590         cfg.putch(lower + '0');
00591     cfg.putch(']');
00592 }
00593 #endif
00594 
00595 /// Run the CommandProcessor
00596 ///
00597 /// This will peek to see if there is a keystroke ready. It will pull that into a
00598 /// buffer if it is part of a valid command in the command set. You may then enter
00599 /// arguments to the command to be run.
00600 ///
00601 /// Primitive editing is permitted with <bs>.
00602 ///
00603 /// When you press <enter> it will evaluate the command and execute the command
00604 /// passing it the parameter string.
00605 ///
00606 /// @returns runok if the command that was run allows continuation of the CommandProcessor
00607 /// @returns runfail if the command that was run is asking the CommandProcessor to exit
00608 ///
00609 RUNRESULT_T CommandProcessor_Run(void) {
00610     RUNRESULT_T val = runok;            // return true when happy, false to exit the prog
00611 
00612     if (cfg.showSignOnBanner) {
00613         cfg.SignOnBanner->callback("");
00614         cfg.showSignOnBanner = 0;
00615     }
00616     if (showPrompt && cfg.echo) {
00617         cfg.putch('>');
00618         showPrompt = FALSE;
00619     }
00620     if (cfg.kbhit()) {
00621         int c = cfg.getch();
00622         //PutCharToHex(c);      // a debug utility
00623         if (leadinChar) {
00624             // some previous character was a lead-in to a more complex sequence
00625             // to be processed
00626             c = ProcessComplexSequence(c);
00627         }
00628         ProcessStandardSequence(c);
00629     }
00630     return val;
00631 }
00632 
00633 static RUNRESULT_T CommandProcessor_Echo(int echo) {
00634     cfg.echo = echo;
00635     return runok;
00636 }
00637 
00638 /// End the CommandProcessor by freeing all the memory that was allocated
00639 ///
00640 /// @returns runok
00641 ///
00642 RUNRESULT_T CommandProcessor_End(void) {
00643     CMDLINK_T *p = head;
00644     CMDLINK_T *n;
00645 
00646     do {
00647         n = p->next;
00648         free(p);            // free each of the allocated links to menu items.
00649         p = n;
00650     } while (n);
00651     free(buffer);            // finally, free the command buffer
00652     buffer = NULL;            // flag it as deallocated
00653     return runok;
00654 }
00655 
00656 
00657 // Helper functions follow. These functions may exist in some environments and
00658 // not in other combinations of libraries and compilers, so private versions
00659 // are here to ensure consistent behavior.
00660 
00661 /// mytolower exists because not all compiler libraries have this function
00662 ///
00663 /// This takes a character and if it is upper-case, it converts it to
00664 /// lower-case and returns it.
00665 ///
00666 /// @param a is the character to convert
00667 /// @returns the lower case equivalent to a
00668 ///
00669 static char mytolower(char a) {
00670     if (a >= 'A' && a <= 'Z')
00671         return (a - 'A' + 'a');
00672     else
00673         return a;
00674 }
00675 
00676 /// mystrnicmp exists because not all compiler libraries have this function.
00677 ///
00678 /// Some have strnicmp, others _strnicmp, and others have C++ methods, which
00679 /// is outside the scope of this C-portable set of functions.
00680 ///
00681 /// @param l is a pointer to the string on the left
00682 /// @param r is a pointer to the string on the right
00683 /// @param n is the number of characters to compare
00684 /// @returns -1 if l < r
00685 /// @returns 0 if l == r
00686 /// @returns +1 if l > r
00687 ///
00688 static int mystrnicmp(const char *l, const char *r, size_t n) {
00689     int result = 0;
00690 
00691     if (n != 0) {
00692         do {
00693             result = mytolower(*l++) - mytolower(*r++);
00694         } while ((result == 0) && (*l != '\0') && (--n > 0));
00695     }
00696     if (result < -1)
00697         result = -1;
00698     else if (result > 1)
00699         result = 1;
00700     return result;
00701 }
00702 
00703 /// mystrcat exists because not all compiler libraries have this function
00704 ///
00705 /// This function concatinates one string onto another. It is generally
00706 /// considered unsafe, because of the potential for buffer overflow.
00707 /// Some libraries offer a strcat_s as the safe version, and others may have
00708 /// _strcat. Because this is needed only internal to the CommandProcessor,
00709 /// this version was created.
00710 ///
00711 /// @param dst is a pointer to the destination string
00712 /// @param src is a pointer to the source string
00713 /// @returns nothing
00714 ///
00715 static void mystrcat(char *dst, char *src) {
00716     while (*dst)
00717         dst++;
00718     do
00719         *dst++ = *src;
00720     while (*src++);
00721 }
00722 
00723 /// myisprint exists because not all compiler libraries have this function
00724 ///
00725 /// This function tests a character to see if it is printable (a member
00726 /// of the standard ASCII set).
00727 ///
00728 /// @param c is the character to test
00729 /// @returns TRUE if the character is printable
00730 /// @returns FALSE if the character is not printable
00731 ///
00732 static int myisprint(int c) {
00733     if (c >= ' ' && c <= '~')
00734         return TRUE;
00735     else
00736         return FALSE;
00737 }