/trunk/lkcdutils/lib/librl/rl.c
C | 673 lines | 484 code | 73 blank | 116 comment | 89 complexity | f4761d4e231f4bf210d62de05df72a85 MD5 | raw file
Possible License(s): GPL-2.0, AGPL-1.0, LGPL-2.1, LGPL-2.0
- /*
- * Copyright 1999 Silicon Graphics, Inc. All rights reserved.
- */
- /*
- Leaner command input module.
- Support basic command line editing, history mechanism and completion
- mechanism.
- History mechanism supported:
- A command name 'h' or 'history' will not be returned by the
- rl utilities and will generate a list of the current history.
- An optional parameter can be passed along with the 'h' or
- 'history' command to set the number of command being remembered.
-
- !! Refer to the previous command. By itself, this substitution
- repeats the previous command.
- !n Refer to command line n .
- !-n Refer to the current command line minus n.
- !str Refer to the most recent command starting with str.
- The standard ^ keys are supported:
- ^W : delete to previous word
- ^D : delete current character
- ^A : goto start of line
- ^E : goto end of line
- ^F : forward one character
- ^B : backward one character
- ^H : delete previous character
- ^N : down history
- ^K : erase to eol (from cursor)
- ^L : clear screen and redisplay prompt
- ^P : up history
- ^U : erase to beginning of line (from cursor)
- ^R : redraw input line
- ESC-f : forward one word
- ESC-b : backward one word
- ESC-d : delete next word
- ESC-DEL : delete previous word
- Completion mechanism supported:
- When TAB is pressed while having inputted line, call the function
- registered beforehand to complete line.
- */
- #include <stdlib.h>
- #include <string.h>
- #include <stdio.h>
- #include <unistd.h>
- #include <ctype.h>
- #include <curses.h>
- #include <term.h>
- #include <termio.h>
- #include "rl.h"
- #define ctrl(c) ((c) & 0x1f)
- static int maxh=DEF_HIST; /* number of buffered commands */
- static int maxl=DEF_LENGTH; /* maximum command length */
- static int in; /* input tty handle */
- static char *prompt=">"; /* prompt to be used */
- static char *bol, *leftN, *rightN, *upN, *downN, *home; /* cursor movement */
- static const char *kup, *kdown, *kleft, *kright, *kdel, *kbksp; /* keyboard input sequences */
- static char *cod; /* display management */
- static char *bip; /* sound the bell */
- static int notty=0; /* no controling terminal or missing basic kbd functionality */
- static int width=80; /* width of the screen we are working from */
- static int xenl=0;
- static char *buf;
- static struct termio tio, stio;
- static const char *const kwb="\033\177";
- static const char *const kwf="\033d";
- static const char *const fw="\033f";
- static const char *const bw="\033b";
- static rl_complete_func_t rl_complete_func; /* function for completing line */
- /*
- setup terminal characteristics and allocate initial stuff
- */
- int
- rl_init(char *p, int bufsize, int histsize)
- {
- char *term;
- int ret;
- /* set prompt if specified */
- if(p) prompt=p;
- if(bufsize) maxl=bufsize;
- if(histsize) maxh=histsize;
- if(!(hist_init(maxh, maxl))) return 0;
- /* allocate a new buffer */
- if(!(buf=malloc(maxl))) return 0;
- /* setup terminal. If we do not have a terminal
- we'll use the gets() call. If we have a terminal
- but no TERM, we use the "dumb" terminal */
- in=fileno(stdin);
- if(!isatty(in))
- {
- notty=1;
- return 1;
- }
- printf("\n");
- bip="\007";
- if(!(term = getenv ("TERM"))) term="dumb";
- else
- {
- /* XXX force iris-ansi-net to iris-ansi
- Current Linux terminfo does'nt know iris-ansi-net */
- if(!strcmp(term,"iris-ansi-net")) term[9]='\0';
- }
- if(setupterm(term, in, &ret)!=ERR)
- {
- /* if any of these basics go back to fgets() */
- if(!(upN=tigetstr("cuu")) ||
- !(downN=tigetstr("cud")) ||
- !(leftN=tigetstr("cub")) ||
- !(bol=tigetstr("cr")) ||
- !(rightN=tigetstr("cuf")) ||
- !(cod=tigetstr("ed"))) { notty=1; return 1; }
- xenl=tigetflag("xenl");
- home=tigetstr("clear");
- kup=tigetstr("kcuu1");
- kdown=tigetstr("kcud1");
- kleft=tigetstr("kcub1");
- kright=tigetstr("kcuf1");
- kdel=tigetstr("kdch1");
- kbksp=tigetstr("kbs");
- } else
- {
- if(!ret)
- printf("Unknown TERM : %s\n", term);
- notty=1;
- return 1;
- }
- /* get window size */
- {
- struct winsize w;
- if (ioctl (in, TIOCGWINSZ, &w) == 0)
- {
- width=w.ws_col;
- }
- else /* use ENV */
- {
- char *ewidth;
- if ((ewidth = getenv ("COLUMNS")))
- width = atoi (ewidth);
- /* use what's in terminfo */
- if (width <= 0)
- width = tigetnum ("co");
- }
- if (width <= 1) width = 80;
- }
- /* set ourselves in the proper mode */
- {
- if(ioctl(in, TCGETA, &tio)) { notty=1; return 1;}
- stio=tio;
- tio.c_lflag &= ~(ICANON | ECHO);
- tio.c_iflag &= ~(ICRNL | INLCR);
- tio.c_cc[VMIN] = 1;
- tio.c_cc[VTIME] = 0;
- }
-
- return 1;
- }
- #define UP_HISTORY 1001
- #define DOWN_HISTORY 1002
- #define CURSOR_LEFT 1003
- #define CURSOR_RIGHT 1004
- #define DELETE 1005
- #define BACKSPACE 1006
- #define KILLLINE 1007
- #define LINEDONE 1008
- #define KILLWORD 1009
- #define KILLTOBOL 1010
- #define KILLTOEOL 1011
- #define GOTOBOL 1012
- #define GOTOEOL 1013
- #define CLRSCR 1014
- #define REDRAW 1015
- #define KILL_WORD_FORWARD 1016
- #define WORD_BACKWARD 1017
- #define WORD_FORWARD 1018
- #define COMPLETE_LINE 1019 /* for completing line */
- #define DEL 1020
- #define NCTRL 16
- static int ctrls[NCTRL][2]=
- {
- {UP_HISTORY, ctrl('P')},
- {DOWN_HISTORY, ctrl('N')},
- {CURSOR_LEFT, ctrl('B')},
- {CURSOR_RIGHT, ctrl('F')},
- {DELETE , ctrl('D')},
- {BACKSPACE, ctrl('H')},
- {LINEDONE, ctrl('J')},
- {KILLWORD, ctrl('W')},
- {KILLTOBOL, ctrl('U')},
- {KILLTOEOL, ctrl('K')},
- {GOTOBOL, ctrl('A')},
- {GOTOEOL, ctrl('E')},
- {CLRSCR, ctrl('L')},
- {REDRAW, ctrl('R')},
- {DEL, '\177'},
- {LINEDONE, '\r'},
- };
- #define NBIND (sizeof(codes)/sizeof(codes[0]))
- static const int codes[]={
- UP_HISTORY,DOWN_HISTORY,CURSOR_LEFT,CURSOR_RIGHT,DELETE,
- BACKSPACE,KILLWORD,KILL_WORD_FORWARD,WORD_BACKWARD,WORD_FORWARD,
- };
- static const char *const *const seqs[]={
- &kup,&kdown,&kleft,&kright,&kdel,
- &kbksp,&kwb,&kwf,&bw,&fw
- };
- #define buz() putp(bip)
- static int
- getinput(void)
- {
- int idx=0;
- char curseq[10];
- while(1)
- {
- int c=getc(stdin);
- int i;
- int found=0;
- if (c == -1) {
- return c;
- }
- if (c == '\t') {
- /* completing line */
- return COMPLETE_LINE;
- }
- /* check the control characters */
- for(i=0;i<NCTRL;i++)
- if(ctrls[i][1]==c) return ctrls[i][0];
- /* check the keyboard sequences */
- curseq[idx++]=c;
- for(i=0;i<NBIND;i++)
- {
- int j;
- if(!*(seqs[i])) continue;
- for(j=0;j<idx;j++)
- {
- if((*seqs[i])[j]==curseq[j])
- {
- /* set found if we match the entire current input */
- if(j==idx-1) found=1;
- if((*seqs[i])[j+1]=='\0') return codes[i];
- }
- }
- }
- if(!found) {
- if(isprint(c))
- return c;
- else {
- buz(); idx=0;
- }
- }
- }
- }
- static int curpos; /* position of cursor inside the input string */
- static int maxpos=0; /* current length of the input string */
- static void curboth(int n)
- {
- int curx=curpos%width;
- int cury=curpos/width;
- int newx=(curpos+n)%width;
- int newy=(curpos+n)/width;
- if(newy > cury) putp(tparm(downN, newy-cury));
- else if(cury > newy) putp(tparm(upN, cury-newy));
- if(newx > curx) putp(tparm(rightN, newx-curx));
- else if(curx > newx) putp(tparm(leftN, curx-newx));
- curpos+=n;
- }
- static void curleft(int n) { curboth(-n); }
- static void curright(int n) { curboth(n); }
- /*
- This function clears the screen button. Displays the current buffer and
- sets the cursor at the proper position.
- */
- static void
- showbuf(int repos)
- {
- int max, i;
- int pos=curpos;
- /* clear to end of display from where we are now */
- putp(cod);
- /* display the current buffer */
- for(i=curpos; i<maxpos; i+=max) {
- int c;
- max=width-(i%width);
- if(i+max > maxpos) max=maxpos-i;
- c=buf[i+max];
- buf[i+max]='\0';
- putp(buf+i);
- buf[i+max]=c;
- if(!((i+max)%width) && xenl) {
- putp("\n");
- putp(bol);
- }
- }
- curpos=maxpos;
- if(repos) curleft(maxpos-pos);
- }
- static char *
- getline(int *error)
- {
- int plen=strlen(prompt);
- int histoff=0;
- /* show the prompt */
- strcpy(buf, prompt);
- maxpos=plen;
- curpos=0;
- showbuf(0);
- curpos=plen;
- ioctl(in, TCSETA, &tio);
- {
- struct winsize w;
- int nw=0;
- if (ioctl (in, TIOCGWINSZ, &w) == 0) nw=w.ws_col;
- if(nw>1) width=nw;
- }
- while(1) {
- int i;
- switch((i=getinput())) {
- case UP_HISTORY: case DOWN_HISTORY:
- {
- int inc=(i==UP_HISTORY?1:-1);
- char *p;
- if((p=hist_getcmd(histoff+inc)))
- {
- curleft(curpos-plen);
- strcpy(buf+plen, p);
- maxpos=strlen(buf);
- showbuf(0);
- curpos=maxpos;
- histoff+=inc;
- }else buz();
- }
- break;
- case BACKSPACE: case DEL: case CURSOR_LEFT:
- {
- if(curpos==plen) buz();
- else
- {
- curleft(1);
- /* we need to reprint if backspace */
- if(i==BACKSPACE || i==DEL)
- {
- memmove(buf+curpos, buf+curpos+1, maxl-curpos-1);
- maxpos--;
- showbuf(1);
- }
- }
- }
- break;
- case DELETE:
- {
- if(curpos==maxpos) buz();
- else
- {
- memmove(buf+curpos, buf+curpos+1, maxl-curpos-1);
- maxpos--;
- showbuf(1);
- }
- }
- break;
- case CURSOR_RIGHT:
- {
- if(curpos==maxpos) buz();
- else { curright(1); }
- }
- break;
- case LINEDONE:
- {
- /* we're about to return, so set the cursor position */
- curright(maxpos-curpos);
- putp("\r\n");
- ioctl(in, TCSETA, &stio);
- return buf+plen;
-
- }
- /* erase entire line . Currently not linked to any keys */
- case KILLLINE:
- {
- curleft(curpos-plen);
- maxpos=plen;
- buf[plen]='\0';
- showbuf(1);
- }
- /* erase the current word */
- case KILLWORD:
- {
- /* if we are at the start of the line , bip */
- if(curpos==plen) buz();
- else
- {
- int i=curpos-1;
- /* if the cursor sits on a white character already
- find the first non white one */
- while(!isalnum(buf[i])&&i>plen) i--;
- /* skip back untill beginning of line or white again */
- while(isalnum(buf[i])&&i>plen) i--;
- if(i<maxpos && !isalnum(buf[i])) i++;
- /* move every backward */
- memmove(buf+i, buf+curpos, maxl-curpos);
- curleft(curpos-i);
- maxpos=strlen(buf);
- showbuf(1);
- }
- }
- break;
- case KILLTOBOL:
- {
- memmove(buf+plen, buf+curpos, maxl-curpos);
- curleft(curpos-plen);
- maxpos=strlen(buf);
- showbuf(1);
- }
- break;
- case KILLTOEOL:
- {
- buf[curpos]='\0';
- maxpos=strlen(buf);
- showbuf(1);
- }
- break;
- case GOTOBOL: { curleft(curpos-plen); } break;
- case GOTOEOL: { curright(maxpos-curpos); } break;
- case CLRSCR:
- {
- if(home) {
- int i=curpos;
- putp(home);
- curpos=0;
- showbuf(0);
- curpos=maxpos;
- curleft(maxpos-i);
- } else buz();
- } break;
-
- case REDRAW: { } break; /* do nothing */
- case WORD_FORWARD:
- /* if we are at the start of the line , bip */
- if(curpos==maxpos) buz();
- else
- {
- int i=curpos;
- /* if the cursor sits on a white character already
- find the first non white one */
- while(!isalnum(buf[i])&&i<maxpos) i++;
- /* scip back untill beginning of line or white again */
- while(isalnum(buf[i])&&i<maxpos) i++;
- curright(i-curpos);
- }
- break;
- case WORD_BACKWARD:
- /* if we are at the start of the line , bip */
- if(curpos==plen) buz();
- else
- {
- int i=curpos;
- /* if the cursor sits on a white character already
- find the first non white one */
- if(i>plen) i--;
- while(!isalnum(buf[i])&&i>plen) i--;
- /* scip back untill beginning of line or white again */
- while(isalnum(buf[i])&&i>plen) i--;
- while(!isalnum(buf[i])&&i>plen) i++;
- curleft(curpos-i);
- }
- break;
- case KILL_WORD_FORWARD:
- /* if we are at the start of the line , bip */
- if(curpos==maxpos) buz();
- else
- {
- int i=curpos;
- /* if the cursor sits on a white character already
- find the first non white one */
- while(!isalnum(buf[i])&&i<maxpos) i++;
- /* scip back untill beginning of line or white again */
- while(isalnum(buf[i])&&i<maxpos) i++;
- while(!isalnum(buf[i])&&i<maxpos) i++;
- /* keep one space */
- if(i<maxpos && isalnum(buf[i])) i--;
- /* move every backward */
- memmove(buf+curpos, buf+i, maxl-i);
- maxpos=strlen(buf);
- showbuf(1);
- }
- break;
- case COMPLETE_LINE: /* complete line */
- {
- char *ret;
- int retstr_len;
- int save_curpos;
- /* if rl_complete_func is not registered, bip */
- if (!rl_complete_func) {
- buz();
- } else {
- /* save current cursor position */
- save_curpos = curpos;
- /* since command list may be printed in rl_complete_func(),
- move cursor to max position */
- curright(maxpos - curpos);
- /* call line completion function */
- ret = rl_complete_func(buf+plen, save_curpos-plen);
- if (ret == DRAW_NEW_ENTIRE_LINE) {
- /* draw new entire line */
- curpos = 0;
- showbuf(0);
- /* resume cursor position */
- curleft(maxpos - save_curpos);
- } else if (ret == PRINT_BEEP) {
- /* resume cursor position */
- curleft(maxpos - save_curpos);
- /* print beep character */
- buz();
- } else if (ret > 0) {
- /* insert string that returned before position of cursor */
- retstr_len = strlen(ret);
- /* resume cursor position */
- curleft(maxpos - save_curpos);
- if (maxpos+retstr_len>maxl) {
- /* if we exceed maximum command length, bip */
- buz();
- } else {
- /* insert string that returned */
- memmove(buf+curpos+retstr_len, buf+curpos, maxl-curpos-retstr_len);
- strncpy(buf+curpos, ret, retstr_len);
- maxpos+=retstr_len;
- showbuf(1);
- curright(retstr_len);
- }
- }
- }
- }
- break;
- default:
- {
- if (i == -1) {
- *error = 1;
- return ((char *)NULL);
- }
- if(maxpos==maxl) buz();
- else
- {
- memmove(buf+curpos+1, buf+curpos, maxl-curpos-1);
- buf[curpos]=i;
- maxpos++;
- showbuf(1);
- curright(1);
- }
- }
- break;
- }
- }
- }
- char *
- rl_getline(int *error)
- {
- char *p;
- char *command;
- if (error) {
- *error = 0;
- }
- while(1)
- {
- if(notty)
- {
- printf("%s", prompt);
- if(!(command=fgets(buf, maxl, stdin))) return NULL;
- command[strlen(command)-1]='\0';
- }
- else if(!(command=getline(error))) return NULL;
- for(p=command;*p==' '||*p=='\t';p++);
- /* if not empty, pass it thought history */
- if(*p && (command=hist_cmd(command)))
- {
- /* hist_cmd() return a pointer to the actual history
- entry, so make a copy to buf and return to user */
- strcpy(buf,command);
- return buf;
- }
- }
- }
- /* register function which complete line */
- void
- rl_register_complete_func(rl_complete_func_t complete_func)
- {
- rl_complete_func = complete_func;
- }
- #ifdef MAIN
- main()
- {
- int error = 0;
- if(!rl_init(">> ", 1024, 100)) exit(0);
- printf("notty=%d\n", notty);
- while(1) printf("command=(%s)\n", rl_getline(&error));
- }
- #endif