A Command Interpreter with support for used defined commands, subsystems, macros, help and parameter parsing.

Files at this revision

API Documentation at this revision

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

cmdb.cpp Show annotated file Show diff for this revision Revisions of this file
cmdb.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show diff for this revision Revisions of this file
mbed.bld Show diff for this revision Revisions of this file
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