mirror of
https://github.com/arduino/Arduino.git
synced 2024-11-30 11:24:12 +01:00
449 lines
12 KiB
C
Executable File
449 lines
12 KiB
C
Executable File
/*! \file cmdline.c \brief Command-Line Interface Library. */
|
|
//*****************************************************************************
|
|
//
|
|
// File Name : 'cmdline.c'
|
|
// Title : Command-Line Interface Library
|
|
// Author : Pascal Stang - Copyright (C) 2003
|
|
// Created : 2003.07.16
|
|
// Revised : 2003.07.23
|
|
// Version : 0.1
|
|
// Target MCU : Atmel AVR Series
|
|
// Editor Tabs : 4
|
|
//
|
|
// NOTE: This code is currently below version 1.0, and therefore is considered
|
|
// to be lacking in some functionality or documentation, or may not be fully
|
|
// tested. Nonetheless, you can expect most functions to work.
|
|
//
|
|
// This code is distributed under the GNU Public License
|
|
// which can be found at http://www.gnu.org/licenses/gpl.txt
|
|
//
|
|
//*****************************************************************************
|
|
|
|
//----- Include Files ---------------------------------------------------------
|
|
#include <avr/io.h> // include I/O definitions (port names, pin names, etc)
|
|
#include <avr/signal.h> // include "signal" names (interrupt names)
|
|
#include <avr/interrupt.h> // include interrupt support
|
|
#include <avr/pgmspace.h> // include AVR program memory support
|
|
#include <string.h> // include standard C string functions
|
|
#include <stdlib.h> // include stdlib for string conversion functions
|
|
|
|
#include "global.h" // include our global settings
|
|
#include "cmdline.h"
|
|
|
|
// include project-specific configuration
|
|
#include "cmdlineconf.h"
|
|
|
|
// defines
|
|
#define ASCII_BEL 0x07
|
|
#define ASCII_BS 0x08
|
|
#define ASCII_CR 0x0D
|
|
#define ASCII_LF 0x0A
|
|
#define ASCII_ESC 0x1B
|
|
#define ASCII_DEL 0x7F
|
|
|
|
#define VT100_ARROWUP 'A'
|
|
#define VT100_ARROWDOWN 'B'
|
|
#define VT100_ARROWRIGHT 'C'
|
|
#define VT100_ARROWLEFT 'D'
|
|
|
|
#define CMDLINE_HISTORY_SAVE 0
|
|
#define CMDLINE_HISTORY_PREV 1
|
|
#define CMDLINE_HISTORY_NEXT 2
|
|
|
|
|
|
// Global variables
|
|
|
|
// strings
|
|
u08 PROGMEM CmdlinePrompt[] = "cmd>";
|
|
u08 PROGMEM CmdlineNotice[] = "cmdline: ";
|
|
u08 PROGMEM CmdlineCmdNotFound[] = "command not found";
|
|
|
|
// command list
|
|
// -commands are null-terminated strings
|
|
static char CmdlineCommandList[CMDLINE_MAX_COMMANDS][CMDLINE_MAX_CMD_LENGTH];
|
|
// command function pointer list
|
|
static CmdlineFuncPtrType CmdlineFunctionList[CMDLINE_MAX_COMMANDS];
|
|
// number of commands currently registered
|
|
u08 CmdlineNumCommands;
|
|
|
|
u08 CmdlineBuffer[CMDLINE_BUFFERSIZE];
|
|
u08 CmdlineBufferLength;
|
|
u08 CmdlineBufferEditPos;
|
|
u08 CmdlineInputVT100State;
|
|
u08 CmdlineHistory[CMDLINE_HISTORYSIZE][CMDLINE_BUFFERSIZE];
|
|
CmdlineFuncPtrType CmdlineExecFunction;
|
|
|
|
// Functions
|
|
|
|
// function pointer to single character output routine
|
|
static void (*cmdlineOutputFunc)(unsigned char c);
|
|
|
|
void cmdlineInit(void)
|
|
{
|
|
// reset vt100 processing state
|
|
CmdlineInputVT100State = 0;
|
|
// initialize input buffer
|
|
CmdlineBufferLength = 0;
|
|
CmdlineBufferEditPos = 0;
|
|
// initialize executing function
|
|
CmdlineExecFunction = 0;
|
|
// initialize command list
|
|
CmdlineNumCommands = 0;
|
|
}
|
|
|
|
void cmdlineAddCommand(u08* newCmdString, CmdlineFuncPtrType newCmdFuncPtr)
|
|
{
|
|
// add command string to end of command list
|
|
strcpy(CmdlineCommandList[CmdlineNumCommands], newCmdString);
|
|
// add command function ptr to end of function list
|
|
CmdlineFunctionList[CmdlineNumCommands] = newCmdFuncPtr;
|
|
// increment number of registered commands
|
|
CmdlineNumCommands++;
|
|
}
|
|
|
|
void cmdlineSetOutputFunc(void (*output_func)(unsigned char c))
|
|
{
|
|
// set new output function
|
|
cmdlineOutputFunc = output_func;
|
|
|
|
// should we really do this?
|
|
// print a prompt
|
|
//cmdlinePrintPrompt();
|
|
}
|
|
|
|
void cmdlineInputFunc(unsigned char c)
|
|
{
|
|
u08 i;
|
|
// process the received character
|
|
|
|
// VT100 handling
|
|
// are we processing a VT100 command?
|
|
if(CmdlineInputVT100State == 2)
|
|
{
|
|
// we have already received ESC and [
|
|
// now process the vt100 code
|
|
switch(c)
|
|
{
|
|
case VT100_ARROWUP:
|
|
cmdlineDoHistory(CMDLINE_HISTORY_PREV);
|
|
break;
|
|
case VT100_ARROWDOWN:
|
|
cmdlineDoHistory(CMDLINE_HISTORY_NEXT);
|
|
break;
|
|
case VT100_ARROWRIGHT:
|
|
// if the edit position less than current string length
|
|
if(CmdlineBufferEditPos < CmdlineBufferLength)
|
|
{
|
|
// increment the edit position
|
|
CmdlineBufferEditPos++;
|
|
// move cursor forward one space (no erase)
|
|
cmdlineOutputFunc(ASCII_ESC);
|
|
cmdlineOutputFunc('[');
|
|
cmdlineOutputFunc(VT100_ARROWRIGHT);
|
|
}
|
|
else
|
|
{
|
|
// else, ring the bell
|
|
cmdlineOutputFunc(ASCII_BEL);
|
|
}
|
|
break;
|
|
case VT100_ARROWLEFT:
|
|
// if the edit position is non-zero
|
|
if(CmdlineBufferEditPos)
|
|
{
|
|
// decrement the edit position
|
|
CmdlineBufferEditPos--;
|
|
// move cursor back one space (no erase)
|
|
cmdlineOutputFunc(ASCII_BS);
|
|
}
|
|
else
|
|
{
|
|
// else, ring the bell
|
|
cmdlineOutputFunc(ASCII_BEL);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
// done, reset state
|
|
CmdlineInputVT100State = 0;
|
|
return;
|
|
}
|
|
else if(CmdlineInputVT100State == 1)
|
|
{
|
|
// we last received [ESC]
|
|
if(c == '[')
|
|
{
|
|
CmdlineInputVT100State = 2;
|
|
return;
|
|
}
|
|
else
|
|
CmdlineInputVT100State = 0;
|
|
}
|
|
else
|
|
{
|
|
// anything else, reset state
|
|
CmdlineInputVT100State = 0;
|
|
}
|
|
|
|
// Regular handling
|
|
if( (c >= 0x20) && (c < 0x7F) )
|
|
{
|
|
// character is printable
|
|
// is this a simple append
|
|
if(CmdlineBufferEditPos == CmdlineBufferLength)
|
|
{
|
|
// echo character to the output
|
|
cmdlineOutputFunc(c);
|
|
// add it to the command line buffer
|
|
CmdlineBuffer[CmdlineBufferEditPos++] = c;
|
|
// update buffer length
|
|
CmdlineBufferLength++;
|
|
}
|
|
else
|
|
{
|
|
// edit/cursor position != end of buffer
|
|
// we're inserting characters at a mid-line edit position
|
|
// make room at the insert point
|
|
CmdlineBufferLength++;
|
|
for(i=CmdlineBufferLength; i>CmdlineBufferEditPos; i--)
|
|
CmdlineBuffer[i] = CmdlineBuffer[i-1];
|
|
// insert character
|
|
CmdlineBuffer[CmdlineBufferEditPos++] = c;
|
|
// repaint
|
|
cmdlineRepaint();
|
|
// reposition cursor
|
|
for(i=CmdlineBufferEditPos; i<CmdlineBufferLength; i++)
|
|
cmdlineOutputFunc(ASCII_BS);
|
|
}
|
|
}
|
|
// handle special characters
|
|
else if(c == ASCII_CR)
|
|
{
|
|
// user pressed [ENTER]
|
|
// echo CR and LF to terminal
|
|
cmdlineOutputFunc(ASCII_CR);
|
|
cmdlineOutputFunc(ASCII_LF);
|
|
// add null termination to command
|
|
CmdlineBuffer[CmdlineBufferLength++] = 0;
|
|
CmdlineBufferEditPos++;
|
|
// command is complete, process it
|
|
cmdlineProcessInputString();
|
|
// reset buffer
|
|
CmdlineBufferLength = 0;
|
|
CmdlineBufferEditPos = 0;
|
|
}
|
|
else if(c == ASCII_BS)
|
|
{
|
|
if(CmdlineBufferEditPos)
|
|
{
|
|
// is this a simple delete (off the end of the line)
|
|
if(CmdlineBufferEditPos == CmdlineBufferLength)
|
|
{
|
|
// destructive backspace
|
|
// echo backspace-space-backspace
|
|
cmdlineOutputFunc(ASCII_BS);
|
|
cmdlineOutputFunc(' ');
|
|
cmdlineOutputFunc(ASCII_BS);
|
|
// decrement our buffer length and edit position
|
|
CmdlineBufferLength--;
|
|
CmdlineBufferEditPos--;
|
|
}
|
|
else
|
|
{
|
|
// edit/cursor position != end of buffer
|
|
// we're deleting characters at a mid-line edit position
|
|
// shift characters down, effectively deleting
|
|
CmdlineBufferLength--;
|
|
CmdlineBufferEditPos--;
|
|
for(i=CmdlineBufferEditPos; i<CmdlineBufferLength; i++)
|
|
CmdlineBuffer[i] = CmdlineBuffer[i+1];
|
|
// repaint
|
|
cmdlineRepaint();
|
|
// add space to clear leftover characters
|
|
cmdlineOutputFunc(' ');
|
|
// reposition cursor
|
|
for(i=CmdlineBufferEditPos; i<(CmdlineBufferLength+1); i++)
|
|
cmdlineOutputFunc(ASCII_BS);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// else, ring the bell
|
|
cmdlineOutputFunc(ASCII_BEL);
|
|
}
|
|
}
|
|
else if(c == ASCII_DEL)
|
|
{
|
|
// not yet handled
|
|
}
|
|
else if(c == ASCII_ESC)
|
|
{
|
|
CmdlineInputVT100State = 1;
|
|
}
|
|
}
|
|
|
|
void cmdlineRepaint(void)
|
|
{
|
|
u08* ptr;
|
|
u08 i;
|
|
|
|
// carriage return
|
|
cmdlineOutputFunc(ASCII_CR);
|
|
// print fresh prompt
|
|
cmdlinePrintPrompt();
|
|
// print the new command line buffer
|
|
i = CmdlineBufferLength;
|
|
ptr = CmdlineBuffer;
|
|
while(i--) cmdlineOutputFunc(*ptr++);
|
|
}
|
|
|
|
void cmdlineDoHistory(u08 action)
|
|
{
|
|
switch(action)
|
|
{
|
|
case CMDLINE_HISTORY_SAVE:
|
|
// copy CmdlineBuffer to history if not null string
|
|
if( strlen(CmdlineBuffer) )
|
|
strcpy(CmdlineHistory[0], CmdlineBuffer);
|
|
break;
|
|
case CMDLINE_HISTORY_PREV:
|
|
// copy history to current buffer
|
|
strcpy(CmdlineBuffer, CmdlineHistory[0]);
|
|
// set the buffer position to the end of the line
|
|
CmdlineBufferLength = strlen(CmdlineBuffer);
|
|
CmdlineBufferEditPos = CmdlineBufferLength;
|
|
// "re-paint" line
|
|
cmdlineRepaint();
|
|
break;
|
|
case CMDLINE_HISTORY_NEXT:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void cmdlineProcessInputString(void)
|
|
{
|
|
u08 cmdIndex;
|
|
u08 i=0;
|
|
|
|
// save command in history
|
|
cmdlineDoHistory(CMDLINE_HISTORY_SAVE);
|
|
|
|
// find the end of the command (excluding arguments)
|
|
// find first whitespace character in CmdlineBuffer
|
|
while( !((CmdlineBuffer[i] == ' ') || (CmdlineBuffer[i] == 0)) ) i++;
|
|
|
|
if(!i)
|
|
{
|
|
// command was null or empty
|
|
// output a new prompt
|
|
cmdlinePrintPrompt();
|
|
// we're done
|
|
return;
|
|
}
|
|
|
|
// search command list for match with entered command
|
|
for(cmdIndex=0; cmdIndex<CmdlineNumCommands; cmdIndex++)
|
|
{
|
|
if( !strncmp(CmdlineCommandList[cmdIndex], CmdlineBuffer, i) )
|
|
{
|
|
// user-entered command matched a command in the list (database)
|
|
// run the corresponding function
|
|
CmdlineExecFunction = CmdlineFunctionList[cmdIndex];
|
|
// new prompt will be output after user function runs
|
|
// and we're done
|
|
return;
|
|
}
|
|
}
|
|
|
|
// if we did not get a match
|
|
// output an error message
|
|
cmdlinePrintError();
|
|
// output a new prompt
|
|
cmdlinePrintPrompt();
|
|
}
|
|
|
|
void cmdlineMainLoop(void)
|
|
{
|
|
// do we have a command/function to be executed
|
|
if(CmdlineExecFunction)
|
|
{
|
|
// run it
|
|
CmdlineExecFunction();
|
|
// reset
|
|
CmdlineExecFunction = 0;
|
|
// output new prompt
|
|
cmdlinePrintPrompt();
|
|
}
|
|
}
|
|
|
|
void cmdlinePrintPrompt(void)
|
|
{
|
|
// print a new command prompt
|
|
u08* ptr = CmdlinePrompt;
|
|
while(pgm_read_byte(ptr)) cmdlineOutputFunc( pgm_read_byte(ptr++) );
|
|
}
|
|
|
|
void cmdlinePrintError(void)
|
|
{
|
|
u08 * ptr;
|
|
|
|
// print a notice header
|
|
// (u08*) cast used to avoid compiler warning
|
|
ptr = (u08*)CmdlineNotice;
|
|
while(pgm_read_byte(ptr)) cmdlineOutputFunc( pgm_read_byte(ptr++) );
|
|
|
|
// print the offending command
|
|
ptr = CmdlineBuffer;
|
|
while((*ptr) && (*ptr != ' ')) cmdlineOutputFunc(*ptr++);
|
|
|
|
cmdlineOutputFunc(':');
|
|
cmdlineOutputFunc(' ');
|
|
|
|
// print the not-found message
|
|
// (u08*) cast used to avoid compiler warning
|
|
ptr = (u08*)CmdlineCmdNotFound;
|
|
while(pgm_read_byte(ptr)) cmdlineOutputFunc( pgm_read_byte(ptr++) );
|
|
|
|
cmdlineOutputFunc('\r');
|
|
cmdlineOutputFunc('\n');
|
|
}
|
|
|
|
// argument retrieval commands
|
|
|
|
// return string pointer to argument [argnum]
|
|
u08* cmdlineGetArgStr(u08 argnum)
|
|
{
|
|
// find the offset of argument number [argnum]
|
|
u08 idx=0;
|
|
u08 arg;
|
|
|
|
// find the first non-whitespace character
|
|
while( (CmdlineBuffer[idx] != 0) && (CmdlineBuffer[idx] == ' ')) idx++;
|
|
|
|
// we are at the first argument
|
|
for(arg=0; arg<argnum; arg++)
|
|
{
|
|
// find the next whitespace character
|
|
while( (CmdlineBuffer[idx] != 0) && (CmdlineBuffer[idx] != ' ')) idx++;
|
|
// find the first non-whitespace character
|
|
while( (CmdlineBuffer[idx] != 0) && (CmdlineBuffer[idx] == ' ')) idx++;
|
|
}
|
|
// we are at the requested argument or the end of the buffer
|
|
return &CmdlineBuffer[idx];
|
|
}
|
|
|
|
// return argument [argnum] interpreted as a decimal integer
|
|
long cmdlineGetArgInt(u08 argnum)
|
|
{
|
|
char* endptr;
|
|
return strtol(cmdlineGetArgStr(argnum), &endptr, 10);
|
|
}
|
|
|
|
// return argument [argnum] interpreted as a hex integer
|
|
long cmdlineGetArgHex(u08 argnum)
|
|
{
|
|
char* endptr;
|
|
return strtol(cmdlineGetArgStr(argnum), &endptr, 16);
|
|
}
|