A Command Interpreter with support for used defined commands, subsystems, macros, help and parameter parsing.
Revision 26:34c65d3f6da0, committed 2012-02-15
- Comitter:
- wvd_vegt
- Date:
- Wed Feb 15 09:50:53 2012 +0000
- Parent:
- 25:37a7f1df057c
- Child:
- 27:17ff3b07a392
- Child:
- 28:7ba0b6819aa7
- Commit message:
- Fixing publishing problems
Changed in this revision
diff -r 37a7f1df057c -r 34c65d3f6da0 cmdb.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cmdb.cpp Wed Feb 15 09:50:53 2012 +0000 @@ -0,0 +1,1310 @@ +/* +_____________________________________________________________________________ + + Project: mBed Command Interpreter + Filename: cmdb.h + Version: 0.80 +_____________________________________________________________________________ + Date Comment + -------- -------------------------------------------------------------- + 10022011 -Rewritten into C++ class. + -Pass Serial into constructor for printf, putc and getc. + -CID_<subsystem> must be handled internally. + -Fixed a number of old Index/Id conflicts. + -Got a working version. Much work to be done though. + -Handle CID_HELP internally (like all system commands (IDLE/MACRO etc). + -Handle subsystems internally. + -Removed CMD_TBL_LEN. + -CID_LAST is now defined as CID_HELP+1. + 11022011 -Added Documentation. + -Added code to take number limits from the C++ Runtime instead of hard defined values. + -Renamed id to cid in cmd. + -Added MAX_LONG and MIN_LONG (long==int on mbed). + -Removed cmdb_ prefix from members. + -Tested Macro Support and added it to the Demo. + -Added CID_COMMANDS. + -Fixed a small bug in parse. + -v0.76 + 24032011 -Fixed some left-over bugs caused by index/id mixup. + 09052011 -Added present(), replace() and indexof() methods. + replace() can be used to replace the complete command vector + by a changed done. Inserting directly into cmdb's copy fails + somehow. + 19092011 -Added PrintSection(), PrintValue() and PrintValuef() for + easier Windows Ini File style output. As I use it to transfer + data back to the PC (Easy parsing AND you can add/remove + debug information without breaking PC code). + 20092011 -Breaking change, Made all cmd object static const like: + + static const cmd HELP = {"Help",GLOBALCMD,CID_HELP,"%s","Help"}; + + this saves just to much ram memory on the heap. + Thanks to Igor Skochinsky. + + -Made some more const string static. + -v0.80 + + 20092011 -Corrected Comment Alignment. + -v0.81 + -------- -------------------------------------------------------------- + TODO's + 10022011 -Tweak and Review Documentation. + 11022011 -Check & Test **PARM masks. + -Remove prefix from class members? + -------- -------------------------------------------------------------- +_____________________________________________________________________________ +*/ + +#include <vector> +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <ctype.h> +#include <string.h> + +#include "cmdb.h" +#include "mbed.h" + +//------------------------------------------------------------------------------ + +Cmdb::Cmdb(const Serial& _serial, std::vector<cmd>& _cmds, void (*_callback)(Cmdb&,int)) : + serial(_serial), cmds(_cmds) { + echo = true; + bold = true; + + NoComment = NULL; + DefComPos = 72; + + subsystem = -1; + + user_callback = _callback; + + init(true); +} + +const char* Cmdb::NoComment; + +int Cmdb::DefComPos; + +//------------------------------------------------------------------------------ +// Public Stuff. +//------------------------------------------------------------------------------ + +bool Cmdb::macro_hasnext() { + return macro_ptr!=-1 && macro_ptr<MAX_CMD_LEN && macro_buf[macro_ptr]; +} + +char Cmdb::macro_next() { + char ch = macro_buf[macro_ptr++]; + + if (macro_ptr==MAX_CMD_LEN) { + macro_reset(); + } + + //Translate Special Characters Back + switch (ch) { + case '|': + return cr; + case '_': + return sp; + default: + return ch; + } +} + +char Cmdb::macro_peek() { + return macro_buf[macro_ptr]; +} + +void Cmdb::macro_reset() { + macro_ptr = -1; + macro_buf[0] = '\0'; +} + +//------------------------------------------------------------------------------ + +bool Cmdb::hasnext() { + return serial.readable()==1; +} + +char Cmdb::next() { + return serial.getc(); +} + +//------------------------------------------------------------------------------ + +bool Cmdb::scan(const char c) { + int i; + + //See http://www.interfacebus.com/ASCII_Table.html + + if (c == '\r') { // cr? + print(crlf); // Output it and ... + if (cmdndx) { + strncpy(lstbuf,cmdbuf,cmdndx); + lstbuf[cmdndx]='\0'; + + cmd_dispatcher(cmdbuf); + } + init(false); + prompt(); + + return true; + } + + //TODO BACKSPACE NOT CORRECT FOR TELNET! + + if (c == '\b') { // Backspace + if (cmdndx != 0) { + print(bs); + cmdbuf [--cmdndx] = '\0'; + } else { + printch(bell); // Output Error + } + return false; + } + + if (c == '\177') { // Delete + while (cmdndx>0) { + print(bs); + cmdbuf [--cmdndx] = '\0'; + } + return false; + } + + //Reset Escape Buffer. + if (c == '\033') { + if (escndx!=0) { + //_putchar(bell); // Output Error + //printf("%s\r\n",escbuf); + } + escndx = 0; + escbuf [escndx] = '\0'; // NULL-Terminate buffer + } + + //Extract Escape Sequence. + if (c == '\033' || escndx ) { // Wait for escape + escbuf [escndx++] = (unsigned char) c; // Add to the buffer + escbuf [escndx] = '\0'; // NULL-Terminate buffer + if (isalpha(c)) { + switch (escid_search(escbuf)) { + case EID_CURSOR_LEFT : { + if (cmdndx != 0) { // Backspace? + print(bs); + cmdbuf [--cmdndx] = '\0'; + } else { + printch(bell); // Output char + } + break; + } + case EID_CURSOR_UP : { + for (i=0; i<cmdndx; i++) { + print(bs); + } + cmdndx=strlen(lstbuf); + strncpy(cmdbuf,lstbuf,cmdndx); + cmdbuf[cmdndx]='\0'; + printf("%s",cmdbuf); + break; + } + case EID_CURSOR_RIGHT: + break; + case EID_CURSOR_DOWN : + break; + case EID_LAST : + break; + default : + printch(bell); + break; + } + escndx=0; + escbuf [escndx] = '\0'; // NULL-Terminate buffer + } + return false; + } + + if (c=='\n') { // LF + return false; // Dump it + } + + if (!isprint (c)) { // Printable character? + printch(bell); + return false; + } + + if (cmdndx >= MAX_CMD_LEN) { // Past buffer length? + printch(bell); + return false; + } + + cmdbuf [cmdndx++] = (unsigned char) c; // Add to the buffer + cmdbuf [cmdndx] = '\0'; // NULL-Terminate buffer + + if (echo) { + printch(c); // Output char + } + + return false; +} + +//------------------------------------------------------------------------------ + +int Cmdb::printf(const char *format, ...) { + int cnt; + + va_list args; + char buf[1024]; + + memset(buf,'\0',sizeof(buf)); + + va_start(args, format); + cnt = vsnprintf(buf, sizeof(buf), format, args); + if (cnt==-1) { + //Error + } + va_end(args); + + return print(buf); +} + +int Cmdb::print(const char *msg) { + return serial.printf(msg); +} + +int Cmdb::println(const char *msg) { + return serial.printf("%s\r\n", msg); +} + +int Cmdb::printsection(const char *section) { + return printf("[%s]\r\n", section); +} + +int Cmdb::printmsg(const char *msg) { + return printf("Msg=%s\r\n", msg); +} + +int Cmdb::printerror(const char *errormsg) { + int a = printsection("Error"); + return a==0?a:a+printmsg(errormsg); +} + +int Cmdb::printerrorf(const char *format, ...) { + char buf[256]; + + int a = printsection("Error"); + + va_list args; + va_start(args, format); + + vsnprintf(buf, sizeof(buf), format, args); + + va_end(args); + + return a + printf("Msg=%s\r\n", buf); +} + +int Cmdb::printvaluef(const char *key, const char *format, ...) { + char buf[256]; + + va_list args; + va_start(args, format); + + vsnprintf(buf, sizeof(buf), format, args); + + va_end(args); + + return printf("%s=%s\r\n",key, buf); +} + +int Cmdb::printvaluef(const char *key, const int width, const char *comment, const char *format, ...) { + char buf[256]; + + int cnt = printf("%s=",key); + + va_list args; + va_start(args, format); + + vsnprintf(buf, sizeof(buf), format, args); + + va_end(args); + + if (comment!=NULL) { + cnt += printf("%s", buf); + + if (cnt<width) { + cnt += printf("%-*s ; %s\r\n", width - cnt - 1, "", comment); + } else { + cnt += printf("%s ; %s\r\n", "", comment); + } + } else { + cnt += printf("%s\r\n",buf); + } + + return cnt; +} + +int Cmdb::printvalue(const char *key, const char *value, const char *comment, const int width) { + if (comment) { + char buf[256]; + int cnt = 0; + + memset(buf,'\0',sizeof(buf)); + + cnt = snprintf(buf, sizeof(buf), "%s=%s", key, value); + + if (cnt<=width) { + return printf("%-*s ; %s\r\n", width - cnt + 1, buf, comment); + } else { + return printf("%s ; %s\r\n", buf, comment); + } + } else { + return printf("%s=%s\r\n", key, value); + } +} + +int Cmdb::printcomment(const char *comment, const int width) { + return printf("%-*s; %s\r\n", width, "", comment); +} + +char Cmdb::printch(const char ch) { + return serial.putc(ch); +} + +//Mode=1 ; Profile Position Mode +//1234567890123456789012 + +//------------------------------------------------------------------------------ + +void Cmdb::init(const char full) { + if (full) { + echo = true; + bold = true; + + subsystem = -1; + + lstbuf [cmdndx] = '\0'; + + macro_reset(); + + prompt(); + } + + cmdndx = 0; + cmdbuf [cmdndx] = '\0'; + + escndx = 0; + escbuf [escndx] = '\0'; +} + +//------------------------------------------------------------------------------ +//Private Stuff. +//------------------------------------------------------------------------------ + +int Cmdb::escid_search(char *escstr) { + for (int i=0; i<ESC_TBL_LEN; i++) { + if (strcmp (esc_tbl[i].escstr, escstr) == 0) + return (esc_tbl[i].id); + } + + return (EID_LAST); +} + +int Cmdb::cmdid_search(char *cmdstr) { + //Warning, we return the ID but somewhere assume it's equal to the array index! + for (int i=0; i<cmds.size(); i++) { + if ((stricmp((char*)cmds[i].cmdstr, cmdstr) == 0) && ((cmds[i].subs == subsystem) || (cmds[i].subs<0))) + return (cmds[i].cid); + } + + return CID_LAST; +} + +int Cmdb::cmdid_index(int cmdid) { + for (int i=0; i<cmds.size(); i++) { + if (cmds[i].cid==cmdid) + return i; + } + + return -1; +} + +//------------------------------------------------------------------------------ + +int Cmdb::parse(char *cmd) { + //Command + char cmdstr_buf [1 + MAX_CMD_LEN]; + + //Parameters + char argstr_buf [1 + MAX_CMD_LEN]; + char *argsep; + + char prmstr_buf [1 + MAX_CMD_LEN]; //copy of sscanf pattern + char *tok; //current token + void *toks[MAX_ARGS]; //pointers to string tokens IN commandline (argstr_buf) + char *prms[MAX_ARGS]; //patterns IN copy of sscanf string (*parms) + + char typ = '\0'; //Var type + char mod = '\0'; //Var modifier (for cardinal types) + unsigned int base; //Var number base (8,10,16) + //unsigned int bytes; //Var size in bytes (used for malloc) + + float f; //Temp var for conversion, 4 bytes + //unsigned char b; //Temp var for conversion, 1 byte + //char c; //Temp var for conversion, 1 byte + //short h; //Temp var for conversion, 2 bytes + //int k; //Temp var for conversion, 2 bytes + long l; //Temp var for conversion, 4 bytes + + char* endptr; //strtoXX() Error detection + + int cid = -1; //Signals empty string... + int ndx = -1; + + //Init (global) variables. + argfnd=0; + argcnt=0; + error =0; + + //Zero the two string buffers for splitting cmd string into. + zeromemory((char*)cmdstr_buf,sizeof(cmdstr_buf)); + zeromemory(argstr_buf,sizeof(argstr_buf)); + + //Make it worse in Lint + for (int i=0; i<MAX_ARGS; i++) { + parms[i].type=PARM_UNUSED; + zeromemory((char*)&(parms[i].val),sizeof(parms[i].val)); + } + + /*------------------------------------------------ + First, copy the command and convert it to all + uppercase. + ------------------------------------------------*/ + + strncpy(cmdstr_buf, cmd, sizeof (cmdstr_buf) - 1); + cmdstr_buf [sizeof (cmdstr_buf) - 1] = '\0'; + + /*------------------------------------------------ + Next, find the end of the first thing in the + buffer. Since the command ends with a space, + we'll look for that. NULL-Terminate the command + and keep a pointer to the arguments. + ------------------------------------------------*/ + + argsep = strchr(cmdstr_buf, ' '); + + if (argsep == NULL) { + argstr_buf [0] = '\0'; + } else { + strcpy (argstr_buf, argsep + 1); + *argsep = '\0'; + } + + /*------------------------------------------------ + Search for a command ID, then switch on it. + ------------------------------------------------*/ + + //1) Find the Command Id + cid = cmdid_search(cmdstr_buf); + + if (cid!=CID_LAST) { + //2) Tokenize a copy of the parms from the cmd_tbl. + + ndx = cmdid_index(cid); + + //Get Format patterns from cmd_tbl[id].parms. + //note: strtok inserts \0 into the original string. Hence the copy. + zeromemory((char *)(&prmstr_buf),sizeof(prmstr_buf)); + + strncpy (prmstr_buf, cmds[ndx].parms, sizeof (prmstr_buf) - 1); + + argcnt=0; + tok = strtok(prmstr_buf, " "); + while (tok != NULL) { + //Store Pointers + prms[argcnt++] = tok; + + //printf("prm_%2.2d='%s'\r\n",argcnt, tok); + + tok = strtok(NULL, " "); + } + + //3) Tokenize the commandline. + + //Get Tokens from arguments. + //Note: strtok inserts \0 into the original string. Won't harm here as we do not re-use it. + + argfnd=0; + + if (strlen(argstr_buf)!=0) { + tok = strtok(argstr_buf, " "); + } else { + tok=NULL; + } + + while (tok != NULL) { + //Store Pointers + toks[argfnd++]=tok; + + //printf("tok_%2.2d='%s'\r\n",argfnd, tok); + + tok = strtok(NULL, " "); + } + + if (argfnd==argcnt || (cid==CID_HELP && argfnd==0)) { + + error = 0; + + for (int i=0; i<argcnt; i++) { + //printf("prm_%2.2d=%s\r\n",i, prms[i]); + + switch (strlen(prms[i])) { + case 0: + break; + case 1: + break; + case 2: //Simple pattern, no modifier + mod='\0'; + typ=prms[i][1]; + + break; + case 3: //pattern with Modifier. + mod=prms[i][1]; + typ=prms[i][2]; + + break; + default: + break; + } + + switch (typ) { + case 'o' : + base=8; + break; + case 'x' : + base=16; + break; + default: + base=10; + break; + } + + endptr = (char*)toks[i]; + + switch (typ) { + //Signed Cardinal Types + case 'd' : //Check mod + case 'i' : //Check mod + switch (mod) { + case 'b' : //char + //test range + l=strtol((char*)toks[i], &endptr, base); + if (l>=MIN_CHAR && l<=MAX_CHAR) { + parms[i].type=PARM_CHAR; + parms[i].val.uc =(unsigned char)l; + } else { + error = i+1; + } + + break; + case 'h' : //short + l=strtol((char*)toks[i], &endptr, base); + if (l>=MIN_SHORT && l<=MAX_SHORT) { + parms[i].type=PARM_SHORT; + parms[i].val.w=(short)l; + } else { + error = i+1; + } + + break; + case 'l' : //long + l=strtol((char*)toks[i], &endptr, base); + parms[i].type=PARM_LONG; + parms[i].val.l=l; + + break; + default: //int + l=strtol((char*)toks[i], &endptr, base); + if (l>=MIN_INT && l<=MAX_INT) { + parms[i].type=PARM_INT; + parms[i].val.l=(int)l; + } else { + error = i+1; + } + break; + } + + if (error==0 && + (endptr==toks[i] //No Conversion at all. + || *endptr)) { //Incomplete conversion. + error = i+1; + } + + break; + + //Unsigned Cardinal Types + case 'u' : //Check mod + case 'o' : //Check mod + case 'x' : //Check mod + switch (mod) { + case 'b' : //char + //test range + l=strtol((char*)toks[i], &endptr, base); + if (l>=MIN_BYTE && l<=MAX_BYTE) { + parms[i].type=PARM_CHAR; + parms[i].val.uc =(unsigned char)l; + } else { + error = i+1; + } + + break; + case 'h' : //short + l=strtol((char*)toks[i], &endptr, base); + if (l>=MIN_USHORT && l<=MAX_USHORT) { + parms[i].type=PARM_SHORT; + parms[i].val.w=(short)l; + } else { + error = i+1; + } + + break; + case 'l' : //long + l=strtol((char*)toks[i], &endptr, base); + parms[i].type=PARM_LONG; + parms[i].val.l=l; + + break; + default: //int + l=strtol((char*)toks[i], &endptr, base); + if (l>=MIN_UINT && l<=MAX_UINT) { + parms[i].type=PARM_INT; + parms[i].val.l=(int)l; + } else { + error = i+1; + } + break; + } + + if (error==0 && + (endptr==toks[i] //No Conversion at all. + || *endptr)) { //Incomplete conversion. + error = i+1; + } + + break; + } + + //Floating Point Types + switch (typ) { + case 'e' : + case 'f' : + case 'g' : + f = strtod((char*)toks[i], &endptr); + + parms[i].type=PARM_FLOAT; + parms[i].val.f=f; + + if (error==0 && + (endptr==toks[i] //No Conversion at all. + || *endptr)) { //Incomplete conversion. + error = i; + } + + break; + } + + //String types + switch (typ) { + case 'c' : + parms[i].type=PARM_CHAR; + parms[i].val.c=((char*)toks[i])[0]; + + if (error==0 && strlen((char*)toks[i])!=1) { //Incomplete conversion. + error = i; + } + + break; + + case 's' : + parms[i].type=PARM_STRING; + strncpy(parms[i].val.s,(char*)toks[i], strlen((char*)toks[i])); + + break; + } + } + } else { + //cid=CID_LAST; + } + } + + return cid; +} + +//------------------------------------------------------------------------------ + +void Cmdb::cmd_dispatcher(char *cmd) { + int cid; + int ndx; + + cid = parse(cmd); + ndx = cmdid_index(cid); + + if (cid!=-1) { + //printf("cmds[%d]=%d\r\n",ndx, cid); + + /*------------------------------------------------ + Process the command and it's arguments that are + found. id contains the command id and argcnt & + argfnd the number of found and expected paramaters + parms contains the parsed argument values and their + types. + ------------------------------------------------*/ + + //printf("cmds[%d]=%d\r\n",ndx, cid); + + if (cid==CID_LAST) { + print("Unknown command, type 'Help' for a list of available commands.\r\n"); + } else { + //printf("cmds[%d]=%d [%s]\r\n",ndx, cid, cmds[ndx].cmdstr); + + //Test for more commandline than allowed too. + //i.e. run 1 is wrong. + + if (argcnt==0 && argfnd==0 && error==0 && ndx!=-1 && cmds[ndx].subs==SUBSYSTEM) { + //Handle all SubSystems. + subsystem=cid; + } else if ( ((cid==CID_HELP) || (argcnt==argfnd)) && error==0 ) { + switch (cid) { + +#ifdef ENABLEMACROS + /////// GLOBAL MACRO COMMANDS /////// + + //Define Macro from commandline + case CID_MACRO: + macro_ptr=-1; + + zeromemory((char*)macro_buf, sizeof(macro_buf)); + strncpy(macro_buf, STRINGPARM(0), sizeof(macro_buf) - 1); + + //DEBUG + printf("Macro=%s\r\n",macro_buf); + break; + + //Run Macro + case CID_RUN: + macro_ptr=0; + break; + + //List Macro's + case CID_MACROS: + print("[Macro]\r\n"); + if (macro_buf[0]!='\0') { + printf("Value=%s\r\n",macro_buf); + } else { + printf(";No Macro Defined\r\n"); + } + break; + +#endif //ENABLEMACROS + +#ifdef STATEMACHINE + /////// GLOBAL STATEMACHINE COMMANDS /////// + + //Start State Machine + case CID_STATE: + statemachine(BYTEPARM(0)); + + break; +#endif + + /////// GLOBAL COMMANDS /////// + case CID_COMMANDS: + cmd_dump(); + break; + + //Echo + case CID_ECHO: + echo = BOOLPARM(0); + break; + + //Bold + case CID_BOLD: + bold = BOOLPARM(0); + break; + + //Warm Boot + case CID_BOOT: + mbed_reset(); + break; + + //Sends an ANSI escape code to clear the screen. + case CID_CLS: + print(cls); + break; + + //Returns to CMD> prompt where most commands are disabled. + case CID_IDLE: + subsystem=-1; + break; + + //Help + case CID_HELP: { + print("\r\n"); + + if (argfnd>0) { + cid = cmdid_search(STRINGPARM(0)); + } else { + cid=CID_LAST; + } + + if (argfnd>0 && cid!=CID_LAST) { + + //Help with a valid command as first parameter + ndx = cmdid_index(cid); + + switch (cmds[ndx].subs) { + case SUBSYSTEM: { //Dump whole subsystem + printf("%s subsystem commands:\r\n\r\n",cmds[ndx].cmdstr); + + //Count SubSystem Commands. + int subcmds =0; + for (int i=0; i<cmds.size(); i++) { + if (cmds[i].subs==cid) { + subcmds++; + } + } + + //Print SubSystem Commands. + for (int i=0; i<cmds.size()-1; i++) { + if (cmds[i].subs==cid) { + subcmds--; + if (subcmds!=0) { + cmd_help("",i,",\r\n"); + } else { + cmd_help("",i,".\r\n"); + } + } + } + } + break; + + case GLOBALCMD: //Dump command only + //print("Global command:\r\n\r\n",cmd_tbl[cmd_tbl[ndx].subs].cmdstr); + cmd_help("Syntax: ",ndx,".\r\n"); + + break; + + default: { //Dump one subsystem command + int sndx = cmdid_index(cmds[ndx].subs); + + printf("%s subsystem command:\r\n\r\n",cmds[sndx].cmdstr); + + cmd_help("Syntax: ",ndx,".\r\n"); + } + break; + } + } else { + if (argfnd>0) { + //Help with invalid command as first parameter + print("Unknown command, type 'Help' for a list of available commands.\r\n"); + } else { + //Help + + //Dump Active Subsystem, Global & Other (dormant) Subsystems + //-1 because we want comma's and for the last a . + for (int i=0; i<cmds.size()-1; i++) { + if ((cmds[i].subs<0) || (cmds[i].subs==subsystem)) { + cmd_help("",i,",\r\n"); + } + } + cmd_help("",cmds.size()-1,".\r\n"); + } + } + print("\r\n"); + break; + } //CID_HELP + + default : { + // Do a Call to the Application's Command Dispatcher. + (*user_callback)(*this, cid); + } + } + } else { + cmd_help("Syntax: ",ndx,".\r\n"); + } + + } + + } else { + //cid==-1 + } +} + +//------------------------------------------------------------------------------ +//----Dump commands table as a ini file. +//------------------------------------------------------------------------------ + +void Cmdb::cmd_dump() { + int ndx; + int j; + int k; + int lastmod; + + k = 0; + lastmod = 0; + + for (ndx=0; ndx<cmds.size(); ndx++) { + +#ifndef SHOWHIDDEN + if (cmds[ndx].subs==HIDDENSUB) { + continue; + } +#endif + + switch (cmds[ndx].subs) { + case SUBSYSTEM : + printf("[command%2.2d]\r\n",ndx+1); + print("type=Subsystem\r\n"); + print("subsystem=Global\r\n"); + break; + case HIDDENSUB : +#ifdef SHOWHIDDEN + printf("[command%2.2d]\r\n",ndx+1); + print("type=HiddenSubystem\r\n"); + print("subsystem=Global\r\n"); +#endif + break; + case GLOBALCMD : + printf("[command%2.2d]\r\n",ndx+1); + print("type=GlobalCommand\r\n"); + print("subsystem=Global\r\n"); + break; + default : + int sndx = cmdid_index(cmds[ndx].subs); + + if (cmds[sndx].subs==HIDDENSUB) { +#ifdef SHOWHIDDEN + printf("[command%2.2d]\r\n",ndx+1); + print("type=HiddenCommand\r\n"); + print("subsystem=HiddenSubystem\r\n"); +#endif + continue; + } + + printf("[command%2.2d]\r\n",ndx+1); + print("type=Command\r\n"); + printf("subsystem=%s\r\n",cmds[sndx].cmdstr); + } + + if (cmds[ndx].subs==HIDDENSUB) { + continue; + } + + printf("command=%s\r\n",cmds[ndx].cmdstr); + printf("helpmsg=%s\r\n",cmds[ndx].cmddescr); + print("parameters="); + for (j=0; j<strlen(cmds[ndx].parms); j++) { + switch (cmds[ndx].parms[j]) { + case '%' : + lastmod=0; + break; + + case 'b' : + lastmod=8; + break; + case 'h' : + lastmod=16; + break; + case 'l' : + lastmod=32; + break; + + case 'd' : + case 'i' : { + switch (lastmod) { + case 0 : + case 16 : + print("int"); + k+=3; + break; + case 8 : + print("shortint"); + k+=8; + break; + case 32: + print("longint"); + k+=7; + break; + } + break; + } + + case 'u' : + case 'o' : + case 'x' : { + switch (lastmod) { + case 0 : + case 16 : + print("word"); + k+=4; + break; + case 8 : + print("byte"); + k+=4; + break; + case 32 : + print("dword"); + k+=5; + break; + } + + switch (cmds[ndx].parms[j]) { + case 'o' : + print("[o]"); + k+=3; + break; + case 'x' : + print("[h]"); + k+=3; + break; + } + + break; + } + + case 'e' : + case 'f' : + case 'g' : + print("float"); + k+=5; + break; + + case 'c' : + print("char"); + k+=4; + break; + + case 's' : + print("string"); + k+=6; + break; + + case ' ' : + printch(sp); + k++; + break; + } + } + print("\r\n"); + printf("syntax=%s\r\n",cmds[ndx].parmdescr); + } +} + +void Cmdb::prompt(void) { +#ifdef SUBSYSTEMPROMPTS + if (subsystem!=-1) { + int ndx = cmdid_index(subsystem); + + printf("%s>",cmds[ndx].cmdstr); + + return; + } +#endif //SUBSYSTEMPROMPTS + + printf(PROMPT); +} + +void Cmdb::cmd_help(char *pre, int ndx, char *post) { + int j; + int k; + int lastmod; + + k=0; + lastmod=0; + + switch (cmds[ndx].subs) { + case SUBSYSTEM : + break; + case GLOBALCMD : + break; + case HIDDENSUB : + return; + default : + if (strlen(pre)==0 && bold) { + print(boldon); + } + break; + } + + print(pre); + k+=strlen(pre); + + if (k==0) { + printf("%12s",cmds[ndx].cmdstr); + k+=12; + } else { + if (strlen(pre)>0 && bold) { + print(boldon); + } + + printf("%s",cmds[ndx].cmdstr); + k+=strlen(cmds[ndx].cmdstr); + + if (strlen(pre)>0 && bold) { + print(boldoff); + } + } + + if (strlen(cmds[ndx].parms)) { + printch(sp); + k++; + } + + for (j=0; j<strlen(cmds[ndx].parms); j++) { + switch (cmds[ndx].parms[j]) { + case '%' : + lastmod=0; + break; + + case 'b' : + lastmod=8; + break; + case 'h' : + lastmod=16; + break; + case 'l' : + lastmod=32; + break; + + case 'd' : + case 'i' : { + switch (lastmod) { + case 0 : + case 16 : + print("int"); + k+=3; + break; + case 8 : + print("shortint"); + k+=8; + break; + case 32: + print("longint"); + k+=7; + break; + } + break; + } + + case 'u' : + case 'o' : + case 'x' : { + switch (lastmod) { + case 0 : + case 16 : + print("word"); + k+=4; + break; + case 8 : + print("byte"); + k+=4; + break; + case 32 : + print("dword"); + k+=5; + break; + } + + switch (cmds[ndx].parms[j]) { + case 'o' : + print("[o]"); + k+=3; + break; + case 'x' : + print("[h]"); + k+=3; + break; + } + + break; + } + + case 'e' : + case 'f' : + case 'g' : + print("float"); + k+=5; + break; + + case 'c' : + print("char"); + k+=4; + break; + + case 's' : + print("string"); + k+=6; + break; + + case ' ' : + printch(sp); + k++; + break; + } + } + + for (j=k; j<40; j++) printch(sp); + + switch (cmds[ndx].subs) { + case SUBSYSTEM : + if (ndx==subsystem) { + printf("- %s (active subsystem)%s",cmds[ndx].cmddescr,post); + } else { + printf("- %s (dormant subsystem)%s",cmds[ndx].cmddescr,post); + } + break; + case HIDDENSUB : + break; + case GLOBALCMD : + printf("- %s (global command)%s",cmds[ndx].cmddescr,post); + break; + default : + printf("- %s%s",cmds[ndx].cmddescr,post); + if (strlen(pre)==0 && bold) { + print(boldoff); + } + break; + } + + if (strlen(pre)>0 && strlen(cmds[ndx].parmdescr)) { + printf("Params: %s",cmds[ndx].parmdescr); + print("\r\n"); + } +} + +//------------------------------------------------------------------------------ +//----Wrappers +//------------------------------------------------------------------------------ + +void Cmdb::zeromemory(char *p,unsigned int siz) { + memset(p,'\0',siz); +} + +int Cmdb::stricmp (char *s1, char *s2) { + int i; + int len1,len2; + + len1=strlen(s1); + len2=strlen(s2); + + for (i = 0; (i<len1) && (i<len2); i++) { + if ( toupper (s1[i])<toupper(s2[i]) ) return (-1); + if ( toupper (s1[i])>toupper(s2[i]) ) return (+1); + } + + if (len1<len2) return (-1); + if (len1>len2) return (+1); + + return (0); +} + +//------------------------------------------------------------------------------
diff -r 37a7f1df057c -r 34c65d3f6da0 cmdb.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cmdb.h Wed Feb 15 09:50:53 2012 +0000 @@ -0,0 +1,1021 @@ +/* mbed Command Interpreter Library + * Copyright (c) 2011 wvd_vegt + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef MBED_CMDB_H +#define MBED_CMDB_H + +#include "mbed.h" + +#include <vector> +#include <limits> + +//------------------------------------------------------------------------------ + +/** Max size of an Ansi escape code. + */ +#define MAX_ESC_LEN 5 + +/** Max (strlen) of a Param. + */ +#define MAX_PARM_LEN 32 + +/** Max eight parms. + */ +#define MAX_ARGS 8 + +/** Max 132 characters commandline. + */ +#define MAX_CMD_LEN 132 + +/** 'Show' hidden subsystems and commands. + */ +#define SHOWHIDDEN + +/** Enable macro commands. + */ +#define ENABLEMACROS + +/** Enable statemachine. + * + * Used to implement a series of commands running at power-up. + * + * @note Not Implemented! + */ +#undef STATEMACHINE + +/** Enable subsystem prompts. + * + * When defined, prompts will reflect the SubSystem. + */ +#define SUBSYSTEMPROMPTS + +//------------------------------------------------------------------------------ + +/** 8 bit limits. + * + * @see http://www.daniweb.com/forums/thread18963.html + */ +#define MIN_BYTE std::numeric_limits<unsigned char>::min() + +/** 8 bit limits. + * + * @see http://www.daniweb.com/forums/thread18963.html + */ +#define MAX_BYTE std::numeric_limits<unsigned char>::max() + +/** 8 bit limits. + * + * @see http://www.daniweb.com/forums/thread18963.html + */ +#define MIN_CHAR std::numeric_limits<signed char>::min() + +/** 8 bit limits. + * + * @see http://www.daniweb.com/forums/thread18963.html + */ +#define MAX_CHAR std::numeric_limits<signed char>::max() + +/** 16 bit limits. + * + * @see http://www.daniweb.com/forums/thread18963.html + */ +#define MIN_SHORT std::numeric_limits<short int>::min() + +/** 16 bit limits. + * + * @see http://www.daniweb.com/forums/thread18963.html + */ +#define MAX_SHORT std::numeric_limits<short int>::max() + +/** 16 bit limits. + * + * @see http://www.daniweb.com/forums/thread18963.html + */ +#define MIN_USHORT std::numeric_limits<unsigned short int>::min() + +/** 16 bit limits. + * + * @see http://www.daniweb.com/forums/thread18963.html + */ +#define MAX_USHORT std::numeric_limits<unsigned short int>::max() + +/** 32 bit limits. + * + * @see http://www.daniweb.com/forums/thread18963.html + */ +#define MIN_INT std::numeric_limits<int>::min() +#define MAX_INT std::numeric_limits<int>::max() + +/** 32 bit limits. + * + * @see http://www.daniweb.com/forums/thread18963.html + */ +#define MIN_UINT std::numeric_limits<unsigned int>::min() +#define MAX_UINT std::numeric_limits<unsigned int>::max() + +/** 32 bit limits. + * + * @see http://www.daniweb.com/forums/thread18963.html + */ +#define MIN_LONG std::numeric_limits<long>::min() +#define MAX_LONG std::numeric_limits<long>::max() + +//------------------------------------------------------------------------------ + +/** Description of a command. + */ +struct cmd { +public: + const char *cmdstr; + int subs; + int cid; + const char *parms; + const char *cmddescr; + const char *parmdescr; +}; + +//------------------------------------------------------------------------------ + +/** Cr. + */ +static const char cr = '\r'; + +/** Lf. + */ +static const char lf = '\n'; + +/** Bell. + */ +static const char bell = '\7'; + +/** Escape. + */ +static const char esc = '\033'; + +/** Space. + */ +static const char sp = ' '; + +/** CrLf. + */ +static const char crlf[] = "\r\n"; + +/** Backspace that 'tries' to wipe the last character. + */ +static const char bs[] = "\b \b"; + +/** VT100 Bold Command. + */ +static const char boldon[] = "\033[1m"; + +/** VT100 Normal Command. + */ +static const char boldoff[] = "\033[0m"; + +/** VT100 Cls Command. + */ +static const char cls[] = "\033[2J"; + +/** VT100 Home Command. + */ +static const char home[] = "\033[H"; + +/** The default command prompt. + */ +static const char PROMPT[] = "CMD>"; + +//------------------------------------------------------------------------------ + +/** Subsystem Id for a Subsystem. + */ +#define SUBSYSTEM -1 + +/** Subsystem Id for a Global Command (always available). + */ +#define GLOBALCMD -2 + +/** Subsystem Id for a Hidden Subsystem (ommitted from help). + */ +#define HIDDENSUB -3 + +/** Predefined Dump Command. + */ +#define CID_COMMANDS 9989 + +/** Predefined Boot Command. + */ +#define CID_BOOT 9990 + +/** Predefined Macro Command. + * + * This command will take a string with spaces replace by _ and cr replace by | for later replay with run. + */ +#define CID_MACRO 9991 + +/** Predefined Macro Command. + * + * This command replay a macro. + */ +#define CID_RUN 9992 + +/** Predefined Macro Command. + * + * This command print the current macro. + */ +#define CID_MACROS 9993 + +/** Predefined Echo Command. + * + * This command turn echo on or off. + */ +#define CID_ECHO 9994 + +/** Predefined VT100 Bold Command. + * + * This command turn VT100 bold usage on or off. + */ +#define CID_BOLD 9995 + +/** Predefined VT100 Cls Command. + * + * This command will clear the screen. + */ +#define CID_CLS 9996 + +/** Predefined Idle Command. + * + * This command will return to the global command level, leaving the active subsystem. + */ +#define CID_IDLE 9997 + +/** Predefined Help Command. + * + * This command will either print all active command (without parameters) or a more detailed + * help for a command passed as parameter. + */ +#define CID_HELP 9998 + +/** Predefided Semi Command. + * + * CID_LAST only functions as a special Commend Id to signal unknown commands. + */ +#define CID_LAST 9999 + +//------------------------------------------------------------------------------ + +/** The Boot Command. + * + * @note: this command can be used to list all commands in Windows ini file format for host processing. + * + * Optional. + */ +static const cmd COMMANDS = {"Commands",GLOBALCMD,CID_COMMANDS,"","Dump Commands"}; + +/** The Boot Command. + * + * Optional. + */ +static const cmd BOOT = {"Boot",GLOBALCMD,CID_BOOT,"","Boot mBed"}; + +/** The Macro Command. + * + * Optional. + */ +static const cmd MACRO = {"Macro",GLOBALCMD,CID_MACRO,"%s","Define macro (sp->_, cr->|)","command(s)"}; + +/** The Run Command. + * + * Optional. + */ +static const cmd RUN = {"Run",GLOBALCMD,CID_RUN,"","Run a macro"}; + +/** The Macros Command. + * + * Optional. + */ +static const cmd MACROS = {"Macros",GLOBALCMD,CID_MACROS,"","List macro(s)"}; + +/** The Echo Command. + * + * Optional. + */ +static const cmd ECHO = {"Echo",GLOBALCMD,CID_ECHO,"%bu","Echo On|Off (1|0)","state"}; + +/** The Bold Command. + * + * Optional. + */ +static const cmd BOLD = {"Bold",GLOBALCMD,CID_BOLD,"%bu","Bold On|Off (1|0)","state"}; + +/** The Cls Command. + * + * Optional. + */ +static const cmd CLS = {"Cls",GLOBALCMD,CID_CLS,"","Clears the terminal screen"}; + +/** The Idle Command. + * + * Mandatory if you use subsystems. + */ +static const cmd IDLE = {"Idle",GLOBALCMD,CID_IDLE,"","Deselect Subsystems"}; + +/** The Help Command. + * + * Mandatory. + */ +static const cmd HELP = {"Help",GLOBALCMD,CID_HELP,"%s","Help"}; + +//------------------------------------------------------------------------------ + +/** We'll only define the 4 cursor codes at the moment. + */ +#define ESC_TBL_LEN 4 + +/** Escape code definition struct. + */ +struct esc { + char *escstr; + int id; +}; + +/** The Escape Code Id's. + */ +enum { + EID_CURSOR_UP, + EID_CURSOR_DOWN, + EID_CURSOR_RIGHT, + EID_CURSOR_LEFT, + EID_LAST +}; + +/** The Escape Codes Table. + */ +static const struct esc esc_tbl [ESC_TBL_LEN] = { + { "\033[A", EID_CURSOR_UP }, + { "\033[B", EID_CURSOR_DOWN }, + { "\033[C", EID_CURSOR_RIGHT }, + { "\033[D", EID_CURSOR_LEFT }, +}; + +//------------------------------------------------------------------------------ + +/** The Command Interpreter Version. + */ +#define CMDB_VERSION 0.81 + +//------------------------------------------------------------------------------ + +/** Command Interpreter class. + * + * Steps to take: + * + * 1) Create a std::vector<cmd> and fill it with at least + * the mandatory commands IDLE and HELP. + * + * 2) Create an Cmdb class instance and pass it the vector, + * a Serial port object like Serial serial(USBTX, USBRX); + * and finally a command dispatcher function. + * + * 3) Feed the interpreter with characters received from your serial port. + * Note: Cmdb self does not retrieve input it must be handed to it. + * It implements basic members for checking/reading the serial port. + * + * 4) Handle commands added by the application by the Cid and parameters passed. + * + * Note: Predefined commands and all subsystems transitions are handled by the internal dispatcher. + * So the passed dispatcher only has to handle user/application defined commands'. + * + * @see main.cpp for a demo. + */ +class Cmdb { +public: + /** Create a Command Interpreter. + * + * @see http://www.newty.de/fpt/fpt.html#chapter2 for function pointers. + * @see http://stackoverflow.com/questions/9410/how-do-you-pass-a-function-as-a-parameter-in-c + * @see http://www.daniweb.com/forums/thread293338.html + * + * @param serial a Serial port used for communication. + * @param cmds a vector with the command table. + */ + Cmdb(const Serial& _serial, std::vector<cmd>& _cmds, void (*_callback)(Cmdb&,int) ); + + /** The version of the Command Interpreter. + * + * returns the version. + */ + static float version() { + return CMDB_VERSION; + } + + /** NULL is used as No Comment Value. + */ + static const char* NoComment; + + /** Column 72 is used as Default Comment Starting Position. + */ + static int DefComPos; + + /** Checks if the macro buffer has any characters left. + * + * @returns true if any characters left. + */ + bool macro_hasnext(); + + /** Gets the next character from the macro buffer and + * advances the macro buffer pointer. + * + * @note Do not call if no more characters are left! + * + * @returns the next character. + */ + char macro_next(); + + /** Gets the next character from the macro buffer + * but does not advance the macro buffer pointer. + * + * @note Do not call if no more characters are left! + * + * @returns the next character. + */ + char macro_peek(); + + /** Resets the macro buffer and macro buffer pointer. + * + */ + void macro_reset(); + + /** Checks if the serial port has any characters + * left to read by calling serial.readable(). + * + * @returns true if any characters available. + */ + bool hasnext(); + + /** Gets the next character from the serial port by + * calling serial.getc(). + * + * Do not call if no characters are left! + * + * @returns the next character. + */ + char next(); + + /** Add a character to the command being processed. + * If a cr is added, the command is parsed and executed if possible + * If supported special keys are encountered (like backspace, delete and cursor up) they are processed. + * + * @param c the character to add. + * + * @returns true if a command was recognized and executed. + */ + bool scan(const char c); + + /** printf substitute using the serial parameter passed to the constructor. + * + * @see http://www.cplusplus.com/reference/clibrary/cstdio/printf/ + * + * @parm format the printf format string. + * @parm ... optional paramaters to be merged into the format string. + * + * @returns the printf return value. + */ + int printf(const char *format, ...); + + /** print is simply printf without parameters using the serial parameter passed to the constructor. + * + * @parm msg the string to print. + * + * @returns the printf return value. + */ + int print(const char *msg); + + /** println is simply printf without parameters using the serial parameter passed to the constructor. + * + * @parm msg the string to print followed by a crlf. + * + * @returns the printf return value. + */ + int println(const char *msg); + + /** printch is simply putc subsitute using the serial parameter passed to the constructor. + * + * @parm msg the string to print. + * + * @returns the printf return value. + */ + char printch(const char ch); + + /** printsection prints an inifile Section Header + * like: + * + * [Section]\r\n + * + * Usage: cmdb.printsection("GP"); + * + * @parm section the section to print. + * + * @returns the printf return value. + */ + int printsection(const char *section); + + /** printmsg prints an inifile Msg Key=Value pair. + * like: + * + * Msg={msg}\r\n + * + * Usage: cmdb.printmsg("Validation successfull"); + * + * @parm msg the msg to print. + * + * @returns the printf return value. + */ + int printmsg(const char *msg); + + /** printerror prints an inifile Error Section Header and Error Msg Key=Value pair. + * like: + * + * [Error]\r\nmsg={errormsg}\r\n + * + * Usage: cmdb.printerror("Data Size Incorrect"); + * + * @parm errormsg the error msg to print. + * + * @returns the printf return value. + */ + int printerror(const char *errormsg); + + /** printerror prints an inifile Error Section Header and Error Msg Key=Value pair. + * like: + * + * [Error]\r\nmsg={errormsg}\r\n + * + * Usage: cmdb.printerrorf("Data Size Incorrect %d", 15); + * + * @parm format the error msg to print. + * @parm parameter to print. + * + * @returns the printf return value. + */ + int printerrorf(const char *format, ...); + + /** printvalue prints an inifile Key/Value Pair + * like: + * + * Key=Value ;comment\r\n + * + * Note: the Comment is (if present) located at position 72. + * + * Usage: cmdb.printvaluef("Value", Cmdb::DefComPos, "Hex", "0x%8.8X", LPC_RTC->GPREG0); + * + * @parm key the key to print. + * @parm comment the comment to print. + * @parm width the location of the comment to print. + * @parm format the value to print. + * @parm parameter to print. + * + * @returns the printf return value. + */ + int printvaluef(const char *key, const int width, const char *comment, const char *format, ...); + + /** printvalue prints an inifile Key/Value Pair + * like: + * + * Key=Value\r\n + * + * Usage: cmdb.printvaluef("Value", "0x%8.8X", LPC_RTC->GPREG0); + * + * @parm key the key to print. + * @parm format the value to print. + * @parm parameter to print. + * + * @returns the printf return value. + */ + int printvaluef(const char *key, const char *format, ...); + + /** printvalue prints an inifile Key/Value Pair + * like: + * + * Key=Value ;comment\r\n + * + * Note the Comment is (if present) located at position 72. + * + * @parm key the key to print. + * @parm value the value to print. + * @parm comment the comment to print. + * @parm width the location of the comment to print. + * + * @returns the printf return value. + */ + int printvalue(const char *key, const char *value, const char *comment = NoComment, const int width = DefComPos); + + int printcomment(const char *comment, const int width = DefComPos); + +//------------------------------------------------------------------------------ + + /** Initializes the parser (called by the constructor). + * + * @parm full if true the macro buffer is also cleared else only the command interpreter is reset. + */ + void init(const char full); + +//------------------------------------------------------------------------------ +//----These helper functions retieve parameters in the correct format. +//------------------------------------------------------------------------------ + + /** Typecasts parameter ndx to a bool. + * + * mask: %bu + * + * @parm the parameter index + * + * @return a bool + */ + bool BOOLPARM(int ndx) { + return parms[ndx].val.uc!=0; + } + + /** Typecasts parameter ndx to a byte/unsigned char. + * + * mask: %bu + * + * @parm the parameter index + * + * @return a byte/unsigned char + */ + unsigned char BYTEPARM(int ndx) { + return parms[ndx].val.uc; + } + + /** Typecasts parameter ndx to a char. + * + * mask: %c + * + * @parm the parameter index + * + * @return a char + */ + char CHARPARM(int ndx) { + return parms[ndx].val.c; + } + + /** Typecasts parameter ndx to word/unsigned int. + * + * mask: %hu + * + * @parm the parameter index + * + * @return a word/unsigned int + */ + unsigned int WORDPARM(int ndx) { + return parms[ndx].val.ui; + } + + /** Typecasts parameter ndx to a unsigned int. + * + * mask: %u + * + * @parm the parameter index + * + * @return a unsigned int + */ + unsigned int UINTPARM(int ndx) { + return parms[ndx].val.ui; + } + + /** Typecasts parameter ndx to a int. + * + * mask: %i + * + * @parm the parameter index + * + * @return a int + */ + int INTPARM(int ndx) { + return parms[ndx].val.i; + } + + /** Typecasts parameter ndx to a bool. + * + * mask: %lu + * + * @parm the parameter index + * + * @return a bool + */ + unsigned long DWORDPARM(int ndx) { + return parms[ndx].val.ul; + } + + /** Typecasts parameter ndx to a long. + * + * mask: %li + * + * @parm the parameter index + * + * @return a long + */ + long LONGPARM(int ndx) { + return parms[ndx].val.l; + } + + /** Typecasts parameter ndx to a float. + * + * mask: %f + * + * @parm the parameter index + * + * @return a float + */ + float FLOATPARM(int ndx) { + return parms[ndx].val.f; + } + + /** Typecasts parameter ndx to a string. + * + * @note spaces are not allowed as it makes parsing so much harder. + * + * mask: %s + * + * @parm the parameter index + * + * @return a string + */ + char* STRINGPARM(int ndx) { + return parms[ndx].val.s; + } + + bool present(char *cmdstr) { + return cmdid_search(cmdstr)!=CID_LAST; + } + + void replace(std::vector<cmd>& newcmds) { + cmds.assign(newcmds.begin(), newcmds.end()); + } + + + int indexof(int cid) { + return cmdid_index(cid); + } + + //FAILS... + /* + void insert(int cid, cmd newcmd) { + //Add Command (update our original and then assign/replace cmdb's copy)... + vector<cmd>::iterator iter; + + std::vector<cmd> newcmds = std::vector<cmd>(cmds); + + iter = newcmds.begin(); + + newcmds.insert(iter+indexof(cid)+1,newcmd); + + replace(newcmds); + + printf("Index: %d\r\n", ndx); + + print("check #1\r\n"); + print("check #2\r\n"); + + vector<cmd>::iterator it; + it=newcmds.begin(); + + print("check #3\r\n"); + ndx++; + newcmds.insert(it, newcmd); + print("check #4\r\n"); + + //cmds.push_back(c1); + cmds.assign(newcmds.begin(), newcmds.end()); + } + */ + +private: + + /** Internal Serial Port Storage. + */ + Serial serial; + + /** Internal Command Table Vector Storage. + * + * @see http://www.cplusplus.com/reference/stl/vector/ + */ + std::vector<cmd> cmds; + + /** C callback function + * + * @see See http://www.newty.de/fpt/fpt.html#chapter2 for function pointers. + * + * C++ member equivalent would be void (Cmdb::*callback)(Cmdb&,int); + */ + void (*user_callback)(Cmdb&,int); + + /** Searches the escape code list for a match. + * + * @param char* escstr the escape code to lookup. + * + * @returns the index of the escape code or -1. + */ + int escid_search(char *escstr); + + /** Checks if the command table for a match. + * + * @param char* cmdstr the command to lookup. + * + * @returns the id of the command or -1. + */ + int cmdid_search(char *cmdstr); + + /** Converts an command id to an index of the command table. + * + * @param cmdid the command id to lookup. + * + * @returns the index of the command or -1. + */ + int cmdid_index(int cmdid); + + /** Writes a prompt to the serial port. + * + */ + void prompt(void); + + /** Called by cmd_dispatch it parses the command against the command table. + * + * @param cmd the command and paramaters to parse. + * + * @returns the id of the parsed command. + */ + int parse(char *cmd); + + /** Called by scan it processes the arguments and dispatches the command. + * + * Note: This member calls the callback callback function. + * + * @param cmd the command to dispatch. + */ + void cmd_dispatcher(char *cmd); + + /** Generates Help from the command table and prints it. + * + * @param pre leading text + * @param ndx the index of the command in the command table. + * @param post trailing text. + */ + void cmd_help(char *pre, int ndx, char *post); + + /** Dumps all commands in ini file format. + */ + void cmd_dump(); + + /** memset wrapper. + * + * @param p The string to be cleared. + * @param siz The string size. + */ + void zeromemory(char *p,unsigned int siz); + + /** Case insensitive compare. + * + * @see strcmp. + * + * @param s1 + * @param s2 the second string to compare. + * + * @returns 0 if s1=s2, -1 if s1<s2 or +1 if s1>s2. + */ + int stricmp (char *s1, char *s2); + + /** Internal Echo Flag Storage. + */ + bool echo; + + /** Internal VT100 Bold Flag Storage. + */ + bool bold; + + /** Internal Command Table Length Storage. + */ + //int CMD_TBL_LEN; + + //Macro's. + /** Internal Macro Pointer. + */ + int macro_ptr; + + /** Internal Macro Buffer. + */ + char macro_buf[1 + MAX_CMD_LEN]; + + /** Used for parsing parameters. + */ + enum parmtype { + PARM_UNUSED, //0 + + PARM_FLOAT, //1 (f) + + PARM_LONG, //2 (l/ul) + PARM_INT, //3 (i/ui) + PARM_SHORT, //4 (w/uw) + + PARM_CHAR, //5 (c/uc) + PARM_STRING //6 (s) + }; + + /** Used for parsing parameters. + */ + union value { + float f; + + unsigned long ul; + long l; + + int i; + unsigned int ui; + + short w; + unsigned short uw; + + char c; + unsigned char uc; + + char s[MAX_PARM_LEN]; + }; + + /** Used for parsing parameters. + */ + struct parm { + enum parmtype type; + union value val; + }; + + //------------------------------------------------------------------------------ + //----Buffers & Storage. + //------------------------------------------------------------------------------ + + /** Command Buffer. + */ + char cmdbuf [1 + MAX_CMD_LEN]; // command buffer + + /** Command Buffer Pointer. + */ + char cmdndx; // command index + + /** Last Command Buffer (Used when pressing Cursor Up). + */ + char lstbuf [1 + MAX_CMD_LEN]; // last command buffer + + /** Escape Code Buffer. + */ + char escbuf [1 + MAX_ESC_LEN]; + + /** Escape Code Buffer Pointer. + */ + unsigned char escndx; + + /** Storage for Parsed Parameters + */ + struct parm parms[MAX_ARGS]; + + /** Parsed Parameters Pointer. + */ + int noparms; + + /** Current Selected Subsystem (-1 for Global). + */ + int subsystem; + + /** No of arguments found in command. + */ + int argcnt; + + /** No of arguments to find in parameter definition (Command Table). + */ + int argfnd; + + /** strtoXX() Error detection. + */ + int error; +}; + +extern "C" void mbed_reset(); + +#endif \ No newline at end of file
diff -r 37a7f1df057c -r 34c65d3f6da0 main.cpp --- a/main.cpp Wed Feb 15 09:45:41 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,98 +0,0 @@ -#include <vector> - -#include "mbed.h" -#include "cmdb.h" - -DigitalOut myled(LED1); - -//We'll be using the Usb Serial port -Serial serial(USBTX, USBRX); //tx, rx - -#define CID_TEST (int)1 -#define CID_INT (int)2 - -/** Sample User Command Dispatcher. - * - * @parm cmdb the command interpreter object. - * @parm cid the command id. - */ -void my_dispatcher(Cmdb& cmdb, int cid) { - cmdb.printf("my_dispatcher: cid=%d\r\n", cid); - - switch (cid) { - case CID_INT : - cmdb.printf("my_dispatcher: parm 0=%d\r\n",cmdb.INTPARM(0)); - break; - } -} - -static const cmd c1 = {"Test",SUBSYSTEM,CID_TEST,"" ,"* Test Subsystem"}; -static const cmd c2 = {"Int" ,CID_TEST ,CID_INT ,"%i","* Int as parameter" ,"dummy"}; - -int main() { - // Set the Baudrate. - serial.baud(115200); - - // Test the serial connection by - serial.printf("\r\n\r\nCmdb Command Interpreter Demo Version %0.2f.\r\n\r\n", Cmdb::version()); - - //Create a Command Table Vector. - std::vector<cmd> cmds; - - //Add some of our own first... - cmds.push_back(c1); //Test Subsystem is handled by Cmdb internally. - cmds.push_back(c2); //The Int Command is handled by our 'my_dispatcher' method. - - //Add some predefined... - cmds.push_back(COMMANDS); //Handled by Cmdb internally. - cmds.push_back(BOOT); //Handled by Cmdb internally. - - cmds.push_back(ECHO); //Handled by Cmdb internally. - cmds.push_back(BOLD); //Handled by Cmdb internally. - cmds.push_back(CLS); //Handled by Cmdb internally. - - cmds.push_back(MACRO); //Handled by Cmdb internally. - cmds.push_back(RUN); //Handled by Cmdb internally. - cmds.push_back(MACROS); //Handled by Cmdb internally. - - //Add some predefined and mandatory... - cmds.push_back(IDLE); //Handled by Cmdb internally. - cmds.push_back(HELP); //Handled by Cmdb internally. - - //Create and initialize the Command Interpreter. - Cmdb cmdb(serial, cmds, &my_dispatcher); - - cmdb.printf("%d=%d\r\n",cmds[0].subs,cmds[0].cid); - cmdb.printf("%d=%d\r\n",cmds[1].subs,cmds[1].cid); - - while (1) { - //Check for input... - if (cmdb.hasnext()==true) { - - //Supply input to Command Interpreter - if (cmdb.scan(cmdb.next())) { - - //Flash led when a command has been parsed and dispatched. - myled = 1; - wait(0.2); - - //cmdb.print("Command Parsed and Dispatched\r\n"); - - myled = 0; - wait(0.2); - } - } - - //For Macro Support we basically do the same but take characters from the macro buffer. - //Example Macro: Test|Int_42|Idle - while (cmdb.macro_hasnext()) { - //Get and process next character. - cmdb.scan(cmdb.macro_next()); - - //After the last character we need to add a cr to force execution. - if (!cmdb.macro_peek()) { - cmdb.scan(cr); - } - } - } -}
diff -r 37a7f1df057c -r 34c65d3f6da0 mbed.bld --- a/mbed.bld Wed Feb 15 09:45:41 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1 +0,0 @@ -http://mbed.org/users/mbed_official/code/mbed/builds/14f4805c468c