The CommandProcessor is the interface to install a run-time menu into an embedded system.
Dependents: A_CANAdapter USB2I2C
CommandProcessor.c
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, ¶ms)) { 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, ¶ms); 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, ¶ms)) 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 }
Generated on Wed Jul 13 2022 14:17:53 by 1.7.2