Logo Search packages:      
Sourcecode: lcrash version File versions  Download package

rl.c

/*
 * 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 == '\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(void)
{
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);
                              maxpos--;
                              showbuf(1);
                        }
                  }
            }
            break;
            case DELETE:
            {
                  if(curpos==maxpos) buz();
                  else
                  {
                        memmove(buf+curpos, buf+curpos+1, maxl-curpos);
                        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(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(void)
{
char *p;
char *command;

      while(1)
      {
            if(notty)
            {
                  printf("%s", prompt);
                  if(!(command=fgets(buf, maxl, stdin))) return NULL;
                  command[strlen(command)-1]='\0';
            }
            else if(!(command=getline())) 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()
{
      if(!rl_init(">> ", 1024, 100)) exit(0);

      printf("notty=%d\n", notty);

      while(1) printf("command=(%s)\n", rl_getline());
}
#endif

Generated by  Doxygen 1.6.0   Back to index