summaryrefslogtreecommitdiff
path: root/src/cmd/ksh93/edit/edit.c
diff options
context:
space:
mode:
authorIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
committerIgor Pashev <pashev.igor@gmail.com>2012-06-24 22:28:35 +0000
commit3950ffe2a485479f6561c27364d3d7df5a21d124 (patch)
tree468c6e14449d1b1e279222ec32f676b0311917d2 /src/cmd/ksh93/edit/edit.c
downloadksh-upstream.tar.gz
Imported Upstream version 93u+upstream
Diffstat (limited to 'src/cmd/ksh93/edit/edit.c')
-rw-r--r--src/cmd/ksh93/edit/edit.c1806
1 files changed, 1806 insertions, 0 deletions
diff --git a/src/cmd/ksh93/edit/edit.c b/src/cmd/ksh93/edit/edit.c
new file mode 100644
index 0000000..b050f60
--- /dev/null
+++ b/src/cmd/ksh93/edit/edit.c
@@ -0,0 +1,1806 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1982-2012 AT&T Intellectual Property *
+* and is licensed under the *
+* Eclipse Public License, Version 1.0 *
+* by AT&T Intellectual Property *
+* *
+* A copy of the License is available at *
+* http://www.eclipse.org/org/documents/epl-v10.html *
+* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
+* *
+* 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 "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
+# include <ctype.h>
+ 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 };
+static char KILL_LINE[20] = { ESC, '[', 'J', 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*)(shgd->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*)(shgd->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*)(shgd->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*)(shgd->ed_context);
+ ep->e_keytrap = 0;
+ 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*)(shgd->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|ISIG);
+ else
+ nttyparm.c_lflag &= ~(ICANON|ISIG|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_intr = ttyparm.c_cc[VINTR];
+ 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*)(shgd->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*)(shgd->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, *prev;
+ char *ppmax;
+ int myquote = 0, n;
+ register int qlen = 1, qwid;
+ 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;
+ ep->sh->winch = 0;
+#endif
+#if SHOPT_EDPREDICT
+ ep->hlist = 0;
+ ep->nhlist = 0;
+ ep->hoff = 0;
+#endif /* SHOPT_EDPREDICT */
+#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->gd->hist_ptr)
+ {
+ register History_t *hp = shp->gd->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(prev = last, c = mbchar(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'))
+ {
+ skip = 0;
+ 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)
+ {
+ if(inquote)
+ qlen++;
+ else if(!is_print(c))
+ ep->e_crlf = 0;
+ if((qwid = last - prev) > 1)
+ qlen += qwid - mbwidth(c);
+ while(prev < last && pp < ppmax)
+ *pp++ = *prev++;
+ }
+ break;
+ }
+ }
+ 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+1);
+ }
+ 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;
+ }
+}
+
+static void ed_putstring(register Edit_t *ep, const char *str)
+{
+ register int c;
+ while(c = *str++)
+ ed_putchar(ep,c);
+}
+
+static void ed_nputchar(register Edit_t *ep, int n, int c)
+{
+ while(n-->0)
+ ed_putchar(ep,c);
+}
+
+/*
+ * 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->gd->waitevent;
+ if(ep->e_raw==ALTMODE)
+ mode = 1;
+ if(size < 0)
+ {
+ mode = 1;
+ size = -size;
+ }
+ sh_onstate(SH_TTYWAIT);
+ errno = EINTR;
+ shp->gd->waitevent = 0;
+ while(rv<0 && errno==EINTR)
+ {
+ if(shp->trapnote&(SH_SIGSET|SH_SIGTRAP))
+ goto done;
+ if(ep->sh->winch && sh_isstate(SH_INTERACTIVE) && (sh_isoption(SH_VI) || sh_isoption(SH_EMACS)))
+ {
+ Edpos_t lastpos;
+ int n, rows, newsize;
+ /* move cursor to start of first line */
+ ed_putchar(ep,'\r');
+ ed_flush(ep);
+ astwinsize(2,&rows,&newsize);
+ n = (ep->e_plen+ep->e_cur)/++ep->e_winsz;
+ while(n--)
+ ed_putstring(ep,CURSOR_UP);
+ if(ep->e_multiline && newsize>ep->e_winsz && (lastpos.line=(ep->e_plen+ep->e_peol)/ep->e_winsz))
+ {
+ /* clear the current command line */
+ n = lastpos.line;
+ while(lastpos.line--)
+ {
+ ed_nputchar(ep,ep->e_winsz,' ');
+ ed_putchar(ep,'\n');
+ }
+ ed_nputchar(ep,ep->e_winsz,' ');
+ while(n--)
+ ed_putstring(ep,CURSOR_UP);
+ }
+ ep->sh->winch = 0;
+ ed_flush(ep);
+ sh_delay(.05);
+ astwinsize(2,&rows,&newsize);
+ ep->e_winsz = newsize-1;
+ if(ep->e_winsz < MINWINDOW)
+ ep->e_winsz = MINWINDOW;
+ if(!ep->e_multiline && ep->e_wsize < MAXLINE)
+ ep->e_wsize = ep->e_winsz-2;
+ ep->e_nocrnl=1;
+ if(*ep->e_vi_insert)
+ {
+ buff[0] = ESC;
+ buff[1] = cntl('L');
+ buff[2] = 'a';
+ return(3);
+ }
+ if(sh_isoption(SH_EMACS) || sh_isoption(SH_VI))
+ buff[0] = cntl('L');
+ return(1);
+ }
+ else
+ ep->sh->winch = 0;
+ /* 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->gd->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 */
+ *ep->e_vi_insert = (mode==-2);
+ if((n=ed_read(ep,ep->e_fd,readin,-LOOKAHEAD,0)) > 0)
+ n = putstack(ep,readin,n,1);
+ *ep->e_vi_insert = 0;
+ }
+ if(ep->e_lookahead)
+ {
+ /* check for possible key mapping */
+ if((c = ep->e_lbuf[--ep->e_lookahead]) < 0)
+ {
+ if(mode<=0 && -c == ep->e_intr)
+ {
+ sh_fault(SIGINT);
+ siglongjmp(ep->e_env, UINTR);
+ }
+ if(mode<=0 && ep->sh->st.trap[SH_KEYTRAP])
+ {
+ ep->e_keytrap = 1;
+ 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);
+ ep->e_keytrap = 0;
+ }
+ 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;
+ if(!dp)
+ return;
+ 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);
+}
+
+int ed_setcursor(register Edit_t *ep,genchar *physical,register int old,register int new,int first)
+{
+ static int oldline;
+ register int delta;
+ int clear = 0;
+ Edpos_t newpos;
+
+ delta = new - old;
+ if(first < 0)
+ {
+ first = 0;
+ clear = 1;
+ }
+ if( delta == 0 && !clear)
+ return(new);
+ if(ep->e_multiline)
+ {
+ ep->e_curpos = ed_curpos(ep, physical, old,0,ep->e_curpos);
+ if(clear && old>=ep->e_peol && (clear=ep->e_winsz-ep->e_curpos.col)>0)
+ {
+ ed_nputchar(ep,clear,' ');
+ ed_nputchar(ep,clear,'\b');
+ return(new);
+ }
+ 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);
+ if(newpos.line <= pline)
+ plen -= pline*(ep->e_winsz+1);
+ else
+ plen = 0;
+ 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++]);
+ }
+ ed_nputchar(ep,n,' ');
+ ed_putstring(ep,CURSOR_UP);
+ }
+ }
+ }
+ else if(ep->e_curpos.line < newpos.line)
+ {
+ ed_nputchar(ep, newpos.line-ep->e_curpos.line,'\n');
+ ep->e_curpos.line = newpos.line;
+ 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))) )
+ {
+ ed_nputchar(ep,delta,'\b');
+ delta = 0;
+ }
+ else
+ {
+ if(newpos.line==0)
+ ed_putstring(ep,ep->e_prompt);
+ else
+ {
+ first = 1+(newpos.line*ep->e_winsz - ep->e_plen);
+ ed_putchar(ep,'\r');
+ }
+ 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;
+ ep->e_peol = dp-phys;
+ 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++));
+}
+
+#endif /* SHOPT_MULTIBYTE */
+/*
+ * 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_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
+ */
+
+int tcgetattr(int fd, struct termios *tt)
+{
+ register Edit_t *ep = (Edit_t*)(shgd->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);
+}
+
+int tcsetattr(int fd,int mode,struct termios *tt)
+{
+ register Edit_t *ep = (Edit_t*)(shgd->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 */
+
+#if SHOPT_EDPREDICT
+static int ed_sortdata(const char *s1, const char *s2)
+{
+ Histmatch_t *m1 = (Histmatch_t*)s1;
+ Histmatch_t *m2 = (Histmatch_t*)s2;
+ return(strcmp(m1->data,m2->data));
+}
+
+static int ed_sortindex(const char *s1, const char *s2)
+{
+ Histmatch_t *m1 = (Histmatch_t*)s1;
+ Histmatch_t *m2 = (Histmatch_t*)s2;
+ return(m2->index-m1->index);
+}
+
+static int ed_histlencopy(const char *cp, char *dp)
+{
+ int c,n=1,col=1;
+ const char *oldcp=cp;
+ for(n=0;c = mbchar(cp);oldcp=cp,col++)
+ {
+ if(c=='\n' && *cp)
+ {
+ n += 2;
+ if(dp)
+ {
+ *dp++ = '^';
+ *dp++ = 'J';
+ col +=2;
+ }
+ }
+ else if(c=='\t')
+ {
+ n++;
+ if(dp)
+ *dp++ = ' ';
+ }
+ else
+ {
+ n += cp-oldcp;
+ if(dp)
+ {
+ while(oldcp < cp)
+ *dp++ = *oldcp++;
+ }
+ }
+
+ }
+ return(n);
+}
+
+int ed_histgen(Edit_t *ep,const char *pattern)
+{
+ Histmatch_t *mp,*mplast=0;
+ History_t *hp;
+ off_t offset;
+ int ac=0,l,m,n,index1,index2;
+ char *cp, **argv, **av, **ar;
+ if(!(hp=ep->sh->gd->hist_ptr))
+ return(0);
+ if(*pattern=='#')
+ pattern++;
+ cp = stakalloc(m=strlen(pattern)+6);
+ sfsprintf(cp,m,"@(%s)*%c",pattern,0);
+ if(ep->hlist)
+ {
+ m = strlen(ep->hpat)-4;
+ if(memcmp(pattern,ep->hpat+2,m)==0)
+ {
+ n = strcmp(cp,ep->hpat)==0;
+ for(argv=av=(char**)ep->hlist,mp=ep->hfirst; mp;mp= mp->next)
+ {
+ if(n || strmatch(mp->data,cp))
+ *av++ = (char*)mp;
+ }
+ *av = 0;
+ return(ep->hmax=av-argv);
+ }
+ stakset(ep->e_stkptr,ep->e_stkoff);
+ }
+ pattern = ep->hpat = cp;
+ index1 = (int)hp->histind;
+ for(index2=index1-hp->histsize; index1>index2; index1--)
+ {
+ offset = hist_tell(hp,index1);
+ sfseek(hp->histfp,offset,SEEK_SET);
+ if(!(cp = sfgetr(hp->histfp,0,0)))
+ continue;
+ if(*cp=='#')
+ continue;
+ if(strmatch(cp,pattern))
+ {
+ l = ed_histlencopy(cp,(char*)0);
+ mp = (Histmatch_t*)stakalloc(sizeof(Histmatch_t)+l);
+ mp->next = mplast;
+ mplast = mp;
+ mp->len = l;
+ ed_histlencopy(cp,mp->data);
+ mp->count = 1;
+ mp->data[l] = 0;
+ mp->index = index1;
+ ac++;
+ }
+ }
+ if(ac>0)
+ {
+ l = ac;
+ argv = av = (char**)stakalloc((ac+1)*sizeof(char*));
+ for(mplast=0; l>=0 && (*av= (char*)mp); mplast=mp,mp=mp->next,av++)
+ {
+ l--;
+ }
+ *av = 0;
+ strsort(argv,ac,ed_sortdata);
+ mplast = (Histmatch_t*)argv[0];
+ for(ar= av= &argv[1]; mp=(Histmatch_t*)*av; av++)
+ {
+ if(strcmp(mp->data,mplast->data)==0)
+ {
+ mplast->count++;
+ if(mp->index> mplast->index)
+ mplast->index = mp->index;
+ continue;
+ }
+ *ar++ = (char*)(mplast=mp);
+ }
+ *ar = 0;
+ mplast->next = 0;
+ ac = ar-argv;
+ strsort(argv,ac,ed_sortindex);
+ mplast = (Histmatch_t*)argv[0];
+ for(av= &argv[1]; mp=(Histmatch_t*)*av; av++, mplast=mp)
+ mplast->next = mp;
+ mplast->next = 0;
+ }
+ ep->hlist = (Histmatch_t**)argv;
+ ep->hfirst = ep->hlist?ep->hlist[0]:0;
+ return(ep->hmax=ac);
+}
+
+void ed_histlist(Edit_t *ep,int n)
+{
+ Histmatch_t *mp,**mpp = ep->hlist+ep->hoff;
+ int i,last=0,save[2];
+ if(n)
+ {
+ /* don't bother updating the screen if there is typeahead */
+ if(!ep->e_lookahead && sfpkrd(ep->e_fd,save,1,'\r',200L,-1)>0)
+ ed_ungetchar(ep,save[0]);
+ if(ep->e_lookahead)
+ return;
+ ed_putchar(ep,'\n');
+ ed_putchar(ep,'\r');
+ }
+ else
+ {
+ stakset(ep->e_stkptr,ep->e_stkoff);
+ ep->hlist = 0;
+ ep->nhlist = 0;
+ }
+ ed_putstring(ep,KILL_LINE);
+ if(n)
+ {
+ for(i=1; (mp= *mpp) && i <= 16 ; i++,mpp++)
+ {
+ last = 0;
+ if(mp->len >= ep->e_winsz-4)
+ {
+ last = ep->e_winsz-4;
+ save[0] = mp->data[last-1];
+ save[1] = mp->data[last];
+ mp->data[last-1] = '\n';
+ mp->data[last] = 0;
+ }
+ ed_putchar(ep,i<10?' ':'1');
+ ed_putchar(ep,i<10?'0'+i:'0'+i-10);
+ ed_putchar(ep,')');
+ ed_putchar(ep,' ');
+ ed_putstring(ep,mp->data);
+ if(last)
+ {
+ mp->data[last-1] = save[0];
+ mp->data[last] = save[1];
+ }
+ ep->nhlist = i;
+ }
+ last = i-1;
+ while(i-->0)
+ ed_putstring(ep,CURSOR_UP);
+ }
+ ed_flush(ep);
+}
+#endif /* SHOPT_EDPREDICT */
+
+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);
+}