Main Page | Modules | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

# cmdline.c

Go to the documentation of this file.

    00001 /*! \file cmdline.c \brief Command-Line Interface Library. */
    00002 //*****************************************************************************
    00003 //
    00004 // File Name    : 'cmdline.c'
    00005 // Title        : Command-Line Interface Library
    00006 // Author       : Pascal Stang - Copyright (C) 2003
    00007 // Created      : 2003.07.16
    00008 // Revised      : 2003.07.23
    00009 // Version      : 0.1
    00010 // Target MCU   : Atmel AVR Series
    00011 // Editor Tabs  : 4
    00012 //
    00013 // NOTE: This code is currently below version 1.0, and therefore is considered
    00014 // to be lacking in some functionality or documentation, or may not be fully
    00015 // tested.  Nonetheless, you can expect most functions to work.
    00016 //
    00017 // This code is distributed under the GNU Public License
    00018 //      which can be found at http://www.gnu.org/licenses/gpl.txt
    00019 //
    00020 //*****************************************************************************
    00021
    00022 //----- Include Files ---------------------------------------------------------
    00023 #include <avr/io.h>         // include I/O definitions (port names, pin names, etc)
    00024 #include <avr/interrupt.h>  // include interrupt support
    00025 #include <avr/pgmspace.h>   // include AVR program memory support
    00026 #include <string.h>         // include standard C string functions
    00027 #include <stdlib.h>         // include stdlib for string conversion functions
    00028
    00029 #include "global.h"     // include our global settings
    00030 #include "cmdline.h"
    00031
    00032 // include project-specific configuration
    00033 #include "cmdlineconf.h"
    00034
    00035 // defines
    00036 #define ASCII_BEL               0x07
    00037 #define ASCII_BS                0x08
    00038 #define ASCII_CR                0x0D
    00039 #define ASCII_LF                0x0A
    00040 #define ASCII_ESC               0x1B
    00041 #define ASCII_DEL               0x7F
    00042
    00043 #define VT100_ARROWUP           'A'
    00044 #define VT100_ARROWDOWN         'B'
    00045 #define VT100_ARROWRIGHT        'C'
    00046 #define VT100_ARROWLEFT         'D'
    00047
    00048 #define CMDLINE_HISTORY_SAVE    0
    00049 #define CMDLINE_HISTORY_PREV    1
    00050 #define CMDLINE_HISTORY_NEXT    2
    00051
    00052
    00053 // Global variables
    00054
    00055 // strings
    00056 u08 PROGMEM CmdlinePrompt[] = "cmd>";
    00057 u08 PROGMEM CmdlineNotice[] = "cmdline: ";
    00058 u08 PROGMEM CmdlineCmdNotFound[] = "command not found";
    00059
    00060 // command list
    00061 // -commands are null-terminated strings
    00062 static char CmdlineCommandList[CMDLINE_MAX_COMMANDS][CMDLINE_MAX_CMD_LENGTH];
    00063 // command function pointer list
    00064 static CmdlineFuncPtrType CmdlineFunctionList[CMDLINE_MAX_COMMANDS];
    00065 // number of commands currently registered
    00066 u08 CmdlineNumCommands;
    00067
    00068 u08 CmdlineBuffer[CMDLINE_BUFFERSIZE];
    00069 u08 CmdlineBufferLength;
    00070 u08 CmdlineBufferEditPos;
    00071 u08 CmdlineInputVT100State;
    00072 u08 CmdlineHistory[CMDLINE_HISTORYSIZE][CMDLINE_BUFFERSIZE];
    00073 CmdlineFuncPtrType CmdlineExecFunction;
    00074
    00075 // Functions
    00076
    00077 // function pointer to single character output routine
    00078 static void (*cmdlineOutputFunc)(unsigned char c);
    00079
    00080 void cmdlineInit(void)
    00081 {
    00082     // reset vt100 processing state
    00083     CmdlineInputVT100State = 0;
    00084     // initialize input buffer
    00085     CmdlineBufferLength = 0;
    00086     CmdlineBufferEditPos = 0;
    00087     // initialize executing function
    00088     CmdlineExecFunction = 0;
    00089     // initialize command list
    00090     CmdlineNumCommands = 0;
    00091 }
    00092
    00093 void cmdlineAddCommand(u08* newCmdString, CmdlineFuncPtrType newCmdFuncPtr)
    00094 {
    00095     // add command string to end of command list
    00096     strcpy(CmdlineCommandList[CmdlineNumCommands], newCmdString);
    00097     // add command function ptr to end of function list
    00098     CmdlineFunctionList[CmdlineNumCommands] = newCmdFuncPtr;
    00099     // increment number of registered commands
    00100     CmdlineNumCommands++;
    00101 }
    00102
    00103 void cmdlineSetOutputFunc(void (*output_func)(unsigned char c))
    00104 {
    00105     // set new output function
    00106     cmdlineOutputFunc = output_func;
    00107
    00108     // should we really do this?
    00109     // print a prompt
    00110     //cmdlinePrintPrompt();
    00111 }
    00112
    00113 void cmdlineInputFunc(unsigned char c)
    00114 {
    00115     u08 i;
    00116     // process the received character
    00117
    00118     // VT100 handling
    00119     // are we processing a VT100 command?
    00120     if(CmdlineInputVT100State == 2)
    00121     {
    00122         // we have already received ESC and [
    00123         // now process the vt100 code
    00124         switch(c)
    00125         {
    00126         case VT100_ARROWUP:
    00127             cmdlineDoHistory(CMDLINE_HISTORY_PREV);
    00128             break;
    00129         case VT100_ARROWDOWN:
    00130             cmdlineDoHistory(CMDLINE_HISTORY_NEXT);
    00131             break;
    00132         case VT100_ARROWRIGHT:
    00133             // if the edit position less than current string length
    00134             if(CmdlineBufferEditPos < CmdlineBufferLength)
    00135             {
    00136                 // increment the edit position
    00137                 CmdlineBufferEditPos++;
    00138                 // move cursor forward one space (no erase)
    00139                 cmdlineOutputFunc(ASCII_ESC);
    00140                 cmdlineOutputFunc('[');
    00141                 cmdlineOutputFunc(VT100_ARROWRIGHT);
    00142             }
    00143             else
    00144             {
    00145                 // else, ring the bell
    00146                 cmdlineOutputFunc(ASCII_BEL);
    00147             }
    00148             break;
    00149         case VT100_ARROWLEFT:
    00150             // if the edit position is non-zero
    00151             if(CmdlineBufferEditPos)
    00152             {
    00153                 // decrement the edit position
    00154                 CmdlineBufferEditPos--;
    00155                 // move cursor back one space (no erase)
    00156                 cmdlineOutputFunc(ASCII_BS);
    00157             }
    00158             else
    00159             {
    00160                 // else, ring the bell
    00161                 cmdlineOutputFunc(ASCII_BEL);
    00162             }
    00163             break;
    00164         default:
    00165             break;
    00166         }
    00167         // done, reset state
    00168         CmdlineInputVT100State = 0;
    00169         return;
    00170     }
    00171     else if(CmdlineInputVT100State == 1)
    00172     {
    00173         // we last received [ESC]
    00174         if(c == '[')
    00175         {
    00176             CmdlineInputVT100State = 2;
    00177             return;
    00178         }
    00179         else
    00180             CmdlineInputVT100State = 0;
    00181     }
    00182     else
    00183     {
    00184         // anything else, reset state
    00185         CmdlineInputVT100State = 0;
    00186     }
    00187
    00188     // Regular handling
    00189     if( (c >= 0x20) && (c < 0x7F) )
    00190     {
    00191         // character is printable
    00192         // is this a simple append
    00193         if(CmdlineBufferEditPos == CmdlineBufferLength)
    00194         {
    00195             // echo character to the output
    00196             cmdlineOutputFunc(c);
    00197             // add it to the command line buffer
    00198             CmdlineBuffer[CmdlineBufferEditPos++] = c;
    00199             // update buffer length
    00200             CmdlineBufferLength++;
    00201         }
    00202         else
    00203         {
    00204             // edit/cursor position != end of buffer
    00205             // we're inserting characters at a mid-line edit position
    00206             // make room at the insert point
    00207             CmdlineBufferLength++;
    00208             for(i=CmdlineBufferLength; i>CmdlineBufferEditPos; i--)
    00209                 CmdlineBuffer[i] = CmdlineBuffer[i-1];
    00210             // insert character
    00211             CmdlineBuffer[CmdlineBufferEditPos++] = c;
    00212             // repaint
    00213             cmdlineRepaint();
    00214             // reposition cursor
    00215             for(i=CmdlineBufferEditPos; i<CmdlineBufferLength; i++)
    00216                 cmdlineOutputFunc(ASCII_BS);
    00217         }
    00218     }
    00219     // handle special characters
    00220     else if(c == ASCII_CR)
    00221     {
    00222         // user pressed [ENTER]
    00223         // echo CR and LF to terminal
    00224         cmdlineOutputFunc(ASCII_CR);
    00225         cmdlineOutputFunc(ASCII_LF);
    00226         // add null termination to command
    00227         CmdlineBuffer[CmdlineBufferLength++] = 0;
    00228         CmdlineBufferEditPos++;
    00229         // command is complete, process it
    00230         cmdlineProcessInputString();
    00231         // reset buffer
    00232         CmdlineBufferLength = 0;
    00233         CmdlineBufferEditPos = 0;
    00234     }
    00235     else if(c == ASCII_BS)
    00236     {
    00237         if(CmdlineBufferEditPos)
    00238         {
    00239             // is this a simple delete (off the end of the line)
    00240             if(CmdlineBufferEditPos == CmdlineBufferLength)
    00241             {
    00242                 // destructive backspace
    00243                 // echo backspace-space-backspace
    00244                 cmdlineOutputFunc(ASCII_BS);
    00245                 cmdlineOutputFunc(' ');
    00246                 cmdlineOutputFunc(ASCII_BS);
    00247                 // decrement our buffer length and edit position
    00248                 CmdlineBufferLength--;
    00249                 CmdlineBufferEditPos--;
    00250             }
    00251             else
    00252             {
    00253                 // edit/cursor position != end of buffer
    00254                 // we're deleting characters at a mid-line edit position
    00255                 // shift characters down, effectively deleting
    00256                 CmdlineBufferLength--;
    00257                 CmdlineBufferEditPos--;
    00258                 for(i=CmdlineBufferEditPos; i<CmdlineBufferLength; i++)
    00259                     CmdlineBuffer[i] = CmdlineBuffer[i+1];
    00260                 // repaint
    00261                 cmdlineRepaint();
    00262                 // add space to clear leftover characters
    00263                 cmdlineOutputFunc(' ');
    00264                 // reposition cursor
    00265                 for(i=CmdlineBufferEditPos; i<(CmdlineBufferLength+1); i++)
    00266                     cmdlineOutputFunc(ASCII_BS);
    00267             }
    00268         }
    00269         else
    00270         {
    00271             // else, ring the bell
    00272             cmdlineOutputFunc(ASCII_BEL);
    00273         }
    00274     }
    00275     else if(c == ASCII_DEL)
    00276     {
    00277         // not yet handled
    00278     }
    00279     else if(c == ASCII_ESC)
    00280     {
    00281         CmdlineInputVT100State = 1;
    00282     }
    00283 }
    00284
    00285 void cmdlineRepaint(void)
    00286 {
    00287     u08* ptr;
    00288     u08 i;
    00289
    00290     // carriage return
    00291     cmdlineOutputFunc(ASCII_CR);
    00292     // print fresh prompt
    00293     cmdlinePrintPrompt();
    00294     // print the new command line buffer
    00295     i = CmdlineBufferLength;
    00296     ptr = CmdlineBuffer;
    00297     while(i--) cmdlineOutputFunc(*ptr++);
    00298 }
    00299
    00300 void cmdlineDoHistory(u08 action)
    00301 {
    00302     switch(action)
    00303     {
    00304     case CMDLINE_HISTORY_SAVE:
    00305         // copy CmdlineBuffer to history if not null string
    00306         if( strlen(CmdlineBuffer) )
    00307             strcpy(CmdlineHistory[0], CmdlineBuffer);
    00308         break;
    00309     case CMDLINE_HISTORY_PREV:
    00310         // copy history to current buffer
    00311         strcpy(CmdlineBuffer, CmdlineHistory[0]);
    00312         // set the buffer position to the end of the line
    00313         CmdlineBufferLength = strlen(CmdlineBuffer);
    00314         CmdlineBufferEditPos = CmdlineBufferLength;
    00315         // "re-paint" line
    00316         cmdlineRepaint();
    00317         break;
    00318     case CMDLINE_HISTORY_NEXT:
    00319         break;
    00320     }
    00321 }
    00322
    00323 void cmdlineProcessInputString(void)
    00324 {
    00325     u08 cmdIndex;
    00326     u08 i=0;
    00327
    00328     // save command in history
    00329     cmdlineDoHistory(CMDLINE_HISTORY_SAVE);
    00330
    00331     // find the end of the command (excluding arguments)
    00332     // find first whitespace character in CmdlineBuffer
    00333     while( !((CmdlineBuffer[i] == ' ') || (CmdlineBuffer[i] == 0)) ) i++;
    00334
    00335     if(!i)
    00336     {
    00337         // command was null or empty
    00338         // output a new prompt
    00339         cmdlinePrintPrompt();
    00340         // we're done
    00341         return;
    00342     }
    00343
    00344     // search command list for match with entered command
    00345     for(cmdIndex=0; cmdIndex<CmdlineNumCommands; cmdIndex++)
    00346     {
    00347         if( !strncmp(CmdlineCommandList[cmdIndex], CmdlineBuffer, i) )
    00348         {
    00349             // user-entered command matched a command in the list (database)
    00350             // run the corresponding function
    00351             CmdlineExecFunction = CmdlineFunctionList[cmdIndex];
    00352             // new prompt will be output after user function runs
    00353             // and we're done
    00354             return;
    00355         }
    00356     }
    00357
    00358     // if we did not get a match
    00359     // output an error message
    00360     cmdlinePrintError();
    00361     // output a new prompt
    00362     cmdlinePrintPrompt();
    00363 }
    00364
    00365 void cmdlineMainLoop(void)
    00366 {
    00367     // do we have a command/function to be executed
    00368     if(CmdlineExecFunction)
    00369     {
    00370         // run it
    00371         CmdlineExecFunction();
    00372         // reset
    00373         CmdlineExecFunction = 0;
    00374         // output new prompt
    00375         cmdlinePrintPrompt();
    00376     }
    00377 }
    00378
    00379 void cmdlinePrintPrompt(void)
    00380 {
    00381     // print a new command prompt
    00382     u08* ptr = CmdlinePrompt;
    00383     while(pgm_read_byte(ptr)) cmdlineOutputFunc( pgm_read_byte(ptr++) );
    00384 }
    00385
    00386 void cmdlinePrintError(void)
    00387 {
    00388     u08 * ptr;
    00389
    00390     // print a notice header
    00391     // (u08*) cast used to avoid compiler warning
    00392     ptr = (u08*)CmdlineNotice;
    00393     while(pgm_read_byte(ptr)) cmdlineOutputFunc( pgm_read_byte(ptr++) );
    00394
    00395     // print the offending command
    00396     ptr = CmdlineBuffer;
    00397     while((*ptr) && (*ptr != ' ')) cmdlineOutputFunc(*ptr++);
    00398
    00399     cmdlineOutputFunc(':');
    00400     cmdlineOutputFunc(' ');
    00401
    00402     // print the not-found message
    00403     // (u08*) cast used to avoid compiler warning
    00404     ptr = (u08*)CmdlineCmdNotFound;
    00405     while(pgm_read_byte(ptr)) cmdlineOutputFunc( pgm_read_byte(ptr++) );
    00406
    00407     cmdlineOutputFunc('\r');
    00408     cmdlineOutputFunc('\n');
    00409 }
    00410
    00411 // argument retrieval commands
    00412
    00413 // return string pointer to argument [argnum]
    00414 u08* cmdlineGetArgStr(u08 argnum)
    00415 {
    00416     // find the offset of argument number [argnum]
    00417     u08 idx=0;
    00418     u08 arg;
    00419
    00420     // find the first non-whitespace character
    00421     while( (CmdlineBuffer[idx] != 0) && (CmdlineBuffer[idx] == ' ')) idx++;
    00422
    00423     // we are at the first argument
    00424     for(arg=0; arg<argnum; arg++)
    00425     {
    00426         // find the next whitespace character
    00427         while( (CmdlineBuffer[idx] != 0) && (CmdlineBuffer[idx] != ' ')) idx++;
    00428         // find the first non-whitespace character
    00429         while( (CmdlineBuffer[idx] != 0) && (CmdlineBuffer[idx] == ' ')) idx++;
    00430     }
    00431     // we are at the requested argument or the end of the buffer
    00432     return &CmdlineBuffer[idx];
    00433 }
    00434
    00435 // return argument [argnum] interpreted as a decimal integer
    00436 long cmdlineGetArgInt(u08 argnum)
    00437 {
    00438     char* endptr;
    00439     return strtol(cmdlineGetArgStr(argnum), &endptr, 10);
    00440 }
    00441
    00442 // return argument [argnum] interpreted as a hex integer
    00443 long cmdlineGetArgHex(u08 argnum)
    00444 {
    00445     char* endptr;
    00446     return strtol(cmdlineGetArgStr(argnum), &endptr, 16);
    00447 }