summaryrefslogtreecommitdiff
path: root/src/cmd/ksh93/edit
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
downloadksh-upstream.tar.gz
Imported Upstream version 93u+upstream
Diffstat (limited to 'src/cmd/ksh93/edit')
-rw-r--r--src/cmd/ksh93/edit/completion.c572
-rw-r--r--src/cmd/ksh93/edit/edit.c1806
-rw-r--r--src/cmd/ksh93/edit/emacs.c1571
-rw-r--r--src/cmd/ksh93/edit/hexpand.c734
-rw-r--r--src/cmd/ksh93/edit/history.c1222
-rw-r--r--src/cmd/ksh93/edit/vi.c2753
6 files changed, 8658 insertions, 0 deletions
diff --git a/src/cmd/ksh93/edit/completion.c b/src/cmd/ksh93/edit/completion.c
new file mode 100644
index 0000000..da81f94
--- /dev/null
+++ b/src/cmd/ksh93/edit/completion.c
@@ -0,0 +1,572 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1982-2011 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
+/*
+ * completion.c - command and file completion for shell editors
+ *
+ */
+
+#include "defs.h"
+#include <ast_wchar.h>
+#include "lexstates.h"
+#include "path.h"
+#include "io.h"
+#include "edit.h"
+#include "history.h"
+
+#if !SHOPT_MULTIBYTE
+#define mbchar(p) (*(unsigned char*)p++)
+#endif
+
+static char *fmtx(const char *string)
+{
+ register const char *cp = string;
+ register int n,c;
+ unsigned char *state = (unsigned char*)sh_lexstates[2];
+ int offset = staktell();
+ if(*cp=='#' || *cp=='~')
+ stakputc('\\');
+ while((c=mbchar(cp)),(c>UCHAR_MAX)||(n=state[c])==0 || n==S_EPAT);
+ if(n==S_EOF && *string!='#')
+ return((char*)string);
+ stakwrite(string,--cp-string);
+ for(string=cp;c=mbchar(cp);string=cp)
+ {
+ if((n=cp-string)==1)
+ {
+ if((n=state[c]) && n!=S_EPAT)
+ stakputc('\\');
+ stakputc(c);
+ }
+ else
+ stakwrite(string,n);
+ }
+ stakputc(0);
+ return(stakptr(offset));
+}
+
+static int charcmp(int a, int b, int nocase)
+{
+ if(nocase)
+ {
+ if(isupper(a))
+ a = tolower(a);
+ if(isupper(b))
+ b = tolower(b);
+ }
+ return(a==b);
+}
+
+/*
+ * overwrites <str> to common prefix of <str> and <newstr>
+ * if <str> is equal to <newstr> returns <str>+strlen(<str>)+1
+ * otherwise returns <str>+strlen(<str>)
+ */
+static char *overlaid(register char *str,register const char *newstr,int nocase)
+{
+ register int c,d;
+ while((c= *(unsigned char *)str) && ((d= *(unsigned char*)newstr++),charcmp(c,d,nocase)))
+ str++;
+ if(*str)
+ *str = 0;
+ else if(*newstr==0)
+ str++;
+ return(str);
+}
+
+
+/*
+ * returns pointer to beginning of expansion and sets type of expansion
+ */
+static char *find_begin(char outbuff[], char *last, int endchar, int *type)
+{
+ register char *cp=outbuff, *bp, *xp;
+ register int c,inquote = 0, inassign=0;
+ int mode=*type;
+ bp = outbuff;
+ *type = 0;
+ while(cp < last)
+ {
+ xp = cp;
+ switch(c= mbchar(cp))
+ {
+ case '\'': case '"':
+ if(!inquote)
+ {
+ inquote = c;
+ bp = xp;
+ break;
+ }
+ if(inquote==c)
+ inquote = 0;
+ break;
+ case '\\':
+ if(inquote != '\'')
+ mbchar(cp);
+ break;
+ case '$':
+ if(inquote == '\'')
+ break;
+ c = *(unsigned char*)cp;
+ if(mode!='*' && (isaletter(c) || c=='{'))
+ {
+ int dot = '.';
+ if(c=='{')
+ {
+ xp = cp;
+ mbchar(cp);
+ c = *(unsigned char*)cp;
+ if(c!='.' && !isaletter(c))
+ break;
+ }
+ else
+ dot = 'a';
+ while(cp < last)
+ {
+ if((c= mbchar(cp)) , c!=dot && !isaname(c))
+ break;
+ }
+ if(cp>=last)
+ {
+ if(c==dot || isaname(c))
+ {
+ *type='$';
+ return(++xp);
+ }
+ if(c!='}')
+ bp = cp;
+ }
+ }
+ else if(c=='(')
+ {
+ *type = mode;
+ xp = find_begin(cp,last,')',type);
+ if(*(cp=xp)!=')')
+ bp = xp;
+ else
+ cp++;
+ }
+ break;
+ case '=':
+ if(!inquote)
+ {
+ bp = cp;
+ inassign = 1;
+ }
+ break;
+ case ':':
+ if(!inquote && inassign)
+ bp = cp;
+ break;
+ case '~':
+ if(*cp=='(')
+ break;
+ /* fall through */
+ default:
+ if(c && c==endchar)
+ return(xp);
+ if(!inquote && ismeta(c))
+ {
+ bp = cp;
+ inassign = 0;
+ }
+ break;
+ }
+ }
+ if(inquote && *bp==inquote)
+ *type = *bp++;
+ return(bp);
+}
+
+/*
+ * file name generation for edit modes
+ * non-zero exit for error, <0 ring bell
+ * don't search back past beginning of the buffer
+ * mode is '*' for inline expansion,
+ * mode is '\' for filename completion
+ * mode is '=' cause files to be listed in select format
+ */
+
+int ed_expand(Edit_t *ep, char outbuff[],int *cur,int *eol,int mode, int count)
+{
+ struct comnod *comptr;
+ struct argnod *ap;
+ register char *out;
+ char *av[2], *begin , *dir=0;
+ int addstar=0, rval=0, var=0, strip=1;
+ int nomarkdirs = !sh_isoption(SH_MARKDIRS);
+ sh_onstate(SH_FCOMPLETE);
+ if(ep->e_nlist)
+ {
+ if(mode=='=' && count>0)
+ {
+ if(count> ep->e_nlist)
+ return(-1);
+ mode = '?';
+ av[0] = ep->e_clist[count-1];
+ av[1] = 0;
+ }
+ else
+ {
+ stakset(ep->e_stkptr,ep->e_stkoff);
+ ep->e_nlist = 0;
+ }
+ }
+ comptr = (struct comnod*)stakalloc(sizeof(struct comnod));
+ ap = (struct argnod*)stakseek(ARGVAL);
+#if SHOPT_MULTIBYTE
+ {
+ register int c = *cur;
+ register genchar *cp;
+ /* adjust cur */
+ cp = (genchar *)outbuff + *cur;
+ c = *cp;
+ *cp = 0;
+ *cur = ed_external((genchar*)outbuff,(char*)stakptr(0));
+ *cp = c;
+ *eol = ed_external((genchar*)outbuff,outbuff);
+ }
+#endif /* SHOPT_MULTIBYTE */
+ out = outbuff + *cur + (sh_isoption(SH_VI)!=0);
+ if(out[-1]=='"' || out[-1]=='\'')
+ {
+ rval = -(sh_isoption(SH_VI)!=0);
+ goto done;
+ }
+ comptr->comtyp = COMSCAN;
+ comptr->comarg = ap;
+ ap->argflag = (ARG_MAC|ARG_EXP);
+ ap->argnxt.ap = 0;
+ ap->argchn.cp = 0;
+ {
+ register int c;
+ char *last = out;
+ c = *(unsigned char*)out;
+ var = mode;
+ begin = out = find_begin(outbuff,last,0,&var);
+ /* addstar set to zero if * should not be added */
+ if(var=='$')
+ {
+ stakputs("${!");
+ stakwrite(out,last-out);
+ stakputs("@}");
+ out = last;
+ }
+ else
+ {
+ addstar = '*';
+ while(out < last)
+ {
+ c = *(unsigned char*)out;
+ if(isexp(c))
+ addstar = 0;
+ if (c == '/')
+ {
+ if(addstar == 0)
+ strip = 0;
+ dir = out+1;
+ }
+ stakputc(c);
+ out++;
+ }
+ }
+ if(mode=='?')
+ mode = '*';
+ if(var!='$' && mode=='\\' && out[-1]!='*')
+ addstar = '*';
+ if(*begin=='~' && !strchr(begin,'/'))
+ addstar = 0;
+ stakputc(addstar);
+ ap = (struct argnod*)stakfreeze(1);
+ }
+ if(mode!='*')
+ sh_onoption(SH_MARKDIRS);
+ {
+ register char **com;
+ char *cp=begin, *left=0, *saveout=".";
+ int nocase=0,narg,cmd_completion=0;
+ register int size='x';
+ while(cp>outbuff && ((size=cp[-1])==' ' || size=='\t'))
+ cp--;
+ if(!var && !strchr(ap->argval,'/') && (((cp==outbuff&&ep->sh->nextprompt==1) || (strchr(";&|(",size)) && (cp==outbuff+1||size=='('||cp[-2]!='>') && *begin!='~' )))
+ {
+ cmd_completion=1;
+ sh_onstate(SH_COMPLETE);
+ }
+ if(ep->e_nlist)
+ {
+ narg = 1;
+ com = av;
+ if(dir)
+ begin += (dir-begin);
+ }
+ else
+ {
+ com = sh_argbuild(ep->sh,&narg,comptr,0);
+ /* special handling for leading quotes */
+ if(begin>outbuff && (begin[-1]=='"' || begin[-1]=='\''))
+ begin--;
+ }
+ sh_offstate(SH_COMPLETE);
+ /* allow a search to be aborted */
+ if(ep->sh->trapnote&SH_SIGSET)
+ {
+ rval = -1;
+ goto done;
+ }
+ /* match? */
+ if (*com==0 || (narg <= 1 && (strcmp(ap->argval,*com)==0) || (addstar && com[0][strlen(*com)-1]=='*')))
+ {
+ rval = -1;
+ goto done;
+ }
+ if(mode=='=')
+ {
+ if (strip && !cmd_completion)
+ {
+ register char **ptrcom;
+ for(ptrcom=com;*ptrcom;ptrcom++)
+ /* trim directory prefix */
+ *ptrcom = path_basename(*ptrcom);
+ }
+ sfputc(sfstderr,'\n');
+ sh_menu(sfstderr,narg,com);
+ sfsync(sfstderr);
+ ep->e_nlist = narg;
+ ep->e_clist = com;
+ goto done;
+ }
+ /* see if there is enough room */
+ size = *eol - (out-begin);
+ if(mode=='\\')
+ {
+ int c;
+ if(dir)
+ {
+ c = *dir;
+ *dir = 0;
+ saveout = begin;
+ }
+ if(saveout=astconf("PATH_ATTRIBUTES",saveout,(char*)0))
+ nocase = (strchr(saveout,'c')!=0);
+ if(dir)
+ *dir = c;
+ /* just expand until name is unique */
+ size += strlen(*com);
+ }
+ else
+ {
+ size += narg;
+ {
+ char **savcom = com;
+ while (*com)
+ size += strlen(cp=fmtx(*com++));
+ com = savcom;
+ }
+ }
+ /* see if room for expansion */
+ if(outbuff+size >= &outbuff[MAXLINE])
+ {
+ com[0] = ap->argval;
+ com[1] = 0;
+ }
+ /* save remainder of the buffer */
+ if(*out)
+ left=stakcopy(out);
+ if(cmd_completion && mode=='\\')
+ out = strcopy(begin,path_basename(cp= *com++));
+ else if(mode=='*')
+ {
+ if(ep->e_nlist && dir && var)
+ {
+ if(*cp==var)
+ cp++;
+ else
+ *begin++ = var;
+ out = strcopy(begin,cp);
+ var = 0;
+ }
+ else
+ out = strcopy(begin,fmtx(*com));
+ com++;
+ }
+ else
+ out = strcopy(begin,*com++);
+ if(mode=='\\')
+ {
+ saveout= ++out;
+ while (*com && *begin)
+ {
+ if(cmd_completion)
+ out = overlaid(begin,path_basename(*com++),nocase);
+ else
+ out = overlaid(begin,*com++,nocase);
+ }
+ mode = (out==saveout);
+ if(out[-1]==0)
+ out--;
+ if(mode && out[-1]!='/')
+ {
+ if(cmd_completion)
+ {
+ Namval_t *np;
+ /* add as tracked alias */
+ Pathcomp_t *pp;
+ if(*cp=='/' && (pp=path_dirfind(ep->sh->pathlist,cp,'/')) && (np=nv_search(begin,ep->sh->track_tree,NV_ADD)))
+ path_alias(np,pp);
+ out = strcopy(begin,cp);
+ }
+ /* add quotes if necessary */
+ if((cp=fmtx(begin))!=begin)
+ out = strcopy(begin,cp);
+ if(var=='$' && begin[-1]=='{')
+ *out = '}';
+ else
+ *out = ' ';
+ *++out = 0;
+ }
+ else if((cp=fmtx(begin))!=begin)
+ {
+ out = strcopy(begin,cp);
+ if(out[-1] =='"' || out[-1]=='\'')
+ *--out = 0;
+ }
+ if(*begin==0)
+ ed_ringbell();
+ }
+ else
+ {
+ while (*com)
+ {
+ *out++ = ' ';
+ out = strcopy(out,fmtx(*com++));
+ }
+ }
+ if(ep->e_nlist)
+ {
+ cp = com[-1];
+ if(cp[strlen(cp)-1]!='/')
+ {
+ if(var=='$' && begin[-1]=='{')
+ *out = '}';
+ else
+ *out = ' ';
+ out++;
+ }
+ else if(out[-1] =='"' || out[-1]=='\'')
+ out--;
+ *out = 0;
+ }
+ *cur = (out-outbuff);
+ /* restore rest of buffer */
+ if(left)
+ out = strcopy(out,left);
+ *eol = (out-outbuff);
+ }
+ done:
+ sh_offstate(SH_FCOMPLETE);
+ if(!ep->e_nlist)
+ stakset(ep->e_stkptr,ep->e_stkoff);
+ if(nomarkdirs)
+ sh_offoption(SH_MARKDIRS);
+#if SHOPT_MULTIBYTE
+ {
+ register int c,n=0;
+ /* first re-adjust cur */
+ c = outbuff[*cur];
+ outbuff[*cur] = 0;
+ for(out=outbuff; *out;n++)
+ mbchar(out);
+ outbuff[*cur] = c;
+ *cur = n;
+ outbuff[*eol+1] = 0;
+ *eol = ed_internal(outbuff,(genchar*)outbuff);
+ }
+#endif /* SHOPT_MULTIBYTE */
+ return(rval);
+}
+
+/*
+ * look for edit macro named _i
+ * if found, puts the macro definition into lookahead buffer and returns 1
+ */
+int ed_macro(Edit_t *ep, register int i)
+{
+ register char *out;
+ Namval_t *np;
+ genchar buff[LOOKAHEAD+1];
+ if(i != '@')
+ ep->e_macro[1] = i;
+ /* undocumented feature, macros of the form <ESC>[c evoke alias __c */
+ if(i=='_')
+ ep->e_macro[2] = ed_getchar(ep,1);
+ else
+ ep->e_macro[2] = 0;
+ if (isalnum(i)&&(np=nv_search(ep->e_macro,ep->sh->alias_tree,HASH_SCOPE))&&(out=nv_getval(np)))
+ {
+#if SHOPT_MULTIBYTE
+ /* copy to buff in internal representation */
+ int c = 0;
+ if( strlen(out) > LOOKAHEAD )
+ {
+ c = out[LOOKAHEAD];
+ out[LOOKAHEAD] = 0;
+ }
+ i = ed_internal(out,buff);
+ if(c)
+ out[LOOKAHEAD] = c;
+#else
+ strncpy((char*)buff,out,LOOKAHEAD);
+ buff[LOOKAHEAD] = 0;
+ i = strlen((char*)buff);
+#endif /* SHOPT_MULTIBYTE */
+ while(i-- > 0)
+ ed_ungetchar(ep,buff[i]);
+ return(1);
+ }
+ return(0);
+}
+
+/*
+ * Enter the fc command on the current history line
+ */
+int ed_fulledit(Edit_t *ep)
+{
+ register char *cp;
+ if(!shgd->hist_ptr)
+ return(-1);
+ /* use EDITOR on current command */
+ if(ep->e_hline == ep->e_hismax)
+ {
+ if(ep->e_eol<0)
+ return(-1);
+#if SHOPT_MULTIBYTE
+ ep->e_inbuf[ep->e_eol+1] = 0;
+ ed_external(ep->e_inbuf, (char *)ep->e_inbuf);
+#endif /* SHOPT_MULTIBYTE */
+ sfwrite(shgd->hist_ptr->histfp,(char*)ep->e_inbuf,ep->e_eol+1);
+ sh_onstate(SH_HISTORY);
+ hist_flush(shgd->hist_ptr);
+ }
+ cp = strcopy((char*)ep->e_inbuf,e_runvi);
+ cp = strcopy(cp, fmtbase((long)ep->e_hline,10,0));
+ ep->e_eol = ((unsigned char*)cp - (unsigned char*)ep->e_inbuf)-(sh_isoption(SH_VI)!=0);
+ return(0);
+}
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);
+}
diff --git a/src/cmd/ksh93/edit/emacs.c b/src/cmd/ksh93/edit/emacs.c
new file mode 100644
index 0000000..cb686b7
--- /dev/null
+++ b/src/cmd/ksh93/edit/emacs.c
@@ -0,0 +1,1571 @@
+/***********************************************************************
+* *
+* 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
+/* Original version by Michael T. Veach
+ * Adapted for ksh by David Korn */
+/* EMACS_MODES: c tabstop=4
+
+One line screen editor for any program
+
+*/
+
+
+/* The following is provided by:
+ *
+ * Matthijs N. Melchior
+ * AT&T Network Systems International
+ * APT Nederland
+ * HV BZ335 x2962
+ * hvlpb!mmelchio
+ *
+ * These are now on by default
+ *
+ * ESH_NFIRST
+ * - A ^N as first history related command after the prompt will move
+ * to the next command relative to the last known history position.
+ * It will not start at the position where the last command was entered
+ * as is done by the ^P command. Every history related command will
+ * set both the current and last position. Executing a command will
+ * only set the current position.
+ *
+ * ESH_KAPPEND
+ * - Successive kill and delete commands will accumulate their data
+ * in the kill buffer, by appending or prepending as appropriate.
+ * This mode will be reset by any command not adding something to the
+ * kill buffer.
+ *
+ * ESH_BETTER
+ * - Some enhancements:
+ * - argument for a macro is passed to its replacement
+ * - ^X^H command to find out about history position (debugging)
+ * - ^X^D command to show any debugging info
+ *
+ * I do not pretend these for changes are completely independent,
+ * but you can use them to seperate features.
+ */
+
+#include <ast.h>
+#include "FEATURE/cmds"
+#if KSHELL
+# include "defs.h"
+#else
+# include <ctype.h>
+#endif /* KSHELL */
+#include "io.h"
+
+#include "history.h"
+#include "edit.h"
+#include "terminal.h"
+
+#define ESH_NFIRST
+#define ESH_KAPPEND
+#define ESH_BETTER
+
+#undef putchar
+#define putchar(ed,c) ed_putchar(ed,c)
+#define beep() ed_ringbell()
+
+
+#if SHOPT_MULTIBYTE
+# define gencpy(a,b) ed_gencpy(a,b)
+# define genncpy(a,b,n) ed_genncpy(a,b,n)
+# define genlen(str) ed_genlen(str)
+ static int print(int);
+ static int _isword(int);
+# define isword(c) _isword(out[c])
+
+#else
+# define gencpy(a,b) strcpy((char*)(a),(char*)(b))
+# define genncpy(a,b,n) strncpy((char*)(a),(char*)(b),n)
+# define genlen(str) strlen(str)
+# define print(c) isprint(c)
+# define isword(c) (isalnum(out[c]) || (out[c]=='_'))
+#endif /*SHOPT_MULTIBYTE */
+
+typedef struct _emacs_
+{
+ genchar *screen; /* pointer to window buffer */
+ genchar *cursor; /* Cursor in real screen */
+ int mark;
+ int in_mult;
+ char cr_ok;
+ char CntrlO;
+ char overflow; /* Screen overflow flag set */
+ char scvalid; /* Screen is up to date */
+ char lastdraw; /* last update type */
+ int offset; /* Screen offset */
+ enum
+ {
+ CRT=0, /* Crt terminal */
+ PAPER /* Paper terminal */
+ } terminal;
+ Histloc_t _location;
+ int prevdirection;
+ Edit_t *ed; /* pointer to edit data */
+} Emacs_t;
+
+#define editb (*ep->ed)
+#define eol editb.e_eol
+#define cur editb.e_cur
+#define hline editb.e_hline
+#define hloff editb.e_hloff
+#define hismin editb.e_hismin
+#define usrkill editb.e_kill
+#define usrlnext editb.e_lnext
+#define usreof editb.e_eof
+#define usrerase editb.e_erase
+#define crallowed editb.e_crlf
+#define Prompt editb.e_prompt
+#define plen editb.e_plen
+#define kstack editb.e_killbuf
+#define lstring editb.e_search
+#define lookahead editb.e_lookahead
+#define env editb.e_env
+#define raw editb.e_raw
+#define histlines editb.e_hismax
+#define w_size editb.e_wsize
+#define drawbuff editb.e_inbuf
+#define killing editb.e_mode
+#define location ep->_location
+
+#define LBUF 100
+#define KILLCHAR UKILL
+#define ERASECHAR UERASE
+#define EOFCHAR UEOF
+#define LNEXTCHAR ULNEXT
+#define DELETE ('a'==97?0177:7)
+
+/**********************
+A large lookahead helps when the user is inserting
+characters in the middle of the line.
+************************/
+
+
+typedef enum
+{
+ FIRST, /* First time thru for logical line, prompt on screen */
+ REFRESH, /* Redraw entire screen */
+ APPEND, /* Append char before cursor to screen */
+ UPDATE, /* Update the screen as need be */
+ FINAL /* Update screen even if pending look ahead */
+} Draw_t;
+
+static void draw(Emacs_t*,Draw_t);
+static int escape(Emacs_t*,genchar*, int);
+static void putstring(Emacs_t*,char*);
+static void search(Emacs_t*,genchar*,int);
+static void setcursor(Emacs_t*,int, int);
+static void show_info(Emacs_t*,const char*);
+static void xcommands(Emacs_t*,int);
+
+int ed_emacsread(void *context, int fd,char *buff,int scend, int reedit)
+{
+ Edit_t *ed = (Edit_t*)context;
+ register int c;
+ register int i;
+ register genchar *out;
+ register int count;
+ register Emacs_t *ep = ed->e_emacs;
+ int adjust,oadjust;
+ char backslash;
+ genchar *kptr;
+ char prompt[PRSIZE];
+ genchar Screen[MAXLINE];
+ memset(Screen,0,sizeof(Screen));
+ if(!ep)
+ {
+ ep = ed->e_emacs = newof(0,Emacs_t,1,0);
+ ep->ed = ed;
+ ep->prevdirection = 1;
+ location.hist_command = -5;
+ }
+ Prompt = prompt;
+ ep->screen = Screen;
+ ep->lastdraw = FINAL;
+ if(tty_raw(ERRIO,0) < 0)
+ {
+ return(reedit?reedit:ed_read(context, fd,buff,scend,0));
+ }
+ raw = 1;
+ /* This mess in case the read system call fails */
+
+ ed_setup(ep->ed,fd,reedit);
+ out = (genchar*)buff;
+#if SHOPT_MULTIBYTE
+ out = (genchar*)roundof(buff-(char*)0,sizeof(genchar));
+ if(reedit)
+ ed_internal(buff,out);
+#endif /* SHOPT_MULTIBYTE */
+ if(!kstack)
+ {
+ kstack = (genchar*)malloc(CHARSIZE*MAXLINE);
+ kstack[0] = '\0';
+ }
+ drawbuff = out;
+#ifdef ESH_NFIRST
+ if (location.hist_command == -5) /* to be initialized */
+ {
+ kstack[0] = '\0'; /* also clear kstack... */
+ location.hist_command = hline;
+ location.hist_line = hloff;
+ }
+ if (location.hist_command <= hismin) /* don't start below minimum */
+ {
+ location.hist_command = hismin + 1;
+ location.hist_line = 0;
+ }
+ ep->in_mult = hloff; /* save pos in last command */
+#endif /* ESH_NFIRST */
+ i = sigsetjmp(env,0);
+ if (i !=0)
+ {
+ if(ep->ed->e_multiline)
+ {
+ cur = eol;
+ draw(ep,FINAL);
+ ed_flush(ep->ed);
+ }
+ tty_cooked(ERRIO);
+ if (i == UEOF)
+ {
+ return(0); /* EOF */
+ }
+ return(-1); /* some other error */
+ }
+ out[reedit] = 0;
+ if(scend+plen > (MAXLINE-2))
+ scend = (MAXLINE-2)-plen;
+ ep->mark = 0;
+ cur = eol;
+ draw(ep,reedit?REFRESH:FIRST);
+ adjust = -1;
+ backslash = 0;
+ if (ep->CntrlO)
+ {
+#ifdef ESH_NFIRST
+ ed_ungetchar(ep->ed,cntl('N'));
+#else
+ location = hist_locate(shgd->hist_ptr,location.hist_command,location.hist_line,1);
+ if (location.hist_command < histlines)
+ {
+ hline = location.hist_command;
+ hloff = location.hist_line;
+ hist_copy((char*)kstack,MAXLINE, hline,hloff);
+# if SHOPT_MULTIBYTE
+ ed_internal((char*)kstack,kstack);
+# endif /* SHOPT_MULTIBYTE */
+ ed_ungetchar(ep->ed,cntl('Y'));
+ }
+#endif /* ESH_NFIRST */
+ }
+ ep->CntrlO = 0;
+ while ((c = ed_getchar(ep->ed,0)) != (-1))
+ {
+ if (backslash)
+ {
+ backslash = 0;
+ if (c==usrerase||c==usrkill||(!print(c) &&
+ (c!='\r'&&c!='\n')))
+ {
+ /* accept a backslashed character */
+ cur--;
+ out[cur++] = c;
+ out[eol] = '\0';
+ draw(ep,APPEND);
+ continue;
+ }
+ }
+ if (c == usrkill)
+ {
+ c = KILLCHAR ;
+ }
+ else if (c == usrerase)
+ {
+ c = ERASECHAR ;
+ }
+ else if (c == usrlnext)
+ {
+ c = LNEXTCHAR ;
+ }
+ else if ((c == usreof)&&(eol == 0))
+ {
+ c = EOFCHAR;
+ }
+#ifdef ESH_KAPPEND
+ if (--killing <= 0) /* reset killing flag */
+ killing = 0;
+#endif
+ oadjust = count = adjust;
+ if(count<0)
+ count = 1;
+ adjust = -1;
+ i = cur;
+ switch(c)
+ {
+ case LNEXTCHAR:
+ c = ed_getchar(ep->ed,2);
+ goto do_default_processing;
+ case cntl('V'):
+ show_info(ep,fmtident(e_version));
+ continue;
+ case '\0':
+ ep->mark = i;
+ continue;
+ case cntl('X'):
+ xcommands(ep,count);
+ continue;
+ case EOFCHAR:
+ ed_flush(ep->ed);
+ tty_cooked(ERRIO);
+ return(0);
+#ifdef u370
+ case cntl('S') :
+ case cntl('Q') :
+ continue;
+#endif /* u370 */
+ case '\t':
+ if(cur>0 && ep->ed->sh->nextprompt)
+ {
+ if(ep->ed->e_tabcount==0)
+ {
+ ep->ed->e_tabcount=1;
+ ed_ungetchar(ep->ed,ESC);
+ goto do_escape;
+ }
+ else if(ep->ed->e_tabcount==1)
+ {
+ ed_ungetchar(ep->ed,'=');
+ goto do_escape;
+ }
+ ep->ed->e_tabcount = 0;
+ }
+ do_default_processing:
+ default:
+
+ if ((eol+1) >= (scend)) /* will not fit on line */
+ {
+ ed_ungetchar(ep->ed,c); /* save character for next line */
+ goto process;
+ }
+ for(i= ++eol; i>cur; i--)
+ out[i] = out[i-1];
+ backslash = (c == '\\');
+ out[cur++] = c;
+ draw(ep,APPEND);
+ continue;
+ case cntl('Y') :
+ {
+ c = genlen(kstack);
+ if ((c + eol) > scend)
+ {
+ beep();
+ continue;
+ }
+ ep->mark = i;
+ for(i=eol;i>=cur;i--)
+ out[c+i] = out[i];
+ kptr=kstack;
+ while (i = *kptr++)
+ out[cur++] = i;
+ draw(ep,UPDATE);
+ eol = genlen(out);
+ continue;
+ }
+ case '\n':
+ case '\r':
+ c = '\n';
+ goto process;
+
+ case DELETE: /* delete char 0x7f */
+ case '\b': /* backspace, ^h */
+ case ERASECHAR :
+ if (count > i)
+ count = i;
+#ifdef ESH_KAPPEND
+ kptr = &kstack[count]; /* move old contents here */
+ if (killing) /* prepend to killbuf */
+ {
+ c = genlen(kstack) + CHARSIZE; /* include '\0' */
+ while(c--) /* copy stuff */
+ kptr[c] = kstack[c];
+ }
+ else
+ *kptr = 0; /* this is end of data */
+ killing = 2; /* we are killing */
+ i -= count;
+ eol -= count;
+ genncpy(kstack,out+i,cur-i);
+#else
+ while ((count--)&&(i>0))
+ {
+ i--;
+ eol--;
+ }
+ genncpy(kstack,out+i,cur-i);
+ kstack[cur-i] = 0;
+#endif /* ESH_KAPPEND */
+ gencpy(out+i,out+cur);
+ ep->mark = i;
+ goto update;
+ case cntl('W') :
+#ifdef ESH_KAPPEND
+ ++killing; /* keep killing flag */
+#endif
+ if (ep->mark > eol )
+ ep->mark = eol;
+ if (ep->mark == i)
+ continue;
+ if (ep->mark > i)
+ {
+ adjust = ep->mark - i;
+ ed_ungetchar(ep->ed,cntl('D'));
+ continue;
+ }
+ adjust = i - ep->mark;
+ ed_ungetchar(ep->ed,usrerase);
+ continue;
+ case cntl('D') :
+ ep->mark = i;
+#ifdef ESH_KAPPEND
+ if (killing)
+ kptr = &kstack[genlen(kstack)]; /* append here */
+ else
+ kptr = kstack;
+ killing = 2; /* we are now killing */
+#else
+ kptr = kstack;
+#endif /* ESH_KAPPEND */
+ while ((count--)&&(eol>0)&&(i<eol))
+ {
+ *kptr++ = out[i];
+ eol--;
+ while(1)
+ {
+ if ((out[i] = out[(i+1)])==0)
+ break;
+ i++;
+ }
+ i = cur;
+ }
+ *kptr = '\0';
+ goto update;
+ case cntl('C') :
+ case cntl('F') :
+ {
+ int cntlC = (c==cntl('C'));
+ while (count-- && eol>i)
+ {
+ if (cntlC)
+ {
+ c = out[i];
+#if SHOPT_MULTIBYTE
+ if((c&~STRIP)==0 && islower(c))
+#else
+ if(islower(c))
+#endif /* SHOPT_MULTIBYTE */
+ {
+ c += 'A' - 'a';
+ out[i] = c;
+ }
+ }
+ i++;
+ }
+ goto update;
+ }
+ case cntl(']') :
+ c = ed_getchar(ep->ed,1);
+ if ((count == 0) || (count > eol))
+ {
+ beep();
+ continue;
+ }
+ if (out[i])
+ i++;
+ while (i < eol)
+ {
+ if (out[i] == c && --count==0)
+ goto update;
+ i++;
+ }
+ i = 0;
+ while (i < cur)
+ {
+ if (out[i] == c && --count==0)
+ break;
+ i++;
+ };
+
+update:
+ cur = i;
+ draw(ep,UPDATE);
+ continue;
+
+ case cntl('B') :
+ if (count > i)
+ count = i;
+ i -= count;
+ goto update;
+ case cntl('T') :
+ if ((sh_isoption(SH_EMACS))&& (eol!=i))
+ i++;
+ if (i >= 2)
+ {
+ c = out[i - 1];
+ out[i-1] = out[i-2];
+ out[i-2] = c;
+ }
+ else
+ {
+ if(sh_isoption(SH_EMACS))
+ i--;
+ beep();
+ continue;
+ }
+ goto update;
+ case cntl('A') :
+ i = 0;
+ goto update;
+ case cntl('E') :
+ i = eol;
+ goto update;
+ case cntl('U') :
+ adjust = 4*count;
+ continue;
+ case KILLCHAR :
+ cur = 0;
+ oadjust = -1;
+ case cntl('K') :
+ if(oadjust >= 0)
+ {
+#ifdef ESH_KAPPEND
+ killing = 2; /* set killing signal */
+#endif
+ ep->mark = count;
+ ed_ungetchar(ep->ed,cntl('W'));
+ continue;
+ }
+ i = cur;
+ eol = i;
+ ep->mark = i;
+#ifdef ESH_KAPPEND
+ if (killing) /* append to kill buffer */
+ gencpy(&kstack[genlen(kstack)], &out[i]);
+ else
+ gencpy(kstack,&out[i]);
+ killing = 2; /* set killing signal */
+#else
+ gencpy(kstack,&out[i]);
+#endif /* ESH_KAPPEND */
+ out[i] = 0;
+ draw(ep,UPDATE);
+ if (c == KILLCHAR)
+ {
+ if (ep->terminal == PAPER)
+ {
+ putchar(ep->ed,'\n');
+ putstring(ep,Prompt);
+ }
+ c = ed_getchar(ep->ed,0);
+ if (c != usrkill)
+ {
+ ed_ungetchar(ep->ed,c);
+ continue;
+ }
+ if (ep->terminal == PAPER)
+ ep->terminal = CRT;
+ else
+ {
+ ep->terminal = PAPER;
+ putchar(ep->ed,'\n');
+ putstring(ep,Prompt);
+ }
+ }
+ continue;
+ case cntl('L'):
+ if(!ep->ed->e_nocrnl)
+ ed_crlf(ep->ed);
+ draw(ep,REFRESH);
+ ep->ed->e_nocrnl = 0;
+ continue;
+ case cntl('[') :
+ do_escape:
+ adjust = escape(ep,out,oadjust);
+ continue;
+ case cntl('R') :
+ search(ep,out,count);
+ goto drawline;
+ case cntl('P') :
+#if SHOPT_EDPREDICT
+ if(ep->ed->hlist)
+ {
+ if(ep->ed->hoff == 0)
+ {
+ beep();
+ continue;
+ }
+ ep->ed->hoff--;
+ goto hupdate;
+ }
+#endif /* SHOPT_EDPREDICT */
+ if (count <= hloff)
+ hloff -= count;
+ else
+ {
+ hline -= count - hloff;
+ hloff = 0;
+ }
+#ifdef ESH_NFIRST
+ if (hline <= hismin)
+#else
+ if (hline < hismin)
+#endif /* ESH_NFIRST */
+ {
+ hline = hismin+1;
+ beep();
+#ifndef ESH_NFIRST
+ continue;
+#endif
+ }
+ goto common;
+
+ case cntl('O') :
+ location.hist_command = hline;
+ location.hist_line = hloff;
+ ep->CntrlO = 1;
+ c = '\n';
+ goto process;
+ case cntl('N') :
+#if SHOPT_EDPREDICT
+ if(ep->ed->hlist)
+ {
+ if(ep->ed->hoff >= ep->ed->hmax)
+ {
+ beep();
+ continue;
+ }
+ ep->ed->hoff++;
+ hupdate:
+ ed_histlist(ep->ed,*ep->ed->hlist!=0);
+ draw(ep,REFRESH);
+ continue;
+ }
+#endif /* SHOPT_EDPREDICT */
+#ifdef ESH_NFIRST
+ hline = location.hist_command; /* start at saved position */
+ hloff = location.hist_line;
+#endif /* ESH_NFIRST */
+ location = hist_locate(shgd->hist_ptr,hline,hloff,count);
+ if (location.hist_command > histlines)
+ {
+ beep();
+#ifdef ESH_NFIRST
+ location.hist_command = histlines;
+ location.hist_line = ep->in_mult;
+#else
+ continue;
+#endif /* ESH_NFIRST */
+ }
+ hline = location.hist_command;
+ hloff = location.hist_line;
+ common:
+#ifdef ESH_NFIRST
+ location.hist_command = hline; /* save current position */
+ location.hist_line = hloff;
+#endif
+ cur = 0;
+ draw(ep,UPDATE);
+ hist_copy((char*)out,MAXLINE, hline,hloff);
+#if SHOPT_MULTIBYTE
+ ed_internal((char*)(out),out);
+#endif /* SHOPT_MULTIBYTE */
+ drawline:
+ eol = genlen(out);
+ cur = eol;
+ draw(ep,UPDATE);
+ continue;
+ }
+
+ }
+
+process:
+
+ if (c == (-1))
+ {
+ lookahead = 0;
+ beep();
+ *out = '\0';
+ }
+ draw(ep,FINAL);
+ tty_cooked(ERRIO);
+ if(ed->e_nlist)
+ {
+ ed->e_nlist = 0;
+ stakset(ed->e_stkptr,ed->e_stkoff);
+ }
+ if(c == '\n')
+ {
+ out[eol++] = '\n';
+ out[eol] = '\0';
+ ed_crlf(ep->ed);
+ }
+#if SHOPT_MULTIBYTE
+ ed_external(out,buff);
+#endif /* SHOPT_MULTIBYTE */
+ i = strlen(buff);
+ if (i)
+ return(i);
+ return(-1);
+}
+
+static void show_info(Emacs_t *ep,const char *str)
+{
+ register genchar *out = drawbuff;
+ register int c;
+ genchar string[LBUF];
+ int sav_cur = cur;
+ /* save current line */
+ genncpy(string,out,sizeof(string)/sizeof(*string));
+ *out = 0;
+ cur = 0;
+#if SHOPT_MULTIBYTE
+ ed_internal(str,out);
+#else
+ gencpy(out,str);
+#endif /* SHOPT_MULTIBYTE */
+ draw(ep,UPDATE);
+ c = ed_getchar(ep->ed,0);
+ if(c!=' ')
+ ed_ungetchar(ep->ed,c);
+ /* restore line */
+ cur = sav_cur;
+ genncpy(out,string,sizeof(string)/sizeof(*string));
+ draw(ep,UPDATE);
+}
+
+static void putstring(Emacs_t* ep,register char *sp)
+{
+ register int c;
+ while (c= *sp++)
+ putchar(ep->ed,c);
+}
+
+
+static int escape(register Emacs_t* ep,register genchar *out,int count)
+{
+ register int i,value;
+ int digit,ch;
+ digit = 0;
+ value = 0;
+ while ((i=ed_getchar(ep->ed,0)),isdigit(i))
+ {
+ value *= 10;
+ value += (i - '0');
+ digit = 1;
+ }
+ if (digit)
+ {
+ ed_ungetchar(ep->ed,i) ;
+#ifdef ESH_KAPPEND
+ ++killing; /* don't modify killing signal */
+#endif
+ return(value);
+ }
+ value = count;
+ if(value<0)
+ value = 1;
+ switch(ch=i)
+ {
+ case cntl('V'):
+ show_info(ep,fmtident(e_version));
+ return(-1);
+ case ' ':
+ ep->mark = cur;
+ return(-1);
+
+#ifdef ESH_KAPPEND
+ case '+': /* M-+ = append next kill */
+ killing = 2;
+ return -1; /* no argument for next command */
+#endif
+
+ case 'p': /* M-p == ^W^Y (copy stack == kill & yank) */
+ ed_ungetchar(ep->ed,cntl('Y'));
+ ed_ungetchar(ep->ed,cntl('W'));
+#ifdef ESH_KAPPEND
+ killing = 0; /* start fresh */
+#endif
+ return(-1);
+
+ case 'l': /* M-l == lower-case */
+ case 'd':
+ case 'c':
+ case 'f':
+ {
+ i = cur;
+ while(value-- && i<eol)
+ {
+ while ((out[i])&&(!isword(i)))
+ i++;
+ while ((out[i])&&(isword(i)))
+ i++;
+ }
+ if(ch=='l')
+ {
+ value = i-cur;
+ while (value-- > 0)
+ {
+ i = out[cur];
+#if SHOPT_MULTIBYTE
+ if((i&~STRIP)==0 && isupper(i))
+#else
+ if(isupper(i))
+#endif /* SHOPT_MULTIBYTE */
+ {
+ i += 'a' - 'A';
+ out[cur] = i;
+ }
+ cur++;
+ }
+ draw(ep,UPDATE);
+ return(-1);
+ }
+
+ else if(ch=='f')
+ goto update;
+ else if(ch=='c')
+ {
+ ed_ungetchar(ep->ed,cntl('C'));
+ return(i-cur);
+ }
+ else
+ {
+ if (i-cur)
+ {
+ ed_ungetchar(ep->ed,cntl('D'));
+#ifdef ESH_KAPPEND
+ ++killing; /* keep killing signal */
+#endif
+ return(i-cur);
+ }
+ beep();
+ return(-1);
+ }
+ }
+
+
+ case 'b':
+ case DELETE :
+ case '\b':
+ case 'h':
+ {
+ i = cur;
+ while(value-- && i>0)
+ {
+ i--;
+ while ((i>0)&&(!isword(i)))
+ i--;
+ while ((i>0)&&(isword(i-1)))
+ i--;
+ }
+ if(ch=='b')
+ goto update;
+ else
+ {
+ ed_ungetchar(ep->ed,usrerase);
+#ifdef ESH_KAPPEND
+ ++killing;
+#endif
+ return(cur-i);
+ }
+ }
+
+ case '>':
+ ed_ungetchar(ep->ed,cntl('N'));
+#ifdef ESH_NFIRST
+ if (ep->in_mult)
+ {
+ location.hist_command = histlines;
+ location.hist_line = ep->in_mult - 1;
+ }
+ else
+ {
+ location.hist_command = histlines - 1;
+ location.hist_line = 0;
+ }
+#else
+ hline = histlines-1;
+ hloff = 0;
+#endif /* ESH_NFIRST */
+ return(0);
+
+ case '<':
+ ed_ungetchar(ep->ed,cntl('P'));
+ hloff = 0;
+#ifdef ESH_NFIRST
+ hline = hismin + 1;
+ return 0;
+#else
+ return(hline-hismin);
+#endif /* ESH_NFIRST */
+
+
+ case '#':
+ ed_ungetchar(ep->ed,'\n');
+ ed_ungetchar(ep->ed,(out[0]=='#')?cntl('D'):'#');
+ ed_ungetchar(ep->ed,cntl('A'));
+ return(-1);
+ case '_' :
+ case '.' :
+ {
+ genchar name[MAXLINE];
+ char buf[MAXLINE];
+ char *ptr;
+ ptr = hist_word(buf,MAXLINE,(count?count:-1));
+ if(ptr==0)
+ {
+ beep();
+ break;
+ }
+ if ((eol - cur) >= sizeof(name))
+ {
+ beep();
+ return(-1);
+ }
+ ep->mark = cur;
+ gencpy(name,&out[cur]);
+ while(*ptr)
+ {
+ out[cur++] = *ptr++;
+ eol++;
+ }
+ gencpy(&out[cur],name);
+ draw(ep,UPDATE);
+ return(-1);
+ }
+#if KSHELL
+
+#if SHOPT_EDPREDICT
+ case '\n': case '\t':
+ if(!ep->ed->hlist)
+ {
+ beep();
+ break;
+ }
+ if(ch=='\n')
+ ed_ungetchar(ep->ed,'\n');
+#endif /* SHOPT_EDPREDICT */
+ /* file name expansion */
+ case cntl('[') : /* filename completion */
+#if SHOPT_EDPREDICT
+ if(ep->ed->hlist)
+ {
+ value += ep->ed->hoff;
+ if(value > ep->ed->nhlist)
+ beep();
+ else
+ {
+ value = histlines - ep->ed->hlist[value-1]->index;
+ ed_histlist(ep->ed,0);
+ ed_ungetchar(ep->ed,cntl('P'));
+ return(value);
+ }
+ }
+#endif /* SHOPT_EDPREDICT */
+ i = '\\';
+ case '*': /* filename expansion */
+ case '=': /* escape = - list all matching file names */
+ ep->mark = cur;
+ if(ed_expand(ep->ed,(char*)out,&cur,&eol,i,count) < 0)
+ {
+ if(ep->ed->e_tabcount==1)
+ {
+ ep->ed->e_tabcount=2;
+ ed_ungetchar(ep->ed,cntl('\t'));
+ return(-1);
+ }
+ beep();
+ }
+ else if(i=='=')
+ {
+ draw(ep,REFRESH);
+ if(count>0)
+ ep->ed->e_tabcount=0;
+ else
+ {
+ i=ed_getchar(ep->ed,0);
+ ed_ungetchar(ep->ed,i);
+ if(isdigit(i))
+ ed_ungetchar(ep->ed,ESC);
+ }
+ }
+ else
+ {
+ if(i=='\\' && cur>ep->mark && (out[cur-1]=='/' || out[cur-1]==' '))
+ ep->ed->e_tabcount=0;
+ draw(ep,UPDATE);
+ }
+ return(-1);
+
+ /* search back for character */
+ case cntl(']'): /* feature not in book */
+ {
+ int c = ed_getchar(ep->ed,1);
+ if ((value == 0) || (value > eol))
+ {
+ beep();
+ return(-1);
+ }
+ i = cur;
+ if (i > 0)
+ i--;
+ while (i >= 0)
+ {
+ if (out[i] == c && --value==0)
+ goto update;
+ i--;
+ }
+ i = eol;
+ while (i > cur)
+ {
+ if (out[i] == c && --value==0)
+ break;
+ i--;
+ };
+
+ }
+ update:
+ cur = i;
+ draw(ep,UPDATE);
+ return(-1);
+
+#ifdef _cmd_tput
+ case cntl('L'): /* clear screen */
+ sh_trap("tput clear", 0);
+ draw(ep,REFRESH);
+ return(-1);
+#endif
+ case '[': /* feature not in book */
+ switch(i=ed_getchar(ep->ed,1))
+ {
+ case 'A':
+#if SHOPT_EDPREDICT
+ if(!ep->ed->hlist && cur>0 && eol==cur && (cur<(SEARCHSIZE-2) || ep->prevdirection == -2))
+#else
+ if(cur>0 && eol==cur && (cur<(SEARCHSIZE-2) || ep->prevdirection == -2))
+#endif /* SHOPT_EDPREDICT */
+ {
+ if(ep->lastdraw==APPEND && ep->prevdirection != -2)
+ {
+ out[cur] = 0;
+ gencpy((genchar*)lstring+1,out);
+#if SHOPT_MULTIBYTE
+ ed_external((genchar*)lstring+1,lstring+1);
+#endif /* SHOPT_MULTIBYTE */
+ *lstring = '^';
+ ep->prevdirection = -2;
+ }
+ if(*lstring)
+ {
+ ed_ungetchar(ep->ed,'\r');
+ ed_ungetchar(ep->ed,cntl('R'));
+ return(-1);
+ }
+ }
+ *lstring = 0;
+ ed_ungetchar(ep->ed,cntl('P'));
+ return(-1);
+ case 'B':
+ ed_ungetchar(ep->ed,cntl('N'));
+ return(-1);
+ case 'C':
+ ed_ungetchar(ep->ed,cntl('F'));
+ return(-1);
+ case 'D':
+ ed_ungetchar(ep->ed,cntl('B'));
+ return(-1);
+ case 'H':
+ ed_ungetchar(ep->ed,cntl('A'));
+ return(-1);
+ case 'Y':
+ ed_ungetchar(ep->ed,cntl('E'));
+ return(-1);
+ default:
+ ed_ungetchar(ep->ed,i);
+ }
+ i = '_';
+
+ default:
+ /* look for user defined macro definitions */
+ if(ed_macro(ep->ed,i))
+# ifdef ESH_BETTER
+ return(count); /* pass argument to macro */
+# else
+ return(-1);
+# endif /* ESH_BETTER */
+#else
+ update:
+ cur = i;
+ draw(ep,UPDATE);
+ return(-1);
+
+ default:
+#endif /* KSHELL */
+ beep();
+ return(-1);
+ }
+ return(-1);
+}
+
+
+/*
+ * This routine process all commands starting with ^X
+ */
+
+static void xcommands(register Emacs_t *ep,int count)
+{
+ register int i = ed_getchar(ep->ed,0);
+ NOT_USED(count);
+ switch(i)
+ {
+ case cntl('X'): /* exchange dot and mark */
+ if (ep->mark > eol)
+ ep->mark = eol;
+ i = ep->mark;
+ ep->mark = cur;
+ cur = i;
+ draw(ep,UPDATE);
+ return;
+
+#if KSHELL
+# ifdef ESH_BETTER
+ case cntl('E'): /* invoke emacs on current command */
+ if(ed_fulledit(ep->ed)==-1)
+ beep();
+ else
+ {
+#if SHOPT_MULTIBYTE
+ ed_internal((char*)drawbuff,drawbuff);
+#endif /* SHOPT_MULTIBYTE */
+ ed_ungetchar(ep->ed,'\n');
+ }
+ return;
+
+# define itos(i) fmtbase((long)(i),0,0)/* want signed conversion */
+
+ case cntl('H'): /* ^X^H show history info */
+ {
+ char hbuf[MAXLINE];
+
+ strcpy(hbuf, "Current command ");
+ strcat(hbuf, itos(hline));
+ if (hloff)
+ {
+ strcat(hbuf, " (line ");
+ strcat(hbuf, itos(hloff+1));
+ strcat(hbuf, ")");
+ }
+ if ((hline != location.hist_command) ||
+ (hloff != location.hist_line))
+ {
+ strcat(hbuf, "; Previous command ");
+ strcat(hbuf, itos(location.hist_command));
+ if (location.hist_line)
+ {
+ strcat(hbuf, " (line ");
+ strcat(hbuf, itos(location.hist_line+1));
+ strcat(hbuf, ")");
+ }
+ }
+ show_info(ep,hbuf);
+ return;
+ }
+# if 0 /* debugging, modify as required */
+ case cntl('D'): /* ^X^D show debugging info */
+ {
+ char debugbuf[MAXLINE];
+
+ strcpy(debugbuf, "count=");
+ strcat(debugbuf, itos(count));
+ strcat(debugbuf, " eol=");
+ strcat(debugbuf, itos(eol));
+ strcat(debugbuf, " cur=");
+ strcat(debugbuf, itos(cur));
+ strcat(debugbuf, " crallowed=");
+ strcat(debugbuf, itos(crallowed));
+ strcat(debugbuf, " plen=");
+ strcat(debugbuf, itos(plen));
+ strcat(debugbuf, " w_size=");
+ strcat(debugbuf, itos(w_size));
+
+ show_info(ep,debugbuf);
+ return;
+ }
+# endif /* debugging code */
+# endif /* ESH_BETTER */
+#endif /* KSHELL */
+
+ default:
+ beep();
+ return;
+ }
+}
+
+static void search(Emacs_t* ep,genchar *out,int direction)
+{
+#ifndef ESH_NFIRST
+ Histloc_t location;
+#endif
+ register int i,sl;
+ genchar str_buff[LBUF];
+ register genchar *string = drawbuff;
+ /* save current line */
+ int sav_cur = cur;
+ genncpy(str_buff,string,sizeof(str_buff)/sizeof(*str_buff));
+ string[0] = '^';
+ string[1] = 'R';
+ string[2] = '\0';
+ sl = 2;
+ cur = sl;
+ draw(ep,UPDATE);
+ while ((i = ed_getchar(ep->ed,1))&&(i != '\r')&&(i != '\n'))
+ {
+ if (i==usrerase || i==DELETE || i=='\b' || i==ERASECHAR)
+ {
+ if (sl > 2)
+ {
+ string[--sl] = '\0';
+ cur = sl;
+ draw(ep,UPDATE);
+ }
+ else
+ goto restore;
+ continue;
+ }
+ if(i == ep->ed->e_intr)
+ goto restore;
+ if (i==usrkill)
+ {
+ beep();
+ goto restore;
+ }
+ if (i == '\\')
+ {
+ string[sl++] = '\\';
+ string[sl] = '\0';
+ cur = sl;
+ draw(ep,APPEND);
+ i = ed_getchar(ep->ed,1);
+ string[--sl] = '\0';
+ }
+ string[sl++] = i;
+ string[sl] = '\0';
+ cur = sl;
+ draw(ep,APPEND);
+ }
+ i = genlen(string);
+
+ if(ep->prevdirection == -2 && i!=2 || direction!=1)
+ ep->prevdirection = -1;
+ if (direction < 1)
+ {
+ ep->prevdirection = -ep->prevdirection;
+ direction = 1;
+ }
+ else
+ direction = -1;
+ if (i != 2)
+ {
+#if SHOPT_MULTIBYTE
+ ed_external(string,(char*)string);
+#endif /* SHOPT_MULTIBYTE */
+ strncpy(lstring,((char*)string)+2,SEARCHSIZE);
+ lstring[SEARCHSIZE-1] = 0;
+ ep->prevdirection = direction;
+ }
+ else
+ direction = ep->prevdirection ;
+ location = hist_find(shgd->hist_ptr,(char*)lstring,hline,1,direction);
+ i = location.hist_command;
+ if(i>0)
+ {
+ hline = i;
+#ifdef ESH_NFIRST
+ hloff = location.hist_line = 0; /* display first line of multi line command */
+#else
+ hloff = location.hist_line;
+#endif /* ESH_NFIRST */
+ hist_copy((char*)out,MAXLINE, hline,hloff);
+#if SHOPT_MULTIBYTE
+ ed_internal((char*)out,out);
+#endif /* SHOPT_MULTIBYTE */
+ return;
+ }
+ if (i < 0)
+ {
+ beep();
+#ifdef ESH_NFIRST
+ location.hist_command = hline;
+ location.hist_line = hloff;
+#else
+ hloff = 0;
+ hline = histlines;
+#endif /* ESH_NFIRST */
+ }
+restore:
+ genncpy(string,str_buff,sizeof(str_buff)/sizeof(*str_buff));
+ cur = sav_cur;
+ return;
+}
+
+
+/* Adjust screen to agree with inputs: logical line and cursor */
+/* If 'first' assume screen is blank */
+/* Prompt is always kept on the screen */
+
+static void draw(register Emacs_t *ep,Draw_t option)
+{
+#define NORMAL ' '
+#define LOWER '<'
+#define BOTH '*'
+#define UPPER '>'
+
+ register genchar *sptr; /* Pointer within screen */
+ genchar nscreen[2*MAXLINE]; /* New entire screen */
+ genchar *ncursor; /* New cursor */
+ register genchar *nptr; /* Pointer to New screen */
+ char longline; /* Line overflow */
+ genchar *logcursor;
+ genchar *nscend; /* end of logical screen */
+ register int i;
+
+ nptr = nscreen;
+ sptr = drawbuff;
+ logcursor = sptr + cur;
+ longline = NORMAL;
+ ep->lastdraw = option;
+
+ if (option == FIRST || option == REFRESH)
+ {
+ ep->overflow = NORMAL;
+ ep->cursor = ep->screen;
+ ep->offset = 0;
+ ep->cr_ok = crallowed;
+ if (option == FIRST)
+ {
+ ep->scvalid = 1;
+ return;
+ }
+ *ep->cursor = '\0';
+ putstring(ep,Prompt); /* start with prompt */
+ }
+
+ /*********************
+ Do not update screen if pending characters
+ **********************/
+
+ if ((lookahead)&&(option != FINAL))
+ {
+
+ ep->scvalid = 0; /* Screen is out of date, APPEND will not work */
+
+ return;
+ }
+
+ /***************************************
+ If in append mode, cursor at end of line, screen up to date,
+ the previous character was a 'normal' character,
+ and the window has room for another character.
+ Then output the character and adjust the screen only.
+ *****************************************/
+
+
+ i = *(logcursor-1); /* last character inserted */
+#if SHOPT_EDPREDICT
+ if(option==FINAL)
+ {
+ if(ep->ed->hlist)
+ ed_histlist(ep->ed,0);
+ }
+ else if((option==UPDATE||option==APPEND) && drawbuff[0]=='#' && cur>1 && cur==eol && drawbuff[cur-1]!='*')
+ {
+ int n;
+ drawbuff[cur+1]=0;
+# if SHOPT_MULTIBYTE
+ ed_external(drawbuff,(char*)drawbuff);
+# endif /*SHOPT_MULTIBYTE */
+ n = ed_histgen(ep->ed,(char*)drawbuff);
+# if SHOPT_MULTIBYTE
+ ed_internal((char*)drawbuff,drawbuff);
+# endif /*SHOPT_MULTIBYTE */
+ if(ep->ed->hlist)
+ {
+ ed_histlist(ep->ed,n);
+ putstring(ep,Prompt);
+ ed_setcursor(ep->ed,ep->screen,0,ep->cursor-ep->screen, 0);
+ }
+ else
+ ed_ringbell();
+
+ }
+#endif /* SHOPT_EDPREDICT */
+
+ if ((option == APPEND)&&(ep->scvalid)&&(*logcursor == '\0')&&
+ print(i)&&((ep->cursor-ep->screen)<(w_size-1)))
+ {
+ putchar(ep->ed,i);
+ *ep->cursor++ = i;
+ *ep->cursor = '\0';
+ return;
+ }
+
+ /* copy the line */
+ ncursor = nptr + ed_virt_to_phys(ep->ed,sptr,nptr,cur,0,0);
+ nptr += genlen(nptr);
+ sptr += genlen(sptr);
+ nscend = nptr - 1;
+ if(sptr == logcursor)
+ ncursor = nptr;
+
+ /*********************
+ Does ncursor appear on the screen?
+ If not, adjust the screen offset so it does.
+ **********************/
+
+ i = ncursor - nscreen;
+
+ if ((ep->offset && i<=ep->offset)||(i >= (ep->offset+w_size)))
+ {
+ /* Center the cursor on the screen */
+ ep->offset = i - (w_size>>1);
+ if (--ep->offset < 0)
+ ep->offset = 0;
+ }
+
+ /*********************
+ Is the range of screen[0] thru screen[w_size] up-to-date
+ with nscreen[offset] thru nscreen[offset+w_size] ?
+ If not, update as need be.
+ ***********************/
+
+ nptr = &nscreen[ep->offset];
+ sptr = ep->screen;
+
+ i = w_size;
+
+ while (i-- > 0)
+ {
+
+ if (*nptr == '\0')
+ {
+ *(nptr + 1) = '\0';
+ *nptr = ' ';
+ }
+ if (*sptr == '\0')
+ {
+ *(sptr + 1) = '\0';
+ *sptr = ' ';
+ }
+ if (*nptr == *sptr)
+ {
+ nptr++;
+ sptr++;
+ continue;
+ }
+ setcursor(ep,sptr-ep->screen,*nptr);
+ *sptr++ = *nptr++;
+#if SHOPT_MULTIBYTE
+ while(*nptr==MARKER)
+ {
+ if(*sptr=='\0')
+ *(sptr + 1) = '\0';
+ *sptr++ = *nptr++;
+ i--;
+ ep->cursor++;
+ }
+#endif /* SHOPT_MULTIBYTE */
+ }
+ if(ep->ed->e_multiline && option == REFRESH && ep->ed->e_nocrnl==0)
+ ed_setcursor(ep->ed, ep->screen, ep->cursor-ep->screen, ep->ed->e_peol, -1);
+
+
+ /******************
+
+ Screen overflow checks
+
+ ********************/
+
+ if (nscend >= &nscreen[ep->offset+w_size])
+ {
+ if (ep->offset > 0)
+ longline = BOTH;
+ else
+ longline = UPPER;
+ }
+ else
+ {
+ if (ep->offset > 0)
+ longline = LOWER;
+ }
+
+ /* Update screen overflow indicator if need be */
+
+ if (longline != ep->overflow)
+ {
+ setcursor(ep,w_size,longline);
+ ep->overflow = longline;
+ }
+ i = (ncursor-nscreen) - ep->offset;
+ setcursor(ep,i,0);
+ if(option==FINAL && ep->ed->e_multiline)
+ setcursor(ep,nscend+1-nscreen,0);
+ ep->scvalid = 1;
+ return;
+}
+
+/*
+ * put the cursor to the <newp> position within screen buffer
+ * if <c> is non-zero then output this character
+ * cursor is set to reflect the change
+ */
+
+static void setcursor(register Emacs_t *ep,register int newp,int c)
+{
+ register int oldp = ep->cursor - ep->screen;
+ newp = ed_setcursor(ep->ed, ep->screen, oldp, newp, 0);
+ if(c)
+ {
+ putchar(ep->ed,c);
+ newp++;
+ }
+ ep->cursor = ep->screen+newp;
+ return;
+}
+
+#if SHOPT_MULTIBYTE
+static int print(register int c)
+{
+ return((c&~STRIP)==0 && isprint(c));
+}
+
+static int _isword(register int c)
+{
+ return((c&~STRIP) || isalnum(c) || c=='_');
+}
+#endif /* SHOPT_MULTIBYTE */
diff --git a/src/cmd/ksh93/edit/hexpand.c b/src/cmd/ksh93/edit/hexpand.c
new file mode 100644
index 0000000..3e2d396
--- /dev/null
+++ b/src/cmd/ksh93/edit/hexpand.c
@@ -0,0 +1,734 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1982-2011 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
+/*
+ * bash style history expansion
+ *
+ * Author:
+ * Karsten Fleischer
+ * Omnium Software Engineering
+ * An der Luisenburg 7
+ * D-51379 Leverkusen
+ * Germany
+ *
+ * <K.Fleischer@omnium.de>
+ */
+
+
+#include "defs.h"
+#include "edit.h"
+
+#if ! SHOPT_HISTEXPAND
+
+NoN(hexpand)
+
+#else
+
+static char *modifiers = "htrepqxs&";
+static int mod_flags[] = { 0, 0, 0, 0, HIST_PRINT, HIST_QUOTE, HIST_QUOTE|HIST_QUOTE_BR, 0, 0 };
+
+#define DONE() {flag |= HIST_ERROR; cp = 0; stakseek(0); goto done;}
+
+struct subst
+{
+ char *str[2]; /* [0] is "old", [1] is "new" string */
+};
+
+
+/*
+ * parse an /old/new/ string, delimiter expected as first char.
+ * if "old" not specified, keep sb->str[0]
+ * if "new" not specified, set sb->str[1] to empty string
+ * read up to third delimeter char, \n or \0, whichever comes first.
+ * return adress is one past the last valid char in s:
+ * - the address containing \n or \0 or
+ * - one char beyond the third delimiter
+ */
+
+static char *parse_subst(const char *s, struct subst *sb)
+{
+ char *cp,del;
+ int off,n = 0;
+
+ /* build the strings on the stack, mainly for '&' substition in "new" */
+ off = staktell();
+
+ /* init "new" with empty string */
+ if(sb->str[1])
+ free(sb->str[1]);
+ sb->str[1] = strdup("");
+
+ /* get delimiter */
+ del = *s;
+
+ cp = (char*) s + 1;
+
+ while(n < 2)
+ {
+ if(*cp == del || *cp == '\n' || *cp == '\0')
+ {
+ /* delimiter or EOL */
+ if(staktell() != off)
+ {
+ /* dupe string on stack and rewind stack */
+ stakputc('\0');
+ if(sb->str[n])
+ free(sb->str[n]);
+ sb->str[n] = strdup(stakptr(off));
+ stakseek(off);
+ }
+ n++;
+
+ /* if not delimiter, we've reached EOL. Get outta here. */
+ if(*cp != del)
+ break;
+ }
+ else if(*cp == '\\')
+ {
+ if(*(cp+1) == del) /* quote delimiter */
+ {
+ stakputc(del);
+ cp++;
+ }
+ else if(*(cp+1) == '&' && n == 1)
+ { /* quote '&' only in "new" */
+ stakputc('&');
+ cp++;
+ }
+ else
+ stakputc('\\');
+ }
+ else if(*cp == '&' && n == 1 && sb->str[0])
+ /* substitute '&' with "old" in "new" */
+ stakputs(sb->str[0]);
+ else
+ stakputc(*cp);
+ cp++;
+ }
+
+ /* rewind stack */
+ stakseek(off);
+
+ return cp;
+}
+
+/*
+ * history expansion main routine
+ */
+
+int hist_expand(const char *ln, char **xp)
+{
+ int off, /* stack offset */
+ q, /* quotation flags */
+ p, /* flag */
+ c, /* current char */
+ flag=0; /* HIST_* flags */
+ Sfoff_t n, /* history line number, counter, etc. */
+ i, /* counter */
+ w[2]; /* word range */
+ char *sp, /* stack pointer */
+ *cp, /* current char in ln */
+ *str, /* search string */
+ *evp, /* event/word designator string, for error msgs */
+ *cc=0, /* copy of current line up to cp; temp ptr */
+ hc[3], /* default histchars */
+ *qc="\'\"`"; /* quote characters */
+ Sfio_t *ref=0, /* line referenced by event designator */
+ *tmp=0, /* temporary line buffer */
+ *tmp2=0;/* temporary line buffer */
+ Histloc_t hl; /* history location */
+ static Namval_t *np = 0; /* histchars variable */
+ static struct subst sb = {0,0}; /* substition strings */
+ static Sfio_t *wm=0; /* word match from !?string? event designator */
+
+ if(!wm)
+ wm = sfopen(NULL, NULL, "swr");
+
+ hc[0] = '!';
+ hc[1] = '^';
+ hc[2] = 0;
+ if((np = nv_open("histchars",sh.var_tree,0)) && (cp = nv_getval(np)))
+ {
+ if(cp[0])
+ {
+ hc[0] = cp[0];
+ if(cp[1])
+ {
+ hc[1] = cp[1];
+ if(cp[2])
+ hc[2] = cp[2];
+ }
+ }
+ }
+
+ /* save shell stack */
+ if(off = staktell())
+ sp = stakfreeze(0);
+
+ cp = (char*)ln;
+
+ while(cp && *cp)
+ {
+ /* read until event/quick substitution/comment designator */
+ if((*cp != hc[0] && *cp != hc[1] && *cp != hc[2])
+ || (*cp == hc[1] && cp != ln))
+ {
+ if(*cp == '\\') /* skip escaped designators */
+ stakputc(*cp++);
+ else if(*cp == '\'') /* skip quoted designators */
+ {
+ do
+ stakputc(*cp);
+ while(*++cp && *cp != '\'');
+ }
+ stakputc(*cp++);
+ continue;
+ }
+
+ if(hc[2] && *cp == hc[2]) /* history comment designator, skip rest of line */
+ {
+ stakputc(*cp++);
+ stakputs(cp);
+ DONE();
+ }
+
+ n = -1;
+ str = 0;
+ flag &= HIST_EVENT; /* save event flag for returning later */
+ evp = cp;
+ ref = 0;
+
+ if(*cp == hc[1]) /* shortcut substitution */
+ {
+ flag |= HIST_QUICKSUBST;
+ goto getline;
+ }
+
+ if(*cp == hc[0] && *(cp+1) == hc[0]) /* refer to line -1 */
+ {
+ cp += 2;
+ goto getline;
+ }
+
+ switch(c = *++cp) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\0':
+ case '=':
+ case '(':
+ stakputc(hc[0]);
+ continue;
+ case '#': /* the line up to current position */
+ flag |= HIST_HASH;
+ cp++;
+ n = staktell(); /* terminate string and dup */
+ stakputc('\0');
+ cc = strdup(stakptr(0));
+ stakseek(n); /* remove null byte again */
+ ref = sfopen(ref, cc, "s"); /* open as file */
+ n = 0; /* skip history file referencing */
+ break;
+ case '-': /* back reference by number */
+ if(!isdigit(*(cp+1)))
+ goto string_event;
+ cp++;
+ case '0': /* reference by number */
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ n = 0;
+ while(isdigit(*cp))
+ n = n * 10 + (*cp++) - '0';
+ if(c == '-')
+ n = -n;
+ break;
+ case '$':
+ n = -1;
+ case ':':
+ break;
+ case '?':
+ cp++;
+ flag |= HIST_QUESTION;
+ string_event:
+ default:
+ /* read until end of string or word designator/modifier */
+ str = cp;
+ while(*cp)
+ {
+ cp++;
+ if((!(flag&HIST_QUESTION) &&
+ (*cp == ':' || isspace(*cp)
+ || *cp == '^' || *cp == '$'
+ || *cp == '*' || *cp == '-'
+ || *cp == '%')
+ )
+ || ((flag&HIST_QUESTION) && (*cp == '?' || *cp == '\n')))
+ {
+ c = *cp;
+ *cp = '\0';
+ }
+ }
+ break;
+ }
+
+getline:
+ flag |= HIST_EVENT;
+ if(str) /* !string or !?string? event designator */
+ {
+
+ /* search history for string */
+ hl = hist_find(shgd->hist_ptr, str,
+ shgd->hist_ptr->histind,
+ flag&HIST_QUESTION, -1);
+ if((n = hl.hist_command) == -1)
+ n = 0; /* not found */
+ }
+ if(n)
+ {
+ if(n < 0) /* determine index for backref */
+ n = shgd->hist_ptr->histind + n;
+ /* search and use history file if found */
+ if(n > 0 && hist_seek(shgd->hist_ptr, n) != -1)
+ ref = shgd->hist_ptr->histfp;
+
+ }
+ if(!ref)
+ {
+ /* string not found or command # out of range */
+ c = *cp;
+ *cp = '\0';
+ errormsg(SH_DICT, ERROR_ERROR, "%s: event not found", evp);
+ *cp = c;
+ DONE();
+ }
+
+ if(str) /* string search: restore orig. line */
+ {
+ if(flag&HIST_QUESTION)
+ *cp++ = c; /* skip second question mark */
+ else
+ *cp = c;
+ }
+
+ /* colon introduces either word designators or modifiers */
+ if(*(evp = cp) == ':')
+ cp++;
+
+ w[0] = 0; /* -1 means last word, -2 means match from !?string? */
+ w[1] = -1; /* -1 means last word, -2 means suppress last word */
+
+ if(flag & HIST_QUICKSUBST) /* shortcut substitution */
+ goto getsel;
+
+ n = 0;
+ while(n < 2)
+ {
+ switch(c = *cp++) {
+ case '^': /* first word */
+ if(n == 0)
+ {
+ w[0] = w[1] = 1;
+ goto skip;
+ }
+ else
+ goto skip2;
+ case '$': /* last word */
+ w[n] = -1;
+ goto skip;
+ case '%': /* match from !?string? event designator */
+ if(n == 0)
+ {
+ if(!str)
+ {
+ w[0] = 0;
+ w[1] = -1;
+ ref = wm;
+ }
+ else
+ {
+ w[0] = -2;
+ w[1] = sftell(ref) + hl.hist_char;
+ }
+ sfseek(wm, 0, SEEK_SET);
+ goto skip;
+ }
+ default:
+ skip2:
+ cp--;
+ n = 2;
+ break;
+ case '*': /* until last word */
+ if(n == 0)
+ w[0] = 1;
+ w[1] = -1;
+ skip:
+ flag |= HIST_WORDDSGN;
+ n = 2;
+ break;
+ case '-': /* until last word or specified index */
+ w[1] = -2;
+ flag |= HIST_WORDDSGN;
+ n = 1;
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9': /* specify index */
+ if((*evp == ':') || w[1] == -2)
+ {
+ w[n] = c - '0';
+ while(isdigit(c=*cp++))
+ w[n] = w[n] * 10 + c - '0';
+ flag |= HIST_WORDDSGN;
+ if(n == 0)
+ w[1] = w[0];
+ n++;
+ }
+ else
+ n = 2;
+ cp--;
+ break;
+ }
+ }
+
+ if(w[0] != -2 && w[1] > 0 && w[0] > w[1])
+ {
+ c = *cp;
+ *cp = '\0';
+ errormsg(SH_DICT, ERROR_ERROR, "%s: bad word specifier", evp);
+ *cp = c;
+ DONE();
+ }
+
+ /* no valid word designator after colon, rewind */
+ if(!(flag & HIST_WORDDSGN) && (*evp == ':'))
+ cp = evp;
+
+getsel:
+ /* open temp buffer, let sfio do the (re)allocation */
+ tmp = sfopen(NULL, NULL, "swr");
+
+ /* push selected words into buffer, squash
+ whitespace into single blank or a newline */
+ n = i = q = 0;
+
+ while((c = sfgetc(ref)) > 0)
+ {
+ if(isspace(c))
+ {
+ flag |= (c == '\n' ? HIST_NEWLINE : 0);
+ continue;
+ }
+
+ if(n >= w[0] && ((w[0] != -2) ? (w[1] < 0 || n <= w[1]) : 1))
+ {
+ if(w[0] < 0)
+ sfseek(tmp, 0, SEEK_SET);
+ else
+ i = sftell(tmp);
+
+ if(i > 0)
+ sfputc(tmp, flag & HIST_NEWLINE ? '\n' : ' ');
+
+ flag &= ~HIST_NEWLINE;
+ p = 1;
+ }
+ else
+ p = 0;
+
+ do
+ {
+ cc = strchr(qc, c);
+ q ^= cc ? 1<<(int)(cc - qc) : 0;
+ if(p)
+ sfputc(tmp, c);
+ }
+ while((c = sfgetc(ref)) > 0 && (!isspace(c) || q));
+
+ if(w[0] == -2 && sftell(ref) > w[1])
+ break;
+
+ flag |= (c == '\n' ? HIST_NEWLINE : 0);
+ n++;
+ }
+ if(w[0] != -2 && w[1] >= 0 && w[1] >= n)
+ {
+ c = *cp;
+ *cp = '\0';
+ errormsg(SH_DICT, ERROR_ERROR, "%s: bad word specifier", evp);
+ *cp = c;
+ DONE();
+ }
+ else if(w[1] == -2) /* skip last word */
+ sfseek(tmp, i, SEEK_SET);
+
+ /* remove trailing newline */
+ if(sftell(tmp))
+ {
+ sfseek(tmp, -1, SEEK_CUR);
+ if(sfgetc(tmp) == '\n')
+ sfungetc(tmp, '\n');
+ }
+
+ sfputc(tmp, '\0');
+
+ if(str)
+ {
+ if(wm)
+ sfclose(wm);
+ wm = tmp;
+ }
+
+ if(cc && (flag&HIST_HASH))
+ {
+ /* close !# temp file */
+ sfclose(ref);
+ flag &= ~HIST_HASH;
+ free(cc);
+ cc = 0;
+ }
+
+ evp = cp;
+
+ /* selected line/words are now in buffer, now go for the modifiers */
+ while(*cp == ':' || (flag & HIST_QUICKSUBST))
+ {
+ if(flag & HIST_QUICKSUBST)
+ {
+ flag &= ~HIST_QUICKSUBST;
+ c = 's';
+ cp--;
+ }
+ else
+ c = *++cp;
+
+ sfseek(tmp, 0, SEEK_SET);
+ tmp2 = sfopen(tmp2, NULL, "swr");
+
+ if(c == 'g') /* global substitution */
+ {
+ flag |= HIST_GLOBALSUBST;
+ c = *++cp;
+ }
+
+ if(cc = strchr(modifiers, c))
+ flag |= mod_flags[cc - modifiers];
+ else
+ {
+ errormsg(SH_DICT, ERROR_ERROR, "%c: unrecognized history modifier", c);
+ DONE();
+ }
+
+ if(c == 'h' || c == 'r') /* head or base */
+ {
+ n = -1;
+ while((c = sfgetc(tmp)) > 0)
+ { /* remember position of / or . */
+ if((c == '/' && *cp == 'h') || (c == '.' && *cp == 'r'))
+ n = sftell(tmp2);
+ sfputc(tmp2, c);
+ }
+ if(n > 0)
+ { /* rewind to last / or . */
+ sfseek(tmp2, n, SEEK_SET);
+ /* end string there */
+ sfputc(tmp2, '\0');
+ }
+ }
+ else if(c == 't' || c == 'e') /* tail or suffix */
+ {
+ n = 0;
+ while((c = sfgetc(tmp)) > 0)
+ { /* remember position of / or . */
+ if((c == '/' && *cp == 't') || (c == '.' && *cp == 'e'))
+ n = sftell(tmp);
+ }
+ /* rewind to last / or . */
+ sfseek(tmp, n, SEEK_SET);
+ /* copy from there on */
+ while((c = sfgetc(tmp)) > 0)
+ sfputc(tmp2, c);
+ }
+ else if(c == 's' || c == '&')
+ {
+ cp++;
+
+ if(c == 's')
+ {
+ /* preset old with match from !?string? */
+ if(!sb.str[0] && wm)
+ sb.str[0] = strdup(sfsetbuf(wm, (Void_t*)1, 0));
+ cp = parse_subst(cp, &sb);
+ }
+
+ if(!sb.str[0] || !sb.str[1])
+ {
+ c = *cp;
+ *cp = '\0';
+ errormsg(SH_DICT, ERROR_ERROR,
+ "%s%s: no previous substitution",
+ (flag & HIST_QUICKSUBST) ? ":s" : "",
+ evp);
+ *cp = c;
+ DONE();
+ }
+
+ /* need pointer for strstr() */
+ str = sfsetbuf(tmp, (Void_t*)1, 0);
+
+ flag |= HIST_SUBSTITUTE;
+ while(flag & HIST_SUBSTITUTE)
+ {
+ /* find string */
+ if(cc = strstr(str, sb.str[0]))
+ { /* replace it */
+ c = *cc;
+ *cc = '\0';
+ sfputr(tmp2, str, -1);
+ sfputr(tmp2, sb.str[1], -1);
+ *cc = c;
+ str = cc + strlen(sb.str[0]);
+ }
+ else if(!sftell(tmp2))
+ { /* not successfull */
+ c = *cp;
+ *cp = '\0';
+ errormsg(SH_DICT, ERROR_ERROR,
+ "%s%s: substitution failed",
+ (flag & HIST_QUICKSUBST) ? ":s" : "",
+ evp);
+ *cp = c;
+ DONE();
+ }
+ /* loop if g modifier specified */
+ if(!cc || !(flag & HIST_GLOBALSUBST))
+ flag &= ~HIST_SUBSTITUTE;
+ }
+ /* output rest of line */
+ sfputr(tmp2, str, -1);
+ if(*cp)
+ cp--;
+ }
+
+ if(sftell(tmp2))
+ { /* if any substitions done, swap buffers */
+ if(wm != tmp)
+ sfclose(tmp);
+ tmp = tmp2;
+ tmp2 = 0;
+ }
+ cc = 0;
+ if(*cp)
+ cp++;
+ }
+
+ /* flush temporary buffer to stack */
+ if(tmp)
+ {
+ sfseek(tmp, 0, SEEK_SET);
+
+ if(flag & HIST_QUOTE)
+ stakputc('\'');
+
+ while((c = sfgetc(tmp)) > 0)
+ {
+ if(isspace(c))
+ {
+ flag = flag & ~HIST_NEWLINE;
+
+ /* squash white space to either a
+ blank or a newline */
+ do
+ flag |= (c == '\n' ? HIST_NEWLINE : 0);
+ while((c = sfgetc(tmp)) > 0 && isspace(c));
+
+ sfungetc(tmp, c);
+
+ c = (flag & HIST_NEWLINE) ? '\n' : ' ';
+
+ if(flag & HIST_QUOTE_BR)
+ {
+ stakputc('\'');
+ stakputc(c);
+ stakputc('\'');
+ }
+ else
+ stakputc(c);
+ }
+ else if((c == '\'') && (flag & HIST_QUOTE))
+ {
+ stakputc('\'');
+ stakputc('\\');
+ stakputc(c);
+ stakputc('\'');
+ }
+ else
+ stakputc(c);
+ }
+ if(flag & HIST_QUOTE)
+ stakputc('\'');
+ }
+ }
+
+ stakputc('\0');
+
+done:
+ if(cc && (flag&HIST_HASH))
+ {
+ /* close !# temp file */
+ sfclose(ref);
+ free(cc);
+ cc = 0;
+ }
+
+ /* error? */
+ if(staktell() && !(flag & HIST_ERROR))
+ *xp = strdup(stakfreeze(1));
+
+ /* restore shell stack */
+ if(off)
+ stakset(sp,off);
+ else
+ stakseek(0);
+
+ /* drop temporary files */
+
+ if(tmp && tmp != wm)
+ sfclose(tmp);
+ if(tmp2)
+ sfclose(tmp2);
+
+ return (flag & HIST_ERROR ? HIST_ERROR : flag & HIST_FLAG_RETURN_MASK);
+}
+
+#endif
diff --git a/src/cmd/ksh93/edit/history.c b/src/cmd/ksh93/edit/history.c
new file mode 100644
index 0000000..99fc01d
--- /dev/null
+++ b/src/cmd/ksh93/edit/history.c
@@ -0,0 +1,1222 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1982-2011 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
+/*
+ * History file manipulation routines
+ *
+ * David Korn
+ * AT&T Labs
+ *
+ */
+
+/*
+ * Each command in the history file starts on an even byte is null terminated.
+ * The first byte must contain the special character HIST_UNDO and the second
+ * byte is the version number. The sequence HIST_UNDO 0, following a command,
+ * nullifies the previous command. A six byte sequence starting with
+ * HIST_CMDNO is used to store the command number so that it is not necessary
+ * to read the file from beginning to end to get to the last block of
+ * commands. This format of this sequence is different in version 1
+ * then in version 0. Version 1 allows commands to use the full 8 bit
+ * character set. It can understand version 0 format files.
+ */
+
+
+#define HIST_MAX (sizeof(int)*HIST_BSIZE)
+#define HIST_BIG (0100000-1024) /* 1K less than maximum short */
+#define HIST_LINE 32 /* typical length for history line */
+#define HIST_MARKSZ 6
+#define HIST_RECENT 600
+#define HIST_UNDO 0201 /* invalidate previous command */
+#define HIST_CMDNO 0202 /* next 3 bytes give command number */
+#define HIST_BSIZE 4096 /* size of history file buffer */
+#define HIST_DFLT 512 /* default size of history list */
+
+#if SHOPT_AUDIT
+# define _HIST_AUDIT Sfio_t *auditfp; \
+ char *tty; \
+ int auditmask;
+#else
+# define _HIST_AUDIT
+#endif
+
+#define _HIST_PRIVATE \
+ void *histshell; \
+ off_t histcnt; /* offset into history file */\
+ off_t histmarker; /* offset of last command marker */ \
+ int histflush; /* set if flushed outside of hflush() */\
+ int histmask; /* power of two mask for histcnt */ \
+ char histbuff[HIST_BSIZE+1]; /* history file buffer */ \
+ int histwfail; \
+ _HIST_AUDIT \
+ off_t histcmds[2]; /* offset for recent commands, must be last */
+
+#define hist_ind(hp,c) ((int)((c)&(hp)->histmask))
+
+#include <ast.h>
+#include <sfio.h>
+#include "FEATURE/time"
+#include <error.h>
+#include <ls.h>
+#if KSHELL
+# include "defs.h"
+# include "variables.h"
+# include "path.h"
+# include "builtins.h"
+# include "io.h"
+#else
+# include <ctype.h>
+#endif /* KSHELL */
+#include "history.h"
+
+#if !KSHELL
+# define new_of(type,x) ((type*)malloc((unsigned)sizeof(type)+(x)))
+# define NIL(type) ((type)0)
+# define path_relative(s,x) (s,x)
+# ifdef __STDC__
+# define nv_getval(s) getenv(#s)
+# else
+# define nv_getval(s) getenv("s")
+# endif /* __STDC__ */
+# define e_unknown "unknown"
+# define sh_translate(x) (x)
+ char login_sh = 0;
+ char hist_fname[] = "/.history";
+#endif /* KSHELL */
+
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif /* O_BINARY */
+
+int _Hist = 0;
+static void hist_marker(char*,long);
+static History_t* hist_trim(History_t*, int);
+static int hist_nearend(History_t*,Sfio_t*, off_t);
+static int hist_check(int);
+static int hist_clean(int);
+#ifdef SF_BUFCONST
+ static ssize_t hist_write(Sfio_t*, const void*, size_t, Sfdisc_t*);
+ static int hist_exceptf(Sfio_t*, int, void*, Sfdisc_t*);
+#else
+ static int hist_write(Sfio_t*, const void*, int, Sfdisc_t*);
+ static int hist_exceptf(Sfio_t*, int, Sfdisc_t*);
+#endif
+
+
+static int histinit;
+static mode_t histmode;
+static History_t *wasopen;
+static History_t *hist_ptr;
+
+#if SHOPT_ACCTFILE
+ static int acctfd;
+ static char *logname;
+# include <pwd.h>
+
+ static int acctinit(History_t *hp)
+ {
+ register char *cp, *acctfile;
+ Namval_t *np = nv_search("ACCTFILE",((Shell_t*)hp->histshell)->var_tree,0);
+
+ if(!np || !(acctfile=nv_getval(np)))
+ return(0);
+ if(!(cp = getlogin()))
+ {
+ struct passwd *userinfo = getpwuid(getuid());
+ if(userinfo)
+ cp = userinfo->pw_name;
+ else
+ cp = "unknown";
+ }
+ logname = strdup(cp);
+ if((acctfd=sh_open(acctfile,
+ O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IRUSR|S_IWUSR))>=0 &&
+ (unsigned)acctfd < 10)
+ {
+ int n;
+ if((n = fcntl(acctfd, F_DUPFD, 10)) >= 0)
+ {
+ close(acctfd);
+ acctfd = n;
+ }
+ }
+ if(acctfd < 0)
+ {
+ acctfd = 0;
+ return(0);
+ }
+ if(sh_isdevfd(acctfile))
+ {
+ char newfile[16];
+ sfsprintf(newfile,sizeof(newfile),"%.8s%d\0",e_devfdNN,acctfd);
+ nv_putval(np,newfile,NV_RDONLY);
+ }
+ else
+ fcntl(acctfd,F_SETFD,FD_CLOEXEC);
+ return(1);
+ }
+#endif /* SHOPT_ACCTFILE */
+
+#if SHOPT_AUDIT
+static int sh_checkaudit(History_t *hp, const char *name, char *logbuf, size_t len)
+{
+ char *buff, *cp, *last;
+ int id1, id2, r=0, n, fd;
+ if((fd=open(name, O_RDONLY)) < 0)
+ return(0);
+ if((n = read(fd, logbuf,len-1)) < 0)
+ goto done;
+ while(logbuf[n-1]=='\n')
+ n--;
+ logbuf[n] = 0;
+ if(!(cp=strchr(logbuf,';')) && !(cp=strchr(logbuf,' ')))
+ goto done;
+ *cp = 0;
+ do
+ {
+ cp++;
+ id1 = id2 = strtol(cp,&last,10);
+ if(*last=='-')
+ id1 = strtol(last+1,&last,10);
+ if(shgd->euserid >=id1 && shgd->euserid <= id2)
+ r |= 1;
+ if(shgd->userid >=id1 && shgd->userid <= id2)
+ r |= 2;
+ cp = last;
+ }
+ while(*cp==';' || *cp==' ');
+done:
+ close(fd);
+ return(r);
+
+}
+#endif /*SHOPT_AUDIT*/
+
+static const unsigned char hist_stamp[2] = { HIST_UNDO, HIST_VERSION };
+static const Sfdisc_t hist_disc = { NULL, hist_write, NULL, hist_exceptf, NULL};
+
+static void hist_touch(void *handle)
+{
+ touch((char*)handle, (time_t)0, (time_t)0, 0);
+}
+
+/*
+ * open the history file
+ * if HISTNAME is not given and userid==0 then no history file.
+ * if login_sh and HISTFILE is longer than HIST_MAX bytes then it is
+ * cleaned up.
+ * hist_open() returns 1, if history file is open
+ */
+int sh_histinit(void *sh_context)
+{
+ Shell_t *shp = (Shell_t*)sh_context;
+ register int fd;
+ register History_t *hp;
+ register char *histname;
+ char *fname=0;
+ int histmask, maxlines, hist_start=0;
+ register char *cp;
+ register off_t hsize = 0;
+
+ if(shgd->hist_ptr=hist_ptr)
+ return(1);
+ if(!(histname = nv_getval(HISTFILE)))
+ {
+ int offset = staktell();
+ if(cp=nv_getval(HOME))
+ stakputs(cp);
+ stakputs(hist_fname);
+ stakputc(0);
+ stakseek(offset);
+ histname = stakptr(offset);
+ }
+#ifdef future
+ if(hp=wasopen)
+ {
+ /* reuse history file if same name */
+ wasopen = 0;
+ shgd->hist_ptr = hist_ptr = hp;
+ if(strcmp(histname,hp->histname)==0)
+ return(1);
+ else
+ hist_free();
+ }
+#endif
+retry:
+ cp = path_relative(shp,histname);
+ if(!histinit)
+ histmode = S_IRUSR|S_IWUSR;
+ if((fd=open(cp,O_BINARY|O_APPEND|O_RDWR|O_CREAT,histmode))>=0)
+ {
+ hsize=lseek(fd,(off_t)0,SEEK_END);
+ }
+ if((unsigned)fd <=2)
+ {
+ int n;
+ if((n=fcntl(fd,F_DUPFD,10))>=0)
+ {
+ close(fd);
+ fd=n;
+ }
+ }
+ /* make sure that file has history file format */
+ if(hsize && hist_check(fd))
+ {
+ close(fd);
+ hsize = 0;
+ if(unlink(cp)>=0)
+ goto retry;
+ fd = -1;
+ }
+ if(fd < 0)
+ {
+#if KSHELL
+ /* don't allow root a history_file in /tmp */
+ if(shgd->userid)
+#endif /* KSHELL */
+ {
+ if(!(fname = pathtmp(NIL(char*),0,0,NIL(int*))))
+ return(0);
+ fd = open(fname,O_BINARY|O_APPEND|O_CREAT|O_RDWR,S_IRUSR|S_IWUSR);
+ }
+ }
+ if(fd<0)
+ return(0);
+ /* set the file to close-on-exec */
+ fcntl(fd,F_SETFD,FD_CLOEXEC);
+ if(cp=nv_getval(HISTSIZE))
+ maxlines = (unsigned)strtol(cp, (char**)0, 10);
+ else
+ maxlines = HIST_DFLT;
+ for(histmask=16;histmask <= maxlines; histmask <<=1 );
+ if(!(hp=new_of(History_t,(--histmask)*sizeof(off_t))))
+ {
+ close(fd);
+ return(0);
+ }
+ shgd->hist_ptr = hist_ptr = hp;
+ hp->histshell = (void*)shp;
+ hp->histsize = maxlines;
+ hp->histmask = histmask;
+ hp->histfp= sfnew(NIL(Sfio_t*),hp->histbuff,HIST_BSIZE,fd,SF_READ|SF_WRITE|SF_APPENDWR|SF_SHARE);
+ memset((char*)hp->histcmds,0,sizeof(off_t)*(hp->histmask+1));
+ hp->histind = 1;
+ hp->histcmds[1] = 2;
+ hp->histcnt = 2;
+ hp->histname = strdup(histname);
+ hp->histdisc = hist_disc;
+ if(hsize==0)
+ {
+ /* put special characters at front of file */
+ sfwrite(hp->histfp,(char*)hist_stamp,2);
+ sfsync(hp->histfp);
+ }
+ /* initialize history list */
+ else
+ {
+ int first,last;
+ off_t mark,size = (HIST_MAX/4)+maxlines*HIST_LINE;
+ hp->histind = first = hist_nearend(hp,hp->histfp,hsize-size);
+ histinit = 1;
+ hist_eof(hp); /* this sets histind to last command */
+ if((hist_start = (last=(int)hp->histind)-maxlines) <=0)
+ hist_start = 1;
+ mark = hp->histmarker;
+ while(first > hist_start)
+ {
+ size += size;
+ first = hist_nearend(hp,hp->histfp,hsize-size);
+ hp->histind = first;
+ }
+ histinit = hist_start;
+ hist_eof(hp);
+ if(!histinit)
+ {
+ sfseek(hp->histfp,hp->histcnt=hsize,SEEK_SET);
+ hp->histind = last;
+ hp->histmarker = mark;
+ }
+ histinit = 0;
+ }
+ if(fname)
+ {
+ unlink(fname);
+ free((void*)fname);
+ }
+ if(hist_clean(fd) && hist_start>1 && hsize > HIST_MAX)
+ {
+#ifdef DEBUG
+ sfprintf(sfstderr,"%d: hist_trim hsize=%d\n",getpid(),hsize);
+ sfsync(sfstderr);
+#endif /* DEBUG */
+ hp = hist_trim(hp,(int)hp->histind-maxlines);
+ }
+ sfdisc(hp->histfp,&hp->histdisc);
+#if KSHELL
+ (HISTCUR)->nvalue.lp = (&hp->histind);
+#endif /* KSHELL */
+ sh_timeradd(1000L*(HIST_RECENT-30), 1, hist_touch, (void*)hp->histname);
+#if SHOPT_ACCTFILE
+ if(sh_isstate(SH_INTERACTIVE))
+ acctinit(hp);
+#endif /* SHOPT_ACCTFILE */
+#if SHOPT_AUDIT
+ {
+ char buff[SF_BUFSIZE];
+ hp->auditfp = 0;
+ if(sh_isstate(SH_INTERACTIVE) && (hp->auditmask=sh_checkaudit(hp,SHOPT_AUDITFILE, buff, sizeof(buff))))
+ {
+ if((fd=sh_open(buff,O_BINARY|O_WRONLY|O_APPEND|O_CREAT,S_IRUSR|S_IWUSR))>=0 && fd < 10)
+ {
+ int n;
+ if((n = sh_fcntl(fd,F_DUPFD, 10)) >= 0)
+ {
+ sh_close(fd);
+ fd = n;
+ }
+ }
+ if(fd>=0)
+ {
+ fcntl(fd,F_SETFD,FD_CLOEXEC);
+ hp->tty = strdup(ttyname(2));
+ hp->auditfp = sfnew((Sfio_t*)0,NULL,-1,fd,SF_WRITE);
+ }
+ }
+ }
+#endif
+ return(1);
+}
+
+/*
+ * close the history file and free the space
+ */
+
+void hist_close(register History_t *hp)
+{
+ sfclose(hp->histfp);
+#if SHOPT_AUDIT
+ if(hp->auditfp)
+ {
+ if(hp->tty)
+ free((void*)hp->tty);
+ sfclose(hp->auditfp);
+ }
+#endif /* SHOPT_AUDIT */
+ free((char*)hp);
+ hist_ptr = 0;
+ shgd->hist_ptr = 0;
+#if SHOPT_ACCTFILE
+ if(acctfd)
+ {
+ close(acctfd);
+ acctfd = 0;
+ }
+#endif /* SHOPT_ACCTFILE */
+}
+
+/*
+ * check history file format to see if it begins with special byte
+ */
+static int hist_check(register int fd)
+{
+ unsigned char magic[2];
+ lseek(fd,(off_t)0,SEEK_SET);
+ if((read(fd,(char*)magic,2)!=2) || (magic[0]!=HIST_UNDO))
+ return(1);
+ return(0);
+}
+
+/*
+ * clean out history file OK if not modified in HIST_RECENT seconds
+ */
+static int hist_clean(int fd)
+{
+ struct stat statb;
+ return(fstat(fd,&statb)>=0 && (time((time_t*)0)-statb.st_mtime) >= HIST_RECENT);
+}
+
+/*
+ * Copy the last <n> commands to a new file and make this the history file
+ */
+
+static History_t* hist_trim(History_t *hp, int n)
+{
+ register char *cp;
+ register int incmd=1, c=0;
+ register History_t *hist_new, *hist_old = hp;
+ char *buff, *endbuff, *tmpname=0;
+ off_t oldp,newp;
+ struct stat statb;
+ unlink(hist_old->histname);
+ if(access(hist_old->histname,F_OK) >= 0)
+ {
+ /* The unlink can fail on windows 95 */
+ int fd;
+ char *last, *name=hist_old->histname;
+ close(sffileno(hist_old->histfp));
+ tmpname = (char*)malloc(strlen(name)+14);
+ if(last = strrchr(name,'/'))
+ {
+ *last = 0;
+ pathtmp(tmpname,name,"hist",NIL(int*));
+ *last = '/';
+ }
+ else
+ pathtmp(tmpname,".","hist",NIL(int*));
+ if(rename(name,tmpname) < 0)
+ {
+ free(tmpname);
+ tmpname = name;
+ }
+ fd = open(tmpname,O_RDONLY);
+ sfsetfd(hist_old->histfp,fd);
+ if(tmpname==name)
+ tmpname = 0;
+ }
+ hist_ptr = 0;
+ if(fstat(sffileno(hist_old->histfp),&statb)>=0)
+ {
+ histinit = 1;
+ histmode = statb.st_mode;
+ }
+ if(!sh_histinit(hp->histshell))
+ {
+ /* use the old history file */
+ return hist_ptr = hist_old;
+ }
+ hist_new = hist_ptr;
+ hist_ptr = hist_old;
+ if(--n < 0)
+ n = 0;
+ newp = hist_seek(hist_old,++n);
+ while(1)
+ {
+ if(!incmd)
+ {
+ c = hist_ind(hist_new,++hist_new->histind);
+ hist_new->histcmds[c] = hist_new->histcnt;
+ if(hist_new->histcnt > hist_new->histmarker+HIST_BSIZE/2)
+ {
+ char locbuff[HIST_MARKSZ];
+ hist_marker(locbuff,hist_new->histind);
+ sfwrite(hist_new->histfp,locbuff,HIST_MARKSZ);
+ hist_new->histcnt += HIST_MARKSZ;
+ hist_new->histmarker = hist_new->histcmds[hist_ind(hist_new,c)] = hist_new->histcnt;
+ }
+ oldp = newp;
+ newp = hist_seek(hist_old,++n);
+ if(newp <=oldp)
+ break;
+ }
+ if(!(buff=(char*)sfreserve(hist_old->histfp,SF_UNBOUND,0)))
+ break;
+ *(endbuff=(cp=buff)+sfvalue(hist_old->histfp)) = 0;
+ /* copy to null byte */
+ incmd = 0;
+ while(*cp++);
+ if(cp > endbuff)
+ incmd = 1;
+ else if(*cp==0)
+ cp++;
+ if(cp > endbuff)
+ cp = endbuff;
+ c = cp-buff;
+ hist_new->histcnt += c;
+ sfwrite(hist_new->histfp,buff,c);
+ }
+ hist_cancel(hist_new);
+ sfclose(hist_old->histfp);
+ if(tmpname)
+ {
+ unlink(tmpname);
+ free(tmpname);
+ }
+ free((char*)hist_old);
+ return hist_ptr = hist_new;
+}
+
+/*
+ * position history file at size and find next command number
+ */
+static int hist_nearend(History_t *hp, Sfio_t *iop, register off_t size)
+{
+ register unsigned char *cp, *endbuff;
+ register int n, incmd=1;
+ unsigned char *buff, marker[4];
+ if(size <= 2L || sfseek(iop,size,SEEK_SET)<0)
+ goto begin;
+ /* skip to marker command and return the number */
+ /* numbering commands occur after a null and begin with HIST_CMDNO */
+ while(cp=buff=(unsigned char*)sfreserve(iop,SF_UNBOUND,SF_LOCKR))
+ {
+ n = sfvalue(iop);
+ *(endbuff=cp+n) = 0;
+ while(1)
+ {
+ /* check for marker */
+ if(!incmd && *cp++==HIST_CMDNO && *cp==0)
+ {
+ n = cp+1 - buff;
+ incmd = -1;
+ break;
+ }
+ incmd = 0;
+ while(*cp++);
+ if(cp>endbuff)
+ {
+ incmd = 1;
+ break;
+ }
+ if(*cp==0 && ++cp>endbuff)
+ break;
+ }
+ size += n;
+ sfread(iop,(char*)buff,n);
+ if(incmd < 0)
+ {
+ if((n=sfread(iop,(char*)marker,4))==4)
+ {
+ n = (marker[0]<<16)|(marker[1]<<8)|marker[2];
+ if(n < size/2)
+ {
+ hp->histmarker = hp->histcnt = size+4;
+ return(n);
+ }
+ n=4;
+ }
+ if(n >0)
+ size += n;
+ incmd = 0;
+ }
+ }
+begin:
+ sfseek(iop,(off_t)2,SEEK_SET);
+ hp->histmarker = hp->histcnt = 2L;
+ return(1);
+}
+
+/*
+ * This routine reads the history file from the present position
+ * to the end-of-file and puts the information in the in-core
+ * history table
+ * Note that HIST_CMDNO is only recognized at the beginning of a command
+ * and that HIST_UNDO as the first character of a command is skipped
+ * unless it is followed by 0. If followed by 0 then it cancels
+ * the previous command.
+ */
+
+void hist_eof(register History_t *hp)
+{
+ register char *cp,*first,*endbuff;
+ register int incmd = 0;
+ register off_t count = hp->histcnt;
+ int oldind,n,skip=0;
+ off_t last = sfseek(hp->histfp,(off_t)0,SEEK_END);
+ if(last < count)
+ {
+ last = -1;
+ count = 2+HIST_MARKSZ;
+ oldind = hp->histind;
+ if((hp->histind -= hp->histsize) < 0)
+ hp->histind = 1;
+ }
+again:
+ sfseek(hp->histfp,count,SEEK_SET);
+ while(cp=(char*)sfreserve(hp->histfp,SF_UNBOUND,0))
+ {
+ n = sfvalue(hp->histfp);
+ *(endbuff = cp+n) = 0;
+ first = cp += skip;
+ while(1)
+ {
+ while(!incmd)
+ {
+ if(cp>first)
+ {
+ count += (cp-first);
+ n = hist_ind(hp, ++hp->histind);
+#ifdef future
+ if(count==hp->histcmds[n])
+ {
+ sfprintf(sfstderr,"count match n=%d\n",n);
+ if(histinit)
+ {
+ histinit = 0;
+ return;
+ }
+ }
+ else if(n>=histinit)
+#endif
+ hp->histcmds[n] = count;
+ first = cp;
+ }
+ switch(*((unsigned char*)(cp++)))
+ {
+ case HIST_CMDNO:
+ if(*cp==0)
+ {
+ hp->histmarker=count+2;
+ cp += (HIST_MARKSZ-1);
+ hp->histind--;
+ if(!histinit && (cp <= endbuff))
+ {
+ unsigned char *marker = (unsigned char*)(cp-4);
+ hp->histind = ((marker[0]<<16)|(marker[1]<<8)|marker[2] -1);
+ }
+ }
+ break;
+ case HIST_UNDO:
+ if(*cp==0)
+ {
+ cp+=1;
+ hp->histind-=2;
+ }
+ break;
+ default:
+ cp--;
+ incmd = 1;
+ }
+ if(cp > endbuff)
+ {
+ cp++;
+ goto refill;
+ }
+ }
+ first = cp;
+ while(*cp++);
+ if(cp > endbuff)
+ break;
+ incmd = 0;
+ while(*cp==0)
+ {
+ if(++cp > endbuff)
+ goto refill;
+ }
+ }
+ refill:
+ count += (--cp-first);
+ skip = (cp-endbuff);
+ if(!incmd && !skip)
+ hp->histcmds[hist_ind(hp,++hp->histind)] = count;
+ }
+ hp->histcnt = count;
+ if(incmd && last)
+ {
+ sfputc(hp->histfp,0);
+ hist_cancel(hp);
+ count = 2;
+ skip = 0;
+ oldind -= hp->histind;
+ hp->histind = hp->histind-hp->histsize + oldind +2;
+ if(hp->histind<0)
+ hp->histind = 1;
+ if(last<0)
+ {
+ char buff[HIST_MARKSZ];
+ int fd = open(hp->histname,O_RDWR);
+ if(fd>=0)
+ {
+ hist_marker(buff,hp->histind);
+ write(fd,(char*)hist_stamp,2);
+ write(fd,buff,HIST_MARKSZ);
+ close(fd);
+ }
+ }
+ last = 0;
+ goto again;
+ }
+}
+
+/*
+ * This routine will cause the previous command to be cancelled
+ */
+
+void hist_cancel(register History_t *hp)
+{
+ register int c;
+ if(!hp)
+ return;
+ sfputc(hp->histfp,HIST_UNDO);
+ sfputc(hp->histfp,0);
+ sfsync(hp->histfp);
+ hp->histcnt += 2;
+ c = hist_ind(hp,--hp->histind);
+ hp->histcmds[c] = hp->histcnt;
+}
+
+/*
+ * flush the current history command
+ */
+
+void hist_flush(register History_t *hp)
+{
+ register char *buff;
+ if(hp)
+ {
+ if(buff=(char*)sfreserve(hp->histfp,0,SF_LOCKR))
+ {
+ hp->histflush = sfvalue(hp->histfp)+1;
+ sfwrite(hp->histfp,buff,0);
+ }
+ else
+ hp->histflush=0;
+ if(sfsync(hp->histfp)<0)
+ {
+ hist_close(hp);
+ if(!sh_histinit(hp->histshell))
+ sh_offoption(SH_HISTORY);
+ }
+ hp->histflush = 0;
+ }
+}
+
+/*
+ * This is the write discipline for the history file
+ * When called from hist_flush(), trailing newlines are deleted and
+ * a zero byte. Line sequencing is added as required
+ */
+
+#ifdef SF_BUFCONST
+static ssize_t hist_write(Sfio_t *iop,const void *buff,register size_t insize,Sfdisc_t* handle)
+#else
+static int hist_write(Sfio_t *iop,const void *buff,register int insize,Sfdisc_t* handle)
+#endif
+{
+ register History_t *hp = (History_t*)handle;
+ register char *bufptr = ((char*)buff)+insize;
+ register int c,size = insize;
+ register off_t cur;
+ int saved=0;
+ char saveptr[HIST_MARKSZ];
+ if(!hp->histflush)
+ return(write(sffileno(iop),(char*)buff,size));
+ if((cur = lseek(sffileno(iop),(off_t)0,SEEK_END)) <0)
+ {
+ errormsg(SH_DICT,2,"hist_flush: EOF seek failed errno=%d",errno);
+ return(-1);
+ }
+ hp->histcnt = cur;
+ /* remove whitespace from end of commands */
+ while(--bufptr >= (char*)buff)
+ {
+ c= *bufptr;
+ if(!isspace(c))
+ {
+ if(c=='\\' && *(bufptr+1)!='\n')
+ bufptr++;
+ break;
+ }
+ }
+ /* don't count empty lines */
+ if(++bufptr <= (char*)buff)
+ return(insize);
+ *bufptr++ = '\n';
+ *bufptr++ = 0;
+ size = bufptr - (char*)buff;
+#if SHOPT_AUDIT
+ if(hp->auditfp)
+ {
+ time_t t=time((time_t*)0);
+ sfprintf(hp->auditfp,"%u;%u;%s;%*s%c",sh_isoption(SH_PRIVILEGED)?shgd->euserid:shgd->userid,t,hp->tty,size,buff,0);
+ sfsync(hp->auditfp);
+ }
+#endif /* SHOPT_AUDIT */
+#if SHOPT_ACCTFILE
+ if(acctfd)
+ {
+ int timechars, offset;
+ offset = staktell();
+ stakputs(buff);
+ stakseek(staktell() - 1);
+ timechars = sfprintf(staksp, "\t%s\t%x\n",logname,time(NIL(long *)));
+ lseek(acctfd, (off_t)0, SEEK_END);
+ write(acctfd, stakptr(offset), size - 2 + timechars);
+ stakseek(offset);
+
+ }
+#endif /* SHOPT_ACCTFILE */
+ if(size&01)
+ {
+ size++;
+ *bufptr++ = 0;
+ }
+ hp->histcnt += size;
+ c = hist_ind(hp,++hp->histind);
+ hp->histcmds[c] = hp->histcnt;
+ if(hp->histflush>HIST_MARKSZ && hp->histcnt > hp->histmarker+HIST_BSIZE/2)
+ {
+ memcpy((void*)saveptr,(void*)bufptr,HIST_MARKSZ);
+ saved=1;
+ hp->histcnt += HIST_MARKSZ;
+ hist_marker(bufptr,hp->histind);
+ hp->histmarker = hp->histcmds[hist_ind(hp,c)] = hp->histcnt;
+ size += HIST_MARKSZ;
+ }
+ errno = 0;
+ size = write(sffileno(iop),(char*)buff,size);
+ if(saved)
+ memcpy((void*)bufptr,(void*)saveptr,HIST_MARKSZ);
+ if(size>=0)
+ {
+ hp->histwfail = 0;
+ return(insize);
+ }
+ return(-1);
+}
+
+/*
+ * Put history sequence number <n> into buffer <buff>
+ * The buffer must be large enough to hold HIST_MARKSZ chars
+ */
+
+static void hist_marker(register char *buff,register long cmdno)
+{
+ *buff++ = HIST_CMDNO;
+ *buff++ = 0;
+ *buff++ = (cmdno>>16);
+ *buff++ = (cmdno>>8);
+ *buff++ = cmdno;
+ *buff++ = 0;
+}
+
+/*
+ * return byte offset in history file for command <n>
+ */
+off_t hist_tell(register History_t *hp, int n)
+{
+ return(hp->histcmds[hist_ind(hp,n)]);
+}
+
+/*
+ * seek to the position of command <n>
+ */
+off_t hist_seek(register History_t *hp, int n)
+{
+ return(sfseek(hp->histfp,hp->histcmds[hist_ind(hp,n)],SEEK_SET));
+}
+
+/*
+ * write the command starting at offset <offset> onto file <outfile>.
+ * if character <last> appears before newline it is deleted
+ * each new-line character is replaced with string <nl>.
+ */
+
+void hist_list(register History_t *hp,Sfio_t *outfile, off_t offset,int last, char *nl)
+{
+ register int oldc=0;
+ register int c;
+ if(offset<0 || !hp)
+ {
+ sfputr(outfile,sh_translate(e_unknown),'\n');
+ return;
+ }
+ sfseek(hp->histfp,offset,SEEK_SET);
+ while((c = sfgetc(hp->histfp)) != EOF)
+ {
+ if(c && oldc=='\n')
+ sfputr(outfile,nl,-1);
+ else if(last && (c==0 || (c=='\n' && oldc==last)))
+ return;
+ else if(oldc)
+ sfputc(outfile,oldc);
+ oldc = c;
+ if(c==0)
+ return;
+ }
+ return;
+}
+
+/*
+ * find index for last line with given string
+ * If flag==0 then line must begin with string
+ * direction < 1 for backwards search
+*/
+
+Histloc_t hist_find(register History_t*hp,char *string,register int index1,int flag,int direction)
+{
+ register int index2;
+ off_t offset;
+ int *coffset=0;
+ Histloc_t location;
+ location.hist_command = -1;
+ location.hist_char = 0;
+ location.hist_line = 0;
+ if(!hp)
+ return(location);
+ /* leading ^ means beginning of line unless escaped */
+ if(flag)
+ {
+ index2 = *string;
+ if(index2=='\\')
+ string++;
+ else if(index2=='^')
+ {
+ flag=0;
+ string++;
+ }
+ }
+ if(flag)
+ coffset = &location.hist_char;
+ index2 = (int)hp->histind;
+ if(direction<0)
+ {
+ index2 -= hp->histsize;
+ if(index2<1)
+ index2 = 1;
+ if(index1 <= index2)
+ return(location);
+ }
+ else if(index1 >= index2)
+ return(location);
+ while(index1!=index2)
+ {
+ direction>0?++index1:--index1;
+ offset = hist_tell(hp,index1);
+ if((location.hist_line=hist_match(hp,offset,string,coffset))>=0)
+ {
+ location.hist_command = index1;
+ return(location);
+ }
+#if KSHELL
+ /* allow a search to be aborted */
+ if(((Shell_t*)hp->histshell)->trapnote&SH_SIGSET)
+ break;
+#endif /* KSHELL */
+ }
+ return(location);
+}
+
+/*
+ * search for <string> in history file starting at location <offset>
+ * If coffset==0 then line must begin with string
+ * returns the line number of the match if successful, otherwise -1
+ */
+
+int hist_match(register History_t *hp,off_t offset,char *string,int *coffset)
+{
+ register unsigned char *first, *cp;
+ register int m,n,c=1,line=0;
+#if SHOPT_MULTIBYTE
+ mbinit();
+#endif /* SHOPT_MULTIBYTE */
+ sfseek(hp->histfp,offset,SEEK_SET);
+ if(!(cp = first = (unsigned char*)sfgetr(hp->histfp,0,0)))
+ return(-1);
+ m = sfvalue(hp->histfp);
+ n = strlen(string);
+ while(m > n)
+ {
+ if(*cp==*string && memcmp(cp,string,n)==0)
+ {
+ if(coffset)
+ *coffset = (cp-first);
+ return(line);
+ }
+ if(!coffset)
+ break;
+ if(*cp=='\n')
+ line++;
+#if SHOPT_MULTIBYTE
+ if((c=mbsize(cp)) < 0)
+ c = 1;
+#endif /* SHOPT_MULTIBYTE */
+ cp += c;
+ m -= c;
+ }
+ return(-1);
+}
+
+
+#if SHOPT_ESH || SHOPT_VSH
+/*
+ * copy command <command> from history file to s1
+ * at most <size> characters copied
+ * if s1==0 the number of lines for the command is returned
+ * line=linenumber for emacs copy and only this line of command will be copied
+ * line < 0 for full command copy
+ * -1 returned if there is no history file
+ */
+
+int hist_copy(char *s1,int size,int command,int line)
+{
+ register int c;
+ register History_t *hp = shgd->hist_ptr;
+ register int count = 0;
+ register char *s1max = s1+size;
+ if(!hp)
+ return(-1);
+ hist_seek(hp,command);
+ while ((c = sfgetc(hp->histfp)) && c!=EOF)
+ {
+ if(c=='\n')
+ {
+ if(count++ ==line)
+ break;
+ else if(line >= 0)
+ continue;
+ }
+ if(s1 && (line<0 || line==count))
+ {
+ if(s1 >= s1max)
+ {
+ *--s1 = 0;
+ break;
+ }
+ *s1++ = c;
+ }
+
+ }
+ sfseek(hp->histfp,(off_t)0,SEEK_END);
+ if(s1==0)
+ return(count);
+ if(count && (c= *(s1-1)) == '\n')
+ s1--;
+ *s1 = '\0';
+ return(count);
+}
+
+/*
+ * return word number <word> from command number <command>
+ */
+
+char *hist_word(char *string,int size,int word)
+{
+ register int c;
+ register char *s1 = string;
+ register unsigned char *cp = (unsigned char*)s1;
+ register int flag = 0;
+ History_t *hp = hist_ptr;
+ if(!hp)
+ return(NIL(char*));
+ hist_copy(string,size,(int)hp->histind-1,-1);
+ for(;c = *cp;cp++)
+ {
+ c = isspace(c);
+ if(c && flag)
+ {
+ *cp = 0;
+ if(--word==0)
+ break;
+ flag = 0;
+ }
+ else if(c==0 && flag==0)
+ {
+ s1 = (char*)cp;
+ flag++;
+ }
+ }
+ *cp = 0;
+ if(s1 != string)
+ strcpy(string,s1);
+ return(string);
+}
+
+#endif /* SHOPT_ESH */
+
+#if SHOPT_ESH
+/*
+ * given the current command and line number,
+ * and number of lines back or foward,
+ * compute the new command and line number.
+ */
+
+Histloc_t hist_locate(History_t *hp,register int command,register int line,int lines)
+{
+ Histloc_t next;
+ line += lines;
+ if(!hp)
+ {
+ command = -1;
+ goto done;
+ }
+ if(lines > 0)
+ {
+ register int count;
+ while(command <= hp->histind)
+ {
+ count = hist_copy(NIL(char*),0, command,-1);
+ if(count > line)
+ goto done;
+ line -= count;
+ command++;
+ }
+ }
+ else
+ {
+ register int least = (int)hp->histind-hp->histsize;
+ while(1)
+ {
+ if(line >=0)
+ goto done;
+ if(--command < least)
+ break;
+ line += hist_copy(NIL(char*),0, command,-1);
+ }
+ command = -1;
+ }
+done:
+ next.hist_line = line;
+ next.hist_command = command;
+ return(next);
+}
+#endif /* SHOPT_ESH */
+
+
+/*
+ * Handle history file exceptions
+ */
+#ifdef SF_BUFCONST
+static int hist_exceptf(Sfio_t* fp, int type, void *data, Sfdisc_t *handle)
+#else
+static int hist_exceptf(Sfio_t* fp, int type, Sfdisc_t *handle)
+#endif
+{
+ register int newfd,oldfd;
+ History_t *hp = (History_t*)handle;
+ if(type==SF_WRITE)
+ {
+ if(errno==ENOSPC || hp->histwfail++ >= 10)
+ return(0);
+ /* write failure could be NFS problem, try to re-open */
+ close(oldfd=sffileno(fp));
+ if((newfd=open(hp->histname,O_BINARY|O_APPEND|O_CREAT|O_RDWR,S_IRUSR|S_IWUSR)) >= 0)
+ {
+ if(fcntl(newfd, F_DUPFD, oldfd) !=oldfd)
+ return(-1);
+ fcntl(oldfd,F_SETFD,FD_CLOEXEC);
+ close(newfd);
+ if(lseek(oldfd,(off_t)0,SEEK_END) < hp->histcnt)
+ {
+ register int index = hp->histind;
+ lseek(oldfd,(off_t)2,SEEK_SET);
+ hp->histcnt = 2;
+ hp->histind = 1;
+ hp->histcmds[1] = 2;
+ hist_eof(hp);
+ hp->histmarker = hp->histcnt;
+ hp->histind = index;
+ }
+ return(1);
+ }
+ errormsg(SH_DICT,2,"History file write error-%d %s: file unrecoverable",errno,hp->histname);
+ return(-1);
+ }
+ return(0);
+}
diff --git a/src/cmd/ksh93/edit/vi.c b/src/cmd/ksh93/edit/vi.c
new file mode 100644
index 0000000..376e349
--- /dev/null
+++ b/src/cmd/ksh93/edit/vi.c
@@ -0,0 +1,2753 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1982-2011 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
+/* Adapted for ksh by David Korn */
+/*+ VI.C P.D. Sullivan
+ *
+ * One line editor for the shell based on the vi editor.
+ *
+ * Questions to:
+ * P.D. Sullivan
+ * cbosgd!pds
+-*/
+
+
+#if KSHELL
+# include "defs.h"
+#else
+# include <ast.h>
+# include "FEATURE/options"
+# include <ctype.h>
+#endif /* KSHELL */
+#include "io.h"
+
+#include "history.h"
+#include "edit.h"
+#include "terminal.h"
+#include "FEATURE/time"
+
+#if SHOPT_OLDTERMIO
+# undef ECHOCTL
+# define echoctl (vp->ed->e_echoctl)
+#else
+# ifdef ECHOCTL
+# define echoctl ECHOCTL
+# else
+# define echoctl 0
+# endif /* ECHOCTL */
+#endif /*SHOPT_OLDTERMIO */
+
+#ifndef FIORDCHK
+# define NTICKS 5 /* number of ticks for typeahead */
+#endif /* FIORDCHK */
+
+#define MAXCHAR MAXLINE-2 /* max char per line */
+
+#if SHOPT_MULTIBYTE
+# include "lexstates.h"
+# define gencpy(a,b) ed_gencpy(a,b)
+# define genncpy(a,b,n) ed_genncpy(a,b,n)
+# define genlen(str) ed_genlen(str)
+# define digit(c) ((c&~STRIP)==0 && isdigit(c))
+# define is_print(c) ((c&~STRIP) || isprint(c))
+# if !_lib_iswprint && !defined(iswprint)
+# define iswprint(c) ((c&~0177) || isprint(c))
+# endif
+ static int _isalph(int);
+ static int _ismetach(int);
+ static int _isblank(int);
+# undef isblank
+# define isblank(v) _isblank(virtual[v])
+# define isalph(v) _isalph(virtual[v])
+# define ismetach(v) _ismetach(virtual[v])
+#else
+ static genchar _c;
+# define gencpy(a,b) strcpy((char*)(a),(char*)(b))
+# define genncpy(a,b,n) strncpy((char*)(a),(char*)(b),n)
+# define genlen(str) strlen(str)
+# define isalph(v) ((_c=virtual[v])=='_'||isalnum(_c))
+# undef isblank
+# define isblank(v) isspace(virtual[v])
+# define ismetach(v) ismeta(virtual[v])
+# define digit(c) isdigit(c)
+# define is_print(c) isprint(c)
+#endif /* SHOPT_MULTIBYTE */
+
+#if ( 'a' == 97) /* ASCII? */
+# define fold(c) ((c)&~040) /* lower and uppercase equivalent */
+#else
+# define fold(c) ((c)|0100) /* lower and uppercase equivalent */
+#endif
+
+#ifndef iswascii
+#define iswascii(c) (!((c)&(~0177)))
+#endif
+
+typedef struct _vi_
+{
+ int direction;
+ int lastmacro;
+ char addnl; /* boolean - add newline flag */
+ char last_find; /* last find command */
+ char last_cmd; /* last command */
+ char repeat_set;
+ char nonewline;
+ int findchar; /* last find char */
+ genchar *lastline;
+ int first_wind; /* first column of window */
+ int last_wind; /* last column in window */
+ int lastmotion; /* last motion */
+ int long_char; /* line bigger than window */
+ int long_line; /* line bigger than window */
+ int ocur_phys; /* old current physical position */
+ int ocur_virt; /* old last virtual position */
+ int ofirst_wind; /* old window first col */
+ int o_v_char; /* prev virtual[ocur_virt] */
+ int repeat; /* repeat count for motion cmds */
+ int lastrepeat; /* last repeat count for motion cmds */
+ int u_column; /* undo current column */
+ int U_saved; /* original virtual saved */
+ genchar *U_space; /* used for U command */
+ genchar *u_space; /* used for u command */
+#ifdef FIORDCHK
+ clock_t typeahead; /* typeahead occurred */
+#else
+ int typeahead; /* typeahead occurred */
+#endif /* FIORDCHK */
+#if SHOPT_MULTIBYTE
+ int bigvi;
+#endif
+ Edit_t *ed; /* pointer to edit data */
+} Vi_t;
+
+#define editb (*vp->ed)
+
+#undef putchar
+#define putchar(c) ed_putchar(vp->ed,c)
+
+#define crallowed editb.e_crlf
+#define cur_virt editb.e_cur /* current virtual column */
+#define cur_phys editb.e_pcur /* current phys column cursor is at */
+#define curhline editb.e_hline /* current history line */
+#define first_virt editb.e_fcol /* first allowable column */
+#define globals editb.e_globals /* local global variables */
+#define histmin editb.e_hismin
+#define histmax editb.e_hismax
+#define last_phys editb.e_peol /* last column in physical */
+#define last_virt editb.e_eol /* last column */
+#define lsearch editb.e_search /* last search string */
+#define lookahead editb.e_lookahead /* characters in buffer */
+#define previous editb.e_lbuf /* lookahead buffer */
+#define max_col editb.e_llimit /* maximum column */
+#define Prompt editb.e_prompt /* pointer to prompt */
+#define plen editb.e_plen /* length of prompt */
+#define physical editb.e_physbuf /* physical image */
+#define usreof editb.e_eof /* user defined eof char */
+#define usrerase editb.e_erase /* user defined erase char */
+#define usrlnext editb.e_lnext /* user defined next literal */
+#define usrkill editb.e_kill /* user defined kill char */
+#define virtual editb.e_inbuf /* pointer to virtual image buffer */
+#define window editb.e_window /* window buffer */
+#define w_size editb.e_wsize /* window size */
+#define inmacro editb.e_inmacro /* true when in macro */
+#define yankbuf editb.e_killbuf /* yank/delete buffer */
+
+
+#define ABORT -2 /* user abort */
+#define APPEND -10 /* append chars */
+#define BAD -1 /* failure flag */
+#define BIGVI -15 /* user wants real vi */
+#define CONTROL -20 /* control mode */
+#define ENTER -25 /* enter flag */
+#define GOOD 0 /* success flag */
+#define INPUT -30 /* input mode */
+#define INSERT -35 /* insert mode */
+#define REPLACE -40 /* replace chars */
+#define SEARCH -45 /* search flag */
+#define TRANSLATE -50 /* translate virt to phys only */
+
+#define INVALID (-1) /* invalid column */
+
+static const char paren_chars[] = "([{)]}"; /* for % command */
+
+static void cursor(Vi_t*, int);
+static void del_line(Vi_t*,int);
+static int getcount(Vi_t*,int);
+static void getline(Vi_t*,int);
+static int getrchar(Vi_t*);
+static int mvcursor(Vi_t*,int);
+static void pr_string(Vi_t*,const char*);
+static void putstring(Vi_t*,int, int);
+static void refresh(Vi_t*,int);
+static void replace(Vi_t*,int, int);
+static void restore_v(Vi_t*);
+static void save_last(Vi_t*);
+static void save_v(Vi_t*);
+static int search(Vi_t*,int);
+static void sync_cursor(Vi_t*);
+static int textmod(Vi_t*,int,int);
+
+/*+ VI_READ( fd, shbuf, nchar )
+ *
+ * This routine implements a one line version of vi and is
+ * called by _filbuf.c
+ *
+-*/
+
+/*
+ * if reedit is non-zero, initialize edit buffer with reedit chars
+ */
+int ed_viread(void *context, int fd, register char *shbuf, int nchar, int reedit)
+{
+ Edit_t *ed = (Edit_t*)context;
+ register int i; /* general variable */
+ register int term_char=0; /* read() termination character */
+ register Vi_t *vp = ed->e_vi;
+ char prompt[PRSIZE+2]; /* prompt */
+ genchar Physical[2*MAXLINE]; /* physical image */
+ genchar Ubuf[MAXLINE]; /* used for U command */
+ genchar ubuf[MAXLINE]; /* used for u command */
+ genchar Window[MAXLINE]; /* window image */
+ int Globals[9]; /* local global variables */
+ int esc_or_hang=0; /* <ESC> or hangup */
+ char cntl_char=0; /* TRUE if control character present */
+#if SHOPT_RAWONLY
+# define viraw 1
+#else
+ int viraw = (sh_isoption(SH_VIRAW) || ed->sh->st.trap[SH_KEYTRAP]);
+# ifndef FIORDCHK
+ clock_t oldtime, newtime;
+ struct tms dummy;
+# endif /* FIORDCHK */
+#endif /* SHOPT_RAWONLY */
+ if(!vp)
+ {
+ ed->e_vi = vp = newof(0,Vi_t,1,0);
+ vp->lastline = (genchar*)malloc(MAXLINE*CHARSIZE);
+ vp->direction = -1;
+ vp->ed = ed;
+ }
+
+ /*** setup prompt ***/
+
+ Prompt = prompt;
+ ed_setup(vp->ed,fd, reedit);
+ shbuf[reedit] = 0;
+
+#if !SHOPT_RAWONLY
+ if(!viraw)
+ {
+ /*** Change the eol characters to '\r' and eof ***/
+ /* in addition to '\n' and make eof an ESC */
+ if(tty_alt(ERRIO) < 0)
+ return(reexit?reedit:ed_read(context, fd, shbuf, nchar,0));
+
+#ifdef FIORDCHK
+ ioctl(fd,FIORDCHK,&vp->typeahead);
+#else
+ /* time the current line to determine typeahead */
+ oldtime = times(&dummy);
+#endif /* FIORDCHK */
+#if KSHELL
+ /* abort of interrupt has occurred */
+ if(ed->sh->trapnote&SH_SIGSET)
+ i = -1;
+ else
+#endif /* KSHELL */
+ /*** Read the line ***/
+ i = ed_read(context, fd, shbuf, nchar, 0);
+#ifndef FIORDCHK
+ newtime = times(&dummy);
+ vp->typeahead = ((newtime-oldtime) < NTICKS);
+#endif /* FIORDCHK */
+ if(echoctl)
+ {
+ if( i <= 0 )
+ {
+ /*** read error or eof typed ***/
+ tty_cooked(ERRIO);
+ return(i);
+ }
+ term_char = shbuf[--i];
+ if( term_char == '\r' )
+ term_char = '\n';
+ if( term_char=='\n' || term_char==ESC )
+ shbuf[i--] = '\0';
+ else
+ shbuf[i+1] = '\0';
+ }
+ else
+ {
+ register int c = shbuf[0];
+
+ /*** Save and remove the last character if its an eol, ***/
+ /* changing '\r' to '\n' */
+
+ if( i == 0 )
+ {
+ /*** ESC was typed as first char of line ***/
+ esc_or_hang = 1;
+ term_char = ESC;
+ shbuf[i--] = '\0'; /* null terminate line */
+ }
+ else if( i<0 || c==usreof )
+ {
+ /*** read error or eof typed ***/
+ tty_cooked(ERRIO);
+ if( c == usreof )
+ i = 0;
+ return(i);
+ }
+ else
+ {
+ term_char = shbuf[--i];
+ if( term_char == '\r' )
+ term_char = '\n';
+#if !defined(VEOL2) && !defined(ECHOCTL)
+ if(term_char=='\n')
+ {
+ tty_cooked(ERRIO);
+ return(i+1);
+ }
+#endif
+ if( term_char=='\n' || term_char==usreof )
+ {
+ /*** remove terminator & null terminate ***/
+ shbuf[i--] = '\0';
+ }
+ else
+ {
+ /** terminator was ESC, which is not xmitted **/
+ term_char = ESC;
+ shbuf[i+1] = '\0';
+ }
+ }
+ }
+ }
+ else
+#endif /* SHOPT_RAWONLY */
+ {
+ /*** Set raw mode ***/
+
+#if !SHOPT_RAWONLY
+ if( editb.e_ttyspeed == 0 )
+ {
+ /*** never did TCGETA, so do it ***/
+ /* avoids problem if user does 'sh -o viraw' */
+ tty_alt(ERRIO);
+ }
+#endif /* SHOPT_RAWONLY */
+ if(tty_raw(ERRIO,0) < 0 )
+ return(reedit?reedit:ed_read(context, fd, shbuf, nchar,0));
+ i = last_virt-1;
+ }
+
+ /*** Initialize some things ***/
+
+ virtual = (genchar*)shbuf;
+#if SHOPT_MULTIBYTE
+ virtual = (genchar*)roundof((char*)virtual-(char*)0,sizeof(genchar));
+ shbuf[i+1] = 0;
+ i = ed_internal(shbuf,virtual)-1;
+#endif /* SHOPT_MULTIBYTE */
+ globals = Globals;
+ cur_phys = i + 1;
+ cur_virt = i;
+ first_virt = 0;
+ vp->first_wind = 0;
+ last_virt = i;
+ last_phys = i;
+ vp->last_wind = i;
+ vp->long_line = ' ';
+ vp->long_char = ' ';
+ vp->o_v_char = '\0';
+ vp->ocur_phys = 0;
+ vp->ocur_virt = MAXCHAR;
+ vp->ofirst_wind = 0;
+ physical = Physical;
+ vp->u_column = INVALID - 1;
+ vp->U_space = Ubuf;
+ vp->u_space = ubuf;
+ window = Window;
+ window[0] = '\0';
+
+ if(!yankbuf)
+ yankbuf = (genchar*)malloc(MAXLINE*CHARSIZE);
+ if( vp->last_cmd == '\0' )
+ {
+ /*** first time for this shell ***/
+
+ vp->last_cmd = 'i';
+ vp->findchar = INVALID;
+ vp->lastmotion = '\0';
+ vp->lastrepeat = 1;
+ vp->repeat = 1;
+ *yankbuf = 0;
+ }
+
+ /*** fiddle around with prompt length ***/
+ if( nchar+plen > MAXCHAR )
+ nchar = MAXCHAR - plen;
+ max_col = nchar - 2;
+
+ if( !viraw )
+ {
+ int kill_erase = 0;
+ for(i=(echoctl?last_virt:0); i<last_virt; ++i )
+ {
+ /*** change \r to \n, check for control characters, ***/
+ /* delete appropriate ^Vs, */
+ /* and estimate last physical column */
+
+ if( virtual[i] == '\r' )
+ virtual[i] = '\n';
+ if(!echoctl)
+ {
+ register int c = virtual[i];
+ if( c<=usrerase)
+ {
+ /*** user typed escaped erase or kill char ***/
+ cntl_char = 1;
+ if(is_print(c))
+ kill_erase++;
+ }
+ else if( !is_print(c) )
+ {
+ cntl_char = 1;
+
+ if( c == usrlnext )
+ {
+ if( i == last_virt )
+ {
+ /*** eol/eof was escaped ***/
+ /* so replace ^V with it */
+ virtual[i] = term_char;
+ break;
+ }
+
+ /*** delete ^V ***/
+ gencpy((&virtual[i]), (&virtual[i+1]));
+ --cur_virt;
+ --last_virt;
+ }
+ }
+ }
+ }
+
+ /*** copy virtual image to window ***/
+ if(last_virt > 0)
+ last_phys = ed_virt_to_phys(vp->ed,virtual,physical,last_virt,0,0);
+ if( last_phys >= w_size )
+ {
+ /*** line longer than window ***/
+ vp->last_wind = w_size - 1;
+ }
+ else
+ vp->last_wind = last_phys;
+ genncpy(window, virtual, vp->last_wind+1);
+
+ if( term_char!=ESC && (last_virt==INVALID
+ || virtual[last_virt]!=term_char) )
+ {
+ /*** Line not terminated with ESC or escaped (^V) ***/
+ /* eol, so return after doing a total update */
+ /* if( (speed is greater or equal to 1200 */
+ /* and something was typed) and */
+ /* (control character present */
+ /* or typeahead occurred) ) */
+
+ tty_cooked(ERRIO);
+ if( editb.e_ttyspeed==FAST && last_virt!=INVALID
+ && (vp->typeahead || cntl_char) )
+ {
+ refresh(vp,TRANSLATE);
+ pr_string(vp,Prompt);
+ putstring(vp,0, last_phys+1);
+ if(echoctl)
+ ed_crlf(vp->ed);
+ else
+ while(kill_erase-- > 0)
+ putchar(' ');
+ }
+
+ if( term_char=='\n' )
+ {
+ if(!echoctl)
+ ed_crlf(vp->ed);
+ virtual[++last_virt] = '\n';
+ }
+ vp->last_cmd = 'i';
+ save_last(vp);
+#if SHOPT_MULTIBYTE
+ virtual[last_virt+1] = 0;
+ last_virt = ed_external(virtual,shbuf);
+ return(last_virt);
+#else
+ return(++last_virt);
+#endif /* SHOPT_MULTIBYTE */
+ }
+
+ /*** Line terminated with escape, or escaped eol/eof, ***/
+ /* so set raw mode */
+
+ if( tty_raw(ERRIO,0) < 0 )
+ {
+ tty_cooked(ERRIO);
+ /*
+ * The following prevents drivers that return 0 on
+ * causing an infinite loop
+ */
+ if(esc_or_hang)
+ return(-1);
+ virtual[++last_virt] = '\n';
+#if SHOPT_MULTIBYTE
+ virtual[last_virt+1] = 0;
+ last_virt = ed_external(virtual,shbuf);
+ return(last_virt);
+#else
+ return(++last_virt);
+#endif /* SHOPT_MULTIBYTE */
+ }
+
+ if(echoctl) /*** for cntl-echo erase the ^[ ***/
+ pr_string(vp,"\b\b\b\b \b\b");
+
+
+ if(crallowed)
+ {
+ /*** start over since there may be ***/
+ /*** a control char, or cursor might not ***/
+ /*** be at left margin (this lets us know ***/
+ /*** where we are ***/
+ cur_phys = 0;
+ window[0] = '\0';
+ pr_string(vp,Prompt);
+ if( term_char==ESC && (last_virt<0 || virtual[last_virt]!=ESC))
+ refresh(vp,CONTROL);
+ else
+ refresh(vp,INPUT);
+ }
+ else
+ {
+ /*** just update everything internally ***/
+ refresh(vp,TRANSLATE);
+ }
+ }
+
+ /*** Handle usrintr, usrquit, or EOF ***/
+
+ i = sigsetjmp(editb.e_env,0);
+ if( i != 0 )
+ {
+ if(vp->ed->e_multiline)
+ {
+ cur_virt = last_virt;
+ sync_cursor(vp);
+ }
+ virtual[0] = '\0';
+ tty_cooked(ERRIO);
+
+ switch(i)
+ {
+ case UEOF:
+ /*** EOF ***/
+ return(0);
+
+ case UINTR:
+ /** interrupt **/
+ return(-1);
+ }
+ return(-1);
+ }
+
+ /*** Get a line from the terminal ***/
+
+ vp->U_saved = 0;
+ if(reedit)
+ {
+ cur_phys = vp->first_wind;
+ vp->ofirst_wind = INVALID;
+ refresh(vp,INPUT);
+ }
+ if(viraw)
+ getline(vp,APPEND);
+ else if(last_virt>=0 && virtual[last_virt]==term_char)
+ getline(vp,APPEND);
+ else
+ getline(vp,ESC);
+ if(vp->ed->e_multiline)
+ cursor(vp, last_phys);
+ /*** add a new line if user typed unescaped \n ***/
+ /* to cause the shell to process the line */
+ tty_cooked(ERRIO);
+ if(ed->e_nlist)
+ {
+ ed->e_nlist = 0;
+ stakset(ed->e_stkptr,ed->e_stkoff);
+ }
+ if( vp->addnl )
+ {
+ virtual[++last_virt] = '\n';
+ ed_crlf(vp->ed);
+ }
+ if( ++last_virt >= 0 )
+ {
+#if SHOPT_MULTIBYTE
+ if(vp->bigvi)
+ {
+ vp->bigvi = 0;
+ shbuf[last_virt-1] = '\n';
+ }
+ else
+ {
+ virtual[last_virt] = 0;
+ last_virt = ed_external(virtual,shbuf);
+ }
+#endif /* SHOPT_MULTIBYTE */
+#if SHOPT_EDPREDICT
+ if(vp->ed->nhlist)
+ ed_histlist(vp->ed,0);
+#endif /* SHOPT_EDPREDICT */
+ return(last_virt);
+ }
+ else
+ return(-1);
+}
+
+
+/*{ APPEND( char, mode )
+ *
+ * This routine will append char after cur_virt in the virtual image.
+ * mode = APPEND, shift chars right before appending
+ * REPLACE, replace char if possible
+ *
+}*/
+
+static void append(Vi_t *vp,int c, int mode)
+{
+ register int i,j;
+
+ if( last_virt<max_col && last_phys<max_col )
+ {
+ if( mode==APPEND || (cur_virt==last_virt && last_virt>=0))
+ {
+ j = (cur_virt>=0?cur_virt:0);
+ for(i = ++last_virt; i > j; --i)
+ virtual[i] = virtual[i-1];
+ }
+ virtual[++cur_virt] = c;
+ }
+ else
+ ed_ringbell();
+ return;
+}
+
+/*{ BACKWORD( nwords, cmd )
+ *
+ * This routine will position cur_virt at the nth previous word.
+ *
+}*/
+
+static void backword(Vi_t *vp,int nwords, register int cmd)
+{
+ register int tcur_virt = cur_virt;
+ while( nwords-- && tcur_virt > first_virt )
+ {
+ if( !isblank(tcur_virt) && isblank(tcur_virt-1)
+ && tcur_virt>first_virt )
+ --tcur_virt;
+ else if(cmd != 'B')
+ {
+ register int last = isalph(tcur_virt-1);
+ register int cur = isalph(tcur_virt);
+ if((!cur && last) || (cur && !last))
+ --tcur_virt;
+ }
+ while( isblank(tcur_virt) && tcur_virt>=first_virt )
+ --tcur_virt;
+ if( cmd == 'B' )
+ {
+ while( !isblank(tcur_virt) && tcur_virt>=first_virt )
+ --tcur_virt;
+ }
+ else
+ {
+ if(isalph(tcur_virt))
+ while( isalph(tcur_virt) && tcur_virt>=first_virt )
+ --tcur_virt;
+ else
+ while( !isalph(tcur_virt) && !isblank(tcur_virt)
+ && tcur_virt>=first_virt )
+ --tcur_virt;
+ }
+ cur_virt = ++tcur_virt;
+ }
+ return;
+}
+
+/*{ CNTLMODE()
+ *
+ * This routine implements the vi command subset.
+ * The cursor will always be positioned at the char of interest.
+ *
+}*/
+
+static int cntlmode(Vi_t *vp)
+{
+ register int c;
+ register int i;
+ genchar tmp_u_space[MAXLINE]; /* temporary u_space */
+ genchar *real_u_space; /* points to real u_space */
+ int tmp_u_column = INVALID; /* temporary u_column */
+ int was_inmacro;
+
+ if(!vp->U_saved)
+ {
+ /*** save virtual image if never done before ***/
+ virtual[last_virt+1] = '\0';
+ gencpy(vp->U_space, virtual);
+ vp->U_saved = 1;
+ }
+
+ save_last(vp);
+
+ real_u_space = vp->u_space;
+ curhline = histmax;
+ first_virt = 0;
+ vp->repeat = 1;
+ if( cur_virt > INVALID )
+ {
+ /*** make sure cursor is at the last char ***/
+ sync_cursor(vp);
+ }
+
+ /*** Read control char until something happens to cause a ***/
+ /* return to APPEND/REPLACE mode */
+
+ while( c=ed_getchar(vp->ed,-1) )
+ {
+ vp->repeat_set = 0;
+ was_inmacro = inmacro;
+ if( c == '0' )
+ {
+ /*** move to leftmost column ***/
+ cur_virt = 0;
+ sync_cursor(vp);
+ continue;
+ }
+
+ if( digit(c) )
+ {
+ c = getcount(vp,c);
+ if( c == '.' )
+ vp->lastrepeat = vp->repeat;
+ }
+
+ /*** see if it's a move cursor command ***/
+
+ if(mvcursor(vp,c))
+ {
+ sync_cursor(vp);
+ vp->repeat = 1;
+ continue;
+ }
+
+ /*** see if it's a repeat of the last command ***/
+
+ if( c == '.' )
+ {
+ c = vp->last_cmd;
+ vp->repeat = vp->lastrepeat;
+ i = textmod(vp,c, c);
+ }
+ else
+ {
+ i = textmod(vp,c, 0);
+ }
+
+ /*** see if it's a text modification command ***/
+
+ switch(i)
+ {
+ case BAD:
+ break;
+
+ default: /** input mode **/
+ if(!was_inmacro)
+ {
+ vp->last_cmd = c;
+ vp->lastrepeat = vp->repeat;
+ }
+ vp->repeat = 1;
+ if( i == GOOD )
+ continue;
+ return(i);
+ }
+
+ switch( c )
+ {
+ /***** Other stuff *****/
+
+ case cntl('L'): /** Redraw line **/
+ /*** print the prompt and ***/
+ /* force a total refresh */
+ if(vp->nonewline==0 && !vp->ed->e_nocrnl)
+ putchar('\n');
+ vp->nonewline = 0;
+ pr_string(vp,Prompt);
+ window[0] = '\0';
+ cur_phys = vp->first_wind;
+ vp->ofirst_wind = INVALID;
+ vp->long_line = ' ';
+ break;
+
+ case cntl('V'):
+ {
+ register const char *p = fmtident(e_version);
+ save_v(vp);
+ del_line(vp,BAD);
+ while(c = *p++)
+ append(vp,c,APPEND);
+ refresh(vp,CONTROL);
+ ed_getchar(vp->ed,-1);
+ restore_v(vp);
+ break;
+ }
+
+ case '/': /** Search **/
+ case '?':
+ case 'N':
+ case 'n':
+ save_v(vp);
+ switch( search(vp,c) )
+ {
+ case GOOD:
+ /*** force a total refresh ***/
+ window[0] = '\0';
+ goto newhist;
+
+ case BAD:
+ /*** no match ***/
+ ed_ringbell();
+
+ default:
+ if( vp->u_column == INVALID )
+ del_line(vp,BAD);
+ else
+ restore_v(vp);
+ break;
+ }
+ break;
+
+ case 'j': /** get next command **/
+ case '+': /** get next command **/
+#if SHOPT_EDPREDICT
+ if(vp->ed->hlist)
+ {
+ if(vp->ed->hoff >= vp->ed->hmax)
+ goto ringbell;
+ vp->ed->hoff++;
+ goto hupdate;
+ }
+#endif /* SHOPT_EDPREDICT */
+ curhline += vp->repeat;
+ if( curhline > histmax )
+ {
+ curhline = histmax;
+ goto ringbell;
+ }
+ else if(curhline==histmax && tmp_u_column!=INVALID )
+ {
+ vp->u_space = tmp_u_space;
+ vp->u_column = tmp_u_column;
+ restore_v(vp);
+ vp->u_space = real_u_space;
+ break;
+ }
+ save_v(vp);
+ cur_virt = INVALID;
+ goto newhist;
+
+ case 'k': /** get previous command **/
+ case '-': /** get previous command **/
+#if SHOPT_EDPREDICT
+ if(vp->ed->hlist)
+ {
+ if(vp->ed->hoff == 0)
+ goto ringbell;
+ vp->ed->hoff--;
+ hupdate:
+ ed_histlist(vp->ed,*vp->ed->hlist!=0);
+ vp->nonewline++;
+ ed_ungetchar(vp->ed,cntl('L'));
+ continue;
+ }
+#endif /* SHOPT_EDPREDICT */
+ if( curhline == histmax )
+ {
+ vp->u_space = tmp_u_space;
+ i = vp->u_column;
+ save_v(vp);
+ vp->u_space = real_u_space;
+ tmp_u_column = vp->u_column;
+ vp->u_column = i;
+ }
+
+ curhline -= vp->repeat;
+ if( curhline <= histmin )
+ {
+ curhline += vp->repeat;
+ goto ringbell;
+ }
+ save_v(vp);
+ cur_virt = INVALID;
+ newhist:
+ if(curhline!=histmax || cur_virt==INVALID)
+ hist_copy((char*)virtual, MAXLINE, curhline,-1);
+ else
+ {
+ strcpy((char*)virtual,(char*)vp->u_space);
+#if SHOPT_MULTIBYTE
+ ed_internal((char*)vp->u_space,vp->u_space);
+#endif /* SHOPT_MULTIBYTE */
+ }
+#if SHOPT_MULTIBYTE
+ ed_internal((char*)virtual,virtual);
+#endif /* SHOPT_MULTIBYTE */
+ if((last_virt=genlen(virtual)-1) >= 0 && cur_virt == INVALID)
+ cur_virt = 0;
+#if SHOPT_EDPREDICT
+ if(vp->ed->hlist)
+ {
+ ed_histlist(vp->ed,0);
+ if(c=='\n')
+ ed_ungetchar(vp->ed,c);
+ ed_ungetchar(vp->ed,cntl('L'));
+ vp->nonewline = 1;
+ cur_virt = 0;
+ }
+#endif /*SHOPT_EDPREDICT */
+ break;
+
+
+ case 'u': /** undo the last thing done **/
+ restore_v(vp);
+ break;
+
+ case 'U': /** Undo everything **/
+ save_v(vp);
+ if( virtual[0] == '\0' )
+ goto ringbell;
+ else
+ {
+ gencpy(virtual, vp->U_space);
+ last_virt = genlen(vp->U_space) - 1;
+ cur_virt = 0;
+ }
+ break;
+
+#if KSHELL
+ case 'v':
+ if(vp->repeat_set==0)
+ goto vcommand;
+#endif /* KSHELL */
+
+ case 'G': /** goto command repeat **/
+ if(vp->repeat_set==0)
+ vp->repeat = histmin+1;
+ if( vp->repeat <= histmin || vp->repeat > histmax )
+ {
+ goto ringbell;
+ }
+ curhline = vp->repeat;
+ save_v(vp);
+ if(c == 'G')
+ {
+ cur_virt = INVALID;
+ goto newhist;
+ }
+
+#if KSHELL
+ vcommand:
+ if(ed_fulledit(vp->ed)==GOOD)
+ return(BIGVI);
+ else
+ goto ringbell;
+#endif /* KSHELL */
+
+ case '#': /** insert(delete) # to (no)comment command **/
+ if( cur_virt != INVALID )
+ {
+ register genchar *p = &virtual[last_virt+1];
+ *p = 0;
+ /*** see whether first char is comment char ***/
+ c = (virtual[0]=='#');
+ while(p-- >= virtual)
+ {
+ if(*p=='\n' || p<virtual)
+ {
+ if(c) /* delete '#' */
+ {
+ if(p[1]=='#')
+ {
+ last_virt--;
+ gencpy(p+1,p+2);
+ }
+ }
+ else
+ {
+ cur_virt = p-virtual;
+ append(vp,'#', APPEND);
+ }
+ }
+ }
+ if(c)
+ {
+ curhline = histmax;
+ cur_virt = 0;
+ break;
+ }
+ refresh(vp,INPUT);
+ }
+
+ case '\n': /** send to shell **/
+#if SHOPT_EDPREDICT
+ if(!vp->ed->hlist)
+ return(ENTER);
+ case '\t': /** bring choice to edit **/
+ if(vp->ed->hlist)
+ {
+ if(vp->repeat > vp->ed->nhlist-vp->ed->hoff)
+ goto ringbell;
+ curhline = vp->ed->hlist[vp->repeat+vp->ed->hoff-1]->index;
+ goto newhist;
+ }
+ goto ringbell;
+#else
+ return(ENTER);
+#endif /* SHOPT_EDPREDICT */
+ case ESC:
+ /* don't ring bell if next char is '[' */
+ if(!lookahead)
+ {
+ char x;
+ if(sfpkrd(editb.e_fd,&x,1,'\r',400L,-1)>0)
+ ed_ungetchar(vp->ed,x);
+ }
+ if(lookahead)
+ {
+ ed_ungetchar(vp->ed,c=ed_getchar(vp->ed,1));
+ if(c=='[')
+ {
+ vp->repeat = 1;
+ continue;
+ }
+ }
+ default:
+ ringbell:
+ ed_ringbell();
+ vp->repeat = 1;
+ continue;
+ }
+
+ refresh(vp,CONTROL);
+ vp->repeat = 1;
+ }
+/* NOTREACHED */
+ return(0);
+}
+
+/*{ CURSOR( new_current_physical )
+ *
+ * This routine will position the virtual cursor at
+ * physical column x in the window.
+ *
+}*/
+
+static void cursor(Vi_t *vp,register int x)
+{
+#if SHOPT_MULTIBYTE
+ while(physical[x]==MARKER)
+ x++;
+#endif /* SHOPT_MULTIBYTE */
+ cur_phys = ed_setcursor(vp->ed, physical, cur_phys,x,vp->first_wind);
+}
+
+/*{ DELETE( nchars, mode )
+ *
+ * Delete nchars from the virtual space and leave cur_virt positioned
+ * at cur_virt-1.
+ *
+ * If mode = 'c', do not save the characters deleted
+ * = 'd', save them in yankbuf and delete.
+ * = 'y', save them in yankbuf but do not delete.
+ *
+}*/
+
+static void cdelete(Vi_t *vp,register int nchars, int mode)
+{
+ register int i;
+ register genchar *cp;
+
+ if( cur_virt < first_virt )
+ {
+ ed_ringbell();
+ return;
+ }
+ if( nchars > 0 )
+ {
+ cp = virtual+cur_virt;
+ vp->o_v_char = cp[0];
+ if( (cur_virt-- + nchars) > last_virt )
+ {
+ /*** set nchars to number actually deleted ***/
+ nchars = last_virt - cur_virt;
+ }
+
+ /*** save characters to be deleted ***/
+
+ if( mode != 'c' )
+ {
+ i = cp[nchars];
+ cp[nchars] = 0;
+ gencpy(yankbuf,cp);
+ cp[nchars] = i;
+ }
+
+ /*** now delete these characters ***/
+
+ if( mode != 'y' )
+ {
+ gencpy(cp,cp+nchars);
+ last_virt -= nchars;
+ }
+ }
+ return;
+}
+
+/*{ DEL_LINE( mode )
+ *
+ * This routine will delete the line.
+ * mode = GOOD, do a save_v()
+ *
+}*/
+static void del_line(register Vi_t *vp, int mode)
+{
+ if( last_virt == INVALID )
+ return;
+
+ if( mode == GOOD )
+ save_v(vp);
+
+ cur_virt = 0;
+ first_virt = 0;
+ cdelete(vp,last_virt+1, BAD);
+ refresh(vp,CONTROL);
+
+ cur_virt = INVALID;
+ cur_phys = 0;
+ vp->findchar = INVALID;
+ last_phys = INVALID;
+ last_virt = INVALID;
+ vp->last_wind = INVALID;
+ vp->first_wind = 0;
+ vp->o_v_char = '\0';
+ vp->ocur_phys = 0;
+ vp->ocur_virt = MAXCHAR;
+ vp->ofirst_wind = 0;
+ window[0] = '\0';
+ return;
+}
+
+/*{ DELMOTION( motion, mode )
+ *
+ * Delete thru motion.
+ *
+ * mode = 'd', save deleted characters, delete
+ * = 'c', do not save characters, change
+ * = 'y', save characters, yank
+ *
+ * Returns 1 if operation successful; else 0.
+ *
+}*/
+
+static int delmotion(Vi_t *vp,int motion, int mode)
+{
+ register int begin, end, delta;
+ /* the following saves a register */
+
+ if( cur_virt == INVALID )
+ return(0);
+ if( mode != 'y' )
+ save_v(vp);
+ begin = cur_virt;
+
+ /*** fake out the motion routines by appending a blank ***/
+
+ virtual[++last_virt] = ' ';
+ end = mvcursor(vp,motion);
+ virtual[last_virt--] = 0;
+ if(!end)
+ return(0);
+
+ end = cur_virt;
+ if( mode=='c' && end>begin && strchr("wW", motion) )
+ {
+ /*** called by change operation, user really expects ***/
+ /* the effect of the eE commands, so back up to end of word */
+ while( end>begin && isblank(end-1) )
+ --end;
+ if( end == begin )
+ ++end;
+ }
+
+ delta = end - begin;
+ if( delta >= 0 )
+ {
+ cur_virt = begin;
+ if( strchr("eE;,TtFf%", motion) )
+ ++delta;
+ }
+ else
+ {
+ delta = -delta + (motion=='%');
+ }
+
+ cdelete(vp,delta, mode);
+ if( mode == 'y' )
+ cur_virt = begin;
+ return(1);
+}
+
+
+/*{ ENDWORD( nwords, cmd )
+ *
+ * This routine will move cur_virt to the end of the nth word.
+ *
+}*/
+
+static void endword(Vi_t *vp, int nwords, register int cmd)
+{
+ register int tcur_virt = cur_virt;
+ while( nwords-- )
+ {
+ if( !isblank(tcur_virt) && tcur_virt<=last_virt )
+ ++tcur_virt;
+ while( isblank(tcur_virt) && tcur_virt<=last_virt )
+ ++tcur_virt;
+ if( cmd == 'E' )
+ {
+ while( !isblank(tcur_virt) && tcur_virt<=last_virt )
+ ++tcur_virt;
+ }
+ else
+ {
+ if( isalph(tcur_virt) )
+ while( isalph(tcur_virt) && tcur_virt<=last_virt )
+ ++tcur_virt;
+ else
+ while( !isalph(tcur_virt) && !isblank(tcur_virt)
+ && tcur_virt<=last_virt )
+ ++tcur_virt;
+ }
+ if( tcur_virt > first_virt )
+ tcur_virt--;
+ }
+ cur_virt = tcur_virt;
+ return;
+}
+
+/*{ FORWARD( nwords, cmd )
+ *
+ * This routine will move cur_virt forward to the next nth word.
+ *
+}*/
+
+static void forward(Vi_t *vp,register int nwords, int cmd)
+{
+ register int tcur_virt = cur_virt;
+ while( nwords-- )
+ {
+ if( cmd == 'W' )
+ {
+ while( !isblank(tcur_virt) && tcur_virt < last_virt )
+ ++tcur_virt;
+ }
+ else
+ {
+ if( isalph(tcur_virt) )
+ {
+ while( isalph(tcur_virt) && tcur_virt<last_virt )
+ ++tcur_virt;
+ }
+ else
+ {
+ while( !isalph(tcur_virt) && !isblank(tcur_virt)
+ && tcur_virt < last_virt )
+ ++tcur_virt;
+ }
+ }
+ while( isblank(tcur_virt) && tcur_virt < last_virt )
+ ++tcur_virt;
+ }
+ cur_virt = tcur_virt;
+ return;
+}
+
+
+
+/*{ GETCOUNT(c)
+ *
+ * Set repeat to the user typed number and return the terminating
+ * character.
+ *
+}*/
+
+static int getcount(register Vi_t *vp,register int c)
+{
+ register int i;
+
+ /*** get any repeat count ***/
+
+ if( c == '0' )
+ return(c);
+
+ vp->repeat_set++;
+ i = 0;
+ while( digit(c) )
+ {
+ i = i*10 + c - '0';
+ c = ed_getchar(vp->ed,-1);
+ }
+
+ if( i > 0 )
+ vp->repeat *= i;
+ return(c);
+}
+
+
+/*{ GETLINE( mode )
+ *
+ * This routine will fetch a line.
+ * mode = APPEND, allow escape to cntlmode subroutine
+ * appending characters.
+ * = REPLACE, allow escape to cntlmode subroutine
+ * replacing characters.
+ * = SEARCH, no escape allowed
+ * = ESC, enter control mode immediately
+ *
+ * The cursor will always be positioned after the last
+ * char printed.
+ *
+ * This routine returns when cr, nl, or (eof in column 0) is
+ * received (column 0 is the first char position).
+ *
+}*/
+
+static void getline(register Vi_t* vp,register int mode)
+{
+ register int c;
+ register int tmp;
+ int max_virt=0, last_save=0;
+ genchar saveline[MAXLINE];
+ vp->addnl = 1;
+
+ if( mode == ESC )
+ {
+ /*** go directly to control mode ***/
+ goto escape;
+ }
+
+ for(;;)
+ {
+ if( (c=ed_getchar(vp->ed,mode==SEARCH?1:-2)) == usreof )
+ c = UEOF;
+ else if( c == usrerase )
+ c = UERASE;
+ else if( c == usrkill )
+ c = UKILL;
+ else if( c == editb.e_werase )
+ c = UWERASE;
+ else if( c == usrlnext )
+ c = ULNEXT;
+ else if(mode==SEARCH && c==editb.e_intr)
+ c = UINTR;
+
+ if( c == ULNEXT)
+ {
+ /*** implement ^V to escape next char ***/
+ c = ed_getchar(vp->ed,2);
+ append(vp,c, mode);
+ refresh(vp,INPUT);
+ continue;
+ }
+
+ switch( c )
+ {
+ case ESC: /** enter control mode **/
+ if(!sh_isoption(SH_VI))
+ {
+ append(vp,c, mode);
+ break;
+ }
+ if( mode == SEARCH )
+ {
+ ed_ringbell();
+ continue;
+ }
+ else
+ {
+ escape:
+ if( mode == REPLACE )
+ {
+ c = max_virt-cur_virt;
+ if(c > 0 && last_save>=cur_virt)
+ {
+ genncpy((&virtual[cur_virt]),&saveline[cur_virt],c);
+ if(last_virt>=last_save)
+ last_virt=last_save-1;
+ refresh(vp,INPUT);
+ }
+ --cur_virt;
+ }
+ tmp = cntlmode(vp);
+ if( tmp == ENTER || tmp == BIGVI )
+ {
+#if SHOPT_MULTIBYTE
+ vp->bigvi = (tmp==BIGVI);
+#endif /* SHOPT_MULTIBYTE */
+ return;
+ }
+ if( tmp == INSERT )
+ {
+ mode = APPEND;
+ continue;
+ }
+ mode = tmp;
+ if(mode==REPLACE)
+ {
+ c = last_save = last_virt+1;
+ if(c >= MAXLINE)
+ c = MAXLINE-1;
+ genncpy(saveline, virtual, c);
+ }
+ }
+ break;
+
+ case UINTR:
+ first_virt = 0;
+ cdelete(vp,cur_virt+1, BAD);
+ cur_virt = -1;
+ return;
+ case UERASE: /** user erase char **/
+ /*** treat as backspace ***/
+
+ case '\b': /** backspace **/
+ if( virtual[cur_virt] == '\\' )
+ {
+ cdelete(vp,1, BAD);
+ append(vp,usrerase, mode);
+ }
+ else
+ {
+ if( mode==SEARCH && cur_virt==0 )
+ {
+ first_virt = 0;
+ cdelete(vp,1, BAD);
+ return;
+ }
+ if(mode==REPLACE || (last_save>0 && last_virt<=last_save))
+ {
+ if(cur_virt<=first_virt)
+ ed_ringbell();
+ else if(mode==REPLACE)
+ --cur_virt;
+ mode = REPLACE;
+ sync_cursor(vp);
+ continue;
+ }
+ else
+ cdelete(vp,1, BAD);
+ }
+ break;
+
+ case UWERASE: /** delete back word **/
+ if( cur_virt > first_virt &&
+ !isblank(cur_virt) &&
+ !ispunct(virtual[cur_virt]) &&
+ isblank(cur_virt-1) )
+ {
+ cdelete(vp,1, BAD);
+ }
+ else
+ {
+ tmp = cur_virt;
+ backword(vp,1, 'W');
+ cdelete(vp,tmp - cur_virt + 1, BAD);
+ }
+ break;
+
+ case UKILL: /** user kill line char **/
+ if( virtual[cur_virt] == '\\' )
+ {
+ cdelete(vp,1, BAD);
+ append(vp,usrkill, mode);
+ }
+ else
+ {
+ if( mode == SEARCH )
+ {
+ cur_virt = 1;
+ delmotion(vp, '$', BAD);
+ }
+ else if(first_virt)
+ {
+ tmp = cur_virt;
+ cur_virt = first_virt;
+ cdelete(vp,tmp - cur_virt + 1, BAD);
+ }
+ else
+ del_line(vp,GOOD);
+ }
+ break;
+
+ case UEOF: /** eof char **/
+ if( cur_virt != INVALID )
+ continue;
+ vp->addnl = 0;
+
+ case '\n': /** newline or return **/
+ if( mode != SEARCH )
+ save_last(vp);
+ refresh(vp,INPUT);
+ last_phys++;
+ return;
+
+ case '\t': /** command completion **/
+ if(mode!=SEARCH && last_virt>=0 && (vp->ed->e_tabcount|| !isblank(cur_virt)) && vp->ed->sh->nextprompt)
+ {
+ if(virtual[cur_virt]=='\\')
+ {
+ virtual[cur_virt] = '\t';
+ break;
+ }
+ if(vp->ed->e_tabcount==0)
+ {
+ ed_ungetchar(vp->ed,'\\');
+ vp->ed->e_tabcount=1;
+ goto escape;
+ }
+ else if(vp->ed->e_tabcount==1)
+ {
+ ed_ungetchar(vp->ed,'=');
+ goto escape;
+ }
+ vp->ed->e_tabcount = 0;
+ }
+ /* FALL THRU*/
+ default:
+ if( mode == REPLACE )
+ {
+ if( cur_virt < last_virt )
+ {
+ replace(vp,c, 1);
+ if(cur_virt>max_virt)
+ max_virt = cur_virt;
+ continue;
+ }
+ cdelete(vp,1, BAD);
+ mode = APPEND;
+ max_virt = last_virt+3;
+ }
+ append(vp,c, mode);
+ break;
+ }
+ refresh(vp,INPUT);
+
+ }
+}
+
+/*{ MVCURSOR( motion )
+ *
+ * This routine will move the virtual cursor according to motion
+ * for repeat times.
+ *
+ * It returns GOOD if successful; else BAD.
+ *
+}*/
+
+static int mvcursor(register Vi_t* vp,register int motion)
+{
+ register int count;
+ register int tcur_virt;
+ register int incr = -1;
+ register int bound = 0;
+
+ switch(motion)
+ {
+ /***** Cursor move commands *****/
+
+ case '0': /** First column **/
+ tcur_virt = 0;
+ break;
+
+ case '^': /** First nonblank character **/
+ tcur_virt = first_virt;
+ while( isblank(tcur_virt) && tcur_virt < last_virt )
+ ++tcur_virt;
+ break;
+
+ case '|':
+ tcur_virt = vp->repeat-1;
+ if(tcur_virt <= last_virt)
+ break;
+ /* fall through */
+
+ case '$': /** End of line **/
+ tcur_virt = last_virt;
+ break;
+
+ case '[':
+ switch(motion=getcount(vp,ed_getchar(vp->ed,-1)))
+ {
+ case 'A':
+#if SHOPT_EDPREDICT
+ if(!vp->ed->hlist && cur_virt>=0 && cur_virt<(SEARCHSIZE-2) && cur_virt == last_virt)
+#else
+ if(cur_virt>=0 && cur_virt<(SEARCHSIZE-2) && cur_virt == last_virt)
+#endif /* SHOPT_EDPREDICT */
+ {
+ virtual[last_virt + 1] = '\0';
+#if SHOPT_MULTIBYTE
+ ed_external(virtual,lsearch+1);
+#else
+ strcpy(lsearch+1,virtual);
+#endif /* SHOPT_MULTIBYTE */
+ *lsearch = '^';
+ vp->direction = -2;
+ ed_ungetchar(vp->ed,'n');
+ }
+ else if(cur_virt==0 && vp->direction == -2)
+ ed_ungetchar(vp->ed,'n');
+ else
+ ed_ungetchar(vp->ed,'k');
+ return(1);
+ case 'B':
+ ed_ungetchar(vp->ed,'j');
+ return(1);
+ case 'C':
+ motion = last_virt;
+ incr = 1;
+ goto walk;
+ case 'D':
+ motion = first_virt;
+ goto walk;
+ case 'H':
+ tcur_virt = 0;
+ break;
+ case 'Y':
+ tcur_virt = last_virt;
+ break;
+ default:
+ ed_ungetchar(vp->ed,motion);
+ return(0);
+ }
+ break;
+
+ case 'h': /** Left one **/
+ case '\b':
+ motion = first_virt;
+ goto walk;
+
+ case ' ':
+ case 'l': /** Right one **/
+ motion = last_virt;
+ incr = 1;
+ walk:
+ tcur_virt = cur_virt;
+ if( incr*tcur_virt < motion)
+ {
+ tcur_virt += vp->repeat*incr;
+ if( incr*tcur_virt > motion)
+ tcur_virt = motion;
+ }
+ else
+ return(0);
+ break;
+
+ case 'B':
+ case 'b': /** back word **/
+ tcur_virt = cur_virt;
+ backword(vp,vp->repeat, motion);
+ if( cur_virt == tcur_virt )
+ return(0);
+ return(1);
+
+ case 'E':
+ case 'e': /** end of word **/
+ tcur_virt = cur_virt;
+ if(tcur_virt >=0)
+ endword(vp, vp->repeat, motion);
+ if( cur_virt == tcur_virt )
+ return(0);
+ return(1);
+
+ case ',': /** reverse find old char **/
+ case ';': /** find old char **/
+ switch(vp->last_find)
+ {
+ case 't':
+ case 'f':
+ if(motion==';')
+ {
+ bound = last_virt;
+ incr = 1;
+ }
+ goto find_b;
+
+ case 'T':
+ case 'F':
+ if(motion==',')
+ {
+ bound = last_virt;
+ incr = 1;
+ }
+ goto find_b;
+
+ default:
+ return(0);
+ }
+
+
+ case 't': /** find up to new char forward **/
+ case 'f': /** find new char forward **/
+ bound = last_virt;
+ incr = 1;
+
+ case 'T': /** find up to new char backward **/
+ case 'F': /** find new char backward **/
+ vp->last_find = motion;
+ if((vp->findchar=getrchar(vp))==ESC)
+ return(1);
+find_b:
+ tcur_virt = cur_virt;
+ count = vp->repeat;
+ while( count-- )
+ {
+ while( incr*(tcur_virt+=incr) <= bound
+ && virtual[tcur_virt] != vp->findchar );
+ if( incr*tcur_virt > bound )
+ {
+ return(0);
+ }
+ }
+ if( fold(vp->last_find) == 'T' )
+ tcur_virt -= incr;
+ break;
+
+ case '%':
+ {
+ int nextmotion;
+ int nextc;
+ tcur_virt = cur_virt;
+ while( tcur_virt <= last_virt
+ && strchr(paren_chars,virtual[tcur_virt])==(char*)0)
+ tcur_virt++;
+ if(tcur_virt > last_virt )
+ return(0);
+ nextc = virtual[tcur_virt];
+ count = strchr(paren_chars,nextc)-paren_chars;
+ if(count < 3)
+ {
+ incr = 1;
+ bound = last_virt;
+ nextmotion = paren_chars[count+3];
+ }
+ else
+ nextmotion = paren_chars[count-3];
+ count = 1;
+ while(count >0 && incr*(tcur_virt+=incr) <= bound)
+ {
+ if(virtual[tcur_virt] == nextmotion)
+ count--;
+ else if(virtual[tcur_virt]==nextc)
+ count++;
+ }
+ if(count)
+ return(0);
+ break;
+ }
+
+ case 'W':
+ case 'w': /** forward word **/
+ tcur_virt = cur_virt;
+ forward(vp,vp->repeat, motion);
+ if( tcur_virt == cur_virt )
+ return(0);
+ return(1);
+
+ default:
+ return(0);
+ }
+ cur_virt = tcur_virt;
+
+ return(1);
+}
+
+/*
+ * print a string
+ */
+
+static void pr_string(register Vi_t *vp, register const char *sp)
+{
+ /*** copy string sp ***/
+ register char *ptr = editb.e_outptr;
+ while(*sp)
+ *ptr++ = *sp++;
+ editb.e_outptr = ptr;
+ return;
+}
+
+/*{ PUTSTRING( column, nchars )
+ *
+ * Put nchars starting at column of physical into the workspace
+ * to be printed.
+ *
+}*/
+
+static void putstring(register Vi_t *vp,register int col, register int nchars)
+{
+ while( nchars-- )
+ putchar(physical[col++]);
+ return;
+}
+
+/*{ REFRESH( mode )
+ *
+ * This routine will refresh the crt so the physical image matches
+ * the virtual image and display the proper window.
+ *
+ * mode = CONTROL, refresh in control mode, ie. leave cursor
+ * positioned at last char printed.
+ * = INPUT, refresh in input mode; leave cursor positioned
+ * after last char printed.
+ * = TRANSLATE, perform virtual to physical translation
+ * and adjust left margin only.
+ *
+ * +-------------------------------+
+ * | | | virtual | | |
+ * +-------------------------------+
+ * cur_virt last_virt
+ *
+ * +-----------------------------------------------+
+ * | | | physical | | |
+ * +-----------------------------------------------+
+ * cur_phys last_phys
+ *
+ * 0 w_size - 1
+ * +-----------------------+
+ * | | | window |
+ * +-----------------------+
+ * cur_window = cur_phys - first_wind
+}*/
+
+static void refresh(register Vi_t* vp, int mode)
+{
+ register int p;
+ register int v;
+ register int first_w = vp->first_wind;
+ int p_differ;
+ int new_lw;
+ int ncur_phys;
+ int opflag; /* search optimize flag */
+
+# define w v
+
+ /*** find out if it's necessary to start translating at beginning ***/
+
+ if(lookahead>0)
+ {
+ p = previous[lookahead-1];
+ if(p != ESC && p != '\n' && p != '\r')
+ mode = TRANSLATE;
+ }
+ v = cur_virt;
+#if SHOPT_EDPREDICT
+ if(mode==INPUT && v>0 && virtual[0]=='#' && v==last_virt && virtual[v]!='*' && sh_isoption(SH_VI))
+ {
+ int n;
+ virtual[last_virt+1] = 0;
+# if SHOPT_MULTIBYTE
+ ed_external(virtual,(char*)virtual);
+# endif /* SHOPT_MULTIBYTE */
+ n = ed_histgen(vp->ed,(char*)virtual);
+# if SHOPT_MULTIBYTE
+ ed_internal((char*)virtual,virtual);
+# endif /* SHOPT_MULTIBYTE */
+ if(vp->ed->hlist)
+ {
+ ed_histlist(vp->ed,n);
+ pr_string(vp,Prompt);
+ vp->ocur_virt = INVALID;
+ ed_setcursor(vp->ed,physical,0,cur_phys,0);
+ }
+ else
+ ed_ringbell();
+ }
+ else if(mode==INPUT && v<=1 && vp->ed->hlist)
+ ed_histlist(vp->ed,0);
+#endif /* SHOPT_EDPREDICT */
+ if( v<vp->ocur_virt || vp->ocur_virt==INVALID
+ || ( v==vp->ocur_virt
+ && (!is_print(virtual[v]) || !is_print(vp->o_v_char))) )
+ {
+ opflag = 0;
+ p = 0;
+ v = 0;
+ }
+ else
+ {
+ opflag = 1;
+ p = vp->ocur_phys;
+ v = vp->ocur_virt;
+ if( !is_print(virtual[v]) )
+ {
+ /*** avoid double ^'s ***/
+ ++p;
+ ++v;
+ }
+ }
+ virtual[last_virt+1] = 0;
+ ncur_phys = ed_virt_to_phys(vp->ed,virtual,physical,cur_virt,v,p);
+ p = genlen(physical);
+ if( --p < 0 )
+ last_phys = 0;
+ else
+ last_phys = p;
+
+ /*** see if this was a translate only ***/
+
+ if( mode == TRANSLATE )
+ return;
+
+ /*** adjust left margin if necessary ***/
+
+ if( ncur_phys<first_w || ncur_phys>=(first_w + w_size) )
+ {
+ cursor(vp,first_w);
+ first_w = ncur_phys - (w_size>>1);
+ if( first_w < 0 )
+ first_w = 0;
+ vp->first_wind = cur_phys = first_w;
+ }
+
+ /*** attempt to optimize search somewhat to find ***/
+ /*** out where physical and window images differ ***/
+
+ if( first_w==vp->ofirst_wind && ncur_phys>=vp->ocur_phys && opflag==1 )
+ {
+ p = vp->ocur_phys;
+ w = p - first_w;
+ }
+ else
+ {
+ p = first_w;
+ w = 0;
+ }
+
+ for(; (p<=last_phys && w<=vp->last_wind); ++p, ++w)
+ {
+ if( window[w] != physical[p] )
+ break;
+ }
+ p_differ = p;
+
+ if( (p>last_phys || p>=first_w+w_size) && w>vp->last_wind
+ && cur_virt==vp->ocur_virt )
+ {
+ /*** images are identical ***/
+ return;
+ }
+
+ /*** copy the physical image to the window image ***/
+
+ if( last_virt != INVALID )
+ {
+ while( p <= last_phys && w < w_size )
+ window[w++] = physical[p++];
+ }
+ new_lw = w;
+
+ /*** erase trailing characters if needed ***/
+
+ while( w <= vp->last_wind )
+ window[w++] = ' ';
+ vp->last_wind = --w;
+
+ p = p_differ;
+
+ /*** move cursor to start of difference ***/
+
+ cursor(vp,p);
+
+ /*** and output difference ***/
+
+ w = p - first_w;
+ while( w <= vp->last_wind )
+ putchar(window[w++]);
+
+ cur_phys = w + first_w;
+ vp->last_wind = --new_lw;
+
+ if( last_phys >= w_size )
+ {
+ if( first_w == 0 )
+ vp->long_char = '>';
+ else if( last_phys < (first_w+w_size) )
+ vp->long_char = '<';
+ else
+ vp->long_char = '*';
+ }
+ else
+ vp->long_char = ' ';
+
+ if( vp->long_line != vp->long_char )
+ {
+ /*** indicate lines longer than window ***/
+ while( w++ < w_size )
+ {
+ putchar(' ');
+ ++cur_phys;
+ }
+ putchar(vp->long_char);
+ ++cur_phys;
+ vp->long_line = vp->long_char;
+ }
+
+ if(vp->ed->e_multiline && vp->ofirst_wind==INVALID && !vp->ed->e_nocrnl)
+ ed_setcursor(vp->ed, physical, last_phys+1, last_phys+1, -1);
+ vp->ed->e_nocrnl = 0;
+ vp->ocur_phys = ncur_phys;
+ vp->ocur_virt = cur_virt;
+ vp->ofirst_wind = first_w;
+
+ if( mode==INPUT && cur_virt>INVALID )
+ ++ncur_phys;
+
+ cursor(vp,ncur_phys);
+ ed_flush(vp->ed);
+ return;
+}
+
+/*{ REPLACE( char, increment )
+ *
+ * Replace the cur_virt character with char. This routine attempts
+ * to avoid using refresh().
+ *
+ * increment = 1, increment cur_virt after replacement.
+ * = 0, leave cur_virt where it is.
+ *
+}*/
+
+static void replace(register Vi_t *vp, register int c, register int increment)
+{
+ register int cur_window;
+
+ if( cur_virt == INVALID )
+ {
+ /*** can't replace invalid cursor ***/
+ ed_ringbell();
+ return;
+ }
+ cur_window = cur_phys - vp->first_wind;
+ if( vp->ocur_virt == INVALID || !is_print(c)
+ || !is_print(virtual[cur_virt])
+ || !is_print(vp->o_v_char)
+#if SHOPT_MULTIBYTE
+ || !iswascii(c) || mbwidth(vp->o_v_char)>1
+ || !iswascii(virtual[cur_virt])
+#endif /* SHOPT_MULTIBYTE */
+ || (increment && (cur_window==w_size-1)
+ || !is_print(virtual[cur_virt+1])) )
+ {
+ /*** must use standard refresh routine ***/
+
+ cdelete(vp,1, BAD);
+ append(vp,c, APPEND);
+ if( increment && cur_virt<last_virt )
+ ++cur_virt;
+ refresh(vp,CONTROL);
+ }
+ else
+ {
+ virtual[cur_virt] = c;
+ physical[cur_phys] = c;
+ window[cur_window] = c;
+ putchar(c);
+ if(increment)
+ {
+ c = virtual[++cur_virt];
+ ++cur_phys;
+ }
+ else
+ {
+ putchar('\b');
+ }
+ vp->o_v_char = c;
+ ed_flush(vp->ed);
+ }
+ return;
+}
+
+/*{ RESTORE_V()
+ *
+ * Restore the contents of virtual space from u_space.
+ *
+}*/
+
+static void restore_v(register Vi_t *vp)
+{
+ register int tmpcol;
+ genchar tmpspace[MAXLINE];
+
+ if( vp->u_column == INVALID-1 )
+ {
+ /*** never saved anything ***/
+ ed_ringbell();
+ return;
+ }
+ gencpy(tmpspace, vp->u_space);
+ tmpcol = vp->u_column;
+ save_v(vp);
+ gencpy(virtual, tmpspace);
+ cur_virt = tmpcol;
+ last_virt = genlen(tmpspace) - 1;
+ vp->ocur_virt = MAXCHAR; /** invalidate refresh optimization **/
+ return;
+}
+
+/*{ SAVE_LAST()
+ *
+ * If the user has typed something, save it in last line.
+ *
+}*/
+
+static void save_last(register Vi_t* vp)
+{
+ register int i;
+
+ if( (i = cur_virt - first_virt + 1) > 0 )
+ {
+ /*** save last thing user typed ***/
+ if(i >= MAXLINE)
+ i = MAXLINE-1;
+ genncpy(vp->lastline, (&virtual[first_virt]), i);
+ vp->lastline[i] = '\0';
+ }
+ return;
+}
+
+/*{ SAVE_V()
+ *
+ * This routine will save the contents of virtual in u_space.
+ *
+}*/
+
+static void save_v(register Vi_t *vp)
+{
+ if(!inmacro)
+ {
+ virtual[last_virt + 1] = '\0';
+ gencpy(vp->u_space, virtual);
+ vp->u_column = cur_virt;
+ }
+ return;
+}
+
+/*{ SEARCH( mode )
+ *
+ * Search history file for regular expression.
+ *
+ * mode = '/' require search string and search new to old
+ * mode = '?' require search string and search old to new
+ * mode = 'N' repeat last search in reverse direction
+ * mode = 'n' repeat last search
+ *
+}*/
+
+/*
+ * search for <string> in the current command
+ */
+static int curline_search(Vi_t *vp, const char *string)
+{
+ register int len=strlen(string);
+ register const char *dp,*cp=string, *dpmax;
+#if SHOPT_MULTIBYTE
+ ed_external(vp->u_space,(char*)vp->u_space);
+#endif /* SHOPT_MULTIBYTE */
+ for(dp=(char*)vp->u_space,dpmax=dp+strlen(dp)-len; dp<=dpmax; dp++)
+ {
+ if(*dp==*cp && memcmp(cp,dp,len)==0)
+ return(dp-(char*)vp->u_space);
+ }
+#if SHOPT_MULTIBYTE
+ ed_internal((char*)vp->u_space,vp->u_space);
+#endif /* SHOPT_MULTIBYTE */
+ return(-1);
+}
+
+static int search(register Vi_t* vp,register int mode)
+{
+ register int new_direction;
+ register int oldcurhline;
+ register int i;
+ Histloc_t location;
+
+ if( vp->direction == -2 && mode != 'n')
+ vp->direction = -1;
+ if( mode == '/' || mode == '?')
+ {
+ /*** new search expression ***/
+ del_line(vp,BAD);
+ append(vp,mode, APPEND);
+ refresh(vp,INPUT);
+ first_virt = 1;
+ getline(vp,SEARCH);
+ first_virt = 0;
+ virtual[last_virt + 1] = '\0'; /*** make null terminated ***/
+ vp->direction = mode=='/' ? -1 : 1;
+ }
+
+ if( cur_virt == INVALID )
+ {
+ /*** no operation ***/
+ return(ABORT);
+ }
+
+ if( cur_virt==0 || fold(mode)=='N' )
+ {
+ /*** user wants repeat of last search ***/
+ del_line(vp,BAD);
+ strcpy( ((char*)virtual)+1, lsearch);
+#if SHOPT_MULTIBYTE
+ *((char*)virtual) = '/';
+ ed_internal((char*)virtual,virtual);
+#endif /* SHOPT_MULTIBYTE */
+ }
+
+ if( mode == 'N' )
+ new_direction = -vp->direction;
+ else
+ new_direction = vp->direction;
+
+
+ /*** now search ***/
+
+ oldcurhline = curhline;
+#if SHOPT_MULTIBYTE
+ ed_external(virtual,(char*)virtual);
+#endif /* SHOPT_MULTIBYTE */
+ if(mode=='?' && (i=curline_search(vp,((char*)virtual)+1))>=0)
+ {
+ location.hist_command = curhline;
+ location.hist_char = i;
+ }
+ else
+ {
+ i = INVALID;
+ if( new_direction==1 && curhline >= histmax )
+ curhline = histmin + 1;
+ location = hist_find(shgd->hist_ptr,((char*)virtual)+1, curhline, 1, new_direction);
+ }
+ cur_virt = i;
+ strncpy(lsearch, ((char*)virtual)+1, SEARCHSIZE);
+ lsearch[SEARCHSIZE-1] = 0;
+ if( (curhline=location.hist_command) >=0 )
+ {
+ vp->ocur_virt = INVALID;
+ return(GOOD);
+ }
+
+ /*** could not find matching line ***/
+
+ curhline = oldcurhline;
+ return(BAD);
+}
+
+/*{ SYNC_CURSOR()
+ *
+ * This routine will move the physical cursor to the same
+ * column as the virtual cursor.
+ *
+}*/
+
+static void sync_cursor(register Vi_t *vp)
+{
+ register int p;
+ register int v;
+ register int c;
+ int new_phys;
+
+ if( cur_virt == INVALID )
+ return;
+
+ /*** find physical col that corresponds to virtual col ***/
+
+ new_phys = 0;
+ if(vp->first_wind==vp->ofirst_wind && cur_virt>vp->ocur_virt && vp->ocur_virt!=INVALID)
+ {
+ /*** try to optimize search a little ***/
+ p = vp->ocur_phys + 1;
+#if SHOPT_MULTIBYTE
+ while(physical[p]==MARKER)
+ p++;
+#endif /* SHOPT_MULTIBYTE */
+ v = vp->ocur_virt + 1;
+ }
+ else
+ {
+ p = 0;
+ v = 0;
+ }
+ for(; v <= last_virt; ++p, ++v)
+ {
+#if SHOPT_MULTIBYTE
+ int d;
+ c = virtual[v];
+ if((d = mbwidth(c)) > 1)
+ {
+ if( v != cur_virt )
+ p += (d-1);
+ }
+ else if(!iswprint(c))
+#else
+ c = virtual[v];
+ if(!isprint(c))
+#endif /* SHOPT_MULTIBYTE */
+ {
+ if( c == '\t' )
+ {
+ p -= ((p+editb.e_plen)%TABSIZE);
+ p += (TABSIZE-1);
+ }
+ else
+ {
+ ++p;
+ }
+ }
+ if( v == cur_virt )
+ {
+ new_phys = p;
+ break;
+ }
+ }
+
+ if( new_phys < vp->first_wind || new_phys >= vp->first_wind + w_size )
+ {
+ /*** asked to move outside of window ***/
+
+ window[0] = '\0';
+ refresh(vp,CONTROL);
+ return;
+ }
+
+ cursor(vp,new_phys);
+ ed_flush(vp->ed);
+ vp->ocur_phys = cur_phys;
+ vp->ocur_virt = cur_virt;
+ vp->o_v_char = virtual[vp->ocur_virt];
+
+ return;
+}
+
+/*{ TEXTMOD( command, mode )
+ *
+ * Modify text operations.
+ *
+ * mode != 0, repeat previous operation
+ *
+}*/
+
+static int textmod(register Vi_t *vp,register int c, int mode)
+{
+ register int i;
+ register genchar *p = vp->lastline;
+ register int trepeat = vp->repeat;
+ genchar *savep;
+
+ if(mode && (fold(vp->lastmotion)=='F' || fold(vp->lastmotion)=='T'))
+ vp->lastmotion = ';';
+
+ if( fold(c) == 'P' )
+ {
+ /*** change p from lastline to yankbuf ***/
+ p = yankbuf;
+ }
+
+addin:
+ switch( c )
+ {
+ /***** Input commands *****/
+
+#if KSHELL
+ case '\t':
+ if(vp->ed->e_tabcount!=1)
+ return(BAD);
+ c = '=';
+ case '*': /** do file name expansion in place **/
+ case '\\': /** do file name completion in place **/
+ if( cur_virt == INVALID )
+ return(BAD);
+ case '=': /** list file name expansions **/
+ save_v(vp);
+ i = last_virt;
+ ++last_virt;
+ mode = cur_virt-1;
+ virtual[last_virt] = 0;
+ if(ed_expand(vp->ed,(char*)virtual, &cur_virt, &last_virt, c, vp->repeat_set?vp->repeat:-1)<0)
+ {
+ if(vp->ed->e_tabcount)
+ {
+ vp->ed->e_tabcount=2;
+ ed_ungetchar(vp->ed,'\t');
+ --last_virt;
+ return(APPEND);
+ }
+ last_virt = i;
+ ed_ringbell();
+ }
+ else if(c == '=' && !vp->repeat_set)
+ {
+ last_virt = i;
+ vp->nonewline++;
+ ed_ungetchar(vp->ed,cntl('L'));
+ return(GOOD);
+ }
+ else
+ {
+ --cur_virt;
+ --last_virt;
+ vp->ocur_virt = MAXCHAR;
+ if(c=='=' || (mode<cur_virt && (virtual[cur_virt]==' ' || virtual[cur_virt]=='/')))
+ vp->ed->e_tabcount = 0;
+ return(APPEND);
+ }
+ break;
+
+ case '@': /** macro expansion **/
+ if( mode )
+ c = vp->lastmacro;
+ else
+ if((c=getrchar(vp))==ESC)
+ return(GOOD);
+ if(!inmacro)
+ vp->lastmacro = c;
+ if(ed_macro(vp->ed,c))
+ {
+ save_v(vp);
+ inmacro++;
+ return(GOOD);
+ }
+ ed_ringbell();
+ return(BAD);
+
+#endif /* KSHELL */
+ case '_': /** append last argument of prev command **/
+ save_v(vp);
+ {
+ genchar tmpbuf[MAXLINE];
+ if(vp->repeat_set==0)
+ vp->repeat = -1;
+ p = (genchar*)hist_word((char*)tmpbuf,MAXLINE,vp->repeat);
+ if(p==0)
+ {
+ ed_ringbell();
+ break;
+ }
+#if SHOPT_MULTIBYTE
+ ed_internal((char*)p,tmpbuf);
+ p = tmpbuf;
+#endif /* SHOPT_MULTIBYTE */
+ i = ' ';
+ do
+ {
+ append(vp,i,APPEND);
+ }
+ while(i = *p++);
+ return(APPEND);
+ }
+
+ case 'A': /** append to end of line **/
+ cur_virt = last_virt;
+ sync_cursor(vp);
+
+ case 'a': /** append **/
+ if( fold(mode) == 'A' )
+ {
+ c = 'p';
+ goto addin;
+ }
+ save_v(vp);
+ if( cur_virt != INVALID )
+ {
+ first_virt = cur_virt + 1;
+ cursor(vp,cur_phys + 1);
+ ed_flush(vp->ed);
+ }
+ return(APPEND);
+
+ case 'I': /** insert at beginning of line **/
+ cur_virt = first_virt;
+ sync_cursor(vp);
+
+ case 'i': /** insert **/
+ if( fold(mode) == 'I' )
+ {
+ c = 'P';
+ goto addin;
+ }
+ save_v(vp);
+ if( cur_virt != INVALID )
+ {
+ vp->o_v_char = virtual[cur_virt];
+ first_virt = cur_virt--;
+ }
+ return(INSERT);
+
+ case 'C': /** change to eol **/
+ c = '$';
+ goto chgeol;
+
+ case 'c': /** change **/
+ if( mode )
+ c = vp->lastmotion;
+ else
+ c = getcount(vp,ed_getchar(vp->ed,-1));
+chgeol:
+ vp->lastmotion = c;
+ if( c == 'c' )
+ {
+ del_line(vp,GOOD);
+ return(APPEND);
+ }
+
+ if(!delmotion(vp, c, 'c'))
+ return(BAD);
+
+ if( mode == 'c' )
+ {
+ c = 'p';
+ trepeat = 1;
+ goto addin;
+ }
+ first_virt = cur_virt + 1;
+ return(APPEND);
+
+ case 'D': /** delete to eol **/
+ c = '$';
+ goto deleol;
+
+ case 'd': /** delete **/
+ if( mode )
+ c = vp->lastmotion;
+ else
+ c = getcount(vp,ed_getchar(vp->ed,-1));
+deleol:
+ vp->lastmotion = c;
+ if( c == 'd' )
+ {
+ del_line(vp,GOOD);
+ break;
+ }
+ if(!delmotion(vp, c, 'd'))
+ return(BAD);
+ if( cur_virt < last_virt )
+ ++cur_virt;
+ break;
+
+ case 'P':
+ if( p[0] == '\0' )
+ return(BAD);
+ if( cur_virt != INVALID )
+ {
+ i = virtual[cur_virt];
+ if(!is_print(i))
+ vp->ocur_virt = INVALID;
+ --cur_virt;
+ }
+
+ case 'p': /** print **/
+ if( p[0] == '\0' )
+ return(BAD);
+
+ if( mode != 's' && mode != 'c' )
+ {
+ save_v(vp);
+ if( c == 'P' )
+ {
+ /*** fix stored cur_virt ***/
+ ++vp->u_column;
+ }
+ }
+ if( mode == 'R' )
+ mode = REPLACE;
+ else
+ mode = APPEND;
+ savep = p;
+ for(i=0; i<trepeat; ++i)
+ {
+ while(c= *p++)
+ append(vp,c,mode);
+ p = savep;
+ }
+ break;
+
+ case 'R': /* Replace many chars **/
+ if( mode == 'R' )
+ {
+ c = 'P';
+ goto addin;
+ }
+ save_v(vp);
+ if( cur_virt != INVALID )
+ first_virt = cur_virt;
+ return(REPLACE);
+
+ case 'r': /** replace **/
+ if( mode )
+ c = *p;
+ else
+ if((c=getrchar(vp))==ESC)
+ return(GOOD);
+ *p = c;
+ save_v(vp);
+ while(trepeat--)
+ replace(vp,c, trepeat!=0);
+ return(GOOD);
+
+ case 'S': /** Substitute line - cc **/
+ c = 'c';
+ goto chgeol;
+
+ case 's': /** substitute **/
+ save_v(vp);
+ cdelete(vp,vp->repeat, BAD);
+ if( mode )
+ {
+ c = 'p';
+ trepeat = 1;
+ goto addin;
+ }
+ first_virt = cur_virt + 1;
+ return(APPEND);
+
+ case 'Y': /** Yank to end of line **/
+ c = '$';
+ goto yankeol;
+
+ case 'y': /** yank thru motion **/
+ if( mode )
+ c = vp->lastmotion;
+ else
+ c = getcount(vp,ed_getchar(vp->ed,-1));
+yankeol:
+ vp->lastmotion = c;
+ if( c == 'y' )
+ {
+ gencpy(yankbuf, virtual);
+ }
+ else if(!delmotion(vp, c, 'y'))
+ {
+ return(BAD);
+ }
+ break;
+
+ case 'x': /** delete repeat chars forward - dl **/
+ c = 'l';
+ goto deleol;
+
+ case 'X': /** delete repeat chars backward - dh **/
+ c = 'h';
+ goto deleol;
+
+ case '~': /** invert case and advance **/
+ if( cur_virt != INVALID )
+ {
+ save_v(vp);
+ i = INVALID;
+ while(trepeat-->0 && i!=cur_virt)
+ {
+ i = cur_virt;
+ c = virtual[cur_virt];
+#if SHOPT_MULTIBYTE
+ if((c&~STRIP)==0)
+#endif /* SHOPT_MULTIBYTE */
+ if( isupper(c) )
+ c = tolower(c);
+ else if( islower(c) )
+ c = toupper(c);
+ replace(vp,c, 1);
+ }
+ return(GOOD);
+ }
+ else
+ return(BAD);
+
+ default:
+ return(BAD);
+ }
+ refresh(vp,CONTROL);
+ return(GOOD);
+}
+
+
+#if SHOPT_MULTIBYTE
+ static int _isalph(register int v)
+ {
+#ifdef _lib_iswalnum
+ return(iswalnum(v) || v=='_');
+#else
+ return((v&~STRIP) || isalnum(v) || v=='_');
+#endif
+ }
+
+
+ static int _isblank(register int v)
+ {
+ return((v&~STRIP)==0 && isspace(v));
+ }
+
+ static int _ismetach(register int v)
+ {
+ return((v&~STRIP)==0 && ismeta(v));
+ }
+
+#endif /* SHOPT_MULTIBYTE */
+
+/*
+ * get a character, after ^V processing
+ */
+static int getrchar(register Vi_t *vp)
+{
+ register int c;
+ if((c=ed_getchar(vp->ed,1))== usrlnext)
+ c = ed_getchar(vp->ed,2);
+ return(c);
+}