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

Dependents:   A_CANAdapter USB2I2C

Committer:
WiredHome
Date:
Sat Apr 23 14:15:10 2011 +0000
Revision:
11:4a3cd3f2183b
Parent:
10:9e52bd1a4a71
Child:
12:a8c56bf811b9
Minor documentation update

Who changed what in which revision?

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