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