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

Dependents:   A_CANAdapter USB2I2C

Committer:
WiredHome
Date:
Sat Apr 02 17:12:39 2011 +0000
Revision:
6:1a0512faa75d
Parent:
5:a98bd1f2fd59
Child:
7:0f058d664b21
Added ability to turn prompt echo on or off

Who changed what in which revision?

UserRevisionLine numberNew contents of line
WiredHome 0:198f53da1bc8 1 /// @file CommandProcessor.c is a simple interface to an interactive
WiredHome 0:198f53da1bc8 2 /// command set of user defined commands.
WiredHome 0:198f53da1bc8 3 ///
WiredHome 0:198f53da1bc8 4 /// With this, you can create functions that are exposed to a console
WiredHome 0:198f53da1bc8 5 /// interface. Each command may have 0 or more parameters.
WiredHome 0:198f53da1bc8 6 /// Typing the command, or at least the set of characters that make
WiredHome 0:198f53da1bc8 7 /// it unique from all other commands is enough to activate the command.
WiredHome 0:198f53da1bc8 8 ///
WiredHome 0:198f53da1bc8 9 /// Even though it is a c interface, it is somewhat object oriented.
WiredHome 0:198f53da1bc8 10 ///
WiredHome 0:198f53da1bc8 11 /// @version 1.0
WiredHome 0:198f53da1bc8 12 ///
WiredHome 0:198f53da1bc8 13 /// @note Copyright © 2011 by Smartware Computing, all rights reserved.
WiredHome 0:198f53da1bc8 14 /// This program may be used by others as long as this copyright notice
WiredHome 0:198f53da1bc8 15 /// remains intact.
WiredHome 0:198f53da1bc8 16 /// @author David Smart
WiredHome 0:198f53da1bc8 17 ///
WiredHome 0:198f53da1bc8 18 #include "stdio.h"
WiredHome 0:198f53da1bc8 19 #include "string.h"
WiredHome 0:198f53da1bc8 20 #include "stdlib.h"
WiredHome 0:198f53da1bc8 21 #ifdef WIN32
WiredHome 0:198f53da1bc8 22 #include "windows.h"
WiredHome 0:198f53da1bc8 23 #pragma warning (disable: 4996)
WiredHome 0:198f53da1bc8 24 #endif
WiredHome 0:198f53da1bc8 25
WiredHome 0:198f53da1bc8 26 #include "CommandProcessor.h"
WiredHome 0:198f53da1bc8 27
WiredHome 0:198f53da1bc8 28 /// This holds the single linked list of commands
WiredHome 0:198f53da1bc8 29 ///
WiredHome 0:198f53da1bc8 30 typedef struct CMDLINK_T
WiredHome 0:198f53da1bc8 31 {
WiredHome 0:198f53da1bc8 32 CMD_T * menu; // handle to the menu item
WiredHome 0:198f53da1bc8 33 struct CMDLINK_T * next; // handle to the next link
WiredHome 0:198f53da1bc8 34 } CMDLINK_T;
WiredHome 0:198f53da1bc8 35
WiredHome 0:198f53da1bc8 36 static CMDLINK_T * head = NULL;
WiredHome 0:198f53da1bc8 37
WiredHome 0:198f53da1bc8 38 static char *buffer; // buffer space must be allocated based on the longest command
WiredHome 0:198f53da1bc8 39
WiredHome 0:198f53da1bc8 40 static struct
WiredHome 0:198f53da1bc8 41 {
WiredHome 0:198f53da1bc8 42 int caseinsensitive; // FALSE=casesensitive, TRUE=insensitive
WiredHome 6:1a0512faa75d 43 int echo; // TRUE=echo on, FALSE=echo off
WiredHome 6:1a0512faa75d 44 int bufferSize; // size of the buffer
WiredHome 0:198f53da1bc8 45 int (*kbhit)(void);
WiredHome 0:198f53da1bc8 46 int (*getch)(void);
WiredHome 0:198f53da1bc8 47 int (*putch)(int ch);
WiredHome 0:198f53da1bc8 48 int (*puts)(const char * s);
WiredHome 0:198f53da1bc8 49 } cfg;
WiredHome 0:198f53da1bc8 50
WiredHome 0:198f53da1bc8 51 static INITRESULT_T CommandProcessor_Init(
WiredHome 0:198f53da1bc8 52 int defaultMenu,
WiredHome 0:198f53da1bc8 53 int caseinsensitive,
WiredHome 6:1a0512faa75d 54 int echo,
WiredHome 0:198f53da1bc8 55 int maxCmdLen,
WiredHome 0:198f53da1bc8 56 int (*kbhit)(void),
WiredHome 0:198f53da1bc8 57 int (*getch)(void),
WiredHome 0:198f53da1bc8 58 int (*putch)(int ch),
WiredHome 0:198f53da1bc8 59 int (*puts)(const char * s)
WiredHome 0:198f53da1bc8 60 );
WiredHome 0:198f53da1bc8 61 static ADDRESULT_T CommandProcessor_Add(CMD_T *m);
WiredHome 0:198f53da1bc8 62 static RUNRESULT_T CommandProcessor_Run(void);
WiredHome 0:198f53da1bc8 63 static RUNRESULT_T CommandProcessor_End(void);
WiredHome 6:1a0512faa75d 64 static RUNRESULT_T CommandProcessor_Echo(int echo);
WiredHome 0:198f53da1bc8 65
WiredHome 0:198f53da1bc8 66 static CMDP_T CommandProcessor =
WiredHome 0:198f53da1bc8 67 {
WiredHome 0:198f53da1bc8 68 CommandProcessor_Init,
WiredHome 0:198f53da1bc8 69 CommandProcessor_Add,
WiredHome 0:198f53da1bc8 70 CommandProcessor_Run,
WiredHome 6:1a0512faa75d 71 CommandProcessor_Echo,
WiredHome 0:198f53da1bc8 72 CommandProcessor_End
WiredHome 0:198f53da1bc8 73 };
WiredHome 0:198f53da1bc8 74
WiredHome 0:198f53da1bc8 75 static RUNRESULT_T Help(char *p);
WiredHome 6:1a0512faa75d 76 static RUNRESULT_T Echo(char *p);
WiredHome 0:198f53da1bc8 77 static RUNRESULT_T Exit(char *p);
WiredHome 0:198f53da1bc8 78 static RUNRESULT_T About(char *p);
WiredHome 0:198f53da1bc8 79
WiredHome 0:198f53da1bc8 80 static CMD_T HelpMenu = {"Help", "Shows this help, 'Help ?' shows more details.", Help, visible};
WiredHome 5:a98bd1f2fd59 81 static CMD_T QuestionMenu = {"?", "Shows this help, '? ?' shows more details.", Help, visible};
WiredHome 6:1a0512faa75d 82 static CMD_T EchoMenu = {"Echo", "Echo [1|on|0|off] turns echo on or off.", Echo, visible};
WiredHome 0:198f53da1bc8 83 static CMD_T AboutMenu = {"About", "About this CommandProcessor", About, visible};
WiredHome 0:198f53da1bc8 84 static CMD_T ExitMenu = {"Exit", "Exits the program", Exit, visible};
WiredHome 0:198f53da1bc8 85
WiredHome 0:198f53da1bc8 86 /// Gets a handle to the CommandProcessor
WiredHome 0:198f53da1bc8 87 ///
WiredHome 0:198f53da1bc8 88 /// This returns a handle to the CommandProcessor, which then permits
WiredHome 0:198f53da1bc8 89 /// access to the CommandProcessor functions.
WiredHome 0:198f53da1bc8 90 ///
WiredHome 0:198f53da1bc8 91 /// @returns handle to the CommandProcessor
WiredHome 0:198f53da1bc8 92 ///
WiredHome 0:198f53da1bc8 93 CMDP_T * GetCommandProcessor(void)
WiredHome 0:198f53da1bc8 94 {
WiredHome 0:198f53da1bc8 95 return &CommandProcessor;
WiredHome 0:198f53da1bc8 96 }
WiredHome 0:198f53da1bc8 97
WiredHome 0:198f53da1bc8 98
WiredHome 0:198f53da1bc8 99 static RUNRESULT_T About(char *p)
WiredHome 0:198f53da1bc8 100 {
WiredHome 0:198f53da1bc8 101 cfg.puts("\r\n About this CommandProcessor:\r\n"
WiredHome 0:198f53da1bc8 102 " This CommandProcessor provides easy facility for creating a\r\n"
WiredHome 0:198f53da1bc8 103 " runtime CommandProcessor in an embedded systems.\r\n"
WiredHome 0:198f53da1bc8 104 " author: David Smart, Smartware Computing\r\n"
WiredHome 0:198f53da1bc8 105 );
WiredHome 0:198f53da1bc8 106 return runok;
WiredHome 0:198f53da1bc8 107 }
WiredHome 0:198f53da1bc8 108
WiredHome 6:1a0512faa75d 109 static RUNRESULT_T Echo(char *p) {
WiredHome 6:1a0512faa75d 110 if (*p) {
WiredHome 6:1a0512faa75d 111 if (*p == '1' || mystrnicmp(p, "on", 2) == 0)
WiredHome 6:1a0512faa75d 112 CommandProcessor_Echo(1);
WiredHome 6:1a0512faa75d 113 if (*p == '0' || mystrnicmp(p, "off", 3) == 0)
WiredHome 6:1a0512faa75d 114 CommandProcessor_Echo(0);
WiredHome 6:1a0512faa75d 115 }
WiredHome 6:1a0512faa75d 116 if (cfg.echo)
WiredHome 6:1a0512faa75d 117 cfg.puts("\r\nEcho is on");
WiredHome 6:1a0512faa75d 118 else
WiredHome 6:1a0512faa75d 119 cfg.puts("\r\nEcho is off");
WiredHome 6:1a0512faa75d 120 return runok;
WiredHome 6:1a0512faa75d 121 }
WiredHome 6:1a0512faa75d 122
WiredHome 0:198f53da1bc8 123 static RUNRESULT_T Exit(char *p)
WiredHome 0:198f53da1bc8 124 {
WiredHome 0:198f53da1bc8 125 (void)p;
WiredHome 0:198f53da1bc8 126
WiredHome 0:198f53da1bc8 127 cfg.puts("\r\nbye.");
WiredHome 0:198f53da1bc8 128 return runexit;
WiredHome 0:198f53da1bc8 129 }
WiredHome 0:198f53da1bc8 130
WiredHome 0:198f53da1bc8 131 static RUNRESULT_T Help(char *p)
WiredHome 0:198f53da1bc8 132 {
WiredHome 0:198f53da1bc8 133 CMDLINK_T *link = head;
WiredHome 0:198f53da1bc8 134 char buffer[100];
WiredHome 0:198f53da1bc8 135
WiredHome 0:198f53da1bc8 136 cfg.puts("\r\n");
WiredHome 0:198f53da1bc8 137 sprintf(buffer, " %-10s: %s", "Command", "Description");
WiredHome 0:198f53da1bc8 138 cfg.puts(buffer);
WiredHome 0:198f53da1bc8 139 while (link && link->menu)
WiredHome 0:198f53da1bc8 140 {
WiredHome 0:198f53da1bc8 141 if (link->menu->visible)
WiredHome 0:198f53da1bc8 142 {
WiredHome 0:198f53da1bc8 143 if (strlen(link->menu->command) + strlen(link->menu->helptext) + 5 < sizeof(buffer))
WiredHome 0:198f53da1bc8 144 {
WiredHome 0:198f53da1bc8 145 sprintf(buffer, " %-10s: %s", link->menu->command, link->menu->helptext);
WiredHome 0:198f53da1bc8 146 cfg.puts(buffer);
WiredHome 0:198f53da1bc8 147 }
WiredHome 0:198f53da1bc8 148 }
WiredHome 0:198f53da1bc8 149 link = link->next;
WiredHome 0:198f53da1bc8 150 }
WiredHome 0:198f53da1bc8 151 cfg.puts("");
WiredHome 0:198f53da1bc8 152 if (*p == '?')
WiredHome 0:198f53da1bc8 153 {
WiredHome 0:198f53da1bc8 154 cfg.puts("\r\n Extended Help\r\n"
WiredHome 0:198f53da1bc8 155 " The general form for entering commands is:\r\n"
WiredHome 0:198f53da1bc8 156 " >command option1 option2 ...\r\n"
WiredHome 0:198f53da1bc8 157 " [note that note all commands support optional parameters]\r\n"
WiredHome 0:198f53da1bc8 158 " * Abbreviations of commands may be entered so long as there\r\n"
WiredHome 0:198f53da1bc8 159 " is exactly one match in the list of commands. For example,\r\n"
WiredHome 0:198f53da1bc8 160 " 'he' is the same as 'help', if there is no other command \r\n"
WiredHome 0:198f53da1bc8 161 " that starts with 'he'.\r\n"
WiredHome 0:198f53da1bc8 162 " * <bs> can be used to erase previously entered information.\r\n"
WiredHome 0:198f53da1bc8 163 " * <esc> can be used to cancel a command.\r\n"
WiredHome 0:198f53da1bc8 164 " * <tab> can be used to complete the entry of a partial command.\r\n"
WiredHome 0:198f53da1bc8 165 "");
WiredHome 0:198f53da1bc8 166 }
WiredHome 0:198f53da1bc8 167 return runok;
WiredHome 0:198f53da1bc8 168 }
WiredHome 0:198f53da1bc8 169
WiredHome 0:198f53da1bc8 170 /// mytolower exists because not all compiler libraries have this function
WiredHome 0:198f53da1bc8 171 ///
WiredHome 0:198f53da1bc8 172 /// This takes a character and if it is upper-case, it converts it to
WiredHome 0:198f53da1bc8 173 /// lower-case and returns it.
WiredHome 0:198f53da1bc8 174 ///
WiredHome 0:198f53da1bc8 175 /// @param a is the character to convert
WiredHome 0:198f53da1bc8 176 /// @returns the lower case equivalent to a
WiredHome 0:198f53da1bc8 177 ///
WiredHome 0:198f53da1bc8 178 char mytolower(char a)
WiredHome 0:198f53da1bc8 179 {
WiredHome 0:198f53da1bc8 180 if (a >= 'A' && a <= 'Z')
WiredHome 0:198f53da1bc8 181 return (a - 'A' + 'a');
WiredHome 0:198f53da1bc8 182 else
WiredHome 0:198f53da1bc8 183 return a;
WiredHome 0:198f53da1bc8 184 }
WiredHome 0:198f53da1bc8 185
WiredHome 0:198f53da1bc8 186 /// mystrnicmp exists because not all compiler libraries have this function.
WiredHome 0:198f53da1bc8 187 ///
WiredHome 0:198f53da1bc8 188 /// Some have strnicmp, others _strnicmp, and others have C++ methods, which
WiredHome 0:198f53da1bc8 189 /// is outside the scope of this C-portable set of functions.
WiredHome 0:198f53da1bc8 190 ///
WiredHome 0:198f53da1bc8 191 /// @param l is a pointer to the string on the left
WiredHome 0:198f53da1bc8 192 /// @param r is a pointer to the string on the right
WiredHome 0:198f53da1bc8 193 /// @param n is the number of characters to compare
WiredHome 0:198f53da1bc8 194 /// @returns -1 if l < r
WiredHome 0:198f53da1bc8 195 /// @returns 0 if l == r
WiredHome 0:198f53da1bc8 196 /// @returns +1 if l > r
WiredHome 0:198f53da1bc8 197 ///
WiredHome 0:198f53da1bc8 198 int mystrnicmp(const char *l, const char *r, size_t n)
WiredHome 0:198f53da1bc8 199 {
WiredHome 0:198f53da1bc8 200 int result = 0;
WiredHome 0:198f53da1bc8 201
WiredHome 0:198f53da1bc8 202 if (n != 0)
WiredHome 0:198f53da1bc8 203 {
WiredHome 0:198f53da1bc8 204 do {
WiredHome 0:198f53da1bc8 205 result = mytolower(*l++) - mytolower(*r++);
WiredHome 0:198f53da1bc8 206 } while ((result == 0) && (*l != '\0') && (--n > 0));
WiredHome 0:198f53da1bc8 207 }
WiredHome 0:198f53da1bc8 208 if (result < -1)
WiredHome 0:198f53da1bc8 209 result = -1;
WiredHome 0:198f53da1bc8 210 else if (result > 1)
WiredHome 0:198f53da1bc8 211 result = 1;
WiredHome 0:198f53da1bc8 212 return result;
WiredHome 0:198f53da1bc8 213 }
WiredHome 0:198f53da1bc8 214
WiredHome 0:198f53da1bc8 215 /// mystrcat exists because not all compiler libraries have this function
WiredHome 0:198f53da1bc8 216 ///
WiredHome 0:198f53da1bc8 217 /// This function concatinates one string onto another. It is generally
WiredHome 0:198f53da1bc8 218 /// considered unsafe, because of the potential for buffer overflow.
WiredHome 0:198f53da1bc8 219 /// Some libraries offer a strcat_s as the safe version, and others may have
WiredHome 0:198f53da1bc8 220 /// _strcat. Because this is needed only internal to the CommandProcessor,
WiredHome 0:198f53da1bc8 221 /// this version was created.
WiredHome 0:198f53da1bc8 222 ///
WiredHome 0:198f53da1bc8 223 /// @param dst is a pointer to the destination string
WiredHome 0:198f53da1bc8 224 /// @param src is a pointer to the source string
WiredHome 0:198f53da1bc8 225 /// @returns nothing
WiredHome 0:198f53da1bc8 226 ///
WiredHome 0:198f53da1bc8 227 void mystrcat(char *dst, char *src)
WiredHome 0:198f53da1bc8 228 {
WiredHome 0:198f53da1bc8 229 while (*dst)
WiredHome 0:198f53da1bc8 230 dst++;
WiredHome 0:198f53da1bc8 231 do
WiredHome 0:198f53da1bc8 232 *dst++ = *src;
WiredHome 0:198f53da1bc8 233 while (*src++);
WiredHome 0:198f53da1bc8 234 }
WiredHome 0:198f53da1bc8 235
WiredHome 0:198f53da1bc8 236 /// myisprint exists because not all compiler libraries have this function
WiredHome 0:198f53da1bc8 237 ///
WiredHome 0:198f53da1bc8 238 /// This function tests a character to see if it is printable (a member
WiredHome 0:198f53da1bc8 239 /// of the standard ASCII set).
WiredHome 0:198f53da1bc8 240 ///
WiredHome 0:198f53da1bc8 241 /// @param c is the character to test
WiredHome 0:198f53da1bc8 242 /// @returns TRUE if the character is printable
WiredHome 0:198f53da1bc8 243 /// @returns FALSE if the character is not printable
WiredHome 0:198f53da1bc8 244 ///
WiredHome 0:198f53da1bc8 245 static int myisprint(int c)
WiredHome 0:198f53da1bc8 246 {
WiredHome 0:198f53da1bc8 247 if (c >= ' ' && c <= '~')
WiredHome 0:198f53da1bc8 248 return TRUE;
WiredHome 0:198f53da1bc8 249 else
WiredHome 0:198f53da1bc8 250 return FALSE;
WiredHome 0:198f53da1bc8 251 }
WiredHome 0:198f53da1bc8 252
WiredHome 0:198f53da1bc8 253
WiredHome 0:198f53da1bc8 254 /// CommandMatches is the function that determines if the user is entering a valid
WiredHome 0:198f53da1bc8 255 /// command
WiredHome 0:198f53da1bc8 256 ///
WiredHome 0:198f53da1bc8 257 /// This function gets called whenever the user types a printable character or when
WiredHome 0:198f53da1bc8 258 /// they press \<enter\>.
WiredHome 0:198f53da1bc8 259 /// The buffer of user input is evaluated to determine if the command is legitimate.
WiredHome 0:198f53da1bc8 260 /// It also determines if they want to execute the command, or simply evaluate it.
WiredHome 0:198f53da1bc8 261 /// Finally, it identifies writes to user provided pointers, the menu item that
WiredHome 0:198f53da1bc8 262 /// matches and a pointer to any parameters that the user entered.
WiredHome 0:198f53da1bc8 263 ///
WiredHome 0:198f53da1bc8 264 /// @param buffer is the buffer that the user has entered commands into
WiredHome 0:198f53da1bc8 265 /// @param exec indicates the intention to execute the command, if found
WiredHome 0:198f53da1bc8 266 /// @param menu is a pointer to a pointer to a menu structure, which is updated based on the command
WiredHome 0:198f53da1bc8 267 /// @param params is a pointer to a pointer to the user entered parameters
WiredHome 0:198f53da1bc8 268 /// @returns the number of menu picks that match the user entered command
WiredHome 0:198f53da1bc8 269 ///
WiredHome 0:198f53da1bc8 270 static int CommandMatches(char * buffer, int exec, CMD_T **menu, char ** params)
WiredHome 0:198f53da1bc8 271 {
WiredHome 0:198f53da1bc8 272 char *space;
WiredHome 0:198f53da1bc8 273 int compareLength;
WiredHome 0:198f53da1bc8 274 int foundCount = 0;
WiredHome 0:198f53da1bc8 275 CMDLINK_T *link = head;
WiredHome 0:198f53da1bc8 276
WiredHome 0:198f53da1bc8 277 if (strlen(buffer)) // simple sanity check
WiredHome 0:198f53da1bc8 278 {
WiredHome 0:198f53da1bc8 279 // Try to process the buffer. A command could be "Help", or it could be "Test1 123 abc"
WiredHome 0:198f53da1bc8 280 space = strchr(buffer, ' '); // if the command has parameters, find the delimiter
WiredHome 0:198f53da1bc8 281 if (space)
WiredHome 0:198f53da1bc8 282 {
WiredHome 0:198f53da1bc8 283 compareLength = space - buffer;
WiredHome 0:198f53da1bc8 284 space++; // char after the space
WiredHome 0:198f53da1bc8 285 }
WiredHome 0:198f53da1bc8 286 else
WiredHome 0:198f53da1bc8 287 {
WiredHome 0:198f53da1bc8 288 compareLength = strlen(buffer);
WiredHome 0:198f53da1bc8 289 space = buffer + compareLength;
WiredHome 0:198f53da1bc8 290 }
WiredHome 0:198f53da1bc8 291 while (link && link->menu)
WiredHome 0:198f53da1bc8 292 {
WiredHome 0:198f53da1bc8 293 int cmpResult;
WiredHome 0:198f53da1bc8 294
WiredHome 0:198f53da1bc8 295 if (cfg.caseinsensitive)
WiredHome 0:198f53da1bc8 296 {
WiredHome 0:198f53da1bc8 297 cmpResult = mystrnicmp(buffer, link->menu->command, compareLength);
WiredHome 0:198f53da1bc8 298 }
WiredHome 0:198f53da1bc8 299 else
WiredHome 0:198f53da1bc8 300 cmpResult = strncmp(buffer, link->menu->command, compareLength);
WiredHome 0:198f53da1bc8 301 if (0 == cmpResult) // A match to what they typed
WiredHome 0:198f53da1bc8 302 {
WiredHome 0:198f53da1bc8 303 if (menu) // yes, we have a callback
WiredHome 0:198f53da1bc8 304 {
WiredHome 0:198f53da1bc8 305 if (exec) // command wants to execute it, not just validate the command syntax
WiredHome 0:198f53da1bc8 306 {
WiredHome 0:198f53da1bc8 307 // If they type "He 1234 5678", we backup and rewrite as "Help 1234 5678"
WiredHome 0:198f53da1bc8 308 int diff = strlen(link->menu->command) - compareLength; // e.g. 5 - 3
WiredHome 0:198f53da1bc8 309
WiredHome 0:198f53da1bc8 310 if (diff > 0)
WiredHome 0:198f53da1bc8 311 {
WiredHome 0:198f53da1bc8 312 int back = 0;
WiredHome 0:198f53da1bc8 313 char *p;
WiredHome 0:198f53da1bc8 314 if (strlen(space))
WiredHome 0:198f53da1bc8 315 back = strlen(space) + 1;
WiredHome 0:198f53da1bc8 316
WiredHome 0:198f53da1bc8 317 while (back--)
WiredHome 0:198f53da1bc8 318 cfg.putch(0x08);
WiredHome 0:198f53da1bc8 319 p = link->menu->command + compareLength;
WiredHome 0:198f53da1bc8 320 while (*p)
WiredHome 0:198f53da1bc8 321 cfg.putch(*p++);
WiredHome 0:198f53da1bc8 322 cfg.putch(' ');
WiredHome 0:198f53da1bc8 323 p = space;
WiredHome 0:198f53da1bc8 324 while (*p)
WiredHome 0:198f53da1bc8 325 cfg.putch(*p++);
WiredHome 0:198f53da1bc8 326 //cfg.printf("%s %s", link->menu->command + compareLength, space);
WiredHome 0:198f53da1bc8 327 }
WiredHome 0:198f53da1bc8 328 }
WiredHome 0:198f53da1bc8 329 *menu = link->menu; // accessor to the command they want to execute
WiredHome 0:198f53da1bc8 330 *params = space;
WiredHome 0:198f53da1bc8 331 }
WiredHome 0:198f53da1bc8 332 foundCount++; // how many command match what they typed so far
WiredHome 0:198f53da1bc8 333 }
WiredHome 0:198f53da1bc8 334 link = link->next; // follow the link to the next command
WiredHome 0:198f53da1bc8 335 }
WiredHome 0:198f53da1bc8 336 }
WiredHome 0:198f53da1bc8 337 return foundCount;
WiredHome 0:198f53da1bc8 338 }
WiredHome 0:198f53da1bc8 339
WiredHome 0:198f53da1bc8 340 /// Initialize the CommandProcessor
WiredHome 0:198f53da1bc8 341 ///
WiredHome 0:198f53da1bc8 342 /// This initializes the CommandProcessor by adding the built-in menus
WiredHome 0:198f53da1bc8 343 ///
WiredHome 0:198f53da1bc8 344 /// @param addDefaultMenu configures it to add the Help, About, and Exit menus
WiredHome 0:198f53da1bc8 345 /// @param bufSize configures the size of the longest command, which must be
WiredHome 0:198f53da1bc8 346 /// greater than 6 (the size of "About\0").
WiredHome 0:198f53da1bc8 347 /// @returns initok if it successfully initialized the CommandProcessor
WiredHome 0:198f53da1bc8 348 /// @returns initfailed if it could not allocate memory
WiredHome 0:198f53da1bc8 349 ///
WiredHome 0:198f53da1bc8 350 INITRESULT_T CommandProcessor_Init(
WiredHome 0:198f53da1bc8 351 int addDefaultMenu,
WiredHome 0:198f53da1bc8 352 int caseinsensitive,
WiredHome 6:1a0512faa75d 353 int echo,
WiredHome 0:198f53da1bc8 354 int maxCmdLen,
WiredHome 0:198f53da1bc8 355 int (*kbhit)(void),
WiredHome 0:198f53da1bc8 356 int (*getch)(void),
WiredHome 0:198f53da1bc8 357 int (*putch)(int ch),
WiredHome 0:198f53da1bc8 358 int (*puts)(const char * s)
WiredHome 0:198f53da1bc8 359 )
WiredHome 0:198f53da1bc8 360 {
WiredHome 0:198f53da1bc8 361 if (maxCmdLen < 6)
WiredHome 0:198f53da1bc8 362 maxCmdLen = 6;
WiredHome 0:198f53da1bc8 363 buffer = (char *)malloc(maxCmdLen+1);
WiredHome 0:198f53da1bc8 364 cfg.bufferSize = maxCmdLen;
WiredHome 0:198f53da1bc8 365 if (buffer)
WiredHome 0:198f53da1bc8 366 {
WiredHome 6:1a0512faa75d 367 if (addDefaultMenu & 0x0008)
WiredHome 5:a98bd1f2fd59 368 CommandProcessor.Add(&QuestionMenu);
WiredHome 6:1a0512faa75d 369 if (addDefaultMenu & 0x0008)
WiredHome 6:1a0512faa75d 370 CommandProcessor.Add(&HelpMenu);
WiredHome 5:a98bd1f2fd59 371 if (addDefaultMenu & 0x0004)
WiredHome 6:1a0512faa75d 372 CommandProcessor.Add(&EchoMenu);
WiredHome 0:198f53da1bc8 373 if (addDefaultMenu & 0x0002)
WiredHome 0:198f53da1bc8 374 CommandProcessor.Add(&AboutMenu);
WiredHome 0:198f53da1bc8 375 if (addDefaultMenu & 0x0001)
WiredHome 0:198f53da1bc8 376 CommandProcessor.Add(&ExitMenu);
WiredHome 0:198f53da1bc8 377 cfg.caseinsensitive = caseinsensitive;
WiredHome 6:1a0512faa75d 378 cfg.echo = echo;
WiredHome 0:198f53da1bc8 379 cfg.kbhit = kbhit;
WiredHome 0:198f53da1bc8 380 cfg.getch = getch;
WiredHome 0:198f53da1bc8 381 cfg.putch = putch;
WiredHome 0:198f53da1bc8 382 cfg.puts = puts;
WiredHome 0:198f53da1bc8 383 return initok;
WiredHome 0:198f53da1bc8 384 }
WiredHome 0:198f53da1bc8 385 else
WiredHome 0:198f53da1bc8 386 return initfailed;
WiredHome 0:198f53da1bc8 387 }
WiredHome 0:198f53da1bc8 388
WiredHome 0:198f53da1bc8 389 /// Add a command to the CommandProcessor
WiredHome 0:198f53da1bc8 390 ///
WiredHome 0:198f53da1bc8 391 /// This adds a command to the CommandProcessor. A command has several components
WiredHome 0:198f53da1bc8 392 /// to it, including the command name, a brief description, the function to
WiredHome 0:198f53da1bc8 393 /// activate when the command is entered, and a flag indicating if the command
WiredHome 0:198f53da1bc8 394 /// should show up in the built-in help.
WiredHome 0:198f53da1bc8 395 ///
WiredHome 0:198f53da1bc8 396 /// @todo sort them when adding a menu item
WiredHome 0:198f53da1bc8 397 ///
WiredHome 0:198f53da1bc8 398 /// @param m is the menu to add to the CommandProcessor
WiredHome 0:198f53da1bc8 399 /// @returns addok if the command was added
WiredHome 0:198f53da1bc8 400 /// @returns addfail if the command could not be added (failure to allocate memory for the linked list)
WiredHome 0:198f53da1bc8 401 ///
WiredHome 0:198f53da1bc8 402 ADDRESULT_T CommandProcessor_Add(CMD_T * m)
WiredHome 0:198f53da1bc8 403 {
WiredHome 0:198f53da1bc8 404 CMDLINK_T *p;
WiredHome 0:198f53da1bc8 405
WiredHome 0:198f53da1bc8 406 if (head == NULL)
WiredHome 0:198f53da1bc8 407 {
WiredHome 0:198f53da1bc8 408 head = (CMDLINK_T *)malloc(sizeof(CMDLINK_T));
WiredHome 0:198f53da1bc8 409 if (!head)
WiredHome 0:198f53da1bc8 410 return addfailed;
WiredHome 0:198f53da1bc8 411 head->menu = NULL;
WiredHome 0:198f53da1bc8 412 head->next = NULL;
WiredHome 0:198f53da1bc8 413 }
WiredHome 0:198f53da1bc8 414 p = head;
WiredHome 0:198f53da1bc8 415 while (p->next)
WiredHome 0:198f53da1bc8 416 p = p->next;
WiredHome 0:198f53da1bc8 417 if (p->menu == NULL)
WiredHome 0:198f53da1bc8 418 {
WiredHome 0:198f53da1bc8 419 p->menu = m;
WiredHome 0:198f53da1bc8 420 return addok;
WiredHome 0:198f53da1bc8 421 }
WiredHome 0:198f53da1bc8 422 else if (p->next == NULL)
WiredHome 0:198f53da1bc8 423 {
WiredHome 0:198f53da1bc8 424 p->next = (CMDLINK_T *)malloc(sizeof(CMDLINK_T));
WiredHome 0:198f53da1bc8 425 if (!p->next)
WiredHome 0:198f53da1bc8 426 return addfailed;
WiredHome 0:198f53da1bc8 427 p = p->next;
WiredHome 0:198f53da1bc8 428 p->menu = m;
WiredHome 0:198f53da1bc8 429 p->next = NULL;
WiredHome 0:198f53da1bc8 430 return addok;
WiredHome 0:198f53da1bc8 431 }
WiredHome 0:198f53da1bc8 432 return addfailed;
WiredHome 0:198f53da1bc8 433 }
WiredHome 0:198f53da1bc8 434
WiredHome 0:198f53da1bc8 435 /// Run the CommandProcessor
WiredHome 0:198f53da1bc8 436 ///
WiredHome 0:198f53da1bc8 437 /// This will peek to see if there is a keystroke ready. It will pull that into a
WiredHome 0:198f53da1bc8 438 /// buffer if it is part of a valid command in the command set. You may then enter
WiredHome 0:198f53da1bc8 439 /// arguments to the command to be run.
WiredHome 0:198f53da1bc8 440 ///
WiredHome 0:198f53da1bc8 441 /// Primitive editing is permitted with <bs>.
WiredHome 0:198f53da1bc8 442 ///
WiredHome 0:198f53da1bc8 443 /// When you press <enter> it will evaluate the command and execute the command
WiredHome 0:198f53da1bc8 444 /// passing it the parameter string.
WiredHome 0:198f53da1bc8 445 ///
WiredHome 0:198f53da1bc8 446 /// @returns runok if the command that was run allows continuation of the CommandProcessor
WiredHome 0:198f53da1bc8 447 /// @returns runfail if the command that was run is asking the CommandProcessor to exit
WiredHome 0:198f53da1bc8 448 ///
WiredHome 0:198f53da1bc8 449 RUNRESULT_T CommandProcessor_Run(void)
WiredHome 0:198f53da1bc8 450 {
WiredHome 0:198f53da1bc8 451 static int showPrompt = TRUE;
WiredHome 0:198f53da1bc8 452 static int keycount = 0; // how full?
WiredHome 0:198f53da1bc8 453 RUNRESULT_T val = runok; // return true when happy, false to exit the prog
WiredHome 0:198f53da1bc8 454 CMD_T *cbk = NULL;
WiredHome 0:198f53da1bc8 455 char * params = NULL;
WiredHome 0:198f53da1bc8 456
WiredHome 6:1a0512faa75d 457 if (showPrompt && cfg.echo)
WiredHome 0:198f53da1bc8 458 {
WiredHome 0:198f53da1bc8 459 cfg.putch('>');
WiredHome 0:198f53da1bc8 460 showPrompt = FALSE;
WiredHome 0:198f53da1bc8 461 }
WiredHome 0:198f53da1bc8 462 if (cfg.kbhit())
WiredHome 0:198f53da1bc8 463 {
WiredHome 0:198f53da1bc8 464 int c = cfg.getch();
WiredHome 0:198f53da1bc8 465
WiredHome 0:198f53da1bc8 466 switch (c)
WiredHome 0:198f53da1bc8 467 {
WiredHome 0:198f53da1bc8 468 case 0x09: // <TAB> to request command completion
WiredHome 0:198f53da1bc8 469 if (1 == CommandMatches(buffer, FALSE, &cbk, &params))
WiredHome 0:198f53da1bc8 470 {
WiredHome 0:198f53da1bc8 471 size_t n;
WiredHome 0:198f53da1bc8 472 char *p = strchr(buffer, ' ');
WiredHome 0:198f53da1bc8 473 if (p)
WiredHome 0:198f53da1bc8 474 n = p - buffer;
WiredHome 0:198f53da1bc8 475 else
WiredHome 0:198f53da1bc8 476 n = strlen(buffer);
WiredHome 0:198f53da1bc8 477 if (n < strlen(cbk->command))
WiredHome 0:198f53da1bc8 478 {
WiredHome 0:198f53da1bc8 479 p = cbk->command + strlen(buffer);
WiredHome 0:198f53da1bc8 480 mystrcat(buffer, p);
WiredHome 0:198f53da1bc8 481 keycount = strlen(buffer);
WiredHome 0:198f53da1bc8 482 while (*p)
WiredHome 0:198f53da1bc8 483 cfg.putch(*p++);
WiredHome 0:198f53da1bc8 484 //cfg.printf("%s", p);
WiredHome 0:198f53da1bc8 485 }
WiredHome 0:198f53da1bc8 486 }
WiredHome 0:198f53da1bc8 487 break;
WiredHome 0:198f53da1bc8 488 case 0x1b: // <ESC> to empty the command buffer
WiredHome 0:198f53da1bc8 489 while (keycount--)
WiredHome 0:198f53da1bc8 490 {
WiredHome 0:198f53da1bc8 491 cfg.putch(0x08); // <bs>
WiredHome 0:198f53da1bc8 492 cfg.putch(' ');
WiredHome 0:198f53da1bc8 493 cfg.putch(0x08);
WiredHome 0:198f53da1bc8 494 }
WiredHome 0:198f53da1bc8 495 keycount = 0;
WiredHome 0:198f53da1bc8 496 buffer[keycount] = '\0';
WiredHome 0:198f53da1bc8 497 break;
WiredHome 0:198f53da1bc8 498 case '\x08': // <bs>
WiredHome 0:198f53da1bc8 499 if (keycount)
WiredHome 0:198f53da1bc8 500 {
WiredHome 0:198f53da1bc8 501 buffer[--keycount] = '\0';
WiredHome 0:198f53da1bc8 502 cfg.putch(0x08);
WiredHome 0:198f53da1bc8 503 cfg.putch(' ');
WiredHome 0:198f53da1bc8 504 cfg.putch(0x08);
WiredHome 0:198f53da1bc8 505 }
WiredHome 0:198f53da1bc8 506 else
WiredHome 0:198f53da1bc8 507 cfg.putch(0x07); // bell
WiredHome 0:198f53da1bc8 508 break;
WiredHome 0:198f53da1bc8 509 case '\r':
WiredHome 0:198f53da1bc8 510 case '\n':
WiredHome 0:198f53da1bc8 511 {
WiredHome 0:198f53da1bc8 512 int foundCount = 0;
WiredHome 0:198f53da1bc8 513
WiredHome 0:198f53da1bc8 514 if (strlen(buffer))
WiredHome 0:198f53da1bc8 515 {
WiredHome 0:198f53da1bc8 516 foundCount = CommandMatches(buffer, TRUE, &cbk, &params);
WiredHome 0:198f53da1bc8 517 if (foundCount == 1)
WiredHome 0:198f53da1bc8 518 {
WiredHome 0:198f53da1bc8 519 val = (*cbk->callback)(params); // Execute the command
WiredHome 0:198f53da1bc8 520 }
WiredHome 0:198f53da1bc8 521 else if (foundCount > 1)
WiredHome 0:198f53da1bc8 522 cfg.puts(" *** non-unique command ignored try 'Help' ***");
WiredHome 0:198f53da1bc8 523 else if (foundCount == 0)
WiredHome 0:198f53da1bc8 524 cfg.puts(" *** huh? try 'Help' ***");
WiredHome 0:198f53da1bc8 525 }
WiredHome 0:198f53da1bc8 526 else
WiredHome 0:198f53da1bc8 527 cfg.puts("");
WiredHome 0:198f53da1bc8 528 keycount = 0;
WiredHome 0:198f53da1bc8 529 buffer[keycount] = '\0';
WiredHome 0:198f53da1bc8 530 showPrompt = TRUE; // forces the prompt
WiredHome 0:198f53da1bc8 531 }
WiredHome 0:198f53da1bc8 532 break;
WiredHome 0:198f53da1bc8 533 default:
WiredHome 0:198f53da1bc8 534 if (myisprint(c) && keycount < cfg.bufferSize)
WiredHome 0:198f53da1bc8 535 {
WiredHome 0:198f53da1bc8 536 buffer[keycount++] = c;
WiredHome 0:198f53da1bc8 537 buffer[keycount] = '\0';
WiredHome 0:198f53da1bc8 538 if (CommandMatches(buffer, FALSE, &cbk, &params))
WiredHome 0:198f53da1bc8 539 cfg.putch(c);
WiredHome 0:198f53da1bc8 540 else
WiredHome 0:198f53da1bc8 541 {
WiredHome 0:198f53da1bc8 542 buffer[--keycount] = '\0';
WiredHome 0:198f53da1bc8 543 cfg.putch(0x07); // bell
WiredHome 0:198f53da1bc8 544 }
WiredHome 0:198f53da1bc8 545 }
WiredHome 0:198f53da1bc8 546 else
WiredHome 0:198f53da1bc8 547 cfg.putch(0x07); // bell
WiredHome 0:198f53da1bc8 548 break;
WiredHome 0:198f53da1bc8 549 }
WiredHome 0:198f53da1bc8 550 }
WiredHome 0:198f53da1bc8 551 return val;
WiredHome 0:198f53da1bc8 552 }
WiredHome 0:198f53da1bc8 553
WiredHome 0:198f53da1bc8 554
WiredHome 6:1a0512faa75d 555 static RUNRESULT_T CommandProcessor_Echo(int echo) {
WiredHome 6:1a0512faa75d 556 cfg.echo = echo;
WiredHome 6:1a0512faa75d 557 return runok;
WiredHome 6:1a0512faa75d 558 }
WiredHome 6:1a0512faa75d 559
WiredHome 0:198f53da1bc8 560 /// End the CommandProcessor by freeing all the memory that was allocated
WiredHome 0:198f53da1bc8 561 ///
WiredHome 0:198f53da1bc8 562 /// returns runok
WiredHome 0:198f53da1bc8 563 ///
WiredHome 0:198f53da1bc8 564 RUNRESULT_T CommandProcessor_End(void)
WiredHome 0:198f53da1bc8 565 {
WiredHome 0:198f53da1bc8 566 CMDLINK_T *p = head;
WiredHome 0:198f53da1bc8 567 CMDLINK_T *n;
WiredHome 0:198f53da1bc8 568
WiredHome 0:198f53da1bc8 569 do
WiredHome 0:198f53da1bc8 570 {
WiredHome 0:198f53da1bc8 571 n = p->next;
WiredHome 0:198f53da1bc8 572 free(p); // free each of the allocated links to menu items.
WiredHome 0:198f53da1bc8 573 p = n;
WiredHome 0:198f53da1bc8 574 } while (n);
WiredHome 0:198f53da1bc8 575 free(buffer); // finally, free the command buffer
WiredHome 0:198f53da1bc8 576 buffer = NULL; // flag it as deallocated
WiredHome 0:198f53da1bc8 577 return runok;
WiredHome 0:198f53da1bc8 578 }