/**************************************************************************
*
* Copyright (c) 2013 Alcatel-Lucent
*
* Alcatel Lucent licenses this file to You under the Apache License,
* Version 2.0 (the "License"); you may not use this file except in
* compliance with the License. A copy of the License is contained the
* file LICENSE at the top level of this repository.
* You may also obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**************************************************************************
*
* ledit_vt.c:
*
* This is an alternative to lineedit.c. For those unfamiliar with the
* KSH VI-like editing, this is certainly a more intuitive mechanism
* for CLI edit. It uses the VT100 terminal arrow keys for basic
* line and history traversal...
*
* - UP/DOWN move through the CLI history.
* - RIGHT/LEFT move through the current line.
* - DEL deletes character that cursor is on top of.
*
* Original author: Ed Sutter (ed.sutter@alcatel-lucent.com)
*
*/
#include "config.h"
#if INCLUDE_LINEEDIT
#include <ctype.h>
#include "genlib.h"
#include "stddefs.h"
#include "cli.h"
#define GOT_NUTTIN 0
#define GOT_ESCAPE 1
#define GOT_BRACKET 2
#define HMAX 16
#define ESC 0x1B
#define CTLC 0x03
#define BACKSPACE 0x08
#define OPEN_BRACKET '['
#define VT100_DEL 0x7f
#define VT100_UP 'A'
#define VT100_DOWN 'B'
#define VT100_RIGHT 'C'
#define VT100_LEFT 'D'
#define EDITFILELINE 1
#define EDITCMDLINE 2
static int stridx; /* store index */
static int shwidx; /* show index */
static int srchidx; /* search index */
static int lastsize; /* size of last command */
static char curChar; /* latest input character */
static char *curPos; /* current position on command line */
static char *startOfLine; /* start of command line */
static int lineLen; /* length of line */
static char cmdhistory[HMAX+1][CMDLINESIZE];/* array for command history */
static void shownext(void), showprev(void), ldelete(void), backspace(void);
static void newchar(char c);
static void backup(int count);
/* lineeditor():
* This is a simpler version of lineeditor() (as found in lineedit.c).
* It does not have the capability of the vi-like version; however, for
* those not familiar with ksh, this one is probably a lot more intuitive.
*
* The line is modified in place so, if successful, the function
* returns the same pointer but with its contents modified based
* on the editor commands executed.
* If failure, the function returns (char *)0.
*/
static char *
lineeditor(char *line_to_edit,int type)
{
int state;
if(type == EDITCMDLINE) {
if(getchar() != OPEN_BRACKET) {
putchar('\n');
*line_to_edit = 0;
return((char *)0);
}
}
startOfLine = line_to_edit;
curPos = line_to_edit;
while(*curPos != ESC) {
curPos++;
}
*curPos = 0; /* Remove the escape character from the line */
lineLen = (ulong)curPos - (ulong)startOfLine;
if(lineLen > 0) {
curPos--;
putstr(" \b\b");
} else {
putstr(" \b");
}
state = GOT_BRACKET;
lastsize = 0;
shwidx = stridx;
srchidx = stridx;
while(1) {
curChar = getchar();
switch(curChar) {
case CTLC:
putchar('\n');
*line_to_edit = 0;
return((char *)0);
case VT100_UP:
if(state == GOT_BRACKET) {
if(type == EDITCMDLINE) {
showprev();
}
state = GOT_NUTTIN;
} else {
newchar(curChar);
}
break;
case VT100_DOWN:
if(state == GOT_BRACKET) {
if(type == EDITCMDLINE) {
shownext();
}
state = GOT_NUTTIN;
} else {
newchar(curChar);
}
break;
case VT100_RIGHT:
if(state == GOT_BRACKET) {
if(curPos < startOfLine+lineLen) {
putchar(*curPos);
curPos++;
}
state = GOT_NUTTIN;
} else {
newchar(curChar);
}
break;
case VT100_LEFT:
if(state == GOT_BRACKET) {
if(curPos > startOfLine) {
putchar('\b');
curPos--;
}
state = GOT_NUTTIN;
} else {
newchar(curChar);
}
break;
case OPEN_BRACKET:
if(state == GOT_ESCAPE) {
state = GOT_BRACKET;
} else {
newchar(curChar);
}
break;
case ESC:
state = GOT_ESCAPE;
break;
case VT100_DEL:
if(curPos != (startOfLine + lineLen)) {
ldelete();
}
break;
case '\b':
if(curPos > startOfLine) {
backspace();
}
break;
case '\n':
case '\r':
putchar('\n');
if(lineLen == 0) {
return((char *)0);
}
*(char *)(startOfLine + lineLen) = '\0';
return(startOfLine);
default:
newchar(curChar);
break;
}
}
return((char *)0);
}
/* line_edit() & file_line_edit():
* These two functions are simply front-ends to the lineeditor()
* function. The line_edit() function is called by the command line
* interface and file_line_edit() is called by the flash file editor
* to provide a convenient single line editor when modifying a file.
*/
char *
line_edit(char *line_to_edit)
{
return(lineeditor(line_to_edit,EDITCMDLINE));
}
char *
file_line_edit(char *line_to_edit)
{
return(lineeditor(line_to_edit,EDITFILELINE));
}
static void
ldelete(void)
{
char *eol, *now;
int cnt;
if(lineLen == 0) {
return;
}
cnt = 0;
eol = startOfLine + lineLen - 1;
now = curPos;
#if 0
if(curPos != eol) {
while(curPos <= eol) {
*curPos = *(curPos+1);
curPos++;
cnt++;
}
putbytes(now,cnt-1);
putchar(' ');
backup((int)cnt);
} else {
putstr(" \b\b");
*eol = '\0';
now--;
}
#else
while(curPos <= eol) {
*curPos = *(curPos+1);
curPos++;
cnt++;
}
putbytes(now,cnt-1);
putchar(' ');
backup((int)cnt);
#endif
curPos = now;
lineLen--;
if(lineLen == 0) {
curPos = startOfLine;
}
}
static void
backup(int count)
{
char string[CMDLINESIZE];
int i;
if(count <= 0) {
return;
}
*string = '\0';
for(i=0; i<count; i++) {
strcat(string,"\b");
}
putbytes(string,count);
}
static void
backspace(void)
{
curPos--;
putchar('\b');
ldelete();
}
static void
newchar(char c)
{
char string[CMDLINESIZE];
if(curPos == startOfLine + lineLen) {
putchar(c);
*curPos++ = c;
lineLen++;
} else {
if(!isprint(c)) {
return;
}
putchar(c);
putstr(curPos);
backup((int)strlen(curPos));
strncpy(string,curPos,CMDLINESIZE-1);
*curPos++ = c;
strcpy(curPos,string);
lineLen++;
}
}
static void
lerase(int count)
{
char string[CMDLINESIZE];
int i;
if(count <= 0) {
return;
}
*string = '\0';
for(i=0; i<count; i++) {
strcat(string," ");
}
for(i=0; i<count; i++) {
strcat(string,"\b");
}
putbytes(string,count*2);
}
/* showdone():
* Used as common completion code for the showprev() and
* shownext() functions below.
*/
static void
showdone(int idx)
{
if(idx == HMAX) {
printf("History buffer empty.\007\n");
return;
}
backup((int)(curPos - startOfLine));
lineLen = strlen(cmdhistory[shwidx]);
putbytes(cmdhistory[shwidx],lineLen);
lerase((int)(lastsize-lineLen));
strcpy(startOfLine,cmdhistory[shwidx]);
curPos = startOfLine + lineLen;
lastsize = lineLen;
}
/* showprev() & shownext():
* Show previous or next command in history list based on
* the current position in the list being established by
* the shwidx variable.
*/
static void
showprev(void)
{
int i;
if(shwidx == 0) {
shwidx = HMAX-1;
} else {
shwidx--;
}
for(i=0; i<HMAX; i++) {
if(*cmdhistory[shwidx]) {
break;
}
if(shwidx == 0) {
shwidx = HMAX-1;
} else {
shwidx--;
}
}
showdone(i);
}
static void
shownext(void)
{
int i;
if(shwidx == HMAX-1) {
shwidx = 0;
} else {
shwidx++;
}
for(i=0; i<HMAX; i++) {
if(*cmdhistory[shwidx]) {
break;
}
if(shwidx == HMAX) {
shwidx = 0;
} else {
shwidx++;
}
}
showdone(i);
}
/* History():
* Command used at the CLI to allow the user to dump the content
* of the history buffer.
*/
char *HistoryHelp[] = {
"Display command history",
"",
0,
};
int
History(int argc,char *argv[])
{
int i;
for(i=stridx; i<HMAX; i++) {
if(cmdhistory[i][0]) {
printf("%s\n",cmdhistory[i]);
}
}
if(stridx) {
for(i=0; i<stridx; i++) {
if(cmdhistory[i][0]) {
printf("%s\n",cmdhistory[i]);
}
}
}
return(CMD_SUCCESS);
}
/* historyinit():
* Initialize the command history...
*/
void
historyinit()
{
int i;
shwidx = stridx = 0;
for(i=0; i<HMAX; i++) {
cmdhistory[i][0] = 0;
}
}
/* historylog():
* This function is called by the CLI retrieval code to store away
* the command in the CLI history list, a circular queue
* (size HMAX) of most recent commands.
*/
void
historylog(char *cmdline)
{
int idx;
if(strlen(cmdline) >= CMDLINESIZE) {
return;
}
if(stridx == 0) {
idx = HMAX-1;
} else {
idx = stridx -1;
}
/* don't store if this command is same as last command */
if(strcmp(cmdhistory[idx],cmdline) == 0) {
return;
}
if(stridx == HMAX) {
stridx = 0;
}
strcpy(cmdhistory[stridx++],cmdline);
}
#endif