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

edit.c

/***********************************************************************
*                                                                      *
*               This software is part of the ast package               *
*          Copyright (c) 1982-2007 AT&T Intellectual Property          *
*                      and is licensed under the                       *
*                  Common Public License, Version 1.0                  *
*                    by AT&T Intellectual Property                     *
*                                                                      *
*                A copy of the License is available at                 *
*            http://www.opensource.org/licenses/cpl1.0.txt             *
*         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
*                                                                      *
*              Information and Software Systems Research               *
*                            AT&T Research                             *
*                           Florham Park NJ                            *
*                                                                      *
*                  David Korn <dgk@research.att.com>                   *
*                                                                      *
***********************************************************************/
#pragma prototyped
/*
 *  edit.c - common routines for vi and emacs one line editors in shell
 *
 *   David Korn                     P.D. Sullivan
 *   AT&T Labs
 *
 *   Coded April 1983.
 */

#include    <ast.h>
#include    <errno.h>
#include    <ccode.h>
#include    <ctype.h>
#include    "FEATURE/options"
#include    "FEATURE/time"
#include    "FEATURE/cmds"
#ifdef _hdr_utime
#   include <utime.h>
#   include <ls.h>
#endif

#if KSHELL
#   include "defs.h"
#   include "variables.h"
#else
    extern char ed_errbuf[];
    char e_version[] = "\n@(#)$Id: Editlib version 1993-12-28 r $\0\n";
#endif      /* KSHELL */
#include    "io.h"
#include    "terminal.h"
#include    "history.h"
#include    "edit.h"

static char CURSOR_UP[20] = { ESC, '[', 'A', 0 };

#if SHOPT_MULTIBYTE
#   define is_cntrl(c)  ((c<=STRIP) && iscntrl(c))
#   define is_print(c)  ((c&~STRIP) || isprint(c))
#else
#   define is_cntrl(c)  iscntrl(c)
#   define is_print(c)  isprint(c)
#endif

#if   (CC_NATIVE == CC_ASCII)
#   define printchar(c) ((c) ^ ('A'-cntl('A')))
#else
    static int printchar(int c)
    {
      switch(c)
      {
          
          case cntl('A'): return('A');
          case cntl('B'): return('B');
          case cntl('C'): return('C');
          case cntl('D'): return('D');
          case cntl('E'): return('E');
          case cntl('F'): return('F');
          case cntl('G'): return('G');
          case cntl('H'): return('H');
          case cntl('I'): return('I');
          case cntl('J'): return('J');
          case cntl('K'): return('K');
          case cntl('L'): return('L');
          case cntl('M'): return('M');
          case cntl('N'): return('N');
          case cntl('O'): return('O');
          case cntl('P'): return('P');
          case cntl('Q'): return('Q');
          case cntl('R'): return('R');
          case cntl('S'): return('S');
          case cntl('T'): return('T');
          case cntl('U'): return('U');
          case cntl('V'): return('V');
          case cntl('W'): return('W');
          case cntl('X'): return('X');
          case cntl('Y'): return('Y');
          case cntl('Z'): return('Z');
          case cntl(']'): return(']');
          case cntl('['): return('[');
      }
      return('?');
    }
#endif
#define MINWINDOW 15    /* minimum width window */
#define DFLTWINDOW      80    /* default window width */
#define RAWMODE         1
#define ALTMODE         2
#define ECHOMODE  3
#define     SYSERR      -1

#if SHOPT_OLDTERMIO
#   undef tcgetattr
#   undef tcsetattr
#endif /* SHOPT_OLDTERMIO */

#ifdef RT
#   define VENIX 1
#endif      /* RT */


#ifdef _hdr_sgtty
#   ifdef TIOCGETP
      static int l_mask;
      static struct tchars l_ttychars;
      static struct ltchars l_chars;
      static  char  l_changed;      /* set if mode bits changed */
#     define L_CHARS    4
#     define T_CHARS    2
#     define L_MASK     1
#   endif /* TIOCGETP */
#endif /* _hdr_sgtty */

#if KSHELL
     static int keytrap(Edit_t *,char*, int, int, int);
#else
     Edit_t editb;
#endif      /* KSHELL */


#ifndef _POSIX_DISABLE
#   define _POSIX_DISABLE     0
#endif

#ifdef future
    static int compare(const char*, const char*, int);
#endif  /* future */
#if SHOPT_VSH || SHOPT_ESH
#   define ttyparm      (ep->e_ttyparm)
#   define nttyparm     (ep->e_nttyparm)
    static const char bellchr[] = "\a";   /* bell char */
#endif /* SHOPT_VSH || SHOPT_ESH */


/*
 * This routine returns true if fd refers to a terminal
 * This should be equivalent to isatty
 */
int tty_check(int fd)
{
      register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context);
      struct termios tty;
      ep->e_savefd = -1;
      return(tty_get(fd,&tty)==0);
}

/*
 * Get the current terminal attributes
 * This routine remembers the attributes and just returns them if it
 *   is called again without an intervening tty_set()
 */

int tty_get(register int fd, register struct termios *tty)
{
      register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context);
      if(fd == ep->e_savefd)
            *tty = ep->e_savetty;
      else
      {
            while(tcgetattr(fd,tty) == SYSERR)
            {
                  if(errno !=EINTR)
                        return(SYSERR);
                  errno = 0;
            }
            /* save terminal settings if in cannonical state */
            if(ep->e_raw==0)
            {
                  ep->e_savetty = *tty;
                  ep->e_savefd = fd;
            }
      }
      return(0);
}

/*
 * Set the terminal attributes
 * If fd<0, then current attributes are invalidated
 */

int tty_set(int fd, int action, struct termios *tty)
{
      register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context);
      if(fd >=0)
      {
#ifdef future
            if(ep->e_savefd>=0 && compare(&ep->e_savetty,tty,sizeof(struct termios)))
                  return(0);
#endif
            while(tcsetattr(fd, action, tty) == SYSERR)
            {
                  if(errno !=EINTR)
                        return(SYSERR);
                  errno = 0;
            }
            ep->e_savetty = *tty;
      }
      ep->e_savefd = fd;
      return(0);
}

#if SHOPT_ESH || SHOPT_VSH
/*{   TTY_COOKED( fd )
 *
 *    This routine will set the tty in cooked mode.
 *    It is also called by error.done().
 *
}*/

void tty_cooked(register int fd)
{
      register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context);
      if(ep->e_raw==0)
            return;
      if(fd < 0)
            fd = ep->e_savefd;
#ifdef L_MASK
      /* restore flags */
      if(l_changed&L_MASK)
            ioctl(fd,TIOCLSET,&l_mask);
      if(l_changed&T_CHARS)
            /* restore alternate break character */
            ioctl(fd,TIOCSETC,&l_ttychars);
      if(l_changed&L_CHARS)
            /* restore alternate break character */
            ioctl(fd,TIOCSLTC,&l_chars);
      l_changed = 0;
#endif      /* L_MASK */
      /*** don't do tty_set unless ttyparm has valid data ***/
      if(tty_set(fd, TCSANOW, &ttyparm) == SYSERR)
            return;
      ep->e_raw = 0;
      return;
}

/*{   TTY_RAW( fd )
 *
 *    This routine will set the tty in raw mode.
 *
}*/

int tty_raw(register int fd, int echomode)
{
      int echo = echomode;
#ifdef L_MASK
      struct ltchars lchars;
#endif      /* L_MASK */
      register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context);
      if(ep->e_raw==RAWMODE)
            return(echo?-1:0);
      else if(ep->e_raw==ECHOMODE)
            return(echo?0:-1);
#if !SHOPT_RAWONLY
      if(ep->e_raw != ALTMODE)
#endif /* SHOPT_RAWONLY */
      {
            if(tty_get(fd,&ttyparm) == SYSERR)
                  return(-1);
      }
#if  L_MASK || VENIX
      if(ttyparm.sg_flags&LCASE)
            return(-1);
      if(!(ttyparm.sg_flags&ECHO))
      {
            if(!echomode)
                  return(-1);
            echo = 0;
      }
      nttyparm = ttyparm;
      if(!echo)
            nttyparm.sg_flags &= ~(ECHO | TBDELAY);
#   ifdef CBREAK
      nttyparm.sg_flags |= CBREAK;
#   else
      nttyparm.sg_flags |= RAW;
#   endif /* CBREAK */
      ep->e_erase = ttyparm.sg_erase;
      ep->e_kill = ttyparm.sg_kill;
      ep->e_eof = cntl('D');
      ep->e_werase = cntl('W');
      ep->e_lnext = cntl('V');
      if( tty_set(fd, TCSADRAIN, &nttyparm) == SYSERR )
            return(-1);
      ep->e_ttyspeed = (ttyparm.sg_ospeed>=B1200?FAST:SLOW);
#   ifdef TIOCGLTC
      /* try to remove effect of ^V  and ^Y and ^O */
      if(ioctl(fd,TIOCGLTC,&l_chars) != SYSERR)
      {
            lchars = l_chars;
            lchars.t_lnextc = -1;
            lchars.t_flushc = -1;
            lchars.t_dsuspc = -1;   /* no delayed stop process signal */
            if(ioctl(fd,TIOCSLTC,&lchars) != SYSERR)
                  l_changed |= L_CHARS;
      }
#   endif   /* TIOCGLTC */
#else
      if (!(ttyparm.c_lflag & ECHO ))
      {
            if(!echomode)
                  return(-1);
            echo = 0;
      }
#   ifdef FLUSHO
      ttyparm.c_lflag &= ~FLUSHO;
#   endif /* FLUSHO */
      nttyparm = ttyparm;
#  ifndef u370
      nttyparm.c_iflag &= ~(IGNPAR|PARMRK|INLCR|IGNCR|ICRNL);
      nttyparm.c_iflag |= BRKINT;
#   else
      nttyparm.c_iflag &= 
                  ~(IGNBRK|PARMRK|INLCR|IGNCR|ICRNL|INPCK);
      nttyparm.c_iflag |= (BRKINT|IGNPAR);
#   endif   /* u370 */
      if(echo)
            nttyparm.c_lflag &= ~ICANON;
      else
            nttyparm.c_lflag &= ~(ICANON|ECHO|ECHOK);
      nttyparm.c_cc[VTIME] = 0;
      nttyparm.c_cc[VMIN] = 1;
#   ifdef VREPRINT
      nttyparm.c_cc[VREPRINT] = _POSIX_DISABLE;
#   endif /* VREPRINT */
#   ifdef VDISCARD
      nttyparm.c_cc[VDISCARD] = _POSIX_DISABLE;
#   endif /* VDISCARD */
#   ifdef VDSUSP
      nttyparm.c_cc[VDSUSP] = _POSIX_DISABLE;
#   endif /* VDSUSP */
#   ifdef VWERASE
      if(ttyparm.c_cc[VWERASE] == _POSIX_DISABLE)
            ep->e_werase = cntl('W');
      else
            ep->e_werase = nttyparm.c_cc[VWERASE];
      nttyparm.c_cc[VWERASE] = _POSIX_DISABLE;
#   else
          ep->e_werase = cntl('W');
#   endif /* VWERASE */
#   ifdef VLNEXT
      if(ttyparm.c_cc[VLNEXT] == _POSIX_DISABLE )
            ep->e_lnext = cntl('V');
      else
            ep->e_lnext = nttyparm.c_cc[VLNEXT];
      nttyparm.c_cc[VLNEXT] = _POSIX_DISABLE;
#   else
      ep->e_lnext = cntl('V');
#   endif /* VLNEXT */
      ep->e_eof = ttyparm.c_cc[VEOF];
      ep->e_erase = ttyparm.c_cc[VERASE];
      ep->e_kill = ttyparm.c_cc[VKILL];
      if( tty_set(fd, TCSADRAIN, &nttyparm) == SYSERR )
            return(-1);
      ep->e_ttyspeed = (cfgetospeed(&ttyparm)>=B1200?FAST:SLOW);
#endif
      ep->e_raw = (echomode?ECHOMODE:RAWMODE);
      return(0);
}

#if !SHOPT_RAWONLY

/*
 *
 *    Get tty parameters and make ESC and '\r' wakeup characters.
 *
 */

#   ifdef TIOCGETC
int tty_alt(register int fd)
{
      register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context);
      int mask;
      struct tchars ttychars;
      switch(ep->e_raw)
      {
          case ECHOMODE:
            return(-1);
          case ALTMODE:
            return(0);
          case RAWMODE:
            tty_cooked(fd);
      }
      l_changed = 0;
      if( ep->e_ttyspeed == 0)
      {
            if((tty_get(fd,&ttyparm) != SYSERR))
                  ep->e_ttyspeed = (ttyparm.sg_ospeed>=B1200?FAST:SLOW);
            ep->e_raw = ALTMODE;
      }
      if(ioctl(fd,TIOCGETC,&l_ttychars) == SYSERR)
            return(-1);
      if(ioctl(fd,TIOCLGET,&l_mask)==SYSERR)
            return(-1);
      ttychars = l_ttychars;
      mask =  LCRTBS|LCRTERA|LCTLECH|LPENDIN|LCRTKIL;
      if((l_mask|mask) != l_mask)
            l_changed = L_MASK;
      if(ioctl(fd,TIOCLBIS,&mask)==SYSERR)
            return(-1);
      if(ttychars.t_brkc!=ESC)
      {
            ttychars.t_brkc = ESC;
            l_changed |= T_CHARS;
            if(ioctl(fd,TIOCSETC,&ttychars) == SYSERR)
                  return(-1);
      }
      return(0);
}
#   else
#     ifndef PENDIN
#         define PENDIN 0
#     endif /* PENDIN */
#     ifndef IEXTEN
#         define IEXTEN 0
#     endif /* IEXTEN */

int tty_alt(register int fd)
{
      register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context);
      switch(ep->e_raw)
      {
          case ECHOMODE:
            return(-1);
          case ALTMODE:
            return(0);
          case RAWMODE:
            tty_cooked(fd);
      }
      if((tty_get(fd, &ttyparm)==SYSERR) || (!(ttyparm.c_lflag&ECHO)))
            return(-1);
#     ifdef FLUSHO
          ttyparm.c_lflag &= ~FLUSHO;
#     endif /* FLUSHO */
      nttyparm = ttyparm;
      ep->e_eof = ttyparm.c_cc[VEOF];
#     ifdef ECHOCTL
          /* escape character echos as ^[ */
          nttyparm.c_lflag |= (ECHOE|ECHOK|ECHOCTL|PENDIN|IEXTEN);
          nttyparm.c_cc[VEOL] = ESC;
#     else
          /* switch VEOL2 and EOF, since EOF isn't echo'd by driver */
          nttyparm.c_lflag |= (ECHOE|ECHOK);
          nttyparm.c_cc[VEOF] = ESC;      /* make ESC the eof char */
#         ifdef VEOL2
            nttyparm.c_iflag &= ~(IGNCR|ICRNL);
            nttyparm.c_iflag |= INLCR;
            nttyparm.c_cc[VEOL] = '\r';   /* make CR an eol char */
            nttyparm.c_cc[VEOL2] = ep->e_eof; /* make EOF an eol char */
#         else
            nttyparm.c_cc[VEOL] = ep->e_eof; /* make EOF an eol char */
#         endif /* VEOL2 */
#     endif /* ECHOCTL */
#     ifdef VREPRINT
            nttyparm.c_cc[VREPRINT] = _POSIX_DISABLE;
#     endif /* VREPRINT */
#     ifdef VDISCARD
            nttyparm.c_cc[VDISCARD] = _POSIX_DISABLE;
#     endif /* VDISCARD */
#     ifdef VWERASE
          if(ttyparm.c_cc[VWERASE] == _POSIX_DISABLE)
                nttyparm.c_cc[VWERASE] = cntl('W');
          ep->e_werase = nttyparm.c_cc[VWERASE];
#     else
          ep->e_werase = cntl('W');
#     endif /* VWERASE */
#     ifdef VLNEXT
          if(ttyparm.c_cc[VLNEXT] == _POSIX_DISABLE )
                nttyparm.c_cc[VLNEXT] = cntl('V');
          ep->e_lnext = nttyparm.c_cc[VLNEXT];
#     else
          ep->e_lnext = cntl('V');
#     endif /* VLNEXT */
      ep->e_erase = ttyparm.c_cc[VERASE];
      ep->e_kill = ttyparm.c_cc[VKILL];
      if( tty_set(fd, TCSADRAIN, &nttyparm) == SYSERR )
            return(-1);
      ep->e_ttyspeed = (cfgetospeed(&ttyparm)>=B1200?FAST:SLOW);
      ep->e_raw = ALTMODE;
      return(0);
}

#   endif /* TIOCGETC */
#endif      /* SHOPT_RAWONLY */

/*
 *    ED_WINDOW()
 *
 *    return the window size
 */
int ed_window(void)
{
      int   rows,cols;
      register char *cp = nv_getval(COLUMNS);
      if(cp)
            cols = (int)strtol(cp, (char**)0, 10)-1;
      else
      {
            astwinsize(2,&rows,&cols);
            if(--cols <0)
                  cols = DFLTWINDOW-1;
      }
      if(cols < MINWINDOW)
            cols = MINWINDOW;
      else if(cols > MAXWINDOW)
            cols = MAXWINDOW;
      return(cols);
}

/*    E_FLUSH()
 *
 *    Flush the output buffer.
 *
 */

void ed_flush(Edit_t *ep)
{
      register int n = ep->e_outptr-ep->e_outbase;
      register int fd = ERRIO;
      if(n<=0)
            return;
      write(fd,ep->e_outbase,(unsigned)n);
      ep->e_outptr = ep->e_outbase;
}

/*
 * send the bell character ^G to the terminal
 */

void ed_ringbell(void)
{
      write(ERRIO,bellchr,1);
}

/*
 * send a carriage return line feed to the terminal
 */

void ed_crlf(register Edit_t *ep)
{
#ifdef cray
      ed_putchar(ep,'\r');
#endif /* cray */
#ifdef u370
      ed_putchar(ep,'\r');
#endif      /* u370 */
#ifdef VENIX
      ed_putchar(ep,'\r');
#endif /* VENIX */
      ed_putchar(ep,'\n');
      ed_flush(ep);
}
 
/*    ED_SETUP( max_prompt_size )
 *
 *    This routine sets up the prompt string
 *    The following is an unadvertised feature.
 *      Escape sequences in the prompt can be excluded from the calculated
 *      prompt length.  This is accomplished as follows:
 *      - if the prompt string starts with "%\r, or contains \r%\r", where %
 *        represents any char, then % is taken to be the quote character.
 *      - strings enclosed by this quote character, and the quote character,
 *        are not counted as part of the prompt length.
 */

void  ed_setup(register Edit_t *ep, int fd, int reedit)
{
      Shell_t *shp = ep->sh;
      register char *pp;
      register char *last;
      char *ppmax;
      int myquote = 0, n;
      register int qlen = 1;
      char inquote = 0;
      ep->e_fd = fd;
      ep->e_multiline = sh_isoption(SH_MULTILINE)!=0;
#ifdef SIGWINCH
      if(!(shp->sigflag[SIGWINCH]&SH_SIGFAULT))
      {
            signal(SIGWINCH,sh_fault);
            shp->sigflag[SIGWINCH] |= SH_SIGFAULT;
      }
      pp = shp->st.trapcom[SIGWINCH];
      shp->st.trapcom[SIGWINCH] = 0;
      sh_fault(SIGWINCH);
      shp->st.trapcom[SIGWINCH] = pp;
#endif
#if KSHELL
      ep->e_stkptr = stakptr(0);
      ep->e_stkoff = staktell();
      if(!(last = shp->prompt))
            last = "";
      shp->prompt = 0;
#else
      last = ep->e_prbuff;
#endif /* KSHELL */
      if(shp->hist_ptr)
      {
            register History_t *hp = shp->hist_ptr;
            ep->e_hismax = hist_max(hp);
            ep->e_hismin = hist_min(hp);
      }
      else
      {
            ep->e_hismax = ep->e_hismin = ep->e_hloff = 0;
      }
      ep->e_hline = ep->e_hismax;
      if(!sh_isoption(SH_VI) && !sh_isoption(SH_EMACS) && !sh_isoption(SH_GMACS))
            ep->e_wsize = MAXLINE;
      else
            ep->e_wsize = ed_window()-2;
      ep->e_winsz = ep->e_wsize+2;
      ep->e_crlf = 1;
      ep->e_plen = 0;
      pp = ep->e_prompt;
      ppmax = pp+PRSIZE-1;
      *pp++ = '\r';
      {
            register int c;
            while(c= *last++) switch(c)
            {
                  case ESC:
                  {
                        int skip=0;
                        ep->e_crlf = 0;
                        *pp++ = c;
                        for(n=1; c = *last++; n++)
                        {
                              if(pp < ppmax)
                                    *pp++ = c;
                              if(c=='\a' || c==ESC || c=='\r')
                                    break;
                              if(skip || (c>='0' && c<='9'))
                                    continue;
                              if(n>1 && c==';')
                                    skip = 1;
                              else if(n>2 || (c!= '[' &&  c!= ']'))
                                    break;
                        }
                        if(c==0 || c==ESC || c=='\r')
                              last--;
                        qlen += (n+1);
                        break;
                  }
                  case '\b':
                        if(pp>ep->e_prompt+1)
                              pp--;
                        break;
                  case '\r':
                        if(pp == (ep->e_prompt+2)) /* quote char */
                              myquote = *(pp-1);
                        /*FALLTHROUGH*/

                  case '\n':
                        /* start again */
                        ep->e_crlf = 1;
                        qlen = 1;
                        inquote = 0;
                        pp = ep->e_prompt+1;
                        break;

                  case '\t':
                        /* expand tabs */
                        while((pp-ep->e_prompt)%TABSIZE)
                        {
                              if(pp >= ppmax)
                                    break;
                              *pp++ = ' ';
                        }
                        break;

                  case '\a':
                        /* cut out bells */
                        break;

                  default:
                        if(c==myquote)
                        {
                              qlen += inquote;
                              inquote ^= 1;
                        }
                        if(pp < ppmax)
                        {
                              qlen += inquote;
                              *pp++ = c;
                              if(!inquote && !is_print(c))
                                    ep->e_crlf = 0;
                        }
            }
      }
      if(pp-ep->e_prompt > qlen)
            ep->e_plen = pp - ep->e_prompt - qlen;
      *pp = 0;
      if(!ep->e_multiline && (ep->e_wsize -= ep->e_plen) < 7)
      {
            register int shift = 7-ep->e_wsize;
            ep->e_wsize = 7;
            pp = ep->e_prompt+1;
            strcpy(pp,pp+shift);
            ep->e_plen -= shift;
            last[-ep->e_plen-2] = '\r';
      }
      sfsync(sfstderr);
      if(fd == sffileno(sfstderr))
      {
            /* can't use output buffer when reading from stderr */
            static char *buff;
            if(!buff)
                  buff = (char*)malloc(MAXLINE);
            ep->e_outbase = ep->e_outptr = buff;
            ep->e_outlast = ep->e_outptr + MAXLINE;
            return;
      }
      qlen = sfset(sfstderr,SF_READ,0);
      /* make sure SF_READ not on */
      ep->e_outbase = ep->e_outptr = (char*)sfreserve(sfstderr,SF_UNBOUND,SF_LOCKR);
      ep->e_outlast = ep->e_outptr + sfvalue(sfstderr);
      if(qlen)
            sfset(sfstderr,SF_READ,1);
      sfwrite(sfstderr,ep->e_outptr,0);
      ep->e_eol = reedit;
      if(ep->e_multiline)
      {
#ifdef _cmd_tput
            char *term;
            if(!ep->e_term)
                  ep->e_term = nv_search("TERM",shp->var_tree,0);
            if(ep->e_term && (term=nv_getval(ep->e_term)) && strlen(term)<sizeof(ep->e_termname) && strcmp(term,ep->e_termname))
            {
                  sh_trap(".sh.subscript=$(tput cuu1 2>/dev/null)",0);
                  if(pp=nv_getval(SH_SUBSCRNOD))
                        strncpy(CURSOR_UP,pp,sizeof(CURSOR_UP)-1);
                  nv_unset(SH_SUBSCRNOD);
                  strcpy(ep->e_termname,term);
            }
#endif
            ep->e_wsize = MAXLINE - (ep->e_plen-2);
      }
      if(ep->e_default && (pp = nv_getval(ep->e_default)))
      {
            n = strlen(pp);
            if(n > LOOKAHEAD)
                  n = LOOKAHEAD;
            ep->e_lookahead = n;
            while(n-- > 0)
                  ep->e_lbuf[n] = *pp++;
            ep->e_default = 0;
      }
}

/*
 * Do read, restart on interrupt unless SH_SIGSET or SH_SIGTRAP is set
 * Use sfpkrd() to poll() or select() to wait for input if possible
 * Unfortunately, systems that get interrupted from slow reads update
 * this access time for for the terminal (in violation of POSIX).
 * The fixtime() macro, resets the time to the time at entry in
 * this case.  This is not necessary for systems that can handle
 * sfpkrd() correctly (i,e., those that support poll() or select()
 */
int ed_read(void *context, int fd, char *buff, int size, int reedit)
{
      register Edit_t *ep = (Edit_t*)context;
      register int rv= -1;
      register int delim = (ep->e_raw==RAWMODE?'\r':'\n');
      Shell_t *shp = ep->sh;
      int mode = -1;
      int (*waitevent)(int,long,int) = shp->waitevent;
      if(ep->e_raw==ALTMODE)
            mode = 1;
      if(size < 0)
      {
            mode = 1;
            size = -size;
      }
      sh_onstate(SH_TTYWAIT);
      errno = EINTR;
      shp->waitevent = 0;
      while(rv<0 && errno==EINTR)
      {
            if(shp->trapnote&(SH_SIGSET|SH_SIGTRAP))
                  goto done;
            /* an interrupt that should be ignored */
            errno = 0;
            if(!waitevent || (rv=(*waitevent)(fd,-1L,0))>=0)
                  rv = sfpkrd(fd,buff,size,delim,-1L,mode);
      }
      if(rv < 0)
      {
#ifdef _hdr_utime
#           define fixtime()  if(isdevtty)utime(ep->e_tty,&utimes)
            int   isdevtty=0;
            struct stat statb;
            struct utimbuf utimes;
            if(errno==0 && !ep->e_tty)
            {
                  if((ep->e_tty=ttyname(fd)) && stat(ep->e_tty,&statb)>=0)
                  {
                        ep->e_tty_ino = statb.st_ino;
                        ep->e_tty_dev = statb.st_dev;
                  }
            }
            if(ep->e_tty_ino && fstat(fd,&statb)>=0 && statb.st_ino==ep->e_tty_ino && statb.st_dev==ep->e_tty_dev)
            {
                  utimes.actime = statb.st_atime;
                  utimes.modtime = statb.st_mtime;
                  isdevtty=1;
            }
#else
#           define fixtime()
#endif /* _hdr_utime */
            while(1)
            {
                  rv = read(fd,buff,size);
                  if(rv>=0 || errno!=EINTR)
                        break;
                  if(shp->trapnote&(SH_SIGSET|SH_SIGTRAP))
                        goto done;
                  /* an interrupt that should be ignored */
                  fixtime();
            }
      }
      else if(rv>=0 && mode>0)
            rv = read(fd,buff,rv>0?rv:1);
done:
      shp->waitevent = waitevent;
      sh_offstate(SH_TTYWAIT);
      return(rv);
}


/*
 * put <string> of length <nbyte> onto lookahead stack
 * if <type> is non-zero,  the negation of the character is put
 *    onto the stack so that it can be checked for KEYTRAP
 * putstack() returns 1 except when in the middle of a multi-byte char
 */
static int putstack(Edit_t *ep,char string[], register int nbyte, int type) 
{
      register int c;
#if SHOPT_MULTIBYTE
      char *endp, *p=string;
      int size, offset = ep->e_lookahead + nbyte;
      *(endp = &p[nbyte]) = 0;
      endp = &p[nbyte];
      do
      {
            c = (int)((*p) & STRIP);
            if(c< 0x80 && c!='<')
            {
                  if (type)
                        c = -c;
#   ifndef CBREAK
                  if(c == '\0')
                  {
                        /*** user break key ***/
                        ep->e_lookahead = 0;
#     if KSHELL
                        sh_fault(SIGINT);
                        siglongjmp(ep->e_env, UINTR);
#     endif   /* KSHELL */
                  }
#   endif /* CBREAK */

            }
            else
            {
            again:
                  if((c=mbchar(p)) >=0)
                  {
                        p--;  /* incremented below */
                        if(type)
                              c = -c;
                  }
#ifdef EILSEQ
                  else if(errno == EILSEQ)
                        errno = 0;
#endif
                  else if((endp-p) < mbmax())
                  {
                        if ((c=ed_read(ep,ep->e_fd,endp, 1,0)) == 1)
                        {
                              *++endp = 0;
                              goto again;
                        }
                        return(c);
                  }
                  else
                  {
                        ed_ringbell();
                        c = -(int)((*p) & STRIP);
                        offset += mbmax()-1;
                  }
            }
            ep->e_lbuf[--offset] = c;
            p++;
      }
      while (p < endp);
      /* shift lookahead buffer if necessary */
      if(offset -= ep->e_lookahead)
      {
            for(size=offset;size < nbyte;size++)
                  ep->e_lbuf[ep->e_lookahead+size-offset] = ep->e_lbuf[ep->e_lookahead+size];
      }
      ep->e_lookahead += nbyte-offset;
#else
      while (nbyte > 0)
      {
            c = string[--nbyte] & STRIP;
            ep->e_lbuf[ep->e_lookahead++] = (type?-c:c);
#   ifndef CBREAK
            if( c == '\0' )
            {
                  /*** user break key ***/
                  ep->e_lookahead = 0;
#     if KSHELL
                  sh_fault(SIGINT);
                  siglongjmp(ep->e_env, UINTR);
#     endif /* KSHELL */
            }
#   endif /* CBREAK */
      }
#endif /* SHOPT_MULTIBYTE */
      return(1);
}

/*
 * routine to perform read from terminal for vi and emacs mode
 * <mode> can be one of the following:
 *   -2           vi insert mode - key binding is in effect
 *   -1           vi control mode - key binding is in effect
 *   0            normal command mode - key binding is in effect
 *   1            edit keys not mapped
 *   2            Next key is literal
 */
int ed_getchar(register Edit_t *ep,int mode)
{
      register int n, c;
      char readin[LOOKAHEAD+1];
      if(!ep->e_lookahead)
      {
            ed_flush(ep);
            ep->e_inmacro = 0;
            /* The while is necessary for reads of partial multbyte chars */
            if((n=ed_read(ep,ep->e_fd,readin,-LOOKAHEAD,0)) > 0)
                  n = putstack(ep,readin,n,1);
      }
      if(ep->e_lookahead)
      {
            /* check for possible key mapping */
            if((c = ep->e_lbuf[--ep->e_lookahead]) < 0)
            {
                  if(mode<=0 && ep->sh->st.trap[SH_KEYTRAP])
                  {
                        n=1;
                        if((readin[0]= -c) == ESC)
                        {
                              while(1)
                              {
                                    if(!ep->e_lookahead)
                                    {
                                          if((c=sfpkrd(ep->e_fd,readin+n,1,'\r',(mode?400L:-1L),0))>0)
                                                putstack(ep,readin+n,c,1);
                                    }
                                    if(!ep->e_lookahead)
                                          break;
                                    if((c=ep->e_lbuf[--ep->e_lookahead])>=0)
                                    {
                                          ep->e_lookahead++;
                                          break;
                                    }
                                    c = -c;
                                    readin[n++] = c;
                                    if(c>='0' && c<='9' && n>2)
                                          continue;
                                    if(n>2 || (c!= '['  &&  c!= 'O'))
                                          break;
                              }
                        }
                        if(n=keytrap(ep,readin,n,LOOKAHEAD-n,mode))
                        {
                              putstack(ep,readin,n,0);
                              c = ep->e_lbuf[--ep->e_lookahead];
                        }
                        else
                              c = ed_getchar(ep,mode);
                  }
                  else
                        c = -c;
            }
            /*** map '\r' to '\n' ***/
            if(c == '\r' && mode!=2)
                  c = '\n';
            if(ep->e_tabcount && !(c=='\t'||c==ESC || c=='\\' || c=='=' || c==cntl('L') || isdigit(c)))
                  ep->e_tabcount = 0;
      }
      else
            siglongjmp(ep->e_env,(n==0?UEOF:UINTR));
      return(c);
}

void ed_ungetchar(Edit_t *ep,register int c)
{
      if (ep->e_lookahead < LOOKAHEAD)
            ep->e_lbuf[ep->e_lookahead++] = c;
      return;
}

/*
 * put a character into the output buffer
 */

void  ed_putchar(register Edit_t *ep,register int c)
{
      char buf[8];
      register char *dp = ep->e_outptr;
      register int i,size=1;
      buf[0] = c;
#if SHOPT_MULTIBYTE
      /* check for place holder */
      if(c == MARKER)
            return;
      if((size = mbconv(buf, (wchar_t)c)) > 1)
      {
            for (i = 0; i < (size-1); i++)
                  *dp++ = buf[i];
            c = buf[i];
      }
      else
      {
            buf[0] = c;
            size = 1;
      }
#endif      /* SHOPT_MULTIBYTE */
      if (buf[0] == '_' && size==1)
      {
            *dp++ = ' ';
            *dp++ = '\b';
      }
      *dp++ = c;
      *dp = '\0';
      if(dp >= ep->e_outlast)
            ed_flush(ep);
      else
            ep->e_outptr = dp;
}

/*
 * returns the line and column corresponding to offset <off> in the physical buffer
 * if <cur> is non-zero and <= <off>, then correspodning <curpos> will start the search 
 */
Edpos_t ed_curpos(Edit_t *ep,genchar *phys, int off, int cur, Edpos_t curpos)
{
      register genchar *sp=phys;
      register int c=1, col=ep->e_plen;
      Edpos_t pos;
#if SHOPT_MULTIBYTE
      char p[16];
#endif /* SHOPT_MULTIBYTE */
      if(cur && off>=cur)
      {
            sp += cur; 
            off -= cur;
            pos = curpos;
            col = pos.col;
      }
      else
      {
            pos.line = 0;
            while(col > ep->e_winsz)
            {
                  pos.line++;
                  col -= (ep->e_winsz+1);
            }
      }
      while(off-->0)
      {
            if(c)
                  c = *sp++;
#if SHOPT_MULTIBYTE
            if(c && (mbconv(p, (wchar_t)c))==1 && p[0]=='\n')
#else
            if(c=='\n')
#endif /* SHOPT_MULTIBYTE */
                  col = 0;
            else
                  col++;
            if(col >  ep->e_winsz)
                  col = 0;
            if(col==0)
                  pos.line++;
      }
      pos.col = col;
      return(pos);
}

static void ed_putstring(register Edit_t *ep, const char *str)
{
      register int c;
      while(c = *str++)
            ed_putchar(ep,c);
}

int ed_setcursor(register Edit_t *ep,genchar *physical,register int old,register int new,int first)
{
      static int oldline;
      register int delta;
      Edpos_t newpos;

      delta = new - old;
      if( delta == 0 )
            return(new);
      if(ep->e_multiline)
      {
            ep->e_curpos = ed_curpos(ep, physical, old,0,ep->e_curpos);
            newpos =     ed_curpos(ep, physical, new,old,ep->e_curpos);
            if(ep->e_curpos.col==0 && ep->e_curpos.line>0 && oldline<ep->e_curpos.line && delta<0)
                  ed_putstring(ep,"\r\n");
            oldline = newpos.line;
            if(ep->e_curpos.line > newpos.line)
            {
                  int n,pline,plen=ep->e_plen;
                  for(;ep->e_curpos.line > newpos.line; ep->e_curpos.line--)
                        ed_putstring(ep,CURSOR_UP);
                  pline = plen/(ep->e_winsz+1);
                  plen -= pline*(ep->e_winsz+1);
                  if((n=plen- ep->e_curpos.col)>0)
                  {
                        ep->e_curpos.col += n;
                        ed_putchar(ep,'\r');
                        if(!ep->e_crlf && pline==0)
                              ed_putstring(ep,ep->e_prompt);
                        else
                        {
                              int m = ep->e_winsz+1-plen;
                              ed_putchar(ep,'\n');
                              n = plen;
                              if(m < ed_genlen(physical))
                              {
                                    while(physical[m] && n-->0)
                                          ed_putchar(ep,physical[m++]);
                              }
                              while(n-->0)
                                    ed_putchar(ep,' ');
                              ed_putstring(ep,CURSOR_UP);
                        }
                  }
            }
            else if(ep->e_curpos.line < newpos.line)
            {
                  for(;ep->e_curpos.line < newpos.line;ep->e_curpos.line++)
                        ed_putchar(ep,'\n');
                  ed_putchar(ep,'\r');
                  ep->e_curpos.col = 0;
            }
            delta = newpos.col - ep->e_curpos.col;
            old   =  new - delta;
      }
      else
            newpos.line=0;
      if(delta<0)
      {
            int bs= newpos.line && ep->e_plen>ep->e_winsz;
            /*** move to left ***/
            delta = -delta;
            /*** attempt to optimize cursor movement ***/
            if(!ep->e_crlf || bs || (2*delta <= ((old-first)+(newpos.line?0:ep->e_plen))) )
            {
                  for( ; delta; delta-- )
                        ed_putchar(ep,'\b');
            }
            else
            {
                  if(newpos.line==0)
                        ed_putstring(ep,ep->e_prompt);
                  old = first;
                  delta = new-first;
            }
      }
      while(delta-->0)
            ed_putchar(ep,physical[old++]);
      return(new);
}

/*
 * copy virtual to physical and return the index for cursor in physical buffer
 */
int ed_virt_to_phys(Edit_t *ep,genchar *virt,genchar *phys,int cur,int voff,int poff)
{
      register genchar *sp = virt;
      register genchar *dp = phys;
      register int c;
      genchar *curp = sp + cur;
      genchar *dpmax = phys+MAXLINE;
      int d, r;
      sp += voff;
      dp += poff;
      for(r=poff;c= *sp;sp++)
      {
            if(curp == sp)
                  r = dp - phys;
#if SHOPT_MULTIBYTE
            d = mbwidth((wchar_t)c);
            if(d==1 && is_cntrl(c))
                  d = -1;
            if(d>1)
            {
                  /* multiple width character put in place holders */
                  *dp++ = c;
                  while(--d >0)
                        *dp++ = MARKER;
                  /* in vi mode the cursor is at the last character */
                  if(dp>=dpmax)
                        break;
                  continue;
            }
            else
#else
            d = (is_cntrl(c)?-1:1);
#endif      /* SHOPT_MULTIBYTE */
            if(d<0)
            {
                  if(c=='\t')
                  {
                        c = dp-phys;
                        if(sh_isoption(SH_VI))
                              c += ep->e_plen;
                        c = TABSIZE - c%TABSIZE;
                        while(--c>0)
                              *dp++ = ' ';
                        c = ' ';
                  }
                  else
                  {
                        *dp++ = '^';
                        c = printchar(c);
                  }
                  /* in vi mode the cursor is at the last character */
                  if(curp == sp && sh_isoption(SH_VI))
                        r = dp - phys;
            }
            *dp++ = c;
            if(dp>=dpmax)
                  break;
      }
      *dp = 0;
      return(r);
}

#if SHOPT_MULTIBYTE
/*
 * convert external representation <src> to an array of genchars <dest>
 * <src> and <dest> can be the same
 * returns number of chars in dest
 */

int   ed_internal(const char *src, genchar *dest)
{
      register const unsigned char *cp = (unsigned char *)src;
      register int c;
      register wchar_t *dp = (wchar_t*)dest;
      if(dest == (genchar*)roundof(cp-(unsigned char*)0,sizeof(genchar)))
      {
            genchar buffer[MAXLINE];
            c = ed_internal(src,buffer);
            ed_gencpy((genchar*)dp,buffer);
            return(c);
      }
      while(*cp)
            *dp++ = mbchar(cp);
      *dp = 0;
      return(dp-(wchar_t*)dest);
}

/*
 * convert internal representation <src> into character array <dest>.
 * The <src> and <dest> may be the same.
 * returns number of chars in dest.
 */

int   ed_external(const genchar *src, char *dest)
{
      register genchar wc;
      register int c,size;
      register char *dp = dest;
      char *dpmax = dp+sizeof(genchar)*MAXLINE-2;
      if((char*)src == dp)
      {
            char buffer[MAXLINE*sizeof(genchar)];
            c = ed_external(src,buffer);

#ifdef _lib_wcscpy
            wcscpy((wchar_t *)dest,(const wchar_t *)buffer);
#else
            strcpy(dest,buffer);
#endif
            return(c);
      }
      while((wc = *src++) && dp<dpmax)
      {
            if((size = mbconv(dp, wc)) < 0)
            {
                  /* copy the character as is */
                  size = 1;
                  *dp = wc;
            }
            dp += size;
      }
      *dp = 0;
      return(dp-dest);
}

/*
 * copy <sp> to <dp>
 */

void  ed_gencpy(genchar *dp,const genchar *sp)
{
      dp = (genchar*)roundof((char*)dp-(char*)0,sizeof(genchar));
      sp = (const genchar*)roundof((char*)sp-(char*)0,sizeof(genchar));
      while(*dp++ = *sp++);
}

/*
 * copy at most <n> items from <sp> to <dp>
 */

void  ed_genncpy(register genchar *dp,register const genchar *sp, int n)
{
      dp = (genchar*)roundof((char*)dp-(char*)0,sizeof(genchar));
      sp = (const genchar*)roundof((char*)sp-(char*)0,sizeof(genchar));
      while(n-->0 && (*dp++ = *sp++));
}

/*
 * find the string length of <str>
 */

int   ed_genlen(register const genchar *str)
{
      register const genchar *sp = str;
      sp = (const genchar*)roundof((char*)sp-(char*)0,sizeof(genchar));
      while(*sp++);
      return(sp-str-1);
}
#endif /* SHOPT_MULTIBYTE */
#endif /* SHOPT_ESH || SHOPT_VSH */

#ifdef future
/*
 * returns 1 when <n> bytes starting at <a> and <b> are equal
 */
static int compare(register const char *a,register const char *b,register int n)
{
      while(n-->0)
      {
            if(*a++ != *b++)
                  return(0);
      }
      return(1);
}
#endif

#if SHOPT_OLDTERMIO

#   include <sys/termio.h>

#ifndef ECHOCTL
#   define ECHOCTL      0
#endif /* !ECHOCTL */
#define ott ep->e_ott

/*
 * For backward compatibility only
 * This version will use termios when possible, otherwise termio
 */


tcgetattr(int fd, struct termios *tt)
{
      register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context);
      register int r,i;
      ep->e_tcgeta = 0;
      ep->e_echoctl = (ECHOCTL!=0);
      if((r=ioctl(fd,TCGETS,tt))>=0 ||  errno!=EINVAL)
            return(r);
      if((r=ioctl(fd,TCGETA,&ott)) >= 0)
      {
            tt->c_lflag = ott.c_lflag;
            tt->c_oflag = ott.c_oflag;
            tt->c_iflag = ott.c_iflag;
            tt->c_cflag = ott.c_cflag;
            for(i=0; i<NCC; i++)
                  tt->c_cc[i] = ott.c_cc[i];
            ep->e_tcgeta++;
            ep->e_echoctl = 0;
      }
      return(r);
}

tcsetattr(int fd,int mode,struct termios *tt)
{
      register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context);
      register int r;
      if(ep->e_tcgeta)
      {
            register int i;
            ott.c_lflag = tt->c_lflag;
            ott.c_oflag = tt->c_oflag;
            ott.c_iflag = tt->c_iflag;
            ott.c_cflag = tt->c_cflag;
            for(i=0; i<NCC; i++)
                  ott.c_cc[i] = tt->c_cc[i];
            if(tt->c_lflag&ECHOCTL)
            {
                  ott.c_lflag &= ~(ECHOCTL|IEXTEN);
                  ott.c_iflag &= ~(IGNCR|ICRNL);
                  ott.c_iflag |= INLCR;
                  ott.c_cc[VEOF]= ESC;  /* ESC -> eof char */
                  ott.c_cc[VEOL] = '\r'; /* CR -> eol char */
                  ott.c_cc[VEOL2] = tt->c_cc[VEOF]; /* EOF -> eol char */
            }
            switch(mode)
            {
                  case TCSANOW:
                        mode = TCSETA;
                        break;
                  case TCSADRAIN:
                        mode = TCSETAW;
                        break;
                  case TCSAFLUSH:
                        mode = TCSETAF;
            }
            return(ioctl(fd,mode,&ott));
      }
      return(ioctl(fd,mode,tt));
}
#endif /* SHOPT_OLDTERMIO */

#if KSHELL
/*
 * Execute keyboard trap on given buffer <inbuff> of given size <isize>
 * <mode> < 0 for vi insert mode
 */
static int keytrap(Edit_t *ep,char *inbuff,register int insize, int bufsize, int mode)
{
      register char *cp;
      int savexit;
      Shell_t *shp = ep->sh;
#if SHOPT_MULTIBYTE
      char buff[MAXLINE];
      ed_external(ep->e_inbuf,cp=buff);
#else
      cp = ep->e_inbuf;
#endif /* SHOPT_MULTIBYTE */
      inbuff[insize] = 0;
      ep->e_col = ep->e_cur;
      if(mode== -2)
      {
            ep->e_col++;
            *ep->e_vi_insert = ESC;
      }
      else
            *ep->e_vi_insert = 0;
      nv_putval(ED_CHRNOD,inbuff,NV_NOFREE);
      nv_putval(ED_COLNOD,(char*)&ep->e_col,NV_NOFREE|NV_INTEGER);
      nv_putval(ED_TXTNOD,(char*)cp,NV_NOFREE);
      nv_putval(ED_MODENOD,ep->e_vi_insert,NV_NOFREE);
      savexit = shp->savexit;
      sh_trap(shp->st.trap[SH_KEYTRAP],0);
      shp->savexit = savexit;
      if((cp = nv_getval(ED_CHRNOD)) == inbuff)
            nv_unset(ED_CHRNOD);
      else if(bufsize>0)
      {
            strncpy(inbuff,cp,bufsize);
            inbuff[bufsize-1]='\0';
            insize = strlen(inbuff);
      }
      else
            insize = 0;
      nv_unset(ED_TXTNOD);
      return(insize);
}
#endif /* KSHELL */

void  *ed_open(Shell_t *shp)
{
      Edit_t *ed = newof(0,Edit_t,1,0);
      ed->sh = shp;
      strcpy(ed->e_macro,"_??");
      return((void*)ed);
}

Generated by  Doxygen 1.6.0   Back to index