1*4887Schin /***********************************************************************
2*4887Schin *                                                                      *
3*4887Schin *               This software is part of the ast package               *
4*4887Schin *           Copyright (c) 1982-2007 AT&T Knowledge Ventures            *
5*4887Schin *                      and is licensed under the                       *
6*4887Schin *                  Common Public License, Version 1.0                  *
7*4887Schin *                      by AT&T Knowledge Ventures                      *
8*4887Schin *                                                                      *
9*4887Schin *                A copy of the License is available at                 *
10*4887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
11*4887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12*4887Schin *                                                                      *
13*4887Schin *              Information and Software Systems Research               *
14*4887Schin *                            AT&T Research                             *
15*4887Schin *                           Florham Park NJ                            *
16*4887Schin *                                                                      *
17*4887Schin *                  David Korn <dgk@research.att.com>                   *
18*4887Schin *                                                                      *
19*4887Schin ***********************************************************************/
20*4887Schin #pragma prototyped
21*4887Schin /* Adapted for ksh by David Korn */
22*4887Schin /*+	VI.C			P.D. Sullivan
23*4887Schin  *
24*4887Schin  *	One line editor for the shell based on the vi editor.
25*4887Schin  *
26*4887Schin  *	Questions to:
27*4887Schin  *		P.D. Sullivan
28*4887Schin  *		cbosgd!pds
29*4887Schin -*/
30*4887Schin 
31*4887Schin 
32*4887Schin #if KSHELL
33*4887Schin #   include	"defs.h"
34*4887Schin #else
35*4887Schin #   include	<ast.h>
36*4887Schin #   include	"FEATURE/options"
37*4887Schin #endif	/* KSHELL */
38*4887Schin #include	<ctype.h>
39*4887Schin #include	"io.h"
40*4887Schin 
41*4887Schin #include	"history.h"
42*4887Schin #include	"edit.h"
43*4887Schin #include	"terminal.h"
44*4887Schin #include	"FEATURE/time"
45*4887Schin 
46*4887Schin #if SHOPT_OLDTERMIO
47*4887Schin #   undef ECHOCTL
48*4887Schin #   define echoctl	(vp->ed->e_echoctl)
49*4887Schin #else
50*4887Schin #   ifdef ECHOCTL
51*4887Schin #	define echoctl	ECHOCTL
52*4887Schin #   else
53*4887Schin #	define echoctl	0
54*4887Schin #   endif /* ECHOCTL */
55*4887Schin #endif /*SHOPT_OLDTERMIO */
56*4887Schin 
57*4887Schin #ifndef FIORDCHK
58*4887Schin #   define NTICKS	5		/* number of ticks for typeahead */
59*4887Schin #endif /* FIORDCHK */
60*4887Schin 
61*4887Schin #define	MAXCHAR	MAXLINE-2		/* max char per line */
62*4887Schin 
63*4887Schin #if SHOPT_MULTIBYTE
64*4887Schin #   include	"lexstates.h"
65*4887Schin #   define gencpy(a,b)	ed_gencpy(a,b)
66*4887Schin #   define genncpy(a,b,n)	ed_genncpy(a,b,n)
67*4887Schin #   define genlen(str)	ed_genlen(str)
68*4887Schin #   define digit(c)	((c&~STRIP)==0 && isdigit(c))
69*4887Schin #   define is_print(c)	((c&~STRIP) || isprint(c))
70*4887Schin #   if !_lib_iswprint && !defined(iswprint)
71*4887Schin #	define iswprint(c)	((c&~0177) || isprint(c))
72*4887Schin #   endif
73*4887Schin     static int _isalph(int);
74*4887Schin     static int _ismetach(int);
75*4887Schin     static int _isblank(int);
76*4887Schin #   undef  isblank
77*4887Schin #   define isblank(v)	_isblank(virtual[v])
78*4887Schin #   define isalph(v)	_isalph(virtual[v])
79*4887Schin #   define ismetach(v)	_ismetach(virtual[v])
80*4887Schin #else
81*4887Schin     static genchar	_c;
82*4887Schin #   define gencpy(a,b)	strcpy((char*)(a),(char*)(b))
83*4887Schin #   define genncpy(a,b,n) strncpy((char*)(a),(char*)(b),n)
84*4887Schin #   define genlen(str)	strlen(str)
85*4887Schin #   define isalph(v)	((_c=virtual[v])=='_'||isalnum(_c))
86*4887Schin #   undef  isblank
87*4887Schin #   define isblank(v)	isspace(virtual[v])
88*4887Schin #   define ismetach(v)	ismeta(virtual[v])
89*4887Schin #   define digit(c)	isdigit(c)
90*4887Schin #   define is_print(c)	isprint(c)
91*4887Schin #endif	/* SHOPT_MULTIBYTE */
92*4887Schin 
93*4887Schin #if ( 'a' == 97) /* ASCII? */
94*4887Schin #   define fold(c)	((c)&~040)	/* lower and uppercase equivalent */
95*4887Schin #else
96*4887Schin #   define fold(c)	((c)|0100)	/* lower and uppercase equivalent */
97*4887Schin #endif
98*4887Schin 
99*4887Schin #ifndef iswascii
100*4887Schin #define iswascii(c)	(!((c)&(~0177)))
101*4887Schin #endif
102*4887Schin 
103*4887Schin typedef struct _vi_
104*4887Schin {
105*4887Schin 	int direction;
106*4887Schin 	int lastmacro;
107*4887Schin 	char addnl;		/* boolean - add newline flag */
108*4887Schin 	char last_find;		/* last find command */
109*4887Schin 	char last_cmd;		/* last command */
110*4887Schin 	char repeat_set;
111*4887Schin 	char nonewline;
112*4887Schin 	int findchar;		/* last find char */
113*4887Schin 	genchar *lastline;
114*4887Schin 	int first_wind;		/* first column of window */
115*4887Schin 	int last_wind;		/* last column in window */
116*4887Schin 	int lastmotion;		/* last motion */
117*4887Schin 	int long_char; 		/* line bigger than window */
118*4887Schin 	int long_line;		/* line bigger than window */
119*4887Schin 	int ocur_phys;		/* old current physical position */
120*4887Schin 	int ocur_virt;		/* old last virtual position */
121*4887Schin 	int ofirst_wind;	/* old window first col */
122*4887Schin 	int o_v_char;		/* prev virtual[ocur_virt] */
123*4887Schin 	int repeat;		/* repeat count for motion cmds */
124*4887Schin 	int lastrepeat;		/* last repeat count for motion cmds */
125*4887Schin 	int u_column;		/* undo current column */
126*4887Schin 	int U_saved;		/* original virtual saved */
127*4887Schin 	genchar *U_space;	/* used for U command */
128*4887Schin 	genchar *u_space;	/* used for u command */
129*4887Schin #ifdef FIORDCHK
130*4887Schin 	clock_t typeahead;	/* typeahead occurred */
131*4887Schin #else
132*4887Schin 	int typeahead;		/* typeahead occurred */
133*4887Schin #endif	/* FIORDCHK */
134*4887Schin #if SHOPT_MULTIBYTE
135*4887Schin 	int bigvi;
136*4887Schin #endif
137*4887Schin 	Edit_t	*ed;		/* pointer to edit data */
138*4887Schin } Vi_t;
139*4887Schin 
140*4887Schin #define editb	(*vp->ed)
141*4887Schin 
142*4887Schin #undef putchar
143*4887Schin #define putchar(c)	ed_putchar(vp->ed,c)
144*4887Schin 
145*4887Schin #define crallowed	editb.e_crlf
146*4887Schin #define cur_virt	editb.e_cur		/* current virtual column */
147*4887Schin #define cur_phys	editb.e_pcur	/* current phys column cursor is at */
148*4887Schin #define curhline	editb.e_hline		/* current history line */
149*4887Schin #define first_virt	editb.e_fcol		/* first allowable column */
150*4887Schin #define	globals		editb.e_globals		/* local global variables */
151*4887Schin #define histmin		editb.e_hismin
152*4887Schin #define histmax		editb.e_hismax
153*4887Schin #define last_phys	editb.e_peol		/* last column in physical */
154*4887Schin #define last_virt	editb.e_eol		/* last column */
155*4887Schin #define lsearch		editb.e_search		/* last search string */
156*4887Schin #define lookahead	editb.e_lookahead	/* characters in buffer */
157*4887Schin #define previous	editb.e_lbuf		/* lookahead buffer */
158*4887Schin #define max_col		editb.e_llimit		/* maximum column */
159*4887Schin #define Prompt		editb.e_prompt		/* pointer to prompt */
160*4887Schin #define plen		editb.e_plen		/* length of prompt */
161*4887Schin #define physical	editb.e_physbuf		/* physical image */
162*4887Schin #define usreof		editb.e_eof		/* user defined eof char */
163*4887Schin #define usrerase	editb.e_erase		/* user defined erase char */
164*4887Schin #define usrlnext	editb.e_lnext		/* user defined next literal */
165*4887Schin #define usrkill		editb.e_kill		/* user defined kill char */
166*4887Schin #define virtual		editb.e_inbuf	/* pointer to virtual image buffer */
167*4887Schin #define	window		editb.e_window		/* window buffer */
168*4887Schin #define	w_size		editb.e_wsize		/* window size */
169*4887Schin #define	inmacro		editb.e_inmacro		/* true when in macro */
170*4887Schin #define yankbuf		editb.e_killbuf		/* yank/delete buffer */
171*4887Schin 
172*4887Schin 
173*4887Schin #define	ABORT	-2			/* user abort */
174*4887Schin #define	APPEND	-10			/* append chars */
175*4887Schin #define	BAD	-1			/* failure flag */
176*4887Schin #define	BIGVI	-15			/* user wants real vi */
177*4887Schin #define	CONTROL	-20			/* control mode */
178*4887Schin #define	ENTER	-25			/* enter flag */
179*4887Schin #define	GOOD	0			/* success flag */
180*4887Schin #define	INPUT	-30			/* input mode */
181*4887Schin #define	INSERT	-35			/* insert mode */
182*4887Schin #define	REPLACE	-40			/* replace chars */
183*4887Schin #define	SEARCH	-45			/* search flag */
184*4887Schin #define	TRANSLATE	-50		/* translate virt to phys only */
185*4887Schin 
186*4887Schin #define	INVALID	(-1)			/* invalid column */
187*4887Schin 
188*4887Schin static const char paren_chars[] = "([{)]}";   /* for % command */
189*4887Schin 
190*4887Schin static void	cursor(Vi_t*, int);
191*4887Schin static void	del_line(Vi_t*,int);
192*4887Schin static int	getcount(Vi_t*,int);
193*4887Schin static void	getline(Vi_t*,int);
194*4887Schin static int	getrchar(Vi_t*);
195*4887Schin static int	mvcursor(Vi_t*,int);
196*4887Schin static void	pr_string(Vi_t*,const char*);
197*4887Schin static void	putstring(Vi_t*,int, int);
198*4887Schin static void	refresh(Vi_t*,int);
199*4887Schin static void	replace(Vi_t*,int, int);
200*4887Schin static void	restore_v(Vi_t*);
201*4887Schin static void	save_last(Vi_t*);
202*4887Schin static void	save_v(Vi_t*);
203*4887Schin static int	search(Vi_t*,int);
204*4887Schin static void	sync_cursor(Vi_t*);
205*4887Schin static int	textmod(Vi_t*,int,int);
206*4887Schin 
207*4887Schin /*+	VI_READ( fd, shbuf, nchar )
208*4887Schin  *
209*4887Schin  *	This routine implements a one line version of vi and is
210*4887Schin  * called by _filbuf.c
211*4887Schin  *
212*4887Schin -*/
213*4887Schin 
214*4887Schin /*
215*4887Schin  * if reedit is non-zero, initialize edit buffer with reedit chars
216*4887Schin  */
217*4887Schin int ed_viread(void *context, int fd, register char *shbuf, int nchar, int reedit)
218*4887Schin {
219*4887Schin 	Edit_t *ed = (Edit_t*)context;
220*4887Schin 	register int i;			/* general variable */
221*4887Schin 	register int term_char;		/* read() termination character */
222*4887Schin 	register Vi_t *vp = ed->e_vi;
223*4887Schin 	char prompt[PRSIZE+2];		/* prompt */
224*4887Schin 	genchar Physical[2*MAXLINE];	/* physical image */
225*4887Schin 	genchar Ubuf[MAXLINE];	/* used for U command */
226*4887Schin 	genchar ubuf[MAXLINE];	/* used for u command */
227*4887Schin 	genchar Window[MAXLINE];	/* window image */
228*4887Schin 	int Globals[9];			/* local global variables */
229*4887Schin 	int esc_or_hang=0;		/* <ESC> or hangup */
230*4887Schin 	char cntl_char=0;		/* TRUE if control character present */
231*4887Schin #if SHOPT_RAWONLY
232*4887Schin #   define viraw	1
233*4887Schin #else
234*4887Schin 	int viraw = (sh_isoption(SH_VIRAW) || sh.st.trap[SH_KEYTRAP]);
235*4887Schin #   ifndef FIORDCHK
236*4887Schin 	clock_t oldtime, newtime;
237*4887Schin 	struct tms dummy;
238*4887Schin #   endif /* FIORDCHK */
239*4887Schin #endif /* SHOPT_RAWONLY */
240*4887Schin 	if(!vp)
241*4887Schin 	{
242*4887Schin 		ed->e_vi = vp =  newof(0,Vi_t,1,0);
243*4887Schin 		vp->lastline = (genchar*)malloc(MAXLINE*CHARSIZE);
244*4887Schin 		vp->direction = -1;
245*4887Schin 		vp->ed = ed;
246*4887Schin 	}
247*4887Schin 
248*4887Schin 	/*** setup prompt ***/
249*4887Schin 
250*4887Schin 	Prompt = prompt;
251*4887Schin 	ed_setup(vp->ed,fd, reedit);
252*4887Schin 	shbuf[reedit] = 0;
253*4887Schin 
254*4887Schin #if !SHOPT_RAWONLY
255*4887Schin 	if(!viraw)
256*4887Schin 	{
257*4887Schin 		/*** Change the eol characters to '\r' and eof  ***/
258*4887Schin 		/* in addition to '\n' and make eof an ESC	*/
259*4887Schin 		if(tty_alt(ERRIO) < 0)
260*4887Schin 			return(reexit?reedit:ed_read(context, fd, shbuf, nchar,0));
261*4887Schin 
262*4887Schin #ifdef FIORDCHK
263*4887Schin 		ioctl(fd,FIORDCHK,&vp->typeahead);
264*4887Schin #else
265*4887Schin 		/* time the current line to determine typeahead */
266*4887Schin 		oldtime = times(&dummy);
267*4887Schin #endif /* FIORDCHK */
268*4887Schin #if KSHELL
269*4887Schin 		/* abort of interrupt has occurred */
270*4887Schin 		if(sh.trapnote&SH_SIGSET)
271*4887Schin 			i = -1;
272*4887Schin 		else
273*4887Schin #endif /* KSHELL */
274*4887Schin 		/*** Read the line ***/
275*4887Schin 		i = ed_read(context, fd, shbuf, nchar, 0);
276*4887Schin #ifndef FIORDCHK
277*4887Schin 		newtime = times(&dummy);
278*4887Schin 		vp->typeahead = ((newtime-oldtime) < NTICKS);
279*4887Schin #endif /* FIORDCHK */
280*4887Schin 	    if(echoctl)
281*4887Schin 	    {
282*4887Schin 		if( i <= 0 )
283*4887Schin 		{
284*4887Schin 			/*** read error or eof typed ***/
285*4887Schin 			tty_cooked(ERRIO);
286*4887Schin 			return(i);
287*4887Schin 		}
288*4887Schin 		term_char = shbuf[--i];
289*4887Schin 		if( term_char == '\r' )
290*4887Schin 			term_char = '\n';
291*4887Schin 		if( term_char=='\n' || term_char==ESC )
292*4887Schin 			shbuf[i--] = '\0';
293*4887Schin 		else
294*4887Schin 			shbuf[i+1] = '\0';
295*4887Schin 	    }
296*4887Schin 	    else
297*4887Schin 	    {
298*4887Schin 		register int c = shbuf[0];
299*4887Schin 
300*4887Schin 		/*** Save and remove the last character if its an eol, ***/
301*4887Schin 		/* changing '\r' to '\n' */
302*4887Schin 
303*4887Schin 		if( i == 0 )
304*4887Schin 		{
305*4887Schin 			/*** ESC was typed as first char of line ***/
306*4887Schin 			esc_or_hang = 1;
307*4887Schin 			term_char = ESC;
308*4887Schin 			shbuf[i--] = '\0';	/* null terminate line */
309*4887Schin 		}
310*4887Schin 		else if( i<0 || c==usreof )
311*4887Schin 		{
312*4887Schin 			/*** read error or eof typed ***/
313*4887Schin 			tty_cooked(ERRIO);
314*4887Schin 			if( c == usreof )
315*4887Schin 				i = 0;
316*4887Schin 			return(i);
317*4887Schin 		}
318*4887Schin 		else
319*4887Schin 		{
320*4887Schin 			term_char = shbuf[--i];
321*4887Schin 			if( term_char == '\r' )
322*4887Schin 				term_char = '\n';
323*4887Schin #if !defined(VEOL2) && !defined(ECHOCTL)
324*4887Schin 			if(term_char=='\n')
325*4887Schin 			{
326*4887Schin 				tty_cooked(ERRIO);
327*4887Schin 				return(i+1);
328*4887Schin 			}
329*4887Schin #endif
330*4887Schin 			if( term_char=='\n' || term_char==usreof )
331*4887Schin 			{
332*4887Schin 				/*** remove terminator & null terminate ***/
333*4887Schin 				shbuf[i--] = '\0';
334*4887Schin 			}
335*4887Schin 			else
336*4887Schin 			{
337*4887Schin 				/** terminator was ESC, which is not xmitted **/
338*4887Schin 				term_char = ESC;
339*4887Schin 				shbuf[i+1] = '\0';
340*4887Schin 			}
341*4887Schin 		}
342*4887Schin 	    }
343*4887Schin 	}
344*4887Schin 	else
345*4887Schin #endif /* SHOPT_RAWONLY */
346*4887Schin 	{
347*4887Schin 		/*** Set raw mode ***/
348*4887Schin 
349*4887Schin #if !SHOPT_RAWONLY
350*4887Schin 		if( editb.e_ttyspeed == 0 )
351*4887Schin 		{
352*4887Schin 			/*** never did TCGETA, so do it ***/
353*4887Schin 			/* avoids problem if user does 'sh -o viraw' */
354*4887Schin 			tty_alt(ERRIO);
355*4887Schin 		}
356*4887Schin #endif /* SHOPT_RAWONLY */
357*4887Schin 		if(tty_raw(ERRIO,0) < 0 )
358*4887Schin 			return(reedit?reedit:ed_read(context, fd, shbuf, nchar,0));
359*4887Schin 		i = last_virt-1;
360*4887Schin 	}
361*4887Schin 
362*4887Schin 	/*** Initialize some things ***/
363*4887Schin 
364*4887Schin 	virtual = (genchar*)shbuf;
365*4887Schin #if SHOPT_MULTIBYTE
366*4887Schin 	virtual = (genchar*)roundof((char*)virtual-(char*)0,sizeof(genchar));
367*4887Schin 	shbuf[i+1] = 0;
368*4887Schin 	i = ed_internal(shbuf,virtual)-1;
369*4887Schin #endif /* SHOPT_MULTIBYTE */
370*4887Schin 	globals = Globals;
371*4887Schin 	cur_phys = i + 1;
372*4887Schin 	cur_virt = i;
373*4887Schin 	first_virt = 0;
374*4887Schin 	vp->first_wind = 0;
375*4887Schin 	last_virt = i;
376*4887Schin 	last_phys = i;
377*4887Schin 	vp->last_wind = i;
378*4887Schin 	vp->long_line = ' ';
379*4887Schin 	vp->long_char = ' ';
380*4887Schin 	vp->o_v_char = '\0';
381*4887Schin 	vp->ocur_phys = 0;
382*4887Schin 	vp->ocur_virt = MAXCHAR;
383*4887Schin 	vp->ofirst_wind = 0;
384*4887Schin 	physical = Physical;
385*4887Schin 	vp->u_column = INVALID - 1;
386*4887Schin 	vp->U_space = Ubuf;
387*4887Schin 	vp->u_space = ubuf;
388*4887Schin 	window = Window;
389*4887Schin 	window[0] = '\0';
390*4887Schin 
391*4887Schin 	if(!yankbuf)
392*4887Schin 		yankbuf = (genchar*)malloc(MAXLINE*CHARSIZE);
393*4887Schin 	if( vp->last_cmd == '\0' )
394*4887Schin 	{
395*4887Schin 		/*** first time for this shell ***/
396*4887Schin 
397*4887Schin 		vp->last_cmd = 'i';
398*4887Schin 		vp->findchar = INVALID;
399*4887Schin 		vp->lastmotion = '\0';
400*4887Schin 		vp->lastrepeat = 1;
401*4887Schin 		vp->repeat = 1;
402*4887Schin 		*yankbuf = 0;
403*4887Schin 	}
404*4887Schin 
405*4887Schin 	/*** fiddle around with prompt length ***/
406*4887Schin 	if( nchar+plen > MAXCHAR )
407*4887Schin 		nchar = MAXCHAR - plen;
408*4887Schin 	max_col = nchar - 2;
409*4887Schin 
410*4887Schin 	if( !viraw )
411*4887Schin 	{
412*4887Schin 		int kill_erase = 0;
413*4887Schin 		for(i=(echoctl?last_virt:0); i<last_virt; ++i )
414*4887Schin 		{
415*4887Schin 			/*** change \r to \n, check for control characters, ***/
416*4887Schin 			/* delete appropriate ^Vs,			*/
417*4887Schin 			/* and estimate last physical column */
418*4887Schin 
419*4887Schin 			if( virtual[i] == '\r' )
420*4887Schin 				virtual[i] = '\n';
421*4887Schin 		    if(!echoctl)
422*4887Schin 		    {
423*4887Schin 			register int c = virtual[i];
424*4887Schin 			if( c<=usrerase)
425*4887Schin 			{
426*4887Schin 				/*** user typed escaped erase or kill char ***/
427*4887Schin 				cntl_char = 1;
428*4887Schin 				if(is_print(c))
429*4887Schin 					kill_erase++;
430*4887Schin 			}
431*4887Schin 			else if( !is_print(c) )
432*4887Schin 			{
433*4887Schin 				cntl_char = 1;
434*4887Schin 
435*4887Schin 				if( c == usrlnext )
436*4887Schin 				{
437*4887Schin 					if( i == last_virt )
438*4887Schin 					{
439*4887Schin 						/*** eol/eof was escaped ***/
440*4887Schin 						/* so replace ^V with it */
441*4887Schin 						virtual[i] = term_char;
442*4887Schin 						break;
443*4887Schin 					}
444*4887Schin 
445*4887Schin 					/*** delete ^V ***/
446*4887Schin 					gencpy((&virtual[i]), (&virtual[i+1]));
447*4887Schin 					--cur_virt;
448*4887Schin 					--last_virt;
449*4887Schin 				}
450*4887Schin 			}
451*4887Schin 		    }
452*4887Schin 		}
453*4887Schin 
454*4887Schin 		/*** copy virtual image to window ***/
455*4887Schin 		if(last_virt > 0)
456*4887Schin 			last_phys = ed_virt_to_phys(vp->ed,virtual,physical,last_virt,0,0);
457*4887Schin 		if( last_phys >= w_size )
458*4887Schin 		{
459*4887Schin 			/*** line longer than window ***/
460*4887Schin 			vp->last_wind = w_size - 1;
461*4887Schin 		}
462*4887Schin 		else
463*4887Schin 			vp->last_wind = last_phys;
464*4887Schin 		genncpy(window, virtual, vp->last_wind+1);
465*4887Schin 
466*4887Schin 		if( term_char!=ESC  && (last_virt==INVALID
467*4887Schin 			|| virtual[last_virt]!=term_char) )
468*4887Schin 		{
469*4887Schin 			/*** Line not terminated with ESC or escaped (^V) ***/
470*4887Schin 			/* eol, so return after doing a total update */
471*4887Schin 			/* if( (speed is greater or equal to 1200 */
472*4887Schin 			/* and something was typed) and */
473*4887Schin 			/* (control character present */
474*4887Schin 			/* or typeahead occurred) ) */
475*4887Schin 
476*4887Schin 			tty_cooked(ERRIO);
477*4887Schin 			if( editb.e_ttyspeed==FAST && last_virt!=INVALID
478*4887Schin 				&& (vp->typeahead || cntl_char) )
479*4887Schin 			{
480*4887Schin 				refresh(vp,TRANSLATE);
481*4887Schin 				pr_string(vp,Prompt);
482*4887Schin 				putstring(vp,0, last_phys+1);
483*4887Schin 				if(echoctl)
484*4887Schin 					ed_crlf(vp->ed);
485*4887Schin 				else
486*4887Schin 					while(kill_erase-- > 0)
487*4887Schin 						putchar(' ');
488*4887Schin 			}
489*4887Schin 
490*4887Schin 			if( term_char=='\n' )
491*4887Schin 			{
492*4887Schin 				if(!echoctl)
493*4887Schin 					ed_crlf(vp->ed);
494*4887Schin 				virtual[++last_virt] = '\n';
495*4887Schin 			}
496*4887Schin 			vp->last_cmd = 'i';
497*4887Schin 			save_last(vp);
498*4887Schin #if SHOPT_MULTIBYTE
499*4887Schin 			virtual[last_virt+1] = 0;
500*4887Schin 			last_virt = ed_external(virtual,shbuf);
501*4887Schin 			return(last_virt);
502*4887Schin #else
503*4887Schin 			return(++last_virt);
504*4887Schin #endif /* SHOPT_MULTIBYTE */
505*4887Schin 		}
506*4887Schin 
507*4887Schin 		/*** Line terminated with escape, or escaped eol/eof, ***/
508*4887Schin 		/*  so set raw mode */
509*4887Schin 
510*4887Schin 		if( tty_raw(ERRIO,0) < 0 )
511*4887Schin 		{
512*4887Schin 			tty_cooked(ERRIO);
513*4887Schin 			/*
514*4887Schin 			 * The following prevents drivers that return 0 on
515*4887Schin 			 * causing an infinite loop
516*4887Schin 			 */
517*4887Schin 			if(esc_or_hang)
518*4887Schin 				return(-1);
519*4887Schin 			virtual[++last_virt] = '\n';
520*4887Schin #if SHOPT_MULTIBYTE
521*4887Schin 			virtual[last_virt+1] = 0;
522*4887Schin 			last_virt = ed_external(virtual,shbuf);
523*4887Schin 			return(last_virt);
524*4887Schin #else
525*4887Schin 			return(++last_virt);
526*4887Schin #endif /* SHOPT_MULTIBYTE */
527*4887Schin 		}
528*4887Schin 
529*4887Schin 		if(echoctl) /*** for cntl-echo erase the ^[ ***/
530*4887Schin 			pr_string(vp,"\b\b\b\b      \b\b");
531*4887Schin 
532*4887Schin 
533*4887Schin 		if(crallowed)
534*4887Schin 		{
535*4887Schin 			/*** start over since there may be ***/
536*4887Schin 			/*** a control char, or cursor might not ***/
537*4887Schin 			/*** be at left margin (this lets us know ***/
538*4887Schin 			/*** where we are ***/
539*4887Schin 			cur_phys = 0;
540*4887Schin 			window[0] = '\0';
541*4887Schin 			pr_string(vp,Prompt);
542*4887Schin 			if( term_char==ESC && (last_virt<0 || virtual[last_virt]!=ESC))
543*4887Schin 				refresh(vp,CONTROL);
544*4887Schin 			else
545*4887Schin 				refresh(vp,INPUT);
546*4887Schin 		}
547*4887Schin 		else
548*4887Schin 		{
549*4887Schin 			/*** just update everything internally ***/
550*4887Schin 			refresh(vp,TRANSLATE);
551*4887Schin 		}
552*4887Schin 	}
553*4887Schin 
554*4887Schin 	/*** Handle usrintr, usrquit, or EOF ***/
555*4887Schin 
556*4887Schin 	i = sigsetjmp(editb.e_env,0);
557*4887Schin 	if( i != 0 )
558*4887Schin 	{
559*4887Schin 		virtual[0] = '\0';
560*4887Schin 		tty_cooked(ERRIO);
561*4887Schin 
562*4887Schin 		switch(i)
563*4887Schin 		{
564*4887Schin 		case UEOF:
565*4887Schin 			/*** EOF ***/
566*4887Schin 			return(0);
567*4887Schin 
568*4887Schin 		case UINTR:
569*4887Schin 			/** interrupt **/
570*4887Schin 			return(-1);
571*4887Schin 		}
572*4887Schin 		return(-1);
573*4887Schin 	}
574*4887Schin 
575*4887Schin 	/*** Get a line from the terminal ***/
576*4887Schin 
577*4887Schin 	vp->U_saved = 0;
578*4887Schin 	if(reedit)
579*4887Schin 		refresh(vp,INPUT);
580*4887Schin 	if(viraw)
581*4887Schin 		getline(vp,APPEND);
582*4887Schin 	else if(last_virt>=0 && virtual[last_virt]==term_char)
583*4887Schin 		getline(vp,APPEND);
584*4887Schin 	else
585*4887Schin 		getline(vp,ESC);
586*4887Schin 	if(vp->ed->e_multiline)
587*4887Schin 		cursor(vp, last_phys);
588*4887Schin 	/*** add a new line if user typed unescaped \n ***/
589*4887Schin 	/* to cause the shell to process the line */
590*4887Schin 	tty_cooked(ERRIO);
591*4887Schin 	if(ed->e_nlist)
592*4887Schin 	{
593*4887Schin 		ed->e_nlist = 0;
594*4887Schin 		stakset(ed->e_stkptr,ed->e_stkoff);
595*4887Schin 	}
596*4887Schin 	if( vp->addnl )
597*4887Schin 	{
598*4887Schin 		virtual[++last_virt] = '\n';
599*4887Schin 		ed_crlf(vp->ed);
600*4887Schin 	}
601*4887Schin 	if( ++last_virt >= 0 )
602*4887Schin 	{
603*4887Schin #if SHOPT_MULTIBYTE
604*4887Schin 		if(vp->bigvi)
605*4887Schin 		{
606*4887Schin 			vp->bigvi = 0;
607*4887Schin 			shbuf[last_virt-1] = '\n';
608*4887Schin 		}
609*4887Schin 		else
610*4887Schin 		{
611*4887Schin 			virtual[last_virt] = 0;
612*4887Schin 			last_virt = ed_external(virtual,shbuf);
613*4887Schin 		}
614*4887Schin #endif /* SHOPT_MULTIBYTE */
615*4887Schin 		return(last_virt);
616*4887Schin 	}
617*4887Schin 	else
618*4887Schin 		return(-1);
619*4887Schin }
620*4887Schin 
621*4887Schin 
622*4887Schin /*{	APPEND( char, mode )
623*4887Schin  *
624*4887Schin  *	This routine will append char after cur_virt in the virtual image.
625*4887Schin  * mode	=	APPEND, shift chars right before appending
626*4887Schin  *		REPLACE, replace char if possible
627*4887Schin  *
628*4887Schin }*/
629*4887Schin 
630*4887Schin static void append(Vi_t *vp,int c, int mode)
631*4887Schin {
632*4887Schin 	register int i,j;
633*4887Schin 
634*4887Schin 	if( last_virt<max_col && last_phys<max_col )
635*4887Schin 	{
636*4887Schin 		if( mode==APPEND || (cur_virt==last_virt  && last_virt>=0))
637*4887Schin 		{
638*4887Schin 			j = (cur_virt>=0?cur_virt:0);
639*4887Schin 			for(i = ++last_virt;  i > j; --i)
640*4887Schin 				virtual[i] = virtual[i-1];
641*4887Schin 		}
642*4887Schin 		virtual[++cur_virt] = c;
643*4887Schin 	}
644*4887Schin 	else
645*4887Schin 		ed_ringbell();
646*4887Schin 	return;
647*4887Schin }
648*4887Schin 
649*4887Schin /*{	BACKWORD( nwords, cmd )
650*4887Schin  *
651*4887Schin  *	This routine will position cur_virt at the nth previous word.
652*4887Schin  *
653*4887Schin }*/
654*4887Schin 
655*4887Schin static void backword(Vi_t *vp,int nwords, register int cmd)
656*4887Schin {
657*4887Schin 	register int tcur_virt = cur_virt;
658*4887Schin 	while( nwords-- && tcur_virt > first_virt )
659*4887Schin 	{
660*4887Schin 		if( !isblank(tcur_virt) && isblank(tcur_virt-1)
661*4887Schin 			&& tcur_virt>first_virt )
662*4887Schin 			--tcur_virt;
663*4887Schin 		else if(cmd != 'B')
664*4887Schin 		{
665*4887Schin 			register int last = isalph(tcur_virt-1);
666*4887Schin 			register int cur = isalph(tcur_virt);
667*4887Schin 			if((!cur && last) || (cur && !last))
668*4887Schin 				--tcur_virt;
669*4887Schin 		}
670*4887Schin 		while( isblank(tcur_virt) && tcur_virt>=first_virt )
671*4887Schin 			--tcur_virt;
672*4887Schin 		if( cmd == 'B' )
673*4887Schin 		{
674*4887Schin 			while( !isblank(tcur_virt) && tcur_virt>=first_virt )
675*4887Schin 				--tcur_virt;
676*4887Schin 		}
677*4887Schin 		else
678*4887Schin 		{
679*4887Schin 			if(isalph(tcur_virt))
680*4887Schin 				while( isalph(tcur_virt) && tcur_virt>=first_virt )
681*4887Schin 					--tcur_virt;
682*4887Schin 			else
683*4887Schin 				while( !isalph(tcur_virt) && !isblank(tcur_virt)
684*4887Schin 					&& tcur_virt>=first_virt )
685*4887Schin 					--tcur_virt;
686*4887Schin 		}
687*4887Schin 		cur_virt = ++tcur_virt;
688*4887Schin 	}
689*4887Schin 	return;
690*4887Schin }
691*4887Schin 
692*4887Schin /*{	CNTLMODE()
693*4887Schin  *
694*4887Schin  *	This routine implements the vi command subset.
695*4887Schin  *	The cursor will always be positioned at the char of interest.
696*4887Schin  *
697*4887Schin }*/
698*4887Schin 
699*4887Schin static int cntlmode(Vi_t *vp)
700*4887Schin {
701*4887Schin 	register int c;
702*4887Schin 	register int i;
703*4887Schin 	genchar tmp_u_space[MAXLINE];	/* temporary u_space */
704*4887Schin 	genchar *real_u_space;		/* points to real u_space */
705*4887Schin 	int tmp_u_column = INVALID;	/* temporary u_column */
706*4887Schin 	int was_inmacro;
707*4887Schin 
708*4887Schin 	if(!vp->U_saved)
709*4887Schin 	{
710*4887Schin 		/*** save virtual image if never done before ***/
711*4887Schin 		virtual[last_virt+1] = '\0';
712*4887Schin 		gencpy(vp->U_space, virtual);
713*4887Schin 		vp->U_saved = 1;
714*4887Schin 	}
715*4887Schin 
716*4887Schin 	save_last(vp);
717*4887Schin 
718*4887Schin 	real_u_space = vp->u_space;
719*4887Schin 	curhline = histmax;
720*4887Schin 	first_virt = 0;
721*4887Schin 	vp->repeat = 1;
722*4887Schin 	if( cur_virt > INVALID )
723*4887Schin 	{
724*4887Schin 		/*** make sure cursor is at the last char ***/
725*4887Schin 		sync_cursor(vp);
726*4887Schin 	}
727*4887Schin 
728*4887Schin 	/*** Read control char until something happens to cause a ***/
729*4887Schin 	/* return to APPEND/REPLACE mode	*/
730*4887Schin 
731*4887Schin 	while( c=ed_getchar(vp->ed,-1) )
732*4887Schin 	{
733*4887Schin 		vp->repeat_set = 0;
734*4887Schin 		was_inmacro = inmacro;
735*4887Schin 		if( c == '0' )
736*4887Schin 		{
737*4887Schin 			/*** move to leftmost column ***/
738*4887Schin 			cur_virt = 0;
739*4887Schin 			sync_cursor(vp);
740*4887Schin 			continue;
741*4887Schin 		}
742*4887Schin 
743*4887Schin 		if( digit(c) )
744*4887Schin 		{
745*4887Schin 			c = getcount(vp,c);
746*4887Schin 			if( c == '.' )
747*4887Schin 				vp->lastrepeat = vp->repeat;
748*4887Schin 		}
749*4887Schin 
750*4887Schin 		/*** see if it's a move cursor command ***/
751*4887Schin 
752*4887Schin 		if(mvcursor(vp,c))
753*4887Schin 		{
754*4887Schin 			sync_cursor(vp);
755*4887Schin 			vp->repeat = 1;
756*4887Schin 			continue;
757*4887Schin 		}
758*4887Schin 
759*4887Schin 		/*** see if it's a repeat of the last command ***/
760*4887Schin 
761*4887Schin 		if( c == '.' )
762*4887Schin 		{
763*4887Schin 			c = vp->last_cmd;
764*4887Schin 			vp->repeat = vp->lastrepeat;
765*4887Schin 			i = textmod(vp,c, c);
766*4887Schin 		}
767*4887Schin 		else
768*4887Schin 		{
769*4887Schin 			i = textmod(vp,c, 0);
770*4887Schin 		}
771*4887Schin 
772*4887Schin 		/*** see if it's a text modification command ***/
773*4887Schin 
774*4887Schin 		switch(i)
775*4887Schin 		{
776*4887Schin 		case BAD:
777*4887Schin 			break;
778*4887Schin 
779*4887Schin 		default:		/** input mode **/
780*4887Schin 			if(!was_inmacro)
781*4887Schin 			{
782*4887Schin 				vp->last_cmd = c;
783*4887Schin 				vp->lastrepeat = vp->repeat;
784*4887Schin 			}
785*4887Schin 			vp->repeat = 1;
786*4887Schin 			if( i == GOOD )
787*4887Schin 				continue;
788*4887Schin 			return(i);
789*4887Schin 		}
790*4887Schin 
791*4887Schin 		switch( c )
792*4887Schin 		{
793*4887Schin 			/***** Other stuff *****/
794*4887Schin 
795*4887Schin 		case cntl('L'):		/** Redraw line **/
796*4887Schin 			/*** print the prompt and ***/
797*4887Schin 			/* force a total refresh */
798*4887Schin 			if(vp->nonewline==0)
799*4887Schin 				putchar('\n');
800*4887Schin 			vp->nonewline = 0;
801*4887Schin 			pr_string(vp,Prompt);
802*4887Schin 			window[0] = '\0';
803*4887Schin 			cur_phys = vp->first_wind;
804*4887Schin 			vp->ofirst_wind = INVALID;
805*4887Schin 			vp->long_line = ' ';
806*4887Schin 			break;
807*4887Schin 
808*4887Schin 		case cntl('V'):
809*4887Schin 		{
810*4887Schin 			register const char *p = fmtident(e_version);
811*4887Schin 			save_v(vp);
812*4887Schin 			del_line(vp,BAD);
813*4887Schin 			while(c = *p++)
814*4887Schin 				append(vp,c,APPEND);
815*4887Schin 			refresh(vp,CONTROL);
816*4887Schin 			ed_getchar(vp->ed,-1);
817*4887Schin 			restore_v(vp);
818*4887Schin 			break;
819*4887Schin 		}
820*4887Schin 
821*4887Schin 		case '/':		/** Search **/
822*4887Schin 		case '?':
823*4887Schin 		case 'N':
824*4887Schin 		case 'n':
825*4887Schin 			save_v(vp);
826*4887Schin 			switch( search(vp,c) )
827*4887Schin 			{
828*4887Schin 			case GOOD:
829*4887Schin 				/*** force a total refresh ***/
830*4887Schin 				window[0] = '\0';
831*4887Schin 				goto newhist;
832*4887Schin 
833*4887Schin 			case BAD:
834*4887Schin 				/*** no match ***/
835*4887Schin 					ed_ringbell();
836*4887Schin 
837*4887Schin 			default:
838*4887Schin 				if( vp->u_column == INVALID )
839*4887Schin 					del_line(vp,BAD);
840*4887Schin 				else
841*4887Schin 					restore_v(vp);
842*4887Schin 				break;
843*4887Schin 			}
844*4887Schin 			break;
845*4887Schin 
846*4887Schin 		case 'j':		/** get next command **/
847*4887Schin 		case '+':		/** get next command **/
848*4887Schin 			curhline += vp->repeat;
849*4887Schin 			if( curhline > histmax )
850*4887Schin 			{
851*4887Schin 				curhline = histmax;
852*4887Schin 				goto ringbell;
853*4887Schin 			}
854*4887Schin 			else if(curhline==histmax && tmp_u_column!=INVALID )
855*4887Schin 			{
856*4887Schin 				vp->u_space = tmp_u_space;
857*4887Schin 				vp->u_column = tmp_u_column;
858*4887Schin 				restore_v(vp);
859*4887Schin 				vp->u_space = real_u_space;
860*4887Schin 				break;
861*4887Schin 			}
862*4887Schin 			save_v(vp);
863*4887Schin 			cur_virt = INVALID;
864*4887Schin 			goto newhist;
865*4887Schin 
866*4887Schin 		case 'k':		/** get previous command **/
867*4887Schin 		case '-':		/** get previous command **/
868*4887Schin 			if( curhline == histmax )
869*4887Schin 			{
870*4887Schin 				vp->u_space = tmp_u_space;
871*4887Schin 				i = vp->u_column;
872*4887Schin 				save_v(vp);
873*4887Schin 				vp->u_space = real_u_space;
874*4887Schin 				tmp_u_column = vp->u_column;
875*4887Schin 				vp->u_column = i;
876*4887Schin 			}
877*4887Schin 
878*4887Schin 			curhline -= vp->repeat;
879*4887Schin 			if( curhline <= histmin )
880*4887Schin 			{
881*4887Schin 				curhline += vp->repeat;
882*4887Schin 				goto ringbell;
883*4887Schin 			}
884*4887Schin 			save_v(vp);
885*4887Schin 			cur_virt = INVALID;
886*4887Schin 		newhist:
887*4887Schin 			if(curhline!=histmax || cur_virt==INVALID)
888*4887Schin 				hist_copy((char*)virtual, MAXLINE, curhline,-1);
889*4887Schin 			else
890*4887Schin 			{
891*4887Schin 				strcpy((char*)virtual,(char*)vp->u_space);
892*4887Schin #if SHOPT_MULTIBYTE
893*4887Schin 				ed_internal((char*)vp->u_space,vp->u_space);
894*4887Schin #endif /* SHOPT_MULTIBYTE */
895*4887Schin 			}
896*4887Schin #if SHOPT_MULTIBYTE
897*4887Schin 			ed_internal((char*)virtual,virtual);
898*4887Schin #endif /* SHOPT_MULTIBYTE */
899*4887Schin 			if((last_virt=genlen(virtual)-1) >= 0  && cur_virt == INVALID)
900*4887Schin 				cur_virt = 0;
901*4887Schin 			break;
902*4887Schin 
903*4887Schin 
904*4887Schin 		case 'u':		/** undo the last thing done **/
905*4887Schin 			restore_v(vp);
906*4887Schin 			break;
907*4887Schin 
908*4887Schin 		case 'U':		/** Undo everything **/
909*4887Schin 			save_v(vp);
910*4887Schin 			if( virtual[0] == '\0' )
911*4887Schin 				goto ringbell;
912*4887Schin 			else
913*4887Schin 			{
914*4887Schin 				gencpy(virtual, vp->U_space);
915*4887Schin 				last_virt = genlen(vp->U_space) - 1;
916*4887Schin 				cur_virt = 0;
917*4887Schin 			}
918*4887Schin 			break;
919*4887Schin 
920*4887Schin #if KSHELL
921*4887Schin 		case 'v':
922*4887Schin 			if(vp->repeat_set==0)
923*4887Schin 				goto vcommand;
924*4887Schin #endif /* KSHELL */
925*4887Schin 
926*4887Schin 		case 'G':		/** goto command repeat **/
927*4887Schin 			if(vp->repeat_set==0)
928*4887Schin 				vp->repeat = histmin+1;
929*4887Schin 			if( vp->repeat <= histmin || vp->repeat > histmax )
930*4887Schin 			{
931*4887Schin 				goto ringbell;
932*4887Schin 			}
933*4887Schin 			curhline = vp->repeat;
934*4887Schin 			save_v(vp);
935*4887Schin 			if(c == 'G')
936*4887Schin 			{
937*4887Schin 				cur_virt = INVALID;
938*4887Schin 				goto newhist;
939*4887Schin 			}
940*4887Schin 
941*4887Schin #if KSHELL
942*4887Schin 		vcommand:
943*4887Schin 			if(ed_fulledit(vp->ed)==GOOD)
944*4887Schin 				return(BIGVI);
945*4887Schin 			else
946*4887Schin 				goto ringbell;
947*4887Schin #endif	/* KSHELL */
948*4887Schin 
949*4887Schin 		case '#':	/** insert(delete) # to (no)comment command **/
950*4887Schin 			if( cur_virt != INVALID )
951*4887Schin 			{
952*4887Schin 				register genchar *p = &virtual[last_virt+1];
953*4887Schin 				*p = 0;
954*4887Schin 				/*** see whether first char is comment char ***/
955*4887Schin 				c = (virtual[0]=='#');
956*4887Schin 				while(p-- >= virtual)
957*4887Schin 				{
958*4887Schin 					if(*p=='\n' || p<virtual)
959*4887Schin 					{
960*4887Schin 						if(c) /* delete '#' */
961*4887Schin 						{
962*4887Schin 							if(p[1]=='#')
963*4887Schin 							{
964*4887Schin 								last_virt--;
965*4887Schin 								gencpy(p+1,p+2);
966*4887Schin 							}
967*4887Schin 						}
968*4887Schin 						else
969*4887Schin 						{
970*4887Schin 							cur_virt = p-virtual;
971*4887Schin 							append(vp,'#', APPEND);
972*4887Schin 						}
973*4887Schin 					}
974*4887Schin 				}
975*4887Schin 				if(c)
976*4887Schin 				{
977*4887Schin 					curhline = histmax;
978*4887Schin 					cur_virt = 0;
979*4887Schin 					break;
980*4887Schin 				}
981*4887Schin 				refresh(vp,INPUT);
982*4887Schin 			}
983*4887Schin 
984*4887Schin 		case '\n':		/** send to shell **/
985*4887Schin 			return(ENTER);
986*4887Schin 
987*4887Schin 	        case ESC:
988*4887Schin 			/* don't ring bell if next char is '[' */
989*4887Schin 			if(!lookahead)
990*4887Schin 			{
991*4887Schin 				char x;
992*4887Schin 				if(sfpkrd(editb.e_fd,&x,1,'\r',400L,-1)>0)
993*4887Schin 					ed_ungetchar(vp->ed,x);
994*4887Schin 			}
995*4887Schin 			if(lookahead)
996*4887Schin 			{
997*4887Schin 				ed_ungetchar(vp->ed,c=ed_getchar(vp->ed,1));
998*4887Schin 				if(c=='[')
999*4887Schin 				{
1000*4887Schin 					vp->repeat = 1;
1001*4887Schin 					continue;
1002*4887Schin 				}
1003*4887Schin 			}
1004*4887Schin 		default:
1005*4887Schin 		ringbell:
1006*4887Schin 			ed_ringbell();
1007*4887Schin 			vp->repeat = 1;
1008*4887Schin 			continue;
1009*4887Schin 		}
1010*4887Schin 
1011*4887Schin 		refresh(vp,CONTROL);
1012*4887Schin 		vp->repeat = 1;
1013*4887Schin 	}
1014*4887Schin /* NOTREACHED */
1015*4887Schin 	return(0);
1016*4887Schin }
1017*4887Schin 
1018*4887Schin /*{	CURSOR( new_current_physical )
1019*4887Schin  *
1020*4887Schin  *	This routine will position the virtual cursor at
1021*4887Schin  * physical column x in the window.
1022*4887Schin  *
1023*4887Schin }*/
1024*4887Schin 
1025*4887Schin static void cursor(Vi_t *vp,register int x)
1026*4887Schin {
1027*4887Schin #if SHOPT_MULTIBYTE
1028*4887Schin 	while(physical[x]==MARKER)
1029*4887Schin 		x++;
1030*4887Schin #endif /* SHOPT_MULTIBYTE */
1031*4887Schin 	cur_phys = ed_setcursor(vp->ed, physical, cur_phys,x,vp->first_wind);
1032*4887Schin }
1033*4887Schin 
1034*4887Schin /*{	DELETE( nchars, mode )
1035*4887Schin  *
1036*4887Schin  *	Delete nchars from the virtual space and leave cur_virt positioned
1037*4887Schin  * at cur_virt-1.
1038*4887Schin  *
1039*4887Schin  *	If mode	= 'c', do not save the characters deleted
1040*4887Schin  *		= 'd', save them in yankbuf and delete.
1041*4887Schin  *		= 'y', save them in yankbuf but do not delete.
1042*4887Schin  *
1043*4887Schin }*/
1044*4887Schin 
1045*4887Schin static void cdelete(Vi_t *vp,register int nchars, int mode)
1046*4887Schin {
1047*4887Schin 	register int i;
1048*4887Schin 	register genchar *cp;
1049*4887Schin 
1050*4887Schin 	if( cur_virt < first_virt )
1051*4887Schin 	{
1052*4887Schin 		ed_ringbell();
1053*4887Schin 		return;
1054*4887Schin 	}
1055*4887Schin 	if( nchars > 0 )
1056*4887Schin 	{
1057*4887Schin 		cp = virtual+cur_virt;
1058*4887Schin 		vp->o_v_char = cp[0];
1059*4887Schin 		if( (cur_virt-- + nchars) > last_virt )
1060*4887Schin 		{
1061*4887Schin 			/*** set nchars to number actually deleted ***/
1062*4887Schin 			nchars = last_virt - cur_virt;
1063*4887Schin 		}
1064*4887Schin 
1065*4887Schin 		/*** save characters to be deleted ***/
1066*4887Schin 
1067*4887Schin 		if( mode != 'c' )
1068*4887Schin 		{
1069*4887Schin 			i = cp[nchars];
1070*4887Schin 			cp[nchars] = 0;
1071*4887Schin 			gencpy(yankbuf,cp);
1072*4887Schin 			cp[nchars] = i;
1073*4887Schin 		}
1074*4887Schin 
1075*4887Schin 		/*** now delete these characters ***/
1076*4887Schin 
1077*4887Schin 		if( mode != 'y' )
1078*4887Schin 		{
1079*4887Schin 			gencpy(cp,cp+nchars);
1080*4887Schin 			last_virt -= nchars;
1081*4887Schin 		}
1082*4887Schin 	}
1083*4887Schin 	return;
1084*4887Schin }
1085*4887Schin 
1086*4887Schin /*{	DEL_LINE( mode )
1087*4887Schin  *
1088*4887Schin  *	This routine will delete the line.
1089*4887Schin  *	mode = GOOD, do a save_v()
1090*4887Schin  *
1091*4887Schin }*/
1092*4887Schin static void del_line(register Vi_t *vp, int mode)
1093*4887Schin {
1094*4887Schin 	if( last_virt == INVALID )
1095*4887Schin 		return;
1096*4887Schin 
1097*4887Schin 	if( mode == GOOD )
1098*4887Schin 		save_v(vp);
1099*4887Schin 
1100*4887Schin 	cur_virt = 0;
1101*4887Schin 	first_virt = 0;
1102*4887Schin 	cdelete(vp,last_virt+1, BAD);
1103*4887Schin 	refresh(vp,CONTROL);
1104*4887Schin 
1105*4887Schin 	cur_virt = INVALID;
1106*4887Schin 	cur_phys = 0;
1107*4887Schin 	vp->findchar = INVALID;
1108*4887Schin 	last_phys = INVALID;
1109*4887Schin 	last_virt = INVALID;
1110*4887Schin 	vp->last_wind = INVALID;
1111*4887Schin 	vp->first_wind = 0;
1112*4887Schin 	vp->o_v_char = '\0';
1113*4887Schin 	vp->ocur_phys = 0;
1114*4887Schin 	vp->ocur_virt = MAXCHAR;
1115*4887Schin 	vp->ofirst_wind = 0;
1116*4887Schin 	window[0] = '\0';
1117*4887Schin 	return;
1118*4887Schin }
1119*4887Schin 
1120*4887Schin /*{	DELMOTION( motion, mode )
1121*4887Schin  *
1122*4887Schin  *	Delete thru motion.
1123*4887Schin  *
1124*4887Schin  *	mode	= 'd', save deleted characters, delete
1125*4887Schin  *		= 'c', do not save characters, change
1126*4887Schin  *		= 'y', save characters, yank
1127*4887Schin  *
1128*4887Schin  *	Returns 1 if operation successful; else 0.
1129*4887Schin  *
1130*4887Schin }*/
1131*4887Schin 
1132*4887Schin static int delmotion(Vi_t *vp,int motion, int mode)
1133*4887Schin {
1134*4887Schin 	register int begin, end, delta;
1135*4887Schin 	/* the following saves a register */
1136*4887Schin 
1137*4887Schin 	if( cur_virt == INVALID )
1138*4887Schin 		return(0);
1139*4887Schin 	if( mode != 'y' )
1140*4887Schin 		save_v(vp);
1141*4887Schin 	begin = cur_virt;
1142*4887Schin 
1143*4887Schin 	/*** fake out the motion routines by appending a blank ***/
1144*4887Schin 
1145*4887Schin 	virtual[++last_virt] = ' ';
1146*4887Schin 	end = mvcursor(vp,motion);
1147*4887Schin 	virtual[last_virt--] = 0;
1148*4887Schin 	if(!end)
1149*4887Schin 		return(0);
1150*4887Schin 
1151*4887Schin 	end = cur_virt;
1152*4887Schin 	if( mode=='c' && end>begin && strchr("wW", motion) )
1153*4887Schin 	{
1154*4887Schin 		/*** called by change operation, user really expects ***/
1155*4887Schin 		/* the effect of the eE commands, so back up to end of word */
1156*4887Schin 		while( end>begin && isblank(end-1) )
1157*4887Schin 			--end;
1158*4887Schin 		if( end == begin )
1159*4887Schin 			++end;
1160*4887Schin 	}
1161*4887Schin 
1162*4887Schin 	delta = end - begin;
1163*4887Schin 	if( delta >= 0 )
1164*4887Schin 	{
1165*4887Schin 		cur_virt = begin;
1166*4887Schin 		if( strchr("eE;,TtFf%", motion) )
1167*4887Schin 			++delta;
1168*4887Schin 	}
1169*4887Schin 	else
1170*4887Schin 	{
1171*4887Schin 		delta = -delta + (motion=='%');
1172*4887Schin 	}
1173*4887Schin 
1174*4887Schin 	cdelete(vp,delta, mode);
1175*4887Schin 	if( mode == 'y' )
1176*4887Schin 		cur_virt = begin;
1177*4887Schin 	return(1);
1178*4887Schin }
1179*4887Schin 
1180*4887Schin 
1181*4887Schin /*{	ENDWORD( nwords, cmd )
1182*4887Schin  *
1183*4887Schin  *	This routine will move cur_virt to the end of the nth word.
1184*4887Schin  *
1185*4887Schin }*/
1186*4887Schin 
1187*4887Schin static void endword(Vi_t *vp, int nwords, register int cmd)
1188*4887Schin {
1189*4887Schin 	register int tcur_virt = cur_virt;
1190*4887Schin 	while( nwords-- )
1191*4887Schin 	{
1192*4887Schin 		if( !isblank(tcur_virt) && tcur_virt<=last_virt )
1193*4887Schin 			++tcur_virt;
1194*4887Schin 		while( isblank(tcur_virt) && tcur_virt<=last_virt )
1195*4887Schin 			++tcur_virt;
1196*4887Schin 		if( cmd == 'E' )
1197*4887Schin 		{
1198*4887Schin 			while( !isblank(tcur_virt) && tcur_virt<=last_virt )
1199*4887Schin 				++tcur_virt;
1200*4887Schin 		}
1201*4887Schin 		else
1202*4887Schin 		{
1203*4887Schin 			if( isalph(tcur_virt) )
1204*4887Schin 				while( isalph(tcur_virt) && tcur_virt<=last_virt )
1205*4887Schin 					++tcur_virt;
1206*4887Schin 			else
1207*4887Schin 				while( !isalph(tcur_virt) && !isblank(tcur_virt)
1208*4887Schin 					&& tcur_virt<=last_virt )
1209*4887Schin 					++tcur_virt;
1210*4887Schin 		}
1211*4887Schin 		if( tcur_virt > first_virt )
1212*4887Schin 			tcur_virt--;
1213*4887Schin 	}
1214*4887Schin 	cur_virt = tcur_virt;
1215*4887Schin 	return;
1216*4887Schin }
1217*4887Schin 
1218*4887Schin /*{	FORWARD( nwords, cmd )
1219*4887Schin  *
1220*4887Schin  *	This routine will move cur_virt forward to the next nth word.
1221*4887Schin  *
1222*4887Schin }*/
1223*4887Schin 
1224*4887Schin static void forward(Vi_t *vp,register int nwords, int cmd)
1225*4887Schin {
1226*4887Schin 	register int tcur_virt = cur_virt;
1227*4887Schin 	while( nwords-- )
1228*4887Schin 	{
1229*4887Schin 		if( cmd == 'W' )
1230*4887Schin 		{
1231*4887Schin 			while( !isblank(tcur_virt) && tcur_virt < last_virt )
1232*4887Schin 				++tcur_virt;
1233*4887Schin 		}
1234*4887Schin 		else
1235*4887Schin 		{
1236*4887Schin 			if( isalph(tcur_virt) )
1237*4887Schin 			{
1238*4887Schin 				while( isalph(tcur_virt) && tcur_virt<last_virt )
1239*4887Schin 					++tcur_virt;
1240*4887Schin 			}
1241*4887Schin 			else
1242*4887Schin 			{
1243*4887Schin 				while( !isalph(tcur_virt) && !isblank(tcur_virt)
1244*4887Schin 					&& tcur_virt < last_virt )
1245*4887Schin 					++tcur_virt;
1246*4887Schin 			}
1247*4887Schin 		}
1248*4887Schin 		while( isblank(tcur_virt) && tcur_virt < last_virt )
1249*4887Schin 			++tcur_virt;
1250*4887Schin 	}
1251*4887Schin 	cur_virt = tcur_virt;
1252*4887Schin 	return;
1253*4887Schin }
1254*4887Schin 
1255*4887Schin 
1256*4887Schin 
1257*4887Schin /*{	GETCOUNT(c)
1258*4887Schin  *
1259*4887Schin  *	Set repeat to the user typed number and return the terminating
1260*4887Schin  * character.
1261*4887Schin  *
1262*4887Schin }*/
1263*4887Schin 
1264*4887Schin static int getcount(register Vi_t *vp,register int c)
1265*4887Schin {
1266*4887Schin 	register int i;
1267*4887Schin 
1268*4887Schin 	/*** get any repeat count ***/
1269*4887Schin 
1270*4887Schin 	if( c == '0' )
1271*4887Schin 		return(c);
1272*4887Schin 
1273*4887Schin 	vp->repeat_set++;
1274*4887Schin 	i = 0;
1275*4887Schin 	while( digit(c) )
1276*4887Schin 	{
1277*4887Schin 		i = i*10 + c - '0';
1278*4887Schin 		c = ed_getchar(vp->ed,-1);
1279*4887Schin 	}
1280*4887Schin 
1281*4887Schin 	if( i > 0 )
1282*4887Schin 		vp->repeat *= i;
1283*4887Schin 	return(c);
1284*4887Schin }
1285*4887Schin 
1286*4887Schin 
1287*4887Schin /*{	GETLINE( mode )
1288*4887Schin  *
1289*4887Schin  *	This routine will fetch a line.
1290*4887Schin  *	mode	= APPEND, allow escape to cntlmode subroutine
1291*4887Schin  *		  appending characters.
1292*4887Schin  *		= REPLACE, allow escape to cntlmode subroutine
1293*4887Schin  *		  replacing characters.
1294*4887Schin  *		= SEARCH, no escape allowed
1295*4887Schin  *		= ESC, enter control mode immediately
1296*4887Schin  *
1297*4887Schin  *	The cursor will always be positioned after the last
1298*4887Schin  * char printed.
1299*4887Schin  *
1300*4887Schin  *	This routine returns when cr, nl, or (eof in column 0) is
1301*4887Schin  * received (column 0 is the first char position).
1302*4887Schin  *
1303*4887Schin }*/
1304*4887Schin 
1305*4887Schin static void getline(register Vi_t* vp,register int mode)
1306*4887Schin {
1307*4887Schin 	register int c;
1308*4887Schin 	register int tmp;
1309*4887Schin 	int	max_virt=0, last_save=0;
1310*4887Schin 	genchar saveline[MAXLINE];
1311*4887Schin 
1312*4887Schin 	vp->addnl = 1;
1313*4887Schin 
1314*4887Schin 	if( mode == ESC )
1315*4887Schin 	{
1316*4887Schin 		/*** go directly to control mode ***/
1317*4887Schin 		goto escape;
1318*4887Schin 	}
1319*4887Schin 
1320*4887Schin 	for(;;)
1321*4887Schin 	{
1322*4887Schin 		if( (c=ed_getchar(vp->ed,mode==SEARCH?1:-2)) == usreof )
1323*4887Schin 			c = UEOF;
1324*4887Schin 		else if( c == usrerase )
1325*4887Schin 			c = UERASE;
1326*4887Schin 		else if( c == usrkill )
1327*4887Schin 			c = UKILL;
1328*4887Schin 		else if( c == editb.e_werase )
1329*4887Schin 			c = UWERASE;
1330*4887Schin 		else if( c == usrlnext )
1331*4887Schin 			c = ULNEXT;
1332*4887Schin 
1333*4887Schin 		if( c == ULNEXT)
1334*4887Schin 		{
1335*4887Schin 			/*** implement ^V to escape next char ***/
1336*4887Schin 			c = ed_getchar(vp->ed,2);
1337*4887Schin 			append(vp,c, mode);
1338*4887Schin 			refresh(vp,INPUT);
1339*4887Schin 			continue;
1340*4887Schin 		}
1341*4887Schin 
1342*4887Schin 		switch( c )
1343*4887Schin 		{
1344*4887Schin 		case ESC:		/** enter control mode **/
1345*4887Schin 			if(!sh_isoption(SH_VI))
1346*4887Schin 			{
1347*4887Schin 				append(vp,c, mode);
1348*4887Schin 				break;
1349*4887Schin 			}
1350*4887Schin 			if( mode == SEARCH )
1351*4887Schin 			{
1352*4887Schin 				ed_ringbell();
1353*4887Schin 				continue;
1354*4887Schin 			}
1355*4887Schin 			else
1356*4887Schin 			{
1357*4887Schin 	escape:
1358*4887Schin 				if( mode == REPLACE )
1359*4887Schin 				{
1360*4887Schin 					c = max_virt-cur_virt;
1361*4887Schin 					if(c > 0 && last_save>=cur_virt)
1362*4887Schin 					{
1363*4887Schin 						genncpy((&virtual[cur_virt]),&saveline[cur_virt],c);
1364*4887Schin 						if(last_virt>=last_save)
1365*4887Schin 							last_virt=last_save-1;
1366*4887Schin 						refresh(vp,INPUT);
1367*4887Schin 					}
1368*4887Schin 					--cur_virt;
1369*4887Schin 				}
1370*4887Schin 				tmp = cntlmode(vp);
1371*4887Schin 				if( tmp == ENTER || tmp == BIGVI )
1372*4887Schin 				{
1373*4887Schin #if SHOPT_MULTIBYTE
1374*4887Schin 					vp->bigvi = (tmp==BIGVI);
1375*4887Schin #endif /* SHOPT_MULTIBYTE */
1376*4887Schin 					return;
1377*4887Schin 				}
1378*4887Schin 				if( tmp == INSERT )
1379*4887Schin 				{
1380*4887Schin 					mode = APPEND;
1381*4887Schin 					continue;
1382*4887Schin 				}
1383*4887Schin 				mode = tmp;
1384*4887Schin 				if(mode==REPLACE)
1385*4887Schin 				{
1386*4887Schin 					c = last_save = last_virt+1;
1387*4887Schin 					if(c >= MAXLINE)
1388*4887Schin 						c = MAXLINE-1;
1389*4887Schin 					genncpy(saveline, virtual, c);
1390*4887Schin 				}
1391*4887Schin 			}
1392*4887Schin 			break;
1393*4887Schin 
1394*4887Schin 		case UERASE:		/** user erase char **/
1395*4887Schin 				/*** treat as backspace ***/
1396*4887Schin 
1397*4887Schin 		case '\b':		/** backspace **/
1398*4887Schin 			if( virtual[cur_virt] == '\\' )
1399*4887Schin 			{
1400*4887Schin 				cdelete(vp,1, BAD);
1401*4887Schin 				append(vp,usrerase, mode);
1402*4887Schin 			}
1403*4887Schin 			else
1404*4887Schin 			{
1405*4887Schin 				if( mode==SEARCH && cur_virt==0 )
1406*4887Schin 				{
1407*4887Schin 					first_virt = 0;
1408*4887Schin 					cdelete(vp,1, BAD);
1409*4887Schin 					return;
1410*4887Schin 				}
1411*4887Schin 				if(mode==REPLACE || (last_save>0 && last_virt<=last_save))
1412*4887Schin 				{
1413*4887Schin 					if(cur_virt<=first_virt)
1414*4887Schin 						ed_ringbell();
1415*4887Schin 					else if(mode==REPLACE)
1416*4887Schin 						--cur_virt;
1417*4887Schin 					mode = REPLACE;
1418*4887Schin 					sync_cursor(vp);
1419*4887Schin 					continue;
1420*4887Schin 				}
1421*4887Schin 				else
1422*4887Schin 					cdelete(vp,1, BAD);
1423*4887Schin 			}
1424*4887Schin 			break;
1425*4887Schin 
1426*4887Schin 		case UWERASE:		/** delete back word **/
1427*4887Schin 			if( cur_virt > first_virt &&
1428*4887Schin 				!isblank(cur_virt) &&
1429*4887Schin 				!ispunct(virtual[cur_virt]) &&
1430*4887Schin 				isblank(cur_virt-1) )
1431*4887Schin 			{
1432*4887Schin 				cdelete(vp,1, BAD);
1433*4887Schin 			}
1434*4887Schin 			else
1435*4887Schin 			{
1436*4887Schin 				tmp = cur_virt;
1437*4887Schin 				backword(vp,1, 'W');
1438*4887Schin 				cdelete(vp,tmp - cur_virt + 1, BAD);
1439*4887Schin 			}
1440*4887Schin 			break;
1441*4887Schin 
1442*4887Schin 		case UKILL:		/** user kill line char **/
1443*4887Schin 			if( virtual[cur_virt] == '\\' )
1444*4887Schin 			{
1445*4887Schin 				cdelete(vp,1, BAD);
1446*4887Schin 				append(vp,usrkill, mode);
1447*4887Schin 			}
1448*4887Schin 			else
1449*4887Schin 			{
1450*4887Schin 				if( mode == SEARCH )
1451*4887Schin 				{
1452*4887Schin 					cur_virt = 1;
1453*4887Schin 					delmotion(vp, '$', BAD);
1454*4887Schin 				}
1455*4887Schin 				else if(first_virt)
1456*4887Schin 				{
1457*4887Schin 					tmp = cur_virt;
1458*4887Schin 					cur_virt = first_virt;
1459*4887Schin 					cdelete(vp,tmp - cur_virt + 1, BAD);
1460*4887Schin 				}
1461*4887Schin 				else
1462*4887Schin 					del_line(vp,GOOD);
1463*4887Schin 			}
1464*4887Schin 			break;
1465*4887Schin 
1466*4887Schin 		case UEOF:		/** eof char **/
1467*4887Schin 			if( cur_virt != INVALID )
1468*4887Schin 				continue;
1469*4887Schin 			vp->addnl = 0;
1470*4887Schin 
1471*4887Schin 		case '\n':		/** newline or return **/
1472*4887Schin 			if( mode != SEARCH )
1473*4887Schin 				save_last(vp);
1474*4887Schin 			refresh(vp,INPUT);
1475*4887Schin 			return;
1476*4887Schin 
1477*4887Schin 		case '\t':		/** command completion **/
1478*4887Schin 			if(mode!=SEARCH && last_virt>=0 && (vp->ed->e_tabcount|| !isblank(cur_virt)) && vp->ed->sh->nextprompt)
1479*4887Schin 			{
1480*4887Schin 				if(vp->ed->e_tabcount==0)
1481*4887Schin 				{
1482*4887Schin 					ed_ungetchar(vp->ed,'\\');
1483*4887Schin 					vp->ed->e_tabcount=1;
1484*4887Schin 					goto escape;
1485*4887Schin 				}
1486*4887Schin 				else if(vp->ed->e_tabcount==1)
1487*4887Schin 				{
1488*4887Schin 					ed_ungetchar(vp->ed,'=');
1489*4887Schin 					goto escape;
1490*4887Schin 				}
1491*4887Schin 				vp->ed->e_tabcount = 0;
1492*4887Schin 			}
1493*4887Schin 			/* FALL THRU*/
1494*4887Schin 		default:
1495*4887Schin 			if( mode == REPLACE )
1496*4887Schin 			{
1497*4887Schin 				if( cur_virt < last_virt )
1498*4887Schin 				{
1499*4887Schin 					replace(vp,c, 1);
1500*4887Schin 					if(cur_virt>max_virt)
1501*4887Schin 						max_virt = cur_virt;
1502*4887Schin 					continue;
1503*4887Schin 				}
1504*4887Schin 				cdelete(vp,1, BAD);
1505*4887Schin 				mode = APPEND;
1506*4887Schin 				max_virt = last_virt+3;
1507*4887Schin 			}
1508*4887Schin 			append(vp,c, mode);
1509*4887Schin 			break;
1510*4887Schin 		}
1511*4887Schin 		refresh(vp,INPUT);
1512*4887Schin 
1513*4887Schin 	}
1514*4887Schin }
1515*4887Schin 
1516*4887Schin /*{	MVCURSOR( motion )
1517*4887Schin  *
1518*4887Schin  *	This routine will move the virtual cursor according to motion
1519*4887Schin  * for repeat times.
1520*4887Schin  *
1521*4887Schin  * It returns GOOD if successful; else BAD.
1522*4887Schin  *
1523*4887Schin }*/
1524*4887Schin 
1525*4887Schin static int mvcursor(register Vi_t* vp,register int motion)
1526*4887Schin {
1527*4887Schin 	register int count;
1528*4887Schin 	register int tcur_virt;
1529*4887Schin 	register int incr = -1;
1530*4887Schin 	register int bound = 0;
1531*4887Schin 
1532*4887Schin 	switch(motion)
1533*4887Schin 	{
1534*4887Schin 		/***** Cursor move commands *****/
1535*4887Schin 
1536*4887Schin 	case '0':		/** First column **/
1537*4887Schin 		tcur_virt = 0;
1538*4887Schin 		break;
1539*4887Schin 
1540*4887Schin 	case '^':		/** First nonblank character **/
1541*4887Schin 		tcur_virt = first_virt;
1542*4887Schin 		while( isblank(tcur_virt) && tcur_virt < last_virt )
1543*4887Schin 			++tcur_virt;
1544*4887Schin 		break;
1545*4887Schin 
1546*4887Schin 	case '|':
1547*4887Schin 		tcur_virt = vp->repeat-1;
1548*4887Schin 		if(tcur_virt <= last_virt)
1549*4887Schin 			break;
1550*4887Schin 		/* fall through */
1551*4887Schin 
1552*4887Schin 	case '$':		/** End of line **/
1553*4887Schin 		tcur_virt = last_virt;
1554*4887Schin 		break;
1555*4887Schin 
1556*4887Schin 	case '[':
1557*4887Schin 		switch(motion=getcount(vp,ed_getchar(vp->ed,-1)))
1558*4887Schin 		{
1559*4887Schin 		    case 'A':
1560*4887Schin 			ed_ungetchar(vp->ed,'k');
1561*4887Schin 			return(1);
1562*4887Schin 		    case 'B':
1563*4887Schin 			ed_ungetchar(vp->ed,'j');
1564*4887Schin 			return(1);
1565*4887Schin 		    case 'C':
1566*4887Schin 			motion = last_virt;
1567*4887Schin 			incr = 1;
1568*4887Schin 			goto walk;
1569*4887Schin 		    case 'D':
1570*4887Schin 			motion = first_virt;
1571*4887Schin 			goto walk;
1572*4887Schin 		    case 'H':
1573*4887Schin 			tcur_virt = 0;
1574*4887Schin 			break;
1575*4887Schin 		    case 'Y':
1576*4887Schin 			tcur_virt = last_virt;
1577*4887Schin 			break;
1578*4887Schin 		    default:
1579*4887Schin 			ed_ungetchar(vp->ed,motion);
1580*4887Schin 			return(0);
1581*4887Schin 		}
1582*4887Schin 		break;
1583*4887Schin 
1584*4887Schin 	case 'h':		/** Left one **/
1585*4887Schin 	case '\b':
1586*4887Schin 		motion = first_virt;
1587*4887Schin 		goto walk;
1588*4887Schin 
1589*4887Schin 	case ' ':
1590*4887Schin 	case 'l':		/** Right one **/
1591*4887Schin 		motion = last_virt;
1592*4887Schin 		incr = 1;
1593*4887Schin 	walk:
1594*4887Schin 		tcur_virt = cur_virt;
1595*4887Schin 		if( incr*tcur_virt < motion)
1596*4887Schin 		{
1597*4887Schin 			tcur_virt += vp->repeat*incr;
1598*4887Schin 			if( incr*tcur_virt > motion)
1599*4887Schin 				tcur_virt = motion;
1600*4887Schin 		}
1601*4887Schin 		else
1602*4887Schin 			return(0);
1603*4887Schin 		break;
1604*4887Schin 
1605*4887Schin 	case 'B':
1606*4887Schin 	case 'b':		/** back word **/
1607*4887Schin 		tcur_virt = cur_virt;
1608*4887Schin 		backword(vp,vp->repeat, motion);
1609*4887Schin 		if( cur_virt == tcur_virt )
1610*4887Schin 			return(0);
1611*4887Schin 		return(1);
1612*4887Schin 
1613*4887Schin 	case 'E':
1614*4887Schin 	case 'e':		/** end of word **/
1615*4887Schin 		tcur_virt = cur_virt;
1616*4887Schin 		if(tcur_virt >=0)
1617*4887Schin 			endword(vp, vp->repeat, motion);
1618*4887Schin 		if( cur_virt == tcur_virt )
1619*4887Schin 			return(0);
1620*4887Schin 		return(1);
1621*4887Schin 
1622*4887Schin 	case ',':		/** reverse find old char **/
1623*4887Schin 	case ';':		/** find old char **/
1624*4887Schin 		switch(vp->last_find)
1625*4887Schin 		{
1626*4887Schin 		case 't':
1627*4887Schin 		case 'f':
1628*4887Schin 			if(motion==';')
1629*4887Schin 			{
1630*4887Schin 				bound = last_virt;
1631*4887Schin 				incr = 1;
1632*4887Schin 			}
1633*4887Schin 			goto find_b;
1634*4887Schin 
1635*4887Schin 		case 'T':
1636*4887Schin 		case 'F':
1637*4887Schin 			if(motion==',')
1638*4887Schin 			{
1639*4887Schin 				bound = last_virt;
1640*4887Schin 				incr = 1;
1641*4887Schin 			}
1642*4887Schin 			goto find_b;
1643*4887Schin 
1644*4887Schin 		default:
1645*4887Schin 			return(0);
1646*4887Schin 		}
1647*4887Schin 
1648*4887Schin 
1649*4887Schin 	case 't':		/** find up to new char forward **/
1650*4887Schin 	case 'f':		/** find new char forward **/
1651*4887Schin 		bound = last_virt;
1652*4887Schin 		incr = 1;
1653*4887Schin 
1654*4887Schin 	case 'T':		/** find up to new char backward **/
1655*4887Schin 	case 'F':		/** find new char backward **/
1656*4887Schin 		vp->last_find = motion;
1657*4887Schin 		if((vp->findchar=getrchar(vp))==ESC)
1658*4887Schin 			return(1);
1659*4887Schin find_b:
1660*4887Schin 		tcur_virt = cur_virt;
1661*4887Schin 		count = vp->repeat;
1662*4887Schin 		while( count-- )
1663*4887Schin 		{
1664*4887Schin 			while( incr*(tcur_virt+=incr) <= bound
1665*4887Schin 				&& virtual[tcur_virt] != vp->findchar );
1666*4887Schin 			if( incr*tcur_virt > bound )
1667*4887Schin 			{
1668*4887Schin 				return(0);
1669*4887Schin 			}
1670*4887Schin 		}
1671*4887Schin 		if( fold(vp->last_find) == 'T' )
1672*4887Schin 			tcur_virt -= incr;
1673*4887Schin 		break;
1674*4887Schin 
1675*4887Schin         case '%':
1676*4887Schin 	{
1677*4887Schin 		int nextmotion;
1678*4887Schin 		int nextc;
1679*4887Schin 		tcur_virt = cur_virt;
1680*4887Schin 		while( tcur_virt <= last_virt
1681*4887Schin 			&& strchr(paren_chars,virtual[tcur_virt])==(char*)0)
1682*4887Schin 				tcur_virt++;
1683*4887Schin 		if(tcur_virt > last_virt )
1684*4887Schin 			return(0);
1685*4887Schin 		nextc = virtual[tcur_virt];
1686*4887Schin 		count = strchr(paren_chars,nextc)-paren_chars;
1687*4887Schin 		if(count < 3)
1688*4887Schin 		{
1689*4887Schin 			incr = 1;
1690*4887Schin 			bound = last_virt;
1691*4887Schin 			nextmotion = paren_chars[count+3];
1692*4887Schin 		}
1693*4887Schin 		else
1694*4887Schin 			nextmotion = paren_chars[count-3];
1695*4887Schin 		count = 1;
1696*4887Schin 		while(count >0 &&  incr*(tcur_virt+=incr) <= bound)
1697*4887Schin 		{
1698*4887Schin 		        if(virtual[tcur_virt] == nextmotion)
1699*4887Schin 		        	count--;
1700*4887Schin 		        else if(virtual[tcur_virt]==nextc)
1701*4887Schin 		        	count++;
1702*4887Schin 		}
1703*4887Schin 		if(count)
1704*4887Schin 			return(0);
1705*4887Schin 		break;
1706*4887Schin 	}
1707*4887Schin 
1708*4887Schin 	case 'W':
1709*4887Schin 	case 'w':		/** forward word **/
1710*4887Schin 		tcur_virt = cur_virt;
1711*4887Schin 		forward(vp,vp->repeat, motion);
1712*4887Schin 		if( tcur_virt == cur_virt )
1713*4887Schin 			return(0);
1714*4887Schin 		return(1);
1715*4887Schin 
1716*4887Schin 	default:
1717*4887Schin 		return(0);
1718*4887Schin 	}
1719*4887Schin 	cur_virt = tcur_virt;
1720*4887Schin 
1721*4887Schin 	return(1);
1722*4887Schin }
1723*4887Schin 
1724*4887Schin /*
1725*4887Schin  * print a string
1726*4887Schin  */
1727*4887Schin 
1728*4887Schin static void pr_string(register Vi_t *vp, register const char *sp)
1729*4887Schin {
1730*4887Schin 	/*** copy string sp ***/
1731*4887Schin 	register char *ptr = editb.e_outptr;
1732*4887Schin 	while(*sp)
1733*4887Schin 		*ptr++ = *sp++;
1734*4887Schin 	editb.e_outptr = ptr;
1735*4887Schin 	return;
1736*4887Schin }
1737*4887Schin 
1738*4887Schin /*{	PUTSTRING( column, nchars )
1739*4887Schin  *
1740*4887Schin  *	Put nchars starting at column of physical into the workspace
1741*4887Schin  * to be printed.
1742*4887Schin  *
1743*4887Schin }*/
1744*4887Schin 
1745*4887Schin static void putstring(register Vi_t *vp,register int col, register int nchars)
1746*4887Schin {
1747*4887Schin 	while( nchars-- )
1748*4887Schin 		putchar(physical[col++]);
1749*4887Schin 	return;
1750*4887Schin }
1751*4887Schin 
1752*4887Schin /*{	REFRESH( mode )
1753*4887Schin  *
1754*4887Schin  *	This routine will refresh the crt so the physical image matches
1755*4887Schin  * the virtual image and display the proper window.
1756*4887Schin  *
1757*4887Schin  *	mode	= CONTROL, refresh in control mode, ie. leave cursor
1758*4887Schin  *			positioned at last char printed.
1759*4887Schin  *		= INPUT, refresh in input mode; leave cursor positioned
1760*4887Schin  *			after last char printed.
1761*4887Schin  *		= TRANSLATE, perform virtual to physical translation
1762*4887Schin  *			and adjust left margin only.
1763*4887Schin  *
1764*4887Schin  *		+-------------------------------+
1765*4887Schin  *		|   | |    virtual	  | |   |
1766*4887Schin  *		+-------------------------------+
1767*4887Schin  *		  cur_virt		last_virt
1768*4887Schin  *
1769*4887Schin  *		+-----------------------------------------------+
1770*4887Schin  *		|	  | |	        physical	 | |    |
1771*4887Schin  *		+-----------------------------------------------+
1772*4887Schin  *			cur_phys			last_phys
1773*4887Schin  *
1774*4887Schin  *				0			w_size - 1
1775*4887Schin  *				+-----------------------+
1776*4887Schin  *				| | |  window		|
1777*4887Schin  *				+-----------------------+
1778*4887Schin  *				cur_window = cur_phys - first_wind
1779*4887Schin }*/
1780*4887Schin 
1781*4887Schin static void refresh(register Vi_t* vp, int mode)
1782*4887Schin {
1783*4887Schin 	register int p;
1784*4887Schin 	register int regb;
1785*4887Schin 	register int first_w = vp->first_wind;
1786*4887Schin 	int p_differ;
1787*4887Schin 	int new_lw;
1788*4887Schin 	int ncur_phys;
1789*4887Schin 	int opflag;			/* search optimize flag */
1790*4887Schin 
1791*4887Schin #	define	w	regb
1792*4887Schin #	define	v	regb
1793*4887Schin 
1794*4887Schin 	/*** find out if it's necessary to start translating at beginning ***/
1795*4887Schin 
1796*4887Schin 	if(lookahead>0)
1797*4887Schin 	{
1798*4887Schin 		p = previous[lookahead-1];
1799*4887Schin 		if(p != ESC && p != '\n' && p != '\r')
1800*4887Schin 			mode = TRANSLATE;
1801*4887Schin 	}
1802*4887Schin 	v = cur_virt;
1803*4887Schin 	if( v<vp->ocur_virt || vp->ocur_virt==INVALID
1804*4887Schin 		|| ( v==vp->ocur_virt
1805*4887Schin 			&& (!is_print(virtual[v]) || !is_print(vp->o_v_char))) )
1806*4887Schin 	{
1807*4887Schin 		opflag = 0;
1808*4887Schin 		p = 0;
1809*4887Schin 		v = 0;
1810*4887Schin 	}
1811*4887Schin 	else
1812*4887Schin 	{
1813*4887Schin 		opflag = 1;
1814*4887Schin 		p = vp->ocur_phys;
1815*4887Schin 		v = vp->ocur_virt;
1816*4887Schin 		if( !is_print(virtual[v]) )
1817*4887Schin 		{
1818*4887Schin 			/*** avoid double ^'s ***/
1819*4887Schin 			++p;
1820*4887Schin 			++v;
1821*4887Schin 		}
1822*4887Schin 	}
1823*4887Schin 	virtual[last_virt+1] = 0;
1824*4887Schin 	ncur_phys = ed_virt_to_phys(vp->ed,virtual,physical,cur_virt,v,p);
1825*4887Schin 	p = genlen(physical);
1826*4887Schin 	if( --p < 0 )
1827*4887Schin 		last_phys = 0;
1828*4887Schin 	else
1829*4887Schin 		last_phys = p;
1830*4887Schin 
1831*4887Schin 	/*** see if this was a translate only ***/
1832*4887Schin 
1833*4887Schin 	if( mode == TRANSLATE )
1834*4887Schin 		return;
1835*4887Schin 
1836*4887Schin 	/*** adjust left margin if necessary ***/
1837*4887Schin 
1838*4887Schin 	if( ncur_phys<first_w || ncur_phys>=(first_w + w_size) )
1839*4887Schin 	{
1840*4887Schin 		cursor(vp,first_w);
1841*4887Schin 		first_w = ncur_phys - (w_size>>1);
1842*4887Schin 		if( first_w < 0 )
1843*4887Schin 			first_w = 0;
1844*4887Schin 		vp->first_wind = cur_phys = first_w;
1845*4887Schin 	}
1846*4887Schin 
1847*4887Schin 	/*** attempt to optimize search somewhat to find ***/
1848*4887Schin 	/*** out where physical and window images differ ***/
1849*4887Schin 
1850*4887Schin 	if( first_w==vp->ofirst_wind && ncur_phys>=vp->ocur_phys && opflag==1 )
1851*4887Schin 	{
1852*4887Schin 		p = vp->ocur_phys;
1853*4887Schin 		w = p - first_w;
1854*4887Schin 	}
1855*4887Schin 	else
1856*4887Schin 	{
1857*4887Schin 		p = first_w;
1858*4887Schin 		w = 0;
1859*4887Schin 	}
1860*4887Schin 
1861*4887Schin 	for(; (p<=last_phys && w<=vp->last_wind); ++p, ++w)
1862*4887Schin 	{
1863*4887Schin 		if( window[w] != physical[p] )
1864*4887Schin 			break;
1865*4887Schin 	}
1866*4887Schin 	p_differ = p;
1867*4887Schin 
1868*4887Schin 	if( (p>last_phys || p>=first_w+w_size) && w>vp->last_wind
1869*4887Schin 		&& cur_virt==vp->ocur_virt )
1870*4887Schin 	{
1871*4887Schin 		/*** images are identical ***/
1872*4887Schin 		return;
1873*4887Schin 	}
1874*4887Schin 
1875*4887Schin 	/*** copy the physical image to the window image ***/
1876*4887Schin 
1877*4887Schin 	if( last_virt != INVALID )
1878*4887Schin 	{
1879*4887Schin 		while( p <= last_phys && w < w_size )
1880*4887Schin 			window[w++] = physical[p++];
1881*4887Schin 	}
1882*4887Schin 	new_lw = w;
1883*4887Schin 
1884*4887Schin 	/*** erase trailing characters if needed ***/
1885*4887Schin 
1886*4887Schin 	while( w <= vp->last_wind )
1887*4887Schin 		window[w++] = ' ';
1888*4887Schin 	vp->last_wind = --w;
1889*4887Schin 
1890*4887Schin 	p = p_differ;
1891*4887Schin 
1892*4887Schin 	/*** move cursor to start of difference ***/
1893*4887Schin 
1894*4887Schin 	cursor(vp,p);
1895*4887Schin 
1896*4887Schin 	/*** and output difference ***/
1897*4887Schin 
1898*4887Schin 	w = p - first_w;
1899*4887Schin 	while( w <= vp->last_wind )
1900*4887Schin 		putchar(window[w++]);
1901*4887Schin 
1902*4887Schin 	cur_phys = w + first_w;
1903*4887Schin 	vp->last_wind = --new_lw;
1904*4887Schin 
1905*4887Schin 	if( last_phys >= w_size )
1906*4887Schin 	{
1907*4887Schin 		if( first_w == 0 )
1908*4887Schin 			vp->long_char = '>';
1909*4887Schin 		else if( last_phys < (first_w+w_size) )
1910*4887Schin 			vp->long_char = '<';
1911*4887Schin 		else
1912*4887Schin 			vp->long_char = '*';
1913*4887Schin 	}
1914*4887Schin 	else
1915*4887Schin 		vp->long_char = ' ';
1916*4887Schin 
1917*4887Schin 	if( vp->long_line != vp->long_char )
1918*4887Schin 	{
1919*4887Schin 		/*** indicate lines longer than window ***/
1920*4887Schin 		while( w++ < w_size )
1921*4887Schin 		{
1922*4887Schin 			putchar(' ');
1923*4887Schin 			++cur_phys;
1924*4887Schin 		}
1925*4887Schin 		putchar(vp->long_char);
1926*4887Schin 		++cur_phys;
1927*4887Schin 		vp->long_line = vp->long_char;
1928*4887Schin 	}
1929*4887Schin 
1930*4887Schin 	vp->ocur_phys = ncur_phys;
1931*4887Schin 	vp->ocur_virt = cur_virt;
1932*4887Schin 	vp->ofirst_wind = first_w;
1933*4887Schin 
1934*4887Schin 	if( mode==INPUT && cur_virt>INVALID )
1935*4887Schin 		++ncur_phys;
1936*4887Schin 
1937*4887Schin 	cursor(vp,ncur_phys);
1938*4887Schin 	ed_flush(vp->ed);
1939*4887Schin 	return;
1940*4887Schin }
1941*4887Schin 
1942*4887Schin /*{	REPLACE( char, increment )
1943*4887Schin  *
1944*4887Schin  *	Replace the cur_virt character with char.  This routine attempts
1945*4887Schin  * to avoid using refresh().
1946*4887Schin  *
1947*4887Schin  *	increment	= 1, increment cur_virt after replacement.
1948*4887Schin  *			= 0, leave cur_virt where it is.
1949*4887Schin  *
1950*4887Schin }*/
1951*4887Schin 
1952*4887Schin static void replace(register Vi_t *vp, register int c, register int increment)
1953*4887Schin {
1954*4887Schin 	register int cur_window;
1955*4887Schin 
1956*4887Schin 	if( cur_virt == INVALID )
1957*4887Schin 	{
1958*4887Schin 		/*** can't replace invalid cursor ***/
1959*4887Schin 		ed_ringbell();
1960*4887Schin 		return;
1961*4887Schin 	}
1962*4887Schin 	cur_window = cur_phys - vp->first_wind;
1963*4887Schin 	if( vp->ocur_virt == INVALID || !is_print(c)
1964*4887Schin 		|| !is_print(virtual[cur_virt])
1965*4887Schin 		|| !is_print(vp->o_v_char)
1966*4887Schin #if SHOPT_MULTIBYTE
1967*4887Schin 		|| !iswascii(c) || mbwidth(vp->o_v_char)>1
1968*4887Schin 		|| !iswascii(virtual[cur_virt])
1969*4887Schin #endif /* SHOPT_MULTIBYTE */
1970*4887Schin 		|| (increment && (cur_window==w_size-1)
1971*4887Schin 			|| !is_print(virtual[cur_virt+1])) )
1972*4887Schin 	{
1973*4887Schin 		/*** must use standard refresh routine ***/
1974*4887Schin 
1975*4887Schin 		cdelete(vp,1, BAD);
1976*4887Schin 		append(vp,c, APPEND);
1977*4887Schin 		if( increment && cur_virt<last_virt )
1978*4887Schin 			++cur_virt;
1979*4887Schin 		refresh(vp,CONTROL);
1980*4887Schin 	}
1981*4887Schin 	else
1982*4887Schin 	{
1983*4887Schin 		virtual[cur_virt] = c;
1984*4887Schin 		physical[cur_phys] = c;
1985*4887Schin 		window[cur_window] = c;
1986*4887Schin 		putchar(c);
1987*4887Schin 		if(increment)
1988*4887Schin 		{
1989*4887Schin 			c = virtual[++cur_virt];
1990*4887Schin 			++cur_phys;
1991*4887Schin 		}
1992*4887Schin 		else
1993*4887Schin 		{
1994*4887Schin 			putchar('\b');
1995*4887Schin 		}
1996*4887Schin 		vp->o_v_char = c;
1997*4887Schin 		ed_flush(vp->ed);
1998*4887Schin 	}
1999*4887Schin 	return;
2000*4887Schin }
2001*4887Schin 
2002*4887Schin /*{	RESTORE_V()
2003*4887Schin  *
2004*4887Schin  *	Restore the contents of virtual space from u_space.
2005*4887Schin  *
2006*4887Schin }*/
2007*4887Schin 
2008*4887Schin static void restore_v(register Vi_t *vp)
2009*4887Schin {
2010*4887Schin 	register int tmpcol;
2011*4887Schin 	genchar tmpspace[MAXLINE];
2012*4887Schin 
2013*4887Schin 	if( vp->u_column == INVALID-1 )
2014*4887Schin 	{
2015*4887Schin 		/*** never saved anything ***/
2016*4887Schin 		ed_ringbell();
2017*4887Schin 		return;
2018*4887Schin 	}
2019*4887Schin 	gencpy(tmpspace, vp->u_space);
2020*4887Schin 	tmpcol = vp->u_column;
2021*4887Schin 	save_v(vp);
2022*4887Schin 	gencpy(virtual, tmpspace);
2023*4887Schin 	cur_virt = tmpcol;
2024*4887Schin 	last_virt = genlen(tmpspace) - 1;
2025*4887Schin 	vp->ocur_virt = MAXCHAR;	/** invalidate refresh optimization **/
2026*4887Schin 	return;
2027*4887Schin }
2028*4887Schin 
2029*4887Schin /*{	SAVE_LAST()
2030*4887Schin  *
2031*4887Schin  *	If the user has typed something, save it in last line.
2032*4887Schin  *
2033*4887Schin }*/
2034*4887Schin 
2035*4887Schin static void save_last(register Vi_t* vp)
2036*4887Schin {
2037*4887Schin 	register int i;
2038*4887Schin 
2039*4887Schin 	if( (i = cur_virt - first_virt + 1) > 0 )
2040*4887Schin 	{
2041*4887Schin 		/*** save last thing user typed ***/
2042*4887Schin 		if(i >= MAXLINE)
2043*4887Schin 			i = MAXLINE-1;
2044*4887Schin 		genncpy(vp->lastline, (&virtual[first_virt]), i);
2045*4887Schin 		vp->lastline[i] = '\0';
2046*4887Schin 	}
2047*4887Schin 	return;
2048*4887Schin }
2049*4887Schin 
2050*4887Schin /*{	SAVE_V()
2051*4887Schin  *
2052*4887Schin  *	This routine will save the contents of virtual in u_space.
2053*4887Schin  *
2054*4887Schin }*/
2055*4887Schin 
2056*4887Schin static void save_v(register Vi_t *vp)
2057*4887Schin {
2058*4887Schin 	if(!inmacro)
2059*4887Schin 	{
2060*4887Schin 		virtual[last_virt + 1] = '\0';
2061*4887Schin 		gencpy(vp->u_space, virtual);
2062*4887Schin 		vp->u_column = cur_virt;
2063*4887Schin 	}
2064*4887Schin 	return;
2065*4887Schin }
2066*4887Schin 
2067*4887Schin /*{	SEARCH( mode )
2068*4887Schin  *
2069*4887Schin  *	Search history file for regular expression.
2070*4887Schin  *
2071*4887Schin  *	mode	= '/'	require search string and search new to old
2072*4887Schin  *	mode	= '?'	require search string and search old to new
2073*4887Schin  *	mode	= 'N'	repeat last search in reverse direction
2074*4887Schin  *	mode	= 'n'	repeat last search
2075*4887Schin  *
2076*4887Schin }*/
2077*4887Schin 
2078*4887Schin /*
2079*4887Schin  * search for <string> in the current command
2080*4887Schin  */
2081*4887Schin static int curline_search(Vi_t *vp, const char *string)
2082*4887Schin {
2083*4887Schin 	register int len=strlen(string);
2084*4887Schin 	register const char *dp,*cp=string, *dpmax;
2085*4887Schin #if SHOPT_MULTIBYTE
2086*4887Schin 	ed_external(vp->u_space,(char*)vp->u_space);
2087*4887Schin #endif /* SHOPT_MULTIBYTE */
2088*4887Schin 	for(dp=(char*)vp->u_space,dpmax=dp+strlen(dp)-len; dp<=dpmax; dp++)
2089*4887Schin 	{
2090*4887Schin 		if(*dp==*cp && memcmp(cp,dp,len)==0)
2091*4887Schin 			return(dp-(char*)vp->u_space);
2092*4887Schin 	}
2093*4887Schin #if SHOPT_MULTIBYTE
2094*4887Schin 	ed_internal((char*)vp->u_space,vp->u_space);
2095*4887Schin #endif /* SHOPT_MULTIBYTE */
2096*4887Schin 	return(-1);
2097*4887Schin }
2098*4887Schin 
2099*4887Schin static int search(register Vi_t* vp,register int mode)
2100*4887Schin {
2101*4887Schin 	register int new_direction;
2102*4887Schin 	register int oldcurhline;
2103*4887Schin 	register int i;
2104*4887Schin 	Histloc_t  location;
2105*4887Schin 
2106*4887Schin 	if( mode == '/' || mode == '?')
2107*4887Schin 	{
2108*4887Schin 		/*** new search expression ***/
2109*4887Schin 		del_line(vp,BAD);
2110*4887Schin 		append(vp,mode, APPEND);
2111*4887Schin 		refresh(vp,INPUT);
2112*4887Schin 		first_virt = 1;
2113*4887Schin 		getline(vp,SEARCH);
2114*4887Schin 		first_virt = 0;
2115*4887Schin 		virtual[last_virt + 1] = '\0';	/*** make null terminated ***/
2116*4887Schin 		vp->direction = mode=='/' ? -1 : 1;
2117*4887Schin 	}
2118*4887Schin 
2119*4887Schin 	if( cur_virt == INVALID )
2120*4887Schin 	{
2121*4887Schin 		/*** no operation ***/
2122*4887Schin 		return(ABORT);
2123*4887Schin 	}
2124*4887Schin 
2125*4887Schin 	if( cur_virt==0 ||  fold(mode)=='N' )
2126*4887Schin 	{
2127*4887Schin 		/*** user wants repeat of last search ***/
2128*4887Schin 		del_line(vp,BAD);
2129*4887Schin 		strcpy( ((char*)virtual)+1, lsearch);
2130*4887Schin #if SHOPT_MULTIBYTE
2131*4887Schin 		*((char*)virtual) = '/';
2132*4887Schin 		ed_internal((char*)virtual,virtual);
2133*4887Schin #endif /* SHOPT_MULTIBYTE */
2134*4887Schin 	}
2135*4887Schin 
2136*4887Schin 	if( mode == 'N' )
2137*4887Schin 		new_direction = -vp->direction;
2138*4887Schin 	else
2139*4887Schin 		new_direction = vp->direction;
2140*4887Schin 
2141*4887Schin 
2142*4887Schin 	/*** now search ***/
2143*4887Schin 
2144*4887Schin 	oldcurhline = curhline;
2145*4887Schin #if SHOPT_MULTIBYTE
2146*4887Schin 	ed_external(virtual,(char*)virtual);
2147*4887Schin #endif /* SHOPT_MULTIBYTE */
2148*4887Schin 	if(mode=='?' && (i=curline_search(vp,((char*)virtual)+1))>=0)
2149*4887Schin 	{
2150*4887Schin 		location.hist_command = curhline;
2151*4887Schin 		location.hist_char = i;
2152*4887Schin 	}
2153*4887Schin 	else
2154*4887Schin 	{
2155*4887Schin 		i = INVALID;
2156*4887Schin 		if( new_direction==1 && curhline >= histmax )
2157*4887Schin 			curhline = histmin + 1;
2158*4887Schin 		location = hist_find(sh.hist_ptr,((char*)virtual)+1, curhline, 1, new_direction);
2159*4887Schin 	}
2160*4887Schin 	cur_virt = i;
2161*4887Schin 	strncpy(lsearch, ((char*)virtual)+1, SEARCHSIZE);
2162*4887Schin 	if( (curhline=location.hist_command) >=0 )
2163*4887Schin 	{
2164*4887Schin 		vp->ocur_virt = INVALID;
2165*4887Schin 		return(GOOD);
2166*4887Schin 	}
2167*4887Schin 
2168*4887Schin 	/*** could not find matching line ***/
2169*4887Schin 
2170*4887Schin 	curhline = oldcurhline;
2171*4887Schin 	return(BAD);
2172*4887Schin }
2173*4887Schin 
2174*4887Schin /*{	SYNC_CURSOR()
2175*4887Schin  *
2176*4887Schin  *	This routine will move the physical cursor to the same
2177*4887Schin  * column as the virtual cursor.
2178*4887Schin  *
2179*4887Schin }*/
2180*4887Schin 
2181*4887Schin static void sync_cursor(register Vi_t *vp)
2182*4887Schin {
2183*4887Schin 	register int p;
2184*4887Schin 	register int v;
2185*4887Schin 	register int c;
2186*4887Schin 	int new_phys;
2187*4887Schin 
2188*4887Schin 	if( cur_virt == INVALID )
2189*4887Schin 		return;
2190*4887Schin 
2191*4887Schin 	/*** find physical col that corresponds to virtual col ***/
2192*4887Schin 
2193*4887Schin 	new_phys = 0;
2194*4887Schin 	if(vp->first_wind==vp->ofirst_wind && cur_virt>vp->ocur_virt && vp->ocur_virt!=INVALID)
2195*4887Schin 	{
2196*4887Schin 		/*** try to optimize search a little ***/
2197*4887Schin 		p = vp->ocur_phys + 1;
2198*4887Schin #if SHOPT_MULTIBYTE
2199*4887Schin 		while(physical[p]==MARKER)
2200*4887Schin 			p++;
2201*4887Schin #endif /* SHOPT_MULTIBYTE */
2202*4887Schin 		v = vp->ocur_virt + 1;
2203*4887Schin 	}
2204*4887Schin 	else
2205*4887Schin 	{
2206*4887Schin 		p = 0;
2207*4887Schin 		v = 0;
2208*4887Schin 	}
2209*4887Schin 	for(; v <= last_virt; ++p, ++v)
2210*4887Schin 	{
2211*4887Schin #if SHOPT_MULTIBYTE
2212*4887Schin 		int d;
2213*4887Schin 		c = virtual[v];
2214*4887Schin 		if((d = mbwidth(c)) > 1)
2215*4887Schin 		{
2216*4887Schin 			if( v != cur_virt )
2217*4887Schin 				p += (d-1);
2218*4887Schin 		}
2219*4887Schin 		else if(!iswprint(c))
2220*4887Schin #else
2221*4887Schin 		c = virtual[v];
2222*4887Schin 		if(!isprint(c))
2223*4887Schin #endif	/* SHOPT_MULTIBYTE */
2224*4887Schin 		{
2225*4887Schin 			if( c == '\t' )
2226*4887Schin 			{
2227*4887Schin 				p -= ((p+editb.e_plen)%TABSIZE);
2228*4887Schin 				p += (TABSIZE-1);
2229*4887Schin 			}
2230*4887Schin 			else
2231*4887Schin 			{
2232*4887Schin 				++p;
2233*4887Schin 			}
2234*4887Schin 		}
2235*4887Schin 		if( v == cur_virt )
2236*4887Schin 		{
2237*4887Schin 			new_phys = p;
2238*4887Schin 			break;
2239*4887Schin 		}
2240*4887Schin 	}
2241*4887Schin 
2242*4887Schin 	if( new_phys < vp->first_wind || new_phys >= vp->first_wind + w_size )
2243*4887Schin 	{
2244*4887Schin 		/*** asked to move outside of window ***/
2245*4887Schin 
2246*4887Schin 		window[0] = '\0';
2247*4887Schin 		refresh(vp,CONTROL);
2248*4887Schin 		return;
2249*4887Schin 	}
2250*4887Schin 
2251*4887Schin 	cursor(vp,new_phys);
2252*4887Schin 	ed_flush(vp->ed);
2253*4887Schin 	vp->ocur_phys = cur_phys;
2254*4887Schin 	vp->ocur_virt = cur_virt;
2255*4887Schin 	vp->o_v_char = virtual[vp->ocur_virt];
2256*4887Schin 
2257*4887Schin 	return;
2258*4887Schin }
2259*4887Schin 
2260*4887Schin /*{	TEXTMOD( command, mode )
2261*4887Schin  *
2262*4887Schin  *	Modify text operations.
2263*4887Schin  *
2264*4887Schin  *	mode != 0, repeat previous operation
2265*4887Schin  *
2266*4887Schin }*/
2267*4887Schin 
2268*4887Schin static int textmod(register Vi_t *vp,register int c, int mode)
2269*4887Schin {
2270*4887Schin 	register int i;
2271*4887Schin 	register genchar *p = vp->lastline;
2272*4887Schin 	register int trepeat = vp->repeat;
2273*4887Schin 	genchar *savep;
2274*4887Schin 
2275*4887Schin 	if(mode && (fold(vp->lastmotion)=='F' || fold(vp->lastmotion)=='T'))
2276*4887Schin 		vp->lastmotion = ';';
2277*4887Schin 
2278*4887Schin 	if( fold(c) == 'P' )
2279*4887Schin 	{
2280*4887Schin 		/*** change p from lastline to yankbuf ***/
2281*4887Schin 		p = yankbuf;
2282*4887Schin 	}
2283*4887Schin 
2284*4887Schin addin:
2285*4887Schin 	switch( c )
2286*4887Schin 	{
2287*4887Schin 			/***** Input commands *****/
2288*4887Schin 
2289*4887Schin #if KSHELL
2290*4887Schin         case '\t':
2291*4887Schin 		if(vp->ed->e_tabcount!=1)
2292*4887Schin 			return(BAD);
2293*4887Schin 		c = '=';
2294*4887Schin 	case '*':		/** do file name expansion in place **/
2295*4887Schin 	case '\\':		/** do file name completion in place **/
2296*4887Schin 		if( cur_virt == INVALID )
2297*4887Schin 			return(BAD);
2298*4887Schin 	case '=':		/** list file name expansions **/
2299*4887Schin 		save_v(vp);
2300*4887Schin 		i = last_virt;
2301*4887Schin 		++last_virt;
2302*4887Schin 		mode = cur_virt-1;
2303*4887Schin 		virtual[last_virt] = 0;
2304*4887Schin 		if(ed_expand(vp->ed,(char*)virtual, &cur_virt, &last_virt, c, vp->repeat_set?vp->repeat:-1)<0)
2305*4887Schin 		{
2306*4887Schin 			if(vp->ed->e_tabcount)
2307*4887Schin 			{
2308*4887Schin 				vp->ed->e_tabcount=2;
2309*4887Schin 				ed_ungetchar(vp->ed,'\t');
2310*4887Schin 				--last_virt;
2311*4887Schin 				return(APPEND);
2312*4887Schin 			}
2313*4887Schin 			last_virt = i;
2314*4887Schin 			ed_ringbell();
2315*4887Schin 		}
2316*4887Schin 		else if(c == '=' && !vp->repeat_set)
2317*4887Schin 		{
2318*4887Schin 			last_virt = i;
2319*4887Schin 			vp->nonewline++;
2320*4887Schin 			ed_ungetchar(vp->ed,cntl('L'));
2321*4887Schin 			return(GOOD);
2322*4887Schin 		}
2323*4887Schin 		else
2324*4887Schin 		{
2325*4887Schin 			--cur_virt;
2326*4887Schin 			--last_virt;
2327*4887Schin 			vp->ocur_virt = MAXCHAR;
2328*4887Schin 			if(c=='=' || (mode<cur_virt && (virtual[cur_virt]==' ' || virtual[cur_virt]=='/')))
2329*4887Schin 				vp->ed->e_tabcount = 0;
2330*4887Schin 			return(APPEND);
2331*4887Schin 		}
2332*4887Schin 		break;
2333*4887Schin 
2334*4887Schin 	case '@':		/** macro expansion **/
2335*4887Schin 		if( mode )
2336*4887Schin 			c = vp->lastmacro;
2337*4887Schin 		else
2338*4887Schin 			if((c=getrchar(vp))==ESC)
2339*4887Schin 				return(GOOD);
2340*4887Schin 		if(!inmacro)
2341*4887Schin 			vp->lastmacro = c;
2342*4887Schin 		if(ed_macro(vp->ed,c))
2343*4887Schin 		{
2344*4887Schin 			save_v(vp);
2345*4887Schin 			inmacro++;
2346*4887Schin 			return(GOOD);
2347*4887Schin 		}
2348*4887Schin 		ed_ringbell();
2349*4887Schin 		return(BAD);
2350*4887Schin 
2351*4887Schin #endif	/* KSHELL */
2352*4887Schin 	case '_':		/** append last argument of prev command **/
2353*4887Schin 		save_v(vp);
2354*4887Schin 		{
2355*4887Schin 			genchar tmpbuf[MAXLINE];
2356*4887Schin 			if(vp->repeat_set==0)
2357*4887Schin 				vp->repeat = -1;
2358*4887Schin 			p = (genchar*)hist_word((char*)tmpbuf,MAXLINE,vp->repeat);
2359*4887Schin #if !KSHELL
2360*4887Schin 			if(p==0)
2361*4887Schin 			{
2362*4887Schin 				ed_ringbell();
2363*4887Schin 				break;
2364*4887Schin 			}
2365*4887Schin #endif	/* KSHELL */
2366*4887Schin #if SHOPT_MULTIBYTE
2367*4887Schin 			ed_internal((char*)p,tmpbuf);
2368*4887Schin 			p = tmpbuf;
2369*4887Schin #endif /* SHOPT_MULTIBYTE */
2370*4887Schin 			i = ' ';
2371*4887Schin 			do
2372*4887Schin 			{
2373*4887Schin 				append(vp,i,APPEND);
2374*4887Schin 			}
2375*4887Schin 			while(i = *p++);
2376*4887Schin 			return(APPEND);
2377*4887Schin 		}
2378*4887Schin 
2379*4887Schin 	case 'A':		/** append to end of line **/
2380*4887Schin 		cur_virt = last_virt;
2381*4887Schin 		sync_cursor(vp);
2382*4887Schin 
2383*4887Schin 	case 'a':		/** append **/
2384*4887Schin 		if( fold(mode) == 'A' )
2385*4887Schin 		{
2386*4887Schin 			c = 'p';
2387*4887Schin 			goto addin;
2388*4887Schin 		}
2389*4887Schin 		save_v(vp);
2390*4887Schin 		if( cur_virt != INVALID )
2391*4887Schin 		{
2392*4887Schin 			first_virt = cur_virt + 1;
2393*4887Schin 			cursor(vp,cur_phys + 1);
2394*4887Schin 			ed_flush(vp->ed);
2395*4887Schin 		}
2396*4887Schin 		return(APPEND);
2397*4887Schin 
2398*4887Schin 	case 'I':		/** insert at beginning of line **/
2399*4887Schin 		cur_virt = first_virt;
2400*4887Schin 		sync_cursor(vp);
2401*4887Schin 
2402*4887Schin 	case 'i':		/** insert **/
2403*4887Schin 		if( fold(mode) == 'I' )
2404*4887Schin 		{
2405*4887Schin 			c = 'P';
2406*4887Schin 			goto addin;
2407*4887Schin 		}
2408*4887Schin 		save_v(vp);
2409*4887Schin 		if( cur_virt != INVALID )
2410*4887Schin  		{
2411*4887Schin  			vp->o_v_char = virtual[cur_virt];
2412*4887Schin 			first_virt = cur_virt--;
2413*4887Schin   		}
2414*4887Schin 		return(INSERT);
2415*4887Schin 
2416*4887Schin 	case 'C':		/** change to eol **/
2417*4887Schin 		c = '$';
2418*4887Schin 		goto chgeol;
2419*4887Schin 
2420*4887Schin 	case 'c':		/** change **/
2421*4887Schin 		if( mode )
2422*4887Schin 			c = vp->lastmotion;
2423*4887Schin 		else
2424*4887Schin 			c = getcount(vp,ed_getchar(vp->ed,-1));
2425*4887Schin chgeol:
2426*4887Schin 		vp->lastmotion = c;
2427*4887Schin 		if( c == 'c' )
2428*4887Schin 		{
2429*4887Schin 			del_line(vp,GOOD);
2430*4887Schin 			return(APPEND);
2431*4887Schin 		}
2432*4887Schin 
2433*4887Schin 		if(!delmotion(vp, c, 'c'))
2434*4887Schin 			return(BAD);
2435*4887Schin 
2436*4887Schin 		if( mode == 'c' )
2437*4887Schin 		{
2438*4887Schin 			c = 'p';
2439*4887Schin 			trepeat = 1;
2440*4887Schin 			goto addin;
2441*4887Schin 		}
2442*4887Schin 		first_virt = cur_virt + 1;
2443*4887Schin 		return(APPEND);
2444*4887Schin 
2445*4887Schin 	case 'D':		/** delete to eol **/
2446*4887Schin 		c = '$';
2447*4887Schin 		goto deleol;
2448*4887Schin 
2449*4887Schin 	case 'd':		/** delete **/
2450*4887Schin 		if( mode )
2451*4887Schin 			c = vp->lastmotion;
2452*4887Schin 		else
2453*4887Schin 			c = getcount(vp,ed_getchar(vp->ed,-1));
2454*4887Schin deleol:
2455*4887Schin 		vp->lastmotion = c;
2456*4887Schin 		if( c == 'd' )
2457*4887Schin 		{
2458*4887Schin 			del_line(vp,GOOD);
2459*4887Schin 			break;
2460*4887Schin 		}
2461*4887Schin 		if(!delmotion(vp, c, 'd'))
2462*4887Schin 			return(BAD);
2463*4887Schin 		if( cur_virt < last_virt )
2464*4887Schin 			++cur_virt;
2465*4887Schin 		break;
2466*4887Schin 
2467*4887Schin 	case 'P':
2468*4887Schin 		if( p[0] == '\0' )
2469*4887Schin 			return(BAD);
2470*4887Schin 		if( cur_virt != INVALID )
2471*4887Schin 		{
2472*4887Schin 			i = virtual[cur_virt];
2473*4887Schin 			if(!is_print(i))
2474*4887Schin 				vp->ocur_virt = INVALID;
2475*4887Schin 			--cur_virt;
2476*4887Schin 		}
2477*4887Schin 
2478*4887Schin 	case 'p':		/** print **/
2479*4887Schin 		if( p[0] == '\0' )
2480*4887Schin 			return(BAD);
2481*4887Schin 
2482*4887Schin 		if( mode != 's' && mode != 'c' )
2483*4887Schin 		{
2484*4887Schin 			save_v(vp);
2485*4887Schin 			if( c == 'P' )
2486*4887Schin 			{
2487*4887Schin 				/*** fix stored cur_virt ***/
2488*4887Schin 				++vp->u_column;
2489*4887Schin 			}
2490*4887Schin 		}
2491*4887Schin 		if( mode == 'R' )
2492*4887Schin 			mode = REPLACE;
2493*4887Schin 		else
2494*4887Schin 			mode = APPEND;
2495*4887Schin 		savep = p;
2496*4887Schin 		for(i=0; i<trepeat; ++i)
2497*4887Schin 		{
2498*4887Schin 			while(c= *p++)
2499*4887Schin 				append(vp,c,mode);
2500*4887Schin 			p = savep;
2501*4887Schin 		}
2502*4887Schin 		break;
2503*4887Schin 
2504*4887Schin 	case 'R':		/* Replace many chars **/
2505*4887Schin 		if( mode == 'R' )
2506*4887Schin 		{
2507*4887Schin 			c = 'P';
2508*4887Schin 			goto addin;
2509*4887Schin 		}
2510*4887Schin 		save_v(vp);
2511*4887Schin 		if( cur_virt != INVALID )
2512*4887Schin 			first_virt = cur_virt;
2513*4887Schin 		return(REPLACE);
2514*4887Schin 
2515*4887Schin 	case 'r':		/** replace **/
2516*4887Schin 		if( mode )
2517*4887Schin 			c = *p;
2518*4887Schin 		else
2519*4887Schin 			if((c=getrchar(vp))==ESC)
2520*4887Schin 				return(GOOD);
2521*4887Schin 		*p = c;
2522*4887Schin 		save_v(vp);
2523*4887Schin 		while(trepeat--)
2524*4887Schin 			replace(vp,c, trepeat!=0);
2525*4887Schin 		return(GOOD);
2526*4887Schin 
2527*4887Schin 	case 'S':		/** Substitute line - cc **/
2528*4887Schin 		c = 'c';
2529*4887Schin 		goto chgeol;
2530*4887Schin 
2531*4887Schin 	case 's':		/** substitute **/
2532*4887Schin 		save_v(vp);
2533*4887Schin 		cdelete(vp,vp->repeat, BAD);
2534*4887Schin 		if( mode )
2535*4887Schin 		{
2536*4887Schin 			c = 'p';
2537*4887Schin 			trepeat = 1;
2538*4887Schin 			goto addin;
2539*4887Schin 		}
2540*4887Schin 		first_virt = cur_virt + 1;
2541*4887Schin 		return(APPEND);
2542*4887Schin 
2543*4887Schin 	case 'Y':		/** Yank to end of line **/
2544*4887Schin 		c = '$';
2545*4887Schin 		goto yankeol;
2546*4887Schin 
2547*4887Schin 	case 'y':		/** yank thru motion **/
2548*4887Schin 		if( mode )
2549*4887Schin 			c = vp->lastmotion;
2550*4887Schin 		else
2551*4887Schin 			c = getcount(vp,ed_getchar(vp->ed,-1));
2552*4887Schin yankeol:
2553*4887Schin 		vp->lastmotion = c;
2554*4887Schin 		if( c == 'y' )
2555*4887Schin 		{
2556*4887Schin 			gencpy(yankbuf, virtual);
2557*4887Schin 		}
2558*4887Schin 		else if(!delmotion(vp, c, 'y'))
2559*4887Schin 		{
2560*4887Schin 			return(BAD);
2561*4887Schin 		}
2562*4887Schin 		break;
2563*4887Schin 
2564*4887Schin 	case 'x':		/** delete repeat chars forward - dl **/
2565*4887Schin 		c = 'l';
2566*4887Schin 		goto deleol;
2567*4887Schin 
2568*4887Schin 	case 'X':		/** delete repeat chars backward - dh **/
2569*4887Schin 		c = 'h';
2570*4887Schin 		goto deleol;
2571*4887Schin 
2572*4887Schin 	case '~':		/** invert case and advance **/
2573*4887Schin 		if( cur_virt != INVALID )
2574*4887Schin 		{
2575*4887Schin 			save_v(vp);
2576*4887Schin 			i = INVALID;
2577*4887Schin 			while(trepeat-->0 && i!=cur_virt)
2578*4887Schin 			{
2579*4887Schin 				i = cur_virt;
2580*4887Schin 				c = virtual[cur_virt];
2581*4887Schin #if SHOPT_MULTIBYTE
2582*4887Schin 				if((c&~STRIP)==0)
2583*4887Schin #endif /* SHOPT_MULTIBYTE */
2584*4887Schin 				if( isupper(c) )
2585*4887Schin 					c = tolower(c);
2586*4887Schin 				else if( islower(c) )
2587*4887Schin 					c = toupper(c);
2588*4887Schin 				replace(vp,c, 1);
2589*4887Schin 			}
2590*4887Schin 			return(GOOD);
2591*4887Schin 		}
2592*4887Schin 		else
2593*4887Schin 			return(BAD);
2594*4887Schin 
2595*4887Schin 	default:
2596*4887Schin 		return(BAD);
2597*4887Schin 	}
2598*4887Schin 	refresh(vp,CONTROL);
2599*4887Schin 	return(GOOD);
2600*4887Schin }
2601*4887Schin 
2602*4887Schin 
2603*4887Schin #if SHOPT_MULTIBYTE
2604*4887Schin     static int _isalph(register int v)
2605*4887Schin     {
2606*4887Schin #ifdef _lib_iswalnum
2607*4887Schin 	return(iswalnum(v) || v=='_');
2608*4887Schin #else
2609*4887Schin 	return((v&~STRIP) || isalnum(v) || v=='_');
2610*4887Schin #endif
2611*4887Schin     }
2612*4887Schin 
2613*4887Schin 
2614*4887Schin     static int _isblank(register int v)
2615*4887Schin     {
2616*4887Schin 	return((v&~STRIP)==0 && isspace(v));
2617*4887Schin     }
2618*4887Schin 
2619*4887Schin     static int _ismetach(register int v)
2620*4887Schin     {
2621*4887Schin 	return((v&~STRIP)==0 && ismeta(v));
2622*4887Schin     }
2623*4887Schin 
2624*4887Schin #endif	/* SHOPT_MULTIBYTE */
2625*4887Schin 
2626*4887Schin /*
2627*4887Schin  * get a character, after ^V processing
2628*4887Schin  */
2629*4887Schin static int getrchar(register Vi_t *vp)
2630*4887Schin {
2631*4887Schin 	register int c;
2632*4887Schin 	if((c=ed_getchar(vp->ed,1))== usrlnext)
2633*4887Schin 		c = ed_getchar(vp->ed,2);
2634*4887Schin 	return(c);
2635*4887Schin }
2636