809 lines
26 KiB
C
809 lines
26 KiB
C
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Name: readline.c
|
|
// Created: April 2020
|
|
// Author(s): Alfred Klomp (www.alfredklomp.com) - original readline.c
|
|
// Philip Smart - Port to K64f/ZPU and many upgrades, ie. additions of history buffer logic.
|
|
// Description: A readline module for embedded systems without the overhead of GNU readline or the
|
|
// need to use > C99 standard as the ZPU version of GCC doesnt support it!
|
|
// The readline modules originally came from Alfred Klomp's Raduino project and whilst
|
|
// I was searching for an alternative to GNU after failing to compile it with the ZPU
|
|
// version of GCC (as well as the size), I came across RADUINO and this readline
|
|
// module was very well written and easily portable to this project.
|
|
//
|
|
// Credits:
|
|
// Copyright: (c) -> 2016 - Alfred Klomp (www.alfredklomp.com) original code
|
|
// (C) 2020 - Philip Smart <philip.smart@net2net.org> porting, history buf.
|
|
// local commands and updates.
|
|
//
|
|
// History: April 2020 - With the advent of the tranZPUter SW, I needed a better command
|
|
// line module for entering, recalling and editting commands. This
|
|
// module after adding history buffer mechanism is the ideal
|
|
// solution.
|
|
// features of zOS where applicable.
|
|
//
|
|
// Notes: See Makefile to enable/disable conditional components
|
|
// __SD_CARD__ - Add the SDCard logic.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// This source file is free software: you can redistribute it and#or modify
|
|
// it under the terms of the GNU General Public License as published
|
|
// by the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This source file is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#if defined __K64F__
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <../libraries/include/stdmisc.h>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#elif defined __ZPU__
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <stdmisc.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include <stddef.h>
|
|
#elif defined __M68K__
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#endif
|
|
|
|
#if defined __SD_CARD__
|
|
#include <ff.h>
|
|
#endif
|
|
|
|
#include "utils.h"
|
|
|
|
#if defined __APP__
|
|
#if defined __K64F__
|
|
#undef stdin
|
|
#undef stdout
|
|
#undef stderr
|
|
extern FILE *stdout;
|
|
extern FILE *stdin;
|
|
extern FILE *stderr;
|
|
#endif
|
|
|
|
#define malloc sys_malloc
|
|
#define realloc sys_realloc
|
|
#define calloc sys_calloc
|
|
#define free sys_free
|
|
void *sys_malloc(size_t); // Allocate memory managed by the OS.
|
|
void *sys_realloc(void *, size_t); // Reallocate a block of memory managed by the OS.
|
|
void *sys_calloc(size_t, size_t); // Allocate and zero a block of memory managed by the OS.
|
|
void sys_free(void *); // Free memory managed by the OS.
|
|
#endif
|
|
|
|
// List of local commands processed by readline.
|
|
#define CMD_HISTORY 0x01
|
|
#define CMD_RECALL 0x02
|
|
|
|
// Special key codes recognised by readline.
|
|
#define CTRL_A 0x01
|
|
#define CTRL_B 0x02
|
|
#define CTRL_C 0x03
|
|
#define CTRL_D 0x04
|
|
#define CTRL_E 0x05
|
|
#define CTRL_F 0x06
|
|
#define BACKSPACE 0x08
|
|
#define CTRL_K 0x0b
|
|
#define ENTER 0x0d
|
|
#define CTRL_P 0x10
|
|
#define RIGHTBRACKET 0x5b
|
|
#define TILDA 0x7e
|
|
#define CTRL_N 0x0e
|
|
#define ESC 0x1b
|
|
|
|
// Line buffer control variables.
|
|
static uint8_t llen, lpos;
|
|
|
|
#define MAX_HISTORY_LINES 20
|
|
static uint8_t *history[MAX_HISTORY_LINES] = {};
|
|
static uint8_t histFreeSlot = 0;
|
|
|
|
#if defined __SD_CARD__
|
|
static FIL *histFp = NULL;
|
|
static uint8_t histDisabled = 0;
|
|
#endif
|
|
|
|
// Keys we distinguish:
|
|
enum keytype {
|
|
KEY_REGULAR,
|
|
KEY_BKSP,
|
|
KEY_CTRL_A,
|
|
KEY_CTRL_B,
|
|
KEY_CTRL_C,
|
|
KEY_CTRL_D,
|
|
KEY_CTRL_E,
|
|
KEY_CTRL_F,
|
|
KEY_CTRL_K,
|
|
KEY_CTRL_N,
|
|
KEY_CTRL_P,
|
|
KEY_ENTER,
|
|
KEY_INSERT,
|
|
KEY_HOME,
|
|
KEY_DEL,
|
|
KEY_END,
|
|
KEY_PGUP,
|
|
KEY_PGDN,
|
|
KEY_ARROWUP,
|
|
KEY_ARROWDN,
|
|
KEY_ARROWRT,
|
|
KEY_ARROWLT,
|
|
};
|
|
|
|
// Scancodes for special keys, sorted lexicographically:
|
|
static const uint8_t key_ctrl_a[] = { CTRL_A };
|
|
static const uint8_t key_ctrl_b[] = { CTRL_B };
|
|
static const uint8_t key_ctrl_c[] = { CTRL_C };
|
|
static const uint8_t key_ctrl_d[] = { CTRL_D };
|
|
static const uint8_t key_ctrl_e[] = { CTRL_E };
|
|
static const uint8_t key_ctrl_f[] = { CTRL_F };
|
|
static const uint8_t key_bksp[] = { BACKSPACE };
|
|
static const uint8_t key_ctrl_k[] = { CTRL_K };
|
|
static const uint8_t key_enter[] = { ENTER };
|
|
static const uint8_t key_ctrl_n[] = { CTRL_N };
|
|
static const uint8_t key_ctrl_p[] = { CTRL_P };
|
|
static const uint8_t key_home[] = { ESC, RIGHTBRACKET, '1', TILDA };
|
|
static const uint8_t key_insert[] = { ESC, RIGHTBRACKET, '2', TILDA };
|
|
static const uint8_t key_del[] = { ESC, RIGHTBRACKET, '3', TILDA };
|
|
static const uint8_t key_end_1[] = { ESC, '0', 'F' };
|
|
static const uint8_t key_end_2[] = { ESC, RIGHTBRACKET, '4', TILDA };
|
|
static const uint8_t key_pgup[] = { ESC, RIGHTBRACKET, '5', TILDA };
|
|
static const uint8_t key_pgdn[] = { ESC, RIGHTBRACKET, '6', TILDA };
|
|
static const uint8_t key_arrowup[] = { ESC, RIGHTBRACKET, 'A' };
|
|
static const uint8_t key_arrowdn[] = { ESC, RIGHTBRACKET, 'B' };
|
|
static const uint8_t key_arrowrt[] = { ESC, RIGHTBRACKET, 'C' };
|
|
static const uint8_t key_arrowlt[] = { ESC, RIGHTBRACKET, 'D' };
|
|
|
|
// Table of special keys. We could store this in PROGMEM, but we don't,
|
|
// because the code to retrieve the data is much larger than the savings.
|
|
static const struct key {
|
|
const uint8_t *code;
|
|
uint8_t len;
|
|
enum keytype type;
|
|
}
|
|
// Table is order sensitive.
|
|
keys[] = {
|
|
{ key_ctrl_a, sizeof(key_ctrl_a), KEY_CTRL_A },
|
|
{ key_ctrl_b, sizeof(key_ctrl_b), KEY_CTRL_B },
|
|
{ key_ctrl_c, sizeof(key_ctrl_c), KEY_CTRL_C },
|
|
{ key_ctrl_d, sizeof(key_ctrl_d), KEY_CTRL_D },
|
|
{ key_ctrl_e, sizeof(key_ctrl_e), KEY_CTRL_E },
|
|
{ key_ctrl_f, sizeof(key_ctrl_f), KEY_CTRL_F },
|
|
{ key_bksp, sizeof(key_bksp), KEY_BKSP },
|
|
{ key_ctrl_k, sizeof(key_ctrl_k), KEY_CTRL_K },
|
|
{ key_enter, sizeof(key_enter), KEY_ENTER },
|
|
{ key_ctrl_n, sizeof(key_ctrl_n), KEY_CTRL_N },
|
|
{ key_ctrl_p, sizeof(key_ctrl_p), KEY_CTRL_P },
|
|
{ key_home, sizeof(key_home), KEY_HOME },
|
|
{ key_insert, sizeof(key_insert), KEY_INSERT },
|
|
{ key_del, sizeof(key_del), KEY_DEL },
|
|
{ key_end_1, sizeof(key_end_1), KEY_END },
|
|
{ key_end_2, sizeof(key_end_2), KEY_END },
|
|
{ key_pgup, sizeof(key_pgup), KEY_PGUP },
|
|
{ key_pgdn, sizeof(key_pgdn), KEY_PGDN },
|
|
{ key_arrowup, sizeof(key_arrowup), KEY_ARROWUP },
|
|
{ key_arrowdn, sizeof(key_arrowdn), KEY_ARROWDN },
|
|
{ key_arrowrt, sizeof(key_arrowrt), KEY_ARROWRT },
|
|
{ key_arrowlt, sizeof(key_arrowlt), KEY_ARROWLT },
|
|
};
|
|
|
|
// Local command list.
|
|
//
|
|
typedef struct {
|
|
const char *cmd;
|
|
uint8_t key;
|
|
} t_cmdstruct;
|
|
|
|
// Table of supported local commands. The table contains the command and the case value to act on for the command.
|
|
static t_cmdstruct cmdTable[] = {
|
|
{ "history", CMD_HISTORY },
|
|
{ "hist", CMD_HISTORY },
|
|
{ "!", CMD_RECALL },
|
|
};
|
|
|
|
// Define the number of local commands in the array.
|
|
#define NCMDKEYS (sizeof(cmdTable)/sizeof(t_cmdstruct))
|
|
|
|
static bool
|
|
key_matches (const uint8_t c, const int8_t idx, const int8_t pos)
|
|
{
|
|
// If key is too short, skip:
|
|
if (pos >= keys[idx].len)
|
|
return false;
|
|
|
|
// Character must match:
|
|
return (c == keys[idx].code[pos]);
|
|
}
|
|
|
|
static bool find_key (const uint8_t c, int8_t *idx, const int8_t pos)
|
|
{
|
|
int8_t i;
|
|
|
|
// If character matches current index, return:
|
|
if (c == keys[*idx].code[pos])
|
|
return true;
|
|
|
|
// If character is less than current index, seek up:
|
|
if (c < keys[*idx].code[pos])
|
|
{
|
|
for (i = *idx - 1; i >= 0; i--)
|
|
{
|
|
if (key_matches(c, i, pos))
|
|
{
|
|
*idx = i;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// If character is greater than current index, seek down:
|
|
else {
|
|
for (i = *idx + 1; i < sizeof(keys) / sizeof(keys[0]); i++) {
|
|
if (key_matches(c, i, pos)) {
|
|
*idx = i;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Get next character from serial port.
|
|
static bool next_char (enum keytype *type, uint8_t *val)
|
|
{
|
|
static int8_t idx, pos;
|
|
int8_t keyIn;
|
|
|
|
// Process all available bytes from the serial port.
|
|
do {
|
|
keyIn = getKey(0);
|
|
if(keyIn != -1)
|
|
{
|
|
// Try to find a matching key from the special keys table:
|
|
if (find_key(keyIn, &idx, pos))
|
|
{
|
|
|
|
// If we are at max length, we're done:
|
|
if (++pos == keys[idx].len)
|
|
{
|
|
*type = keys[idx].type;
|
|
idx = pos = 0;
|
|
return true;
|
|
}
|
|
|
|
// Otherwise, need more input to be certain:
|
|
continue;
|
|
} else
|
|
{
|
|
// No matching special key: it's a regular character:
|
|
idx = pos = 0;
|
|
*val = keyIn;
|
|
*type = KEY_REGULAR;
|
|
return true;
|
|
}
|
|
}
|
|
} while(true);
|
|
|
|
return false;
|
|
}
|
|
|
|
// Short method to scrub previous line contents off screen and replace with new contents.
|
|
void refreshLine(uint8_t *line, uint8_t llen, uint8_t *lpos)
|
|
{
|
|
while (*lpos)
|
|
{
|
|
fputc('\b', stdout);
|
|
fputc(' ', stdout);
|
|
fputc('\b', stdout);
|
|
(*lpos)--;
|
|
}
|
|
printf((char *)line);
|
|
*lpos = llen;
|
|
return;
|
|
}
|
|
|
|
// Short method to add a command line onto the end of the history list. If the SD Card is enabled, add the command
|
|
// to the end of the history file as well.
|
|
//
|
|
void addToHistory(char *buf, uint8_t bytes, uint8_t addHistFile)
|
|
{
|
|
// If the slot in history is already being used, free the memory before allocating a new block suitable to the new line size.
|
|
if(history[histFreeSlot] != NULL)
|
|
free(history[histFreeSlot]);
|
|
history[histFreeSlot] = malloc(bytes+1);
|
|
|
|
// If malloc was successful, populate the memory with the command entered.
|
|
if(history[histFreeSlot] != NULL)
|
|
{
|
|
// Copy the command into history and update the pointer, wrap around if needed.
|
|
memcpy(history[histFreeSlot], buf, bytes+1);
|
|
histFreeSlot++;
|
|
if(histFreeSlot >= MAX_HISTORY_LINES)
|
|
histFreeSlot = 0;
|
|
}
|
|
|
|
// Add the command to the history file if selected and the SD Card is enabled.
|
|
//
|
|
if(addHistFile)
|
|
{
|
|
#if defined __SD_CARD__
|
|
unsigned int writeBytes;
|
|
|
|
if(histFp != NULL && histDisabled == 0)
|
|
{
|
|
// Write out the line into the history file.
|
|
f_write(histFp, buf, bytes, &writeBytes);
|
|
f_putc('\n', histFp);
|
|
f_sync(histFp);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Method to clear out the history buffer and return used memory back to the heap.
|
|
//
|
|
void clearHistory(void)
|
|
{
|
|
int idx;
|
|
|
|
// Simply loop through and free used memory, reset pointer to the start.
|
|
//
|
|
for(idx = 0; idx < MAX_HISTORY_LINES; idx++)
|
|
{
|
|
if(history[idx] != NULL)
|
|
{
|
|
free(history[idx]);
|
|
history[idx] = NULL;
|
|
}
|
|
}
|
|
histFreeSlot = 0;
|
|
|
|
// If the history file is being used, close it and free up its memory.
|
|
#if defined __SD_CARD__
|
|
if(histFp != NULL)
|
|
{
|
|
f_close(histFp);
|
|
free(histFp);
|
|
histFp = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Local command to display the history contents.
|
|
void cmdPrintHistory(void)
|
|
{
|
|
// Locals.
|
|
char buf[120];
|
|
FRESULT fr;
|
|
int bytes;
|
|
uint32_t lineCnt = 1;
|
|
|
|
// Rewind the history file to the beginning.
|
|
//
|
|
fr = f_lseek(histFp, 0);
|
|
if(!fr)
|
|
{
|
|
// Simply loop from start to end adding to buffer, if the file history is larger than the memory buffer we just loop round.
|
|
do {
|
|
bytes = -1;
|
|
if(f_gets(buf, sizeof(buf), histFp) != NULL)
|
|
{
|
|
// Get number of bytes read.
|
|
bytes = strlen(buf);
|
|
if(bytes > 0)
|
|
{
|
|
// Remove the CR/newline terminator.
|
|
bytes -= 1;
|
|
buf[bytes] = 0x0;
|
|
|
|
// Print it out.
|
|
printf("%06lu %s\n", lineCnt++, buf);
|
|
}
|
|
}
|
|
} while(bytes != -1);
|
|
}
|
|
}
|
|
|
|
// Local command to load a specific command into the line buffer.
|
|
uint8_t cmdRecallHistory(uint8_t *line, uint8_t *llen, uint32_t lineNo)
|
|
{
|
|
// Locals.
|
|
char buf[120];
|
|
FRESULT fr;
|
|
int bytes;
|
|
uint32_t lineCnt = 1;
|
|
uint8_t retCode = 1;
|
|
|
|
// Rewind the history file to the beginning.
|
|
//
|
|
fr = f_lseek(histFp, 0);
|
|
if(!fr)
|
|
{
|
|
// Simply loop from start to end until we reach the required line, load it into the line buffer and forward to the end of file.
|
|
do {
|
|
bytes = -1;
|
|
if(f_gets(buf, sizeof(buf), histFp) != NULL)
|
|
{
|
|
// Get number of bytes read.
|
|
bytes = strlen(buf);
|
|
lineCnt++;
|
|
if(bytes > 0)
|
|
{
|
|
// Remove the CR/newline terminator.
|
|
buf[--bytes] = 0x0;
|
|
}
|
|
}
|
|
} while(bytes != -1 && lineNo != lineCnt);
|
|
|
|
// Go to end of file.
|
|
fr = f_lseek(histFp, f_size(histFp));
|
|
if(fr)
|
|
{
|
|
printf("Failed to reset the history file to EOF.");
|
|
}
|
|
|
|
if(lineNo == lineCnt)
|
|
{
|
|
strcpy((char *)line, buf);
|
|
*llen = strlen((char *)line);
|
|
printf(">%s\n", buf);
|
|
retCode = 0;
|
|
}
|
|
}
|
|
return(retCode);
|
|
}
|
|
|
|
|
|
// Check to see if the given command is local and process as necessary.
|
|
//
|
|
uint8_t localCommand(uint8_t *line, uint8_t *lineSize)
|
|
{
|
|
uint8_t idx;
|
|
char *paramptr = (char *)line;
|
|
long lineNo;
|
|
|
|
// Skip leading whitespace.
|
|
while(*paramptr == ' ' && paramptr < (char *)line + *lineSize) paramptr++;
|
|
|
|
// Loop through all the commands and try to find a match.
|
|
for (idx=0; idx < NCMDKEYS; idx++)
|
|
{
|
|
t_cmdstruct *sym = &cmdTable[idx];
|
|
if (strncmp(sym->cmd, paramptr,strlen(sym->cmd)) == 0)
|
|
{
|
|
// Process the command
|
|
switch(sym->key)
|
|
{
|
|
// Print out the history buffer.
|
|
//
|
|
case CMD_HISTORY:
|
|
cmdPrintHistory();
|
|
return(1);
|
|
|
|
// Recall a command from the history buffer.
|
|
//
|
|
case CMD_RECALL:
|
|
paramptr++;
|
|
if(xatoi(¶mptr, &lineNo))
|
|
{
|
|
if(cmdRecallHistory(line, lineSize, lineNo-1) == 0)
|
|
{
|
|
// Return no local command result as we want the caller to process the newly filled line buffer.
|
|
return(0);
|
|
}
|
|
}
|
|
break;
|
|
|
|
// This case shouldnt exist as a handler for each command should exist.
|
|
// If this case is triggered then fall through as though there was no command.
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Not a local command.
|
|
return(0);
|
|
}
|
|
|
|
// Take characters from the Rx FIFO and create a line
|
|
uint8_t *readline (uint8_t *line, int lineSize, const char *histFile)
|
|
{
|
|
enum keytype type;
|
|
uint8_t val = 0;
|
|
int8_t i;
|
|
uint8_t histPnt = histFreeSlot;
|
|
|
|
#if defined __SD_CARD__
|
|
char buf[120];
|
|
FRESULT fr;
|
|
int bytes;
|
|
|
|
// If we havent opened the history file and read the contents AND file based history hasnt been disabled, open the history file and read
|
|
// the contents.
|
|
if(histFp == NULL && histDisabled == 0 && histFile != NULL)
|
|
{
|
|
// Allocate a file control block on the heap and open the history file.
|
|
histFp = malloc(sizeof(FIL));
|
|
|
|
if(histFp != NULL)
|
|
{
|
|
fr = f_open(histFp, histFile, FA_OPEN_ALWAYS | FA_WRITE | FA_READ);
|
|
if(fr != FR_OK)
|
|
{
|
|
printf("Cannot open/create history file, disabling.\n");
|
|
} else
|
|
{
|
|
// Simply loop from start to end adding to buffer, if the file history is larger than the memory buffer we just loop round.
|
|
histPnt = 0;
|
|
do {
|
|
bytes = -1;
|
|
if(f_gets(buf, sizeof(buf), histFp) == buf)
|
|
{
|
|
// Get number of bytes read.
|
|
bytes = strlen(buf);
|
|
if(bytes > 0)
|
|
{
|
|
// Remove the CR/newline terminator.
|
|
bytes -= 1;
|
|
buf[bytes] = 0x0;
|
|
|
|
// Add the line into our history list.
|
|
addToHistory(buf, bytes, false);
|
|
|
|
// Update the pointer, if it overflows, wrap around.
|
|
if(++histPnt >= MAX_HISTORY_LINES)
|
|
histPnt = 0;
|
|
}
|
|
}
|
|
} while(bytes != -1);
|
|
}
|
|
} else
|
|
{
|
|
histDisabled = 1;
|
|
}
|
|
}
|
|
// If the history filename hasnt been provided and it was previously being used, this is a signal to close and stop using history, free it up.
|
|
//
|
|
if(histFile == NULL && histFp != NULL)
|
|
{
|
|
clearHistory();
|
|
}
|
|
|
|
#endif
|
|
|
|
while (next_char(&type, &val))
|
|
{
|
|
switch (type)
|
|
{
|
|
case KEY_REGULAR:
|
|
// Refuse insert if we are at the end of the line:
|
|
if (lpos == lineSize)
|
|
break;
|
|
|
|
// If there is string to the right, move it over one place:
|
|
if (llen > lpos)
|
|
for (i = llen; i >= lpos; i--)
|
|
line[i + 1] = line[i];
|
|
|
|
// Insert character:
|
|
line[lpos++] = val;
|
|
|
|
// Increase line length, if possible:
|
|
if (llen < lineSize)
|
|
llen++;
|
|
|
|
// Paint new string:
|
|
for (i = lpos - 1; i < llen; i++)
|
|
fputc(line[i], stdout);
|
|
|
|
// Backtrack to current position:
|
|
for (i = lpos; i < llen; i++)
|
|
fputc('\b', stdout);
|
|
|
|
break;
|
|
|
|
case KEY_CTRL_C:
|
|
// Ctrl-c means abort current line, return CTRL_C in the first character of the buffer.
|
|
line[0] = CTRL_C;
|
|
line[1] = '\0';
|
|
refreshLine((uint8_t *)"", 0, &lpos);
|
|
llen = lpos = 0;
|
|
return line;
|
|
|
|
case KEY_CTRL_D:
|
|
break;
|
|
|
|
case KEY_BKSP:
|
|
// Nothing to do if we are at the start of the line:
|
|
if (lpos == 0)
|
|
break;
|
|
|
|
// Line becomes one character shorter:
|
|
llen--;
|
|
lpos--;
|
|
|
|
fputc('\b', stdout);
|
|
|
|
// Move characters one over to the left:
|
|
for (i = lpos; i < llen; i++) {
|
|
line[i] = line[i + 1];
|
|
fputc(line[i], stdout);
|
|
}
|
|
fputc(' ', stdout);
|
|
|
|
// Backtrack to current position:
|
|
for (i = lpos; i <= llen; i++)
|
|
fputc('\b', stdout);
|
|
|
|
break;
|
|
|
|
case KEY_ENTER:
|
|
// Null-terminate the line and return pointer:
|
|
line[llen] = '\0';
|
|
fputc('\n', stdout);
|
|
|
|
// See if this is a local readline command, if so process otherwise return buffer to caller.
|
|
if(localCommand(line, &llen) == 0)
|
|
{
|
|
// If a command is entered, add to history.
|
|
if(llen > 0)
|
|
{
|
|
// Add the line into our history list.
|
|
addToHistory((char *)line, llen, true);
|
|
}
|
|
} else
|
|
{
|
|
// Return an empty buffer so caller can make any necessary updates.
|
|
line[0] = '\0';
|
|
}
|
|
llen = lpos = 0;
|
|
return line;
|
|
|
|
case KEY_CTRL_A:
|
|
case KEY_HOME:
|
|
while (lpos) {
|
|
fputc('\b', stdout);
|
|
lpos--;
|
|
}
|
|
break;
|
|
|
|
case KEY_DEL:
|
|
// Nothing to do if we are at the end of the line:
|
|
if (lpos == llen)
|
|
break;
|
|
|
|
llen--;
|
|
|
|
// Move characters one over to the left:
|
|
for (i = lpos; i < llen; i++) {
|
|
line[i] = line[i + 1];
|
|
fputc(line[i], stdout);
|
|
}
|
|
fputc(' ', stdout);
|
|
|
|
// Backtrack to current position:
|
|
for (i = lpos; i <= llen; i++)
|
|
fputc('\b', stdout);
|
|
|
|
break;
|
|
|
|
case KEY_INSERT:
|
|
break;
|
|
|
|
case KEY_CTRL_E:
|
|
case KEY_END:
|
|
while (lpos < llen)
|
|
fputc(line[lpos++], stdout);
|
|
break;
|
|
|
|
case KEY_PGUP:
|
|
break;
|
|
|
|
case KEY_PGDN:
|
|
break;
|
|
|
|
case KEY_CTRL_K:
|
|
// Clear the buffer.
|
|
refreshLine((uint8_t *)"", 0, &lpos);
|
|
llen = lpos = 0;
|
|
break;
|
|
|
|
case KEY_CTRL_P:
|
|
case KEY_ARROWUP:
|
|
// Go to the previous history buffer.
|
|
if(histPnt == 0 && history[MAX_HISTORY_LINES-1] != NULL)
|
|
{
|
|
llen = strlen((char *)history[MAX_HISTORY_LINES-1]);
|
|
memcpy(line, history[MAX_HISTORY_LINES-1], llen+1);
|
|
histPnt = MAX_HISTORY_LINES-1;
|
|
refreshLine(line, llen, &lpos);
|
|
} else
|
|
if(history[histPnt-1] != NULL)
|
|
{
|
|
llen = strlen((char *)history[histPnt-1]);
|
|
memcpy(line, history[histPnt-1], llen+1);
|
|
histPnt--;
|
|
refreshLine(line, llen, &lpos);
|
|
} else
|
|
if(history[histPnt] != NULL)
|
|
{
|
|
llen = strlen((char *)history[histPnt]);
|
|
memcpy(line, history[histPnt], llen+1);
|
|
refreshLine(line, llen, &lpos);
|
|
}
|
|
break;
|
|
|
|
case KEY_CTRL_N:
|
|
case KEY_ARROWDN:
|
|
// Go to the next history buffer.
|
|
if(histPnt == MAX_HISTORY_LINES-1 && history[0] != NULL)
|
|
{
|
|
llen = strlen((char *)history[0]);
|
|
memcpy(line, history[0], llen+1);
|
|
histPnt = MAX_HISTORY_LINES-1;
|
|
refreshLine(line, llen, &lpos);
|
|
} else
|
|
if(history[histPnt+1] != NULL)
|
|
{
|
|
llen = strlen((char *)history[histPnt+1]);
|
|
memcpy(line, history[histPnt+1], llen+1);
|
|
histPnt++;
|
|
refreshLine(line, llen, &lpos);
|
|
} else
|
|
// if(history[histPnt] != NULL)
|
|
{
|
|
llen = 0; //strlen((char *)history[histPnt]);
|
|
line[0] = '\0';
|
|
//memcpy(line, history[histPnt], llen+1);
|
|
refreshLine(line, llen, &lpos);
|
|
}
|
|
break;
|
|
|
|
case KEY_CTRL_F:
|
|
case KEY_ARROWRT:
|
|
if (lpos < llen)
|
|
fputc(line[lpos++], stdout);
|
|
break;
|
|
|
|
case KEY_CTRL_B:
|
|
case KEY_ARROWLT:
|
|
if (lpos == 0)
|
|
break;
|
|
|
|
fputc('\b', stdout);
|
|
lpos--;
|
|
break;
|
|
}
|
|
}
|
|
|
|
line[0] = 0x00;
|
|
return(line);
|
|
}
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|