summaryrefslogtreecommitdiff
path: root/usr/src/lib/libshell/common/edit/vi.c
diff options
context:
space:
mode:
authorchin <none@none>2007-08-17 12:01:52 -0700
committerchin <none@none>2007-08-17 12:01:52 -0700
commitda2e3ebdc1edfbc5028edf1354e7dd2fa69a7968 (patch)
tree5280d3b78e289fe9551371ab6e7f15ef9944ea14 /usr/src/lib/libshell/common/edit/vi.c
parent073dbf9103ef2a2b05d8a16e2d26db04e0374b0e (diff)
downloadillumos-gate-da2e3ebdc1edfbc5028edf1354e7dd2fa69a7968.tar.gz
6437624 RFE: Add ksh93 (as /usr/bin/ksh93) and libshell.so to OS/Net
6505835 AST tools and library (libpp) required for creating l10n messages for ksh93 PSARC/2006/550 Korn Shell 93 Integration PSARC/2006/587 /etc/ksh.kshrc for ksh93 PSARC/2007/035 ksh93 Amendments Contributed by Roland Mainz <roland.mainz@nrubsig.org> --HG-- rename : usr/src/lib/libcmd/common/mapfile-vers => deleted_files/usr/src/lib/libcmd/common/mapfile-vers rename : usr/src/lib/libcmd/common/placeholder.c => deleted_files/usr/src/lib/libcmd/common/placeholder.c
Diffstat (limited to 'usr/src/lib/libshell/common/edit/vi.c')
-rw-r--r--usr/src/lib/libshell/common/edit/vi.c2635
1 files changed, 2635 insertions, 0 deletions
diff --git a/usr/src/lib/libshell/common/edit/vi.c b/usr/src/lib/libshell/common/edit/vi.c
new file mode 100644
index 0000000000..5d6fa8e56f
--- /dev/null
+++ b/usr/src/lib/libshell/common/edit/vi.c
@@ -0,0 +1,2635 @@
+/***********************************************************************
+* *
+* This software is part of the ast package *
+* Copyright (c) 1982-2007 AT&T Knowledge Ventures *
+* and is licensed under the *
+* Common Public License, Version 1.0 *
+* by AT&T Knowledge Ventures *
+* *
+* A copy of the License is available at *
+* http://www.opensource.org/licenses/cpl1.0.txt *
+* (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
+* *
+* Information and Software Systems Research *
+* AT&T Research *
+* Florham Park NJ *
+* *
+* David Korn <dgk@research.att.com> *
+* *
+***********************************************************************/
+#pragma prototyped
+/* 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"
+#endif /* KSHELL */
+#include <ctype.h>
+#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; /* 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) || 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(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 )
+ {
+ 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)
+ 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 */
+ 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)
+ 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 **/
+ 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( 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;
+ 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 **/
+ return(ENTER);
+
+ 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;
+
+ 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 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);
+ return;
+
+ case '\t': /** command completion **/
+ if(mode!=SEARCH && last_virt>=0 && (vp->ed->e_tabcount|| !isblank(cur_virt)) && vp->ed->sh->nextprompt)
+ {
+ 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':
+ 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 regb;
+ register int first_w = vp->first_wind;
+ int p_differ;
+ int new_lw;
+ int ncur_phys;
+ int opflag; /* search optimize flag */
+
+# define w regb
+# define v regb
+
+ /*** 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( 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;
+ }
+
+ 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( 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(sh.hist_ptr,((char*)virtual)+1, curhline, 1, new_direction);
+ }
+ cur_virt = i;
+ strncpy(lsearch, ((char*)virtual)+1, SEARCHSIZE);
+ 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 !KSHELL
+ if(p==0)
+ {
+ ed_ringbell();
+ break;
+ }
+#endif /* KSHELL */
+#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);
+}