1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1982-2014 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 <dgkorn@gmail.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	vigetline(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(ed->sh,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 		vigetline(vp,APPEND);
586 	else if(last_virt>=0 && virtual[last_virt]==term_char)
587 		vigetline(vp,APPEND);
588 	else
589 		vigetline(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 		stkset(ed->sh->stk,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 
848 			default:
849 				if( vp->u_column == INVALID )
850 					del_line(vp,BAD);
851 				else
852 					restore_v(vp);
853 				break;
854 			}
855 			break;
856 
857 		case 'j':		/** get next command **/
858 		case '+':		/** get next command **/
859 #if SHOPT_EDPREDICT
860 			if(vp->ed->hlist)
861 			{
862 				if(vp->ed->hoff >= vp->ed->hmax)
863 					goto ringbell;
864 				vp->ed->hoff++;
865 				goto hupdate;
866 			}
867 #endif /* SHOPT_EDPREDICT */
868 			curhline += vp->repeat;
869 			if( curhline > histmax )
870 			{
871 				curhline = histmax;
872 				goto ringbell;
873 			}
874 			else if(curhline==histmax && tmp_u_column!=INVALID )
875 			{
876 				vp->u_space = tmp_u_space;
877 				vp->u_column = tmp_u_column;
878 				restore_v(vp);
879 				vp->u_space = real_u_space;
880 				break;
881 			}
882 			save_v(vp);
883 			cur_virt = INVALID;
884 			goto newhist;
885 
886 		case 'k':		/** get previous command **/
887 		case '-':		/** get previous command **/
888 #if SHOPT_EDPREDICT
889 			if(vp->ed->hlist)
890 			{
891 				if(vp->ed->hoff == 0)
892 					goto ringbell;
893 				vp->ed->hoff--;
894 			 hupdate:
895 				ed_histlist(vp->ed,*vp->ed->hlist!=0);
896 				vp->nonewline++;
897 				ed_ungetchar(vp->ed,cntl('L'));
898 				continue;
899 			}
900 #endif /* SHOPT_EDPREDICT */
901 			if( curhline == histmax )
902 			{
903 				vp->u_space = tmp_u_space;
904 				i = vp->u_column;
905 				save_v(vp);
906 				vp->u_space = real_u_space;
907 				tmp_u_column = vp->u_column;
908 				vp->u_column = i;
909 			}
910 
911 			curhline -= vp->repeat;
912 			if( curhline <= histmin )
913 			{
914 				curhline += vp->repeat;
915 				goto ringbell;
916 			}
917 			save_v(vp);
918 			cur_virt = INVALID;
919 		newhist:
920 			if(curhline!=histmax || cur_virt==INVALID)
921 				hist_copy((char*)virtual, MAXLINE, curhline,-1);
922 			else
923 			{
924 				strcpy((char*)virtual,(char*)vp->u_space);
925 #if SHOPT_MULTIBYTE
926 				ed_internal((char*)vp->u_space,vp->u_space);
927 #endif /* SHOPT_MULTIBYTE */
928 			}
929 #if SHOPT_MULTIBYTE
930 			ed_internal((char*)virtual,virtual);
931 #endif /* SHOPT_MULTIBYTE */
932 			if((last_virt=genlen(virtual)-1) >= 0  && cur_virt == INVALID)
933 				cur_virt = 0;
934 #if SHOPT_EDPREDICT
935 			if(vp->ed->hlist)
936 			{
937 				ed_histlist(vp->ed,0);
938 				if(c=='\n')
939 					ed_ungetchar(vp->ed,c);
940 				ed_ungetchar(vp->ed,cntl('L'));
941 				vp->nonewline = 1;
942 				cur_virt = 0;
943 			}
944 #endif /*SHOPT_EDPREDICT */
945 			break;
946 
947 
948 		case 'u':		/** undo the last thing done **/
949 			restore_v(vp);
950 			break;
951 
952 		case 'U':		/** Undo everything **/
953 			save_v(vp);
954 			if( virtual[0] == '\0' )
955 				goto ringbell;
956 			else
957 			{
958 				gencpy(virtual, vp->U_space);
959 				last_virt = genlen(vp->U_space) - 1;
960 				cur_virt = 0;
961 			}
962 			break;
963 
964 #if KSHELL
965 		case 'v':
966 			if(vp->repeat_set==0)
967 				goto vcommand;
968 #endif /* KSHELL */
969 
970 		case 'G':		/** goto command repeat **/
971 			if(vp->repeat_set==0)
972 				vp->repeat = histmin+1;
973 			if( vp->repeat <= histmin || vp->repeat > histmax )
974 			{
975 				goto ringbell;
976 			}
977 			curhline = vp->repeat;
978 			save_v(vp);
979 			if(c == 'G')
980 			{
981 				cur_virt = INVALID;
982 				goto newhist;
983 			}
984 
985 #if KSHELL
986 		vcommand:
987 			if(ed_fulledit(vp->ed)==GOOD)
988 				return(BIGVI);
989 			else
990 				goto ringbell;
991 #endif	/* KSHELL */
992 
993 		case '#':	/** insert(delete) # to (no)comment command **/
994 			if( cur_virt != INVALID )
995 			{
996 				register genchar *p = &virtual[last_virt+1];
997 				*p = 0;
998 				/*** see whether first char is comment char ***/
999 				c = (virtual[0]=='#');
1000 				while(p-- >= virtual)
1001 				{
1002 					if(*p=='\n' || p<virtual)
1003 					{
1004 						if(c) /* delete '#' */
1005 						{
1006 							if(p[1]=='#')
1007 							{
1008 								last_virt--;
1009 								gencpy(p+1,p+2);
1010 							}
1011 						}
1012 						else
1013 						{
1014 							cur_virt = p-virtual;
1015 							append(vp,'#', APPEND);
1016 						}
1017 					}
1018 				}
1019 				if(c)
1020 				{
1021 					curhline = histmax;
1022 					cur_virt = 0;
1023 					break;
1024 				}
1025 				refresh(vp,INPUT);
1026 			}
1027 
1028 		case '\n':		/** send to shell **/
1029 #if SHOPT_EDPREDICT
1030 			if(!vp->ed->hlist)
1031 			return(ENTER);
1032 		case '\t':		/** bring choice to edit **/
1033 			if(vp->ed->hlist)
1034 			{
1035 				if(vp->repeat > vp->ed->nhlist-vp->ed->hoff)
1036 					goto ringbell;
1037 				curhline = vp->ed->hlist[vp->repeat+vp->ed->hoff-1]->index;
1038 				goto newhist;
1039 			}
1040 			goto ringbell;
1041 #else
1042 			return(ENTER);
1043 #endif /* SHOPT_EDPREDICT */
1044 	        case ESC:
1045 			/* don't ring bell if next char is '[' */
1046 			if(!lookahead)
1047 			{
1048 				char x;
1049 				if(sfpkrd(editb.e_fd,&x,1,'\r',400L,-1)>0)
1050 					ed_ungetchar(vp->ed,x);
1051 			}
1052 			if(lookahead)
1053 			{
1054 				ed_ungetchar(vp->ed,c=ed_getchar(vp->ed,1));
1055 				if(c=='[' || c=='O')
1056 				{
1057 					vp->repeat = 1;
1058 					continue;
1059 				}
1060 			}
1061 		default:
1062 		ringbell:
1063 			ed_ringbell();
1064 			vp->repeat = 1;
1065 			continue;
1066 		}
1067 
1068 		refresh(vp,CONTROL);
1069 		vp->repeat = 1;
1070 	}
1071 /* NOTREACHED */
1072 	return(0);
1073 }
1074 
1075 /*{	CURSOR( new_current_physical )
1076  *
1077  *	This routine will position the virtual cursor at
1078  * physical column x in the window.
1079  *
1080 }*/
1081 
cursor(Vi_t * vp,register int x)1082 static void cursor(Vi_t *vp,register int x)
1083 {
1084 #if SHOPT_MULTIBYTE
1085 	while(physical[x]==MARKER)
1086 		x++;
1087 #endif /* SHOPT_MULTIBYTE */
1088 	cur_phys = ed_setcursor(vp->ed, physical, cur_phys,x,vp->first_wind);
1089 }
1090 
1091 /*{	DELETE( nchars, mode )
1092  *
1093  *	Delete nchars from the virtual space and leave cur_virt positioned
1094  * at cur_virt-1.
1095  *
1096  *	If mode	= 'c', do not save the characters deleted
1097  *		= 'd', save them in yankbuf and delete.
1098  *		= 'y', save them in yankbuf but do not delete.
1099  *
1100 }*/
1101 
cdelete(Vi_t * vp,register int nchars,int mode)1102 static void cdelete(Vi_t *vp,register int nchars, int mode)
1103 {
1104 	register int i;
1105 	register genchar *cp;
1106 
1107 	if( cur_virt < first_virt )
1108 	{
1109 		ed_ringbell();
1110 		return;
1111 	}
1112 	if( nchars > 0 )
1113 	{
1114 		cp = virtual+cur_virt;
1115 		vp->o_v_char = cp[0];
1116 		if( (cur_virt-- + nchars) > last_virt )
1117 		{
1118 			/*** set nchars to number actually deleted ***/
1119 			nchars = last_virt - cur_virt;
1120 		}
1121 
1122 		/*** save characters to be deleted ***/
1123 
1124 		if( mode != 'c' )
1125 		{
1126 			i = cp[nchars];
1127 			cp[nchars] = 0;
1128 			gencpy(yankbuf,cp);
1129 			cp[nchars] = i;
1130 		}
1131 
1132 		/*** now delete these characters ***/
1133 
1134 		if( mode != 'y' )
1135 		{
1136 			gencpy(cp,cp+nchars);
1137 			last_virt -= nchars;
1138 		}
1139 	}
1140 	return;
1141 }
1142 
1143 /*{	DEL_LINE( mode )
1144  *
1145  *	This routine will delete the line.
1146  *	mode = GOOD, do a save_v()
1147  *
1148 }*/
del_line(register Vi_t * vp,int mode)1149 static void del_line(register Vi_t *vp, int mode)
1150 {
1151 	if( last_virt == INVALID )
1152 		return;
1153 
1154 	if( mode == GOOD )
1155 		save_v(vp);
1156 
1157 	cur_virt = 0;
1158 	first_virt = 0;
1159 	cdelete(vp,last_virt+1, BAD);
1160 	refresh(vp,CONTROL);
1161 
1162 	cur_virt = INVALID;
1163 	cur_phys = 0;
1164 	vp->findchar = INVALID;
1165 	last_phys = INVALID;
1166 	last_virt = INVALID;
1167 	vp->last_wind = INVALID;
1168 	vp->first_wind = 0;
1169 	vp->o_v_char = '\0';
1170 	vp->ocur_phys = 0;
1171 	vp->ocur_virt = MAXCHAR;
1172 	vp->ofirst_wind = 0;
1173 	window[0] = '\0';
1174 	return;
1175 }
1176 
1177 /*{	DELMOTION( motion, mode )
1178  *
1179  *	Delete thru motion.
1180  *
1181  *	mode	= 'd', save deleted characters, delete
1182  *		= 'c', do not save characters, change
1183  *		= 'y', save characters, yank
1184  *
1185  *	Returns 1 if operation successful; else 0.
1186  *
1187 }*/
1188 
delmotion(Vi_t * vp,int motion,int mode)1189 static int delmotion(Vi_t *vp,int motion, int mode)
1190 {
1191 	register int begin, end, delta;
1192 	/* the following saves a register */
1193 
1194 	if( cur_virt == INVALID )
1195 		return(0);
1196 	if( mode != 'y' )
1197 		save_v(vp);
1198 	begin = cur_virt;
1199 
1200 	/*** fake out the motion routines by appending a blank ***/
1201 
1202 	virtual[++last_virt] = ' ';
1203 	end = mvcursor(vp,motion);
1204 	virtual[last_virt--] = 0;
1205 	if(!end)
1206 		return(0);
1207 
1208 	end = cur_virt;
1209 	if( mode=='c' && end>begin && strchr("wW", motion) )
1210 	{
1211 		/*** called by change operation, user really expects ***/
1212 		/* the effect of the eE commands, so back up to end of word */
1213 		while( end>begin && isblank(end-1) )
1214 			--end;
1215 		if( end == begin )
1216 			++end;
1217 	}
1218 
1219 	delta = end - begin;
1220 	if( delta >= 0 )
1221 	{
1222 		cur_virt = begin;
1223 		if( strchr("eE;,TtFf%", motion) )
1224 			++delta;
1225 	}
1226 	else
1227 	{
1228 		delta = -delta + (motion=='%');
1229 	}
1230 
1231 	cdelete(vp,delta, mode);
1232 	if( mode == 'y' )
1233 		cur_virt = begin;
1234 	return(1);
1235 }
1236 
1237 
1238 /*{	ENDWORD( nwords, cmd )
1239  *
1240  *	This routine will move cur_virt to the end of the nth word.
1241  *
1242 }*/
1243 
endword(Vi_t * vp,int nwords,register int cmd)1244 static void endword(Vi_t *vp, int nwords, register int cmd)
1245 {
1246 	register int tcur_virt = cur_virt;
1247 	while( nwords-- )
1248 	{
1249 		if( !isblank(tcur_virt) && tcur_virt<=last_virt )
1250 			++tcur_virt;
1251 		while( isblank(tcur_virt) && tcur_virt<=last_virt )
1252 			++tcur_virt;
1253 		if( cmd == 'E' )
1254 		{
1255 			while( !isblank(tcur_virt) && tcur_virt<=last_virt )
1256 				++tcur_virt;
1257 		}
1258 		else
1259 		{
1260 			if( isalph(tcur_virt) )
1261 				while( isalph(tcur_virt) && tcur_virt<=last_virt )
1262 					++tcur_virt;
1263 			else
1264 				while( !isalph(tcur_virt) && !isblank(tcur_virt)
1265 					&& tcur_virt<=last_virt )
1266 					++tcur_virt;
1267 		}
1268 		if( tcur_virt > first_virt )
1269 			tcur_virt--;
1270 	}
1271 	cur_virt = tcur_virt;
1272 	return;
1273 }
1274 
1275 /*{	FORWARD( nwords, cmd )
1276  *
1277  *	This routine will move cur_virt forward to the next nth word.
1278  *
1279 }*/
1280 
forward(Vi_t * vp,register int nwords,int cmd)1281 static void forward(Vi_t *vp,register int nwords, int cmd)
1282 {
1283 	register int tcur_virt = cur_virt;
1284 	while( nwords-- )
1285 	{
1286 		if( cmd == 'W' )
1287 		{
1288 			while( !isblank(tcur_virt) && tcur_virt < last_virt )
1289 				++tcur_virt;
1290 		}
1291 		else
1292 		{
1293 			if( isalph(tcur_virt) )
1294 			{
1295 				while( isalph(tcur_virt) && tcur_virt<last_virt )
1296 					++tcur_virt;
1297 			}
1298 			else
1299 			{
1300 				while( !isalph(tcur_virt) && !isblank(tcur_virt)
1301 					&& tcur_virt < last_virt )
1302 					++tcur_virt;
1303 			}
1304 		}
1305 		while( isblank(tcur_virt) && tcur_virt < last_virt )
1306 			++tcur_virt;
1307 	}
1308 	cur_virt = tcur_virt;
1309 	return;
1310 }
1311 
1312 
1313 
1314 /*{	GETCOUNT(c)
1315  *
1316  *	Set repeat to the user typed number and return the terminating
1317  * character.
1318  *
1319 }*/
1320 
getcount(register Vi_t * vp,register int c)1321 static int getcount(register Vi_t *vp,register int c)
1322 {
1323 	register int i;
1324 
1325 	/*** get any repeat count ***/
1326 
1327 	if( c == '0' )
1328 		return(c);
1329 
1330 	vp->repeat_set++;
1331 	i = 0;
1332 	while( digit(c) )
1333 	{
1334 		i = i*10 + c - '0';
1335 		c = ed_getchar(vp->ed,-1);
1336 	}
1337 
1338 	if( i > 0 )
1339 		vp->repeat *= i;
1340 	return(c);
1341 }
1342 
1343 
1344 /*{	GETLINE( mode )
1345  *
1346  *	This routine will fetch a line.
1347  *	mode	= APPEND, allow escape to cntlmode subroutine
1348  *		  appending characters.
1349  *		= REPLACE, allow escape to cntlmode subroutine
1350  *		  replacing characters.
1351  *		= SEARCH, no escape allowed
1352  *		= ESC, enter control mode immediately
1353  *
1354  *	The cursor will always be positioned after the last
1355  * char printed.
1356  *
1357  *	This routine returns when cr, nl, or (eof in column 0) is
1358  * received (column 0 is the first char position).
1359  *
1360 }*/
1361 
vigetline(register Vi_t * vp,register int mode)1362 static void vigetline(register Vi_t* vp,register int mode)
1363 {
1364 	register int c;
1365 	register int tmp;
1366 	int	max_virt=0, last_save=0;
1367 	genchar saveline[MAXLINE];
1368 	vp->addnl = 1;
1369 
1370 	if( mode == ESC )
1371 	{
1372 		/*** go directly to control mode ***/
1373 		goto escape;
1374 	}
1375 
1376 	for(;;)
1377 	{
1378 		if( (c=ed_getchar(vp->ed,mode==SEARCH?1:-2)) == usreof )
1379 			c = UEOF;
1380 		else if( c == usrerase )
1381 			c = UERASE;
1382 		else if( c == usrkill )
1383 			c = UKILL;
1384 		else if( c == editb.e_werase )
1385 			c = UWERASE;
1386 		else if( c == usrlnext )
1387 			c = ULNEXT;
1388 		else if(mode==SEARCH && c==editb.e_intr)
1389 			c = UINTR;
1390 
1391 		if( c == ULNEXT)
1392 		{
1393 			/*** implement ^V to escape next char ***/
1394 			c = ed_getchar(vp->ed,2);
1395 			append(vp,c, mode);
1396 			refresh(vp,INPUT);
1397 			continue;
1398 		}
1399 		if(c!='\t')
1400 			vp->ed->e_tabcount = 0;
1401 
1402 		switch( c )
1403 		{
1404 		case ESC:		/** enter control mode **/
1405 			if(!sh_isoption(vp->ed->sh,SH_VI))
1406 			{
1407 				append(vp,c, mode);
1408 				break;
1409 			}
1410 			if( mode == SEARCH )
1411 			{
1412 				ed_ringbell();
1413 				continue;
1414 			}
1415 			else
1416 			{
1417 	escape:
1418 				if( mode == REPLACE )
1419 				{
1420 					c = max_virt-cur_virt;
1421 					if(c > 0 && last_save>=cur_virt)
1422 					{
1423 						genncpy((&virtual[cur_virt]),&saveline[cur_virt],c);
1424 						if(last_virt>=last_save)
1425 							last_virt=last_save-1;
1426 						refresh(vp,INPUT);
1427 					}
1428 					--cur_virt;
1429 				}
1430 				tmp = cntlmode(vp);
1431 				if( tmp == ENTER || tmp == BIGVI )
1432 				{
1433 #if SHOPT_MULTIBYTE
1434 					vp->bigvi = (tmp==BIGVI);
1435 #endif /* SHOPT_MULTIBYTE */
1436 					return;
1437 				}
1438 				if( tmp == INSERT )
1439 				{
1440 					mode = APPEND;
1441 					continue;
1442 				}
1443 				mode = tmp;
1444 				if(mode==REPLACE)
1445 				{
1446 					c = last_save = last_virt+1;
1447 					if(c >= MAXLINE)
1448 						c = MAXLINE-1;
1449 					genncpy(saveline, virtual, c);
1450 				}
1451 			}
1452 			break;
1453 
1454 		case UINTR:
1455 				first_virt = 0;
1456 				cdelete(vp,cur_virt+1, BAD);
1457 				cur_virt = -1;
1458 				return;
1459 		case UERASE:		/** user erase char **/
1460 				/*** treat as backspace ***/
1461 
1462 		case '\b':		/** backspace **/
1463 			if(cur_virt>=0 && virtual[cur_virt] == '\\')
1464 			{
1465 				cdelete(vp,1, BAD);
1466 				append(vp,usrerase, mode);
1467 			}
1468 			else
1469 			{
1470 				if( mode==SEARCH && cur_virt==0 )
1471 				{
1472 					first_virt = 0;
1473 					cdelete(vp,1, BAD);
1474 					return;
1475 				}
1476 				if(mode==REPLACE || (last_save>0 && last_virt<=last_save))
1477 				{
1478 					if(cur_virt<=first_virt)
1479 						ed_ringbell();
1480 					else if(mode==REPLACE)
1481 						--cur_virt;
1482 					mode = REPLACE;
1483 					sync_cursor(vp);
1484 					continue;
1485 				}
1486 				else
1487 					cdelete(vp,1, BAD);
1488 			}
1489 			break;
1490 
1491 		case UWERASE:		/** delete back word **/
1492 			if( cur_virt > first_virt &&
1493 				!isblank(cur_virt) &&
1494 				!ispunct(virtual[cur_virt]) &&
1495 				isblank(cur_virt-1) )
1496 			{
1497 				cdelete(vp,1, BAD);
1498 			}
1499 			else
1500 			{
1501 				tmp = cur_virt;
1502 				backword(vp,1, 'W');
1503 				cdelete(vp,tmp - cur_virt + 1, BAD);
1504 			}
1505 			break;
1506 
1507 		case UKILL:		/** user kill line char **/
1508 			if( virtual[cur_virt] == '\\' )
1509 			{
1510 				cdelete(vp,1, BAD);
1511 				append(vp,usrkill, mode);
1512 			}
1513 			else
1514 			{
1515 				if( mode == SEARCH )
1516 				{
1517 					cur_virt = 1;
1518 					delmotion(vp, '$', BAD);
1519 				}
1520 				else if(first_virt)
1521 				{
1522 					tmp = cur_virt;
1523 					cur_virt = first_virt;
1524 					cdelete(vp,tmp - cur_virt + 1, BAD);
1525 				}
1526 				else
1527 					del_line(vp,GOOD);
1528 			}
1529 			break;
1530 
1531 		case UEOF:		/** eof char **/
1532 			if( cur_virt != INVALID )
1533 				continue;
1534 			vp->addnl = 0;
1535 
1536 		case '\n':		/** newline or return **/
1537 			if( mode != SEARCH )
1538 				save_last(vp);
1539 			refresh(vp,INPUT);
1540 			physical[++last_phys] = 0;
1541 			return;
1542 
1543 		case '\t':		/** command completion **/
1544 			if(mode!=SEARCH && (last_virt>=0||vp->ed->e_tabcount) && vp->ed->sh->nextprompt)
1545 			{
1546 				if(virtual[cur_virt]=='\\')
1547 				{
1548 					virtual[cur_virt] = '\t';
1549 					break;
1550 				}
1551 				if(vp->ed->e_tabcount==0)
1552 				{
1553 					ed_ungetchar(vp->ed,'\\');
1554 					vp->ed->e_tabcount=1;
1555 					goto escape;
1556 				}
1557 				else if(vp->ed->e_tabcount==1)
1558 				{
1559 					if(last_virt==0)
1560 #if 1
1561 						cur_virt = --last_virt;
1562 #endif
1563 					ed_ungetchar(vp->ed,'=');
1564 					goto escape;
1565 				}
1566 				vp->ed->e_tabcount = 0;
1567 			}
1568 			else
1569 				vp->ed->e_tabcount = 1;
1570 			/* FALL THRU*/
1571 		default:
1572 			if( mode == REPLACE )
1573 			{
1574 				if( cur_virt < last_virt )
1575 				{
1576 					replace(vp,c, 1);
1577 					if(cur_virt>max_virt)
1578 						max_virt = cur_virt;
1579 					continue;
1580 				}
1581 				cdelete(vp,1, BAD);
1582 				mode = APPEND;
1583 				max_virt = last_virt+3;
1584 			}
1585 			append(vp,c, mode);
1586 			break;
1587 		}
1588 		refresh(vp,INPUT);
1589 
1590 	}
1591 }
1592 
1593 /*{	MVCURSOR( motion )
1594  *
1595  *	This routine will move the virtual cursor according to motion
1596  * for repeat times.
1597  *
1598  * It returns GOOD if successful; else BAD.
1599  *
1600 }*/
1601 
mvcursor(register Vi_t * vp,register int motion)1602 static int mvcursor(register Vi_t* vp,register int motion)
1603 {
1604 	register int count;
1605 	register int tcur_virt;
1606 	register int incr = -1;
1607 	register int bound = 0;
1608 
1609 	switch(motion)
1610 	{
1611 		/***** Cursor move commands *****/
1612 
1613 	case '0':		/** First column **/
1614 		tcur_virt = 0;
1615 		break;
1616 
1617 	case '^':		/** First nonblank character **/
1618 		tcur_virt = first_virt;
1619 		while( isblank(tcur_virt) && tcur_virt < last_virt )
1620 			++tcur_virt;
1621 		break;
1622 
1623 	case '|':
1624 		tcur_virt = vp->repeat-1;
1625 		if(tcur_virt <= last_virt)
1626 			break;
1627 		/* fall through */
1628 
1629 	case '$':		/** End of line **/
1630 		tcur_virt = last_virt;
1631 		break;
1632 
1633 	case 'O':
1634 	case '[':
1635 		switch(motion=getcount(vp,ed_getchar(vp->ed,-1)))
1636 		{
1637 		    case 'A':
1638 #if SHOPT_EDPREDICT
1639 			if(!vp->ed->hlist && cur_virt>=0  && cur_virt<(SEARCHSIZE-2) && cur_virt == last_virt)
1640 #else
1641 			if(cur_virt>=0  && cur_virt<(SEARCHSIZE-2) && cur_virt == last_virt)
1642 #endif /* SHOPT_EDPREDICT */
1643 			{
1644 				virtual[last_virt + 1] = '\0';
1645 #if SHOPT_MULTIBYTE
1646 				ed_external(virtual,lsearch+1);
1647 #else
1648 				strcpy(lsearch+1,virtual);
1649 #endif /* SHOPT_MULTIBYTE */
1650 				*lsearch = '^';
1651 				vp->direction = -2;
1652 				ed_ungetchar(vp->ed,'n');
1653 			}
1654 			else if(cur_virt==0 && vp->direction == -2)
1655 				ed_ungetchar(vp->ed,'n');
1656 			else
1657 				ed_ungetchar(vp->ed,'k');
1658 			return(1);
1659 		    case 'B':
1660 			ed_ungetchar(vp->ed,'j');
1661 			return(1);
1662 		    case 'C':
1663 			motion = last_virt;
1664 			incr = 1;
1665 			goto walk;
1666 		    case 'D':
1667 			motion = first_virt;
1668 			goto walk;
1669 		    case 'H':
1670 			tcur_virt = 0;
1671 			break;
1672 		    case 'Y':
1673 			tcur_virt = last_virt;
1674 			break;
1675 		    default:
1676 			ed_ungetchar(vp->ed,motion);
1677 			return(0);
1678 		}
1679 		break;
1680 
1681 	case 'h':		/** Left one **/
1682 	case '\b':
1683 		motion = first_virt;
1684 		goto walk;
1685 
1686 	case ' ':
1687 	case 'l':		/** Right one **/
1688 		motion = last_virt;
1689 		incr = 1;
1690 	walk:
1691 		tcur_virt = cur_virt;
1692 		if( incr*tcur_virt < motion)
1693 		{
1694 			tcur_virt += vp->repeat*incr;
1695 			if( incr*tcur_virt > motion)
1696 				tcur_virt = motion;
1697 		}
1698 		else
1699 			return(0);
1700 		break;
1701 
1702 	case 'B':
1703 	case 'b':		/** back word **/
1704 		tcur_virt = cur_virt;
1705 		backword(vp,vp->repeat, motion);
1706 		if( cur_virt == tcur_virt )
1707 			return(0);
1708 		return(1);
1709 
1710 	case 'E':
1711 	case 'e':		/** end of word **/
1712 		tcur_virt = cur_virt;
1713 		if(tcur_virt >=0)
1714 			endword(vp, vp->repeat, motion);
1715 		if( cur_virt == tcur_virt )
1716 			return(0);
1717 		return(1);
1718 
1719 	case ',':		/** reverse find old char **/
1720 	case ';':		/** find old char **/
1721 		switch(vp->last_find)
1722 		{
1723 		case 't':
1724 		case 'f':
1725 			if(motion==';')
1726 			{
1727 				bound = last_virt;
1728 				incr = 1;
1729 			}
1730 			goto find_b;
1731 
1732 		case 'T':
1733 		case 'F':
1734 			if(motion==',')
1735 			{
1736 				bound = last_virt;
1737 				incr = 1;
1738 			}
1739 			goto find_b;
1740 
1741 		default:
1742 			return(0);
1743 		}
1744 
1745 
1746 	case 't':		/** find up to new char forward **/
1747 	case 'f':		/** find new char forward **/
1748 		bound = last_virt;
1749 		incr = 1;
1750 
1751 	case 'T':		/** find up to new char backward **/
1752 	case 'F':		/** find new char backward **/
1753 		vp->last_find = motion;
1754 		if((vp->findchar=getrchar(vp))==ESC)
1755 			return(1);
1756 find_b:
1757 		tcur_virt = cur_virt;
1758 		count = vp->repeat;
1759 		while( count-- )
1760 		{
1761 			while( incr*(tcur_virt+=incr) <= bound
1762 				&& virtual[tcur_virt] != vp->findchar );
1763 			if( incr*tcur_virt > bound )
1764 			{
1765 				return(0);
1766 			}
1767 		}
1768 		if( fold(vp->last_find) == 'T' )
1769 			tcur_virt -= incr;
1770 		break;
1771 
1772         case '%':
1773 	{
1774 		int nextmotion;
1775 		int nextc;
1776 		tcur_virt = cur_virt;
1777 		while( tcur_virt <= last_virt
1778 			&& strchr(paren_chars,virtual[tcur_virt])==(char*)0)
1779 				tcur_virt++;
1780 		if(tcur_virt > last_virt )
1781 			return(0);
1782 		nextc = virtual[tcur_virt];
1783 		count = strchr(paren_chars,nextc)-paren_chars;
1784 		if(count < 3)
1785 		{
1786 			incr = 1;
1787 			bound = last_virt;
1788 			nextmotion = paren_chars[count+3];
1789 		}
1790 		else
1791 			nextmotion = paren_chars[count-3];
1792 		count = 1;
1793 		while(count >0 &&  incr*(tcur_virt+=incr) <= bound)
1794 		{
1795 		        if(virtual[tcur_virt] == nextmotion)
1796 		        	count--;
1797 		        else if(virtual[tcur_virt]==nextc)
1798 		        	count++;
1799 		}
1800 		if(count)
1801 			return(0);
1802 		break;
1803 	}
1804 
1805 	case 'W':
1806 	case 'w':		/** forward word **/
1807 		tcur_virt = cur_virt;
1808 		forward(vp,vp->repeat, motion);
1809 		if( tcur_virt == cur_virt )
1810 			return(0);
1811 		return(1);
1812 
1813 	default:
1814 		return(0);
1815 	}
1816 	cur_virt = tcur_virt;
1817 
1818 	return(1);
1819 }
1820 
1821 /*
1822  * print a string
1823  */
1824 
pr_string(register Vi_t * vp,register const char * sp)1825 static void pr_string(register Vi_t *vp, register const char *sp)
1826 {
1827 	/*** copy string sp ***/
1828 	register char *ptr = editb.e_outptr;
1829 	while(*sp)
1830 		*ptr++ = *sp++;
1831 	editb.e_outptr = ptr;
1832 	return;
1833 }
1834 
1835 /*{	PUTSTRING( column, nchars )
1836  *
1837  *	Put nchars starting at column of physical into the workspace
1838  * to be printed.
1839  *
1840 }*/
1841 
putstring(register Vi_t * vp,register int col,register int nchars)1842 static void putstring(register Vi_t *vp,register int col, register int nchars)
1843 {
1844 	while( nchars-- )
1845 		putchar(physical[col++]);
1846 	return;
1847 }
1848 
1849 /*{	REFRESH( mode )
1850  *
1851  *	This routine will refresh the crt so the physical image matches
1852  * the virtual image and display the proper window.
1853  *
1854  *	mode	= CONTROL, refresh in control mode, ie. leave cursor
1855  *			positioned at last char printed.
1856  *		= INPUT, refresh in input mode; leave cursor positioned
1857  *			after last char printed.
1858  *		= TRANSLATE, perform virtual to physical translation
1859  *			and adjust left margin only.
1860  *
1861  *		+-------------------------------+
1862  *		|   | |    virtual	  | |   |
1863  *		+-------------------------------+
1864  *		  cur_virt		last_virt
1865  *
1866  *		+-----------------------------------------------+
1867  *		|	  | |	        physical	 | |    |
1868  *		+-----------------------------------------------+
1869  *			cur_phys			last_phys
1870  *
1871  *				0			w_size - 1
1872  *				+-----------------------+
1873  *				| | |  window		|
1874  *				+-----------------------+
1875  *				cur_window = cur_phys - first_wind
1876 }*/
1877 
refresh(register Vi_t * vp,int mode)1878 static void refresh(register Vi_t* vp, int mode)
1879 {
1880 	register int p;
1881 	register int v;
1882 	register int first_w = vp->first_wind;
1883 	int p_differ;
1884 	int new_lw;
1885 	int ncur_phys;
1886 	int opflag;			/* search optimize flag */
1887 
1888 #	define	w	v
1889 
1890 	/*** find out if it's necessary to start translating at beginning ***/
1891 
1892 	if(lookahead>0)
1893 	{
1894 		p = previous[lookahead-1];
1895 		if(p != ESC && p != '\n' && p != '\r')
1896 			mode = TRANSLATE;
1897 	}
1898 	v = cur_virt;
1899 #if SHOPT_EDPREDICT
1900 	if(mode==INPUT && v>0 && virtual[0]=='#' && v==last_virt && virtual[v]!='*' && sh_isoption(vp->ed->sh,SH_VI))
1901 	{
1902 		int		n;
1903 		virtual[last_virt+1] = 0;
1904 #   if SHOPT_MULTIBYTE
1905 		ed_external(virtual,(char*)virtual);
1906 #   endif /* SHOPT_MULTIBYTE */
1907 		n = ed_histgen(vp->ed,(char*)virtual);
1908 #   if SHOPT_MULTIBYTE
1909 		ed_internal((char*)virtual,virtual);
1910 #   endif /* SHOPT_MULTIBYTE */
1911 		if(vp->ed->hlist)
1912 		{
1913 			ed_histlist(vp->ed,n);
1914 			pr_string(vp,Prompt);
1915 			vp->ocur_virt = INVALID;
1916 			ed_setcursor(vp->ed,physical,0,cur_phys,0);
1917 		}
1918 		else
1919 			ed_ringbell();
1920 	}
1921 	else if(mode==INPUT && v<=1 && vp->ed->hlist)
1922 		ed_histlist(vp->ed,0);
1923 #endif /* SHOPT_EDPREDICT */
1924 	if( v<vp->ocur_virt || vp->ocur_virt==INVALID
1925 		|| ( v==vp->ocur_virt
1926 			&& (!is_print(virtual[v]) || !is_print(vp->o_v_char))) )
1927 	{
1928 		opflag = 0;
1929 		p = 0;
1930 		v = 0;
1931 	}
1932 	else
1933 	{
1934 		opflag = 1;
1935 		p = vp->ocur_phys;
1936 		v = vp->ocur_virt;
1937 		if( !is_print(virtual[v]) )
1938 		{
1939 			/*** avoid double ^'s ***/
1940 			++p;
1941 			++v;
1942 		}
1943 	}
1944 	virtual[last_virt+1] = 0;
1945 	ncur_phys = ed_virt_to_phys(vp->ed,virtual,physical,cur_virt,v,p);
1946 	p = genlen(physical);
1947 	if( --p < 0 )
1948 		last_phys = 0;
1949 	else
1950 		last_phys = p;
1951 
1952 	/*** see if this was a translate only ***/
1953 
1954 	if( mode == TRANSLATE )
1955 		return;
1956 
1957 	/*** adjust left margin if necessary ***/
1958 
1959 	if( ncur_phys<first_w || ncur_phys>=(first_w + w_size) )
1960 	{
1961 		cursor(vp,first_w);
1962 		first_w = ncur_phys - (w_size>>1);
1963 		if( first_w < 0 )
1964 			first_w = 0;
1965 		vp->first_wind = cur_phys = first_w;
1966 	}
1967 
1968 	/*** attempt to optimize search somewhat to find ***/
1969 	/*** out where physical and window images differ ***/
1970 
1971 	if( first_w==vp->ofirst_wind && ncur_phys>=vp->ocur_phys && opflag==1 )
1972 	{
1973 		p = vp->ocur_phys;
1974 		w = p - first_w;
1975 	}
1976 	else
1977 	{
1978 		p = first_w;
1979 		w = 0;
1980 	}
1981 
1982 	for(; (p<=last_phys && w<=vp->last_wind); ++p, ++w)
1983 	{
1984 		if( window[w] != physical[p] )
1985 			break;
1986 	}
1987 	p_differ = p;
1988 
1989 	if( (p>last_phys || p>=first_w+w_size) && w>vp->last_wind
1990 		&& cur_virt==vp->ocur_virt )
1991 	{
1992 		/*** images are identical ***/
1993 		return;
1994 	}
1995 
1996 	/*** copy the physical image to the window image ***/
1997 
1998 	if( last_virt != INVALID )
1999 	{
2000 		while( p <= last_phys && w < w_size )
2001 			window[w++] = physical[p++];
2002 	}
2003 	new_lw = w;
2004 
2005 	/*** erase trailing characters if needed ***/
2006 
2007 	while( w <= vp->last_wind )
2008 		window[w++] = ' ';
2009 	vp->last_wind = --w;
2010 
2011 	p = p_differ;
2012 
2013 	/*** move cursor to start of difference ***/
2014 
2015 	cursor(vp,p);
2016 
2017 	/*** and output difference ***/
2018 
2019 	w = p - first_w;
2020 	while( w <= vp->last_wind )
2021 		putchar(window[w++]);
2022 
2023 	cur_phys = w + first_w;
2024 	vp->last_wind = --new_lw;
2025 
2026 	if( last_phys >= w_size )
2027 	{
2028 		if( first_w == 0 )
2029 			vp->long_char = '>';
2030 		else if( last_phys < (first_w+w_size) )
2031 			vp->long_char = '<';
2032 		else
2033 			vp->long_char = '*';
2034 	}
2035 	else
2036 		vp->long_char = ' ';
2037 
2038 	if( vp->long_line != vp->long_char )
2039 	{
2040 		/*** indicate lines longer than window ***/
2041 		while( w++ < w_size )
2042 		{
2043 			putchar(' ');
2044 			++cur_phys;
2045 		}
2046 		putchar(vp->long_char);
2047 		++cur_phys;
2048 		vp->long_line = vp->long_char;
2049 	}
2050 
2051 	if(vp->ed->e_multiline &&  vp->ofirst_wind==INVALID && !vp->ed->e_nocrnl)
2052 		ed_setcursor(vp->ed, physical, last_phys+1, last_phys+1, -1);
2053 	vp->ed->e_nocrnl = 0;
2054 	vp->ocur_phys = ncur_phys;
2055 	vp->ocur_virt = cur_virt;
2056 	vp->ofirst_wind = first_w;
2057 
2058 	if( mode==INPUT && cur_virt>INVALID )
2059 		++ncur_phys;
2060 
2061 	cursor(vp,ncur_phys);
2062 	ed_flush(vp->ed);
2063 	return;
2064 }
2065 
2066 /*{	REPLACE( char, increment )
2067  *
2068  *	Replace the cur_virt character with char.  This routine attempts
2069  * to avoid using refresh().
2070  *
2071  *	increment	= 1, increment cur_virt after replacement.
2072  *			= 0, leave cur_virt where it is.
2073  *
2074 }*/
2075 
replace(register Vi_t * vp,register int c,register int increment)2076 static void replace(register Vi_t *vp, register int c, register int increment)
2077 {
2078 	register int cur_window;
2079 
2080 	if( cur_virt == INVALID )
2081 	{
2082 		/*** can't replace invalid cursor ***/
2083 		ed_ringbell();
2084 		return;
2085 	}
2086 	cur_window = cur_phys - vp->first_wind;
2087 	if( vp->ocur_virt == INVALID || !is_print(c)
2088 		|| !is_print(virtual[cur_virt])
2089 		|| !is_print(vp->o_v_char)
2090 #if SHOPT_MULTIBYTE
2091 		|| !iswascii(c) || mbwidth(vp->o_v_char)>1
2092 		|| !iswascii(virtual[cur_virt])
2093 #endif /* SHOPT_MULTIBYTE */
2094 		|| (increment && (cur_window==w_size-1)
2095 			|| !is_print(virtual[cur_virt+1])) )
2096 	{
2097 		/*** must use standard refresh routine ***/
2098 
2099 		cdelete(vp,1, BAD);
2100 		append(vp,c, APPEND);
2101 		if( increment && cur_virt<last_virt )
2102 			++cur_virt;
2103 		refresh(vp,CONTROL);
2104 	}
2105 	else
2106 	{
2107 		virtual[cur_virt] = c;
2108 		physical[cur_phys] = c;
2109 		window[cur_window] = c;
2110 		putchar(c);
2111 		if(increment)
2112 		{
2113 			c = virtual[++cur_virt];
2114 			++cur_phys;
2115 		}
2116 		else
2117 		{
2118 			putchar('\b');
2119 		}
2120 		vp->o_v_char = c;
2121 		ed_flush(vp->ed);
2122 	}
2123 	return;
2124 }
2125 
2126 /*{	RESTORE_V()
2127  *
2128  *	Restore the contents of virtual space from u_space.
2129  *
2130 }*/
2131 
restore_v(register Vi_t * vp)2132 static void restore_v(register Vi_t *vp)
2133 {
2134 	register int tmpcol;
2135 	genchar tmpspace[MAXLINE];
2136 
2137 	if( vp->u_column == INVALID-1 )
2138 	{
2139 		/*** never saved anything ***/
2140 		ed_ringbell();
2141 		return;
2142 	}
2143 	gencpy(tmpspace, vp->u_space);
2144 	tmpcol = vp->u_column;
2145 	save_v(vp);
2146 	gencpy(virtual, tmpspace);
2147 	cur_virt = tmpcol;
2148 	last_virt = genlen(tmpspace) - 1;
2149 	vp->ocur_virt = MAXCHAR;	/** invalidate refresh optimization **/
2150 	return;
2151 }
2152 
2153 /*{	SAVE_LAST()
2154  *
2155  *	If the user has typed something, save it in last line.
2156  *
2157 }*/
2158 
save_last(register Vi_t * vp)2159 static void save_last(register Vi_t* vp)
2160 {
2161 	register int i;
2162 
2163 	if( (i = cur_virt - first_virt + 1) > 0 )
2164 	{
2165 		/*** save last thing user typed ***/
2166 		if(i >= MAXLINE)
2167 			i = MAXLINE-1;
2168 		genncpy(vp->lastline, (&virtual[first_virt]), i);
2169 		vp->lastline[i] = '\0';
2170 	}
2171 	return;
2172 }
2173 
2174 /*{	SAVE_V()
2175  *
2176  *	This routine will save the contents of virtual in u_space.
2177  *
2178 }*/
2179 
save_v(register Vi_t * vp)2180 static void save_v(register Vi_t *vp)
2181 {
2182 	if(!inmacro)
2183 	{
2184 		virtual[last_virt + 1] = '\0';
2185 		gencpy(vp->u_space, virtual);
2186 		vp->u_column = cur_virt;
2187 	}
2188 	return;
2189 }
2190 
2191 /*{	SEARCH( mode )
2192  *
2193  *	Search history file for regular expression.
2194  *
2195  *	mode	= '/'	require search string and search new to old
2196  *	mode	= '?'	require search string and search old to new
2197  *	mode	= 'N'	repeat last search in reverse direction
2198  *	mode	= 'n'	repeat last search
2199  *
2200 }*/
2201 
2202 /*
2203  * search for <string> in the current command
2204  */
curline_search(Vi_t * vp,const char * string)2205 static int curline_search(Vi_t *vp, const char *string)
2206 {
2207 	register size_t len=strlen(string);
2208 	register const char *dp,*cp=string, *dpmax;
2209 #if SHOPT_MULTIBYTE
2210 	ed_external(vp->u_space,(char*)vp->u_space);
2211 #endif /* SHOPT_MULTIBYTE */
2212 	for(dp=(char*)vp->u_space,dpmax=dp+strlen(dp)-len; dp<=dpmax; dp++)
2213 	{
2214 		if(*dp==*cp && memcmp(cp,dp,len)==0)
2215 			return(dp-(char*)vp->u_space);
2216 	}
2217 #if SHOPT_MULTIBYTE
2218 	ed_internal((char*)vp->u_space,vp->u_space);
2219 #endif /* SHOPT_MULTIBYTE */
2220 	return(-1);
2221 }
2222 
search(register Vi_t * vp,register int mode)2223 static int search(register Vi_t* vp,register int mode)
2224 {
2225 	register int new_direction;
2226 	register int oldcurhline;
2227 	register int i;
2228 	Histloc_t  location;
2229 
2230 	if( vp->direction == -2 && mode != 'n')
2231 		vp->direction = -1;
2232 	if( mode == '/' || mode == '?')
2233 	{
2234 		/*** new search expression ***/
2235 		del_line(vp,BAD);
2236 		append(vp,mode, APPEND);
2237 		refresh(vp,INPUT);
2238 		first_virt = 1;
2239 		vigetline(vp,SEARCH);
2240 		first_virt = 0;
2241 		virtual[last_virt + 1] = '\0';	/*** make null terminated ***/
2242 		vp->direction = mode=='/' ? -1 : 1;
2243 	}
2244 
2245 	if( cur_virt == INVALID )
2246 	{
2247 		/*** no operation ***/
2248 		return(ABORT);
2249 	}
2250 
2251 	if( cur_virt==0 ||  fold(mode)=='N' )
2252 	{
2253 		/*** user wants repeat of last search ***/
2254 		del_line(vp,BAD);
2255 		strcpy( ((char*)virtual)+1, lsearch);
2256 #if SHOPT_MULTIBYTE
2257 		*((char*)virtual) = '/';
2258 		ed_internal((char*)virtual,virtual);
2259 #endif /* SHOPT_MULTIBYTE */
2260 	}
2261 
2262 	if( mode == 'N' )
2263 		new_direction = -vp->direction;
2264 	else
2265 		new_direction = vp->direction;
2266 
2267 
2268 	/*** now search ***/
2269 
2270 	oldcurhline = curhline;
2271 #if SHOPT_MULTIBYTE
2272 	ed_external(virtual,(char*)virtual);
2273 #endif /* SHOPT_MULTIBYTE */
2274 	if(mode=='?' && (i=curline_search(vp,((char*)virtual)+1))>=0)
2275 	{
2276 		location.hist_command = curhline;
2277 		location.hist_char = i;
2278 	}
2279 	else
2280 	{
2281 		i = INVALID;
2282 		if( new_direction==1 && curhline >= histmax )
2283 			curhline = histmin + 1;
2284 		location = hist_find(shgd->hist_ptr,((char*)virtual)+1, curhline, 1, new_direction);
2285 	}
2286 	cur_virt = i;
2287 	strncpy(lsearch, ((char*)virtual)+1, SEARCHSIZE);
2288 	lsearch[SEARCHSIZE-1] = 0;
2289 	if( (curhline=location.hist_command) >=0 )
2290 	{
2291 		vp->ocur_virt = INVALID;
2292 		return(GOOD);
2293 	}
2294 
2295 	/*** could not find matching line ***/
2296 
2297 	curhline = oldcurhline;
2298 	return(BAD);
2299 }
2300 
2301 /*{	SYNC_CURSOR()
2302  *
2303  *	This routine will move the physical cursor to the same
2304  * column as the virtual cursor.
2305  *
2306 }*/
2307 
sync_cursor(register Vi_t * vp)2308 static void sync_cursor(register Vi_t *vp)
2309 {
2310 	register int p;
2311 	register int v;
2312 	register int c;
2313 	int new_phys;
2314 
2315 	if( cur_virt == INVALID )
2316 		return;
2317 
2318 	/*** find physical col that corresponds to virtual col ***/
2319 
2320 	new_phys = 0;
2321 	if(vp->first_wind==vp->ofirst_wind && cur_virt>vp->ocur_virt && vp->ocur_virt!=INVALID)
2322 	{
2323 		/*** try to optimize search a little ***/
2324 		p = vp->ocur_phys + 1;
2325 #if SHOPT_MULTIBYTE
2326 		while(physical[p]==MARKER)
2327 			p++;
2328 #endif /* SHOPT_MULTIBYTE */
2329 		v = vp->ocur_virt + 1;
2330 	}
2331 	else
2332 	{
2333 		p = 0;
2334 		v = 0;
2335 	}
2336 	for(; v <= last_virt; ++p, ++v)
2337 	{
2338 #if SHOPT_MULTIBYTE
2339 		int d;
2340 		c = virtual[v];
2341 		if((d = mbwidth(c)) > 1)
2342 		{
2343 			if( v != cur_virt )
2344 				p += (d-1);
2345 		}
2346 		else if(!iswprint(c))
2347 #else
2348 		c = virtual[v];
2349 		if(!isprint(c))
2350 #endif	/* SHOPT_MULTIBYTE */
2351 		{
2352 			if( c == '\t' )
2353 			{
2354 				p -= ((p+editb.e_plen)%TABSIZE);
2355 				p += (TABSIZE-1);
2356 			}
2357 			else
2358 			{
2359 				++p;
2360 			}
2361 		}
2362 		if( v == cur_virt )
2363 		{
2364 			new_phys = p;
2365 			break;
2366 		}
2367 	}
2368 
2369 	if( new_phys < vp->first_wind || new_phys >= vp->first_wind + w_size )
2370 	{
2371 		/*** asked to move outside of window ***/
2372 
2373 		window[0] = '\0';
2374 		refresh(vp,CONTROL);
2375 		return;
2376 	}
2377 
2378 	cursor(vp,new_phys);
2379 	ed_flush(vp->ed);
2380 	vp->ocur_phys = cur_phys;
2381 	vp->ocur_virt = cur_virt;
2382 	vp->o_v_char = virtual[vp->ocur_virt];
2383 
2384 	return;
2385 }
2386 
2387 /*{	TEXTMOD( command, mode )
2388  *
2389  *	Modify text operations.
2390  *
2391  *	mode != 0, repeat previous operation
2392  *
2393 }*/
2394 
textmod(register Vi_t * vp,register int c,int mode)2395 static int textmod(register Vi_t *vp,register int c, int mode)
2396 {
2397 	register int i;
2398 	register genchar *p = vp->lastline;
2399 	register int trepeat = vp->repeat;
2400 	genchar *savep;
2401 	int	ch;
2402 
2403 	if(mode && (fold(vp->lastmotion)=='F' || fold(vp->lastmotion)=='T'))
2404 		vp->lastmotion = ';';
2405 
2406 	if( fold(c) == 'P' )
2407 	{
2408 		/*** change p from lastline to yankbuf ***/
2409 		p = yankbuf;
2410 	}
2411 
2412 addin:
2413 	switch( c )
2414 	{
2415 			/***** Input commands *****/
2416 
2417 #if KSHELL
2418         case '\t':
2419 		if(vp->ed->e_tabcount!=1)
2420 			return(BAD);
2421 		c = '=';
2422 	case '*':		/** do file name expansion in place **/
2423 	case '\\':		/** do file name completion in place **/
2424 		if( cur_virt == INVALID )
2425 			return(BAD);
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 		ch = c;
2433 		if(mode>=0 && c=='\\' && virtual[mode+1]=='/')
2434 			c = '=';
2435 		if(ed_expand(vp->ed,(char*)virtual, &cur_virt, &last_virt, ch, vp->repeat_set?vp->repeat:-1)<0)
2436 		{
2437 			if(vp->ed->e_tabcount)
2438 			{
2439 				vp->ed->e_tabcount=2;
2440 				ed_ungetchar(vp->ed,'\t');
2441 				--last_virt;
2442 				return(APPEND);
2443 			}
2444 			last_virt = i;
2445 			ed_ringbell();
2446 		}
2447 		else if((c=='=' || (c=='\\'&&virtual[last_virt]=='/')) && !vp->repeat_set)
2448 		{
2449 			vp->nonewline++;
2450 			ed_ungetchar(vp->ed,cntl('L'));
2451 			return(GOOD);
2452 		}
2453 		else
2454 		{
2455 			--cur_virt;
2456 			--last_virt;
2457 			vp->ocur_virt = MAXCHAR;
2458 			if(c=='=' || (mode<cur_virt && virtual[cur_virt]=='/'))
2459 				vp->ed->e_tabcount = 0;
2460 			return(APPEND);
2461 		}
2462 		break;
2463 
2464 	case '@':		/** macro expansion **/
2465 		if( mode )
2466 			c = vp->lastmacro;
2467 		else
2468 			if((c=getrchar(vp))==ESC)
2469 				return(GOOD);
2470 		if(!inmacro)
2471 			vp->lastmacro = c;
2472 		if(ed_macro(vp->ed,c))
2473 		{
2474 			save_v(vp);
2475 			inmacro++;
2476 			return(GOOD);
2477 		}
2478 		ed_ringbell();
2479 		return(BAD);
2480 
2481 #endif	/* KSHELL */
2482 	case '_':		/** append last argument of prev command **/
2483 		save_v(vp);
2484 		{
2485 			genchar tmpbuf[MAXLINE];
2486 			if(vp->repeat_set==0)
2487 				vp->repeat = -1;
2488 			p = (genchar*)hist_word((char*)tmpbuf,MAXLINE,vp->repeat);
2489 			if(p==0)
2490 			{
2491 				ed_ringbell();
2492 				break;
2493 			}
2494 #if SHOPT_MULTIBYTE
2495 			ed_internal((char*)p,tmpbuf);
2496 			p = tmpbuf;
2497 #endif /* SHOPT_MULTIBYTE */
2498 			i = ' ';
2499 			do
2500 			{
2501 				append(vp,i,APPEND);
2502 			}
2503 			while(i = *p++);
2504 			return(APPEND);
2505 		}
2506 
2507 	case 'A':		/** append to end of line **/
2508 		cur_virt = last_virt;
2509 		sync_cursor(vp);
2510 
2511 	case 'a':		/** append **/
2512 		if( fold(mode) == 'A' )
2513 		{
2514 			c = 'p';
2515 			goto addin;
2516 		}
2517 		save_v(vp);
2518 		if( cur_virt != INVALID )
2519 		{
2520 			first_virt = cur_virt + 1;
2521 			cursor(vp,cur_phys + 1);
2522 			ed_flush(vp->ed);
2523 		}
2524 		return(APPEND);
2525 
2526 	case 'I':		/** insert at beginning of line **/
2527 		cur_virt = first_virt;
2528 		sync_cursor(vp);
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 
2606 	case 'p':		/** print **/
2607 		if( p[0] == '\0' )
2608 			return(BAD);
2609 
2610 		if( mode != 's' && mode != 'c' )
2611 		{
2612 			save_v(vp);
2613 			if( c == 'P' )
2614 			{
2615 				/*** fix stored cur_virt ***/
2616 				++vp->u_column;
2617 			}
2618 		}
2619 		if( mode == 'R' )
2620 			mode = REPLACE;
2621 		else
2622 			mode = APPEND;
2623 		savep = p;
2624 		for(i=0; i<trepeat; ++i)
2625 		{
2626 			while(c= *p++)
2627 				append(vp,c,mode);
2628 			p = savep;
2629 		}
2630 		break;
2631 
2632 	case 'R':		/* Replace many chars **/
2633 		if( mode == 'R' )
2634 		{
2635 			c = 'P';
2636 			goto addin;
2637 		}
2638 		save_v(vp);
2639 		if( cur_virt != INVALID )
2640 			first_virt = cur_virt;
2641 		return(REPLACE);
2642 
2643 	case 'r':		/** replace **/
2644 		if( mode )
2645 			c = *p;
2646 		else
2647 			if((c=getrchar(vp))==ESC)
2648 				return(GOOD);
2649 		*p = c;
2650 		save_v(vp);
2651 		while(trepeat--)
2652 			replace(vp,c, trepeat!=0);
2653 		return(GOOD);
2654 
2655 	case 'S':		/** Substitute line - cc **/
2656 		c = 'c';
2657 		goto chgeol;
2658 
2659 	case 's':		/** substitute **/
2660 		save_v(vp);
2661 		cdelete(vp,vp->repeat, BAD);
2662 		if( mode )
2663 		{
2664 			c = 'p';
2665 			trepeat = 1;
2666 			goto addin;
2667 		}
2668 		first_virt = cur_virt + 1;
2669 		return(APPEND);
2670 
2671 	case 'Y':		/** Yank to end of line **/
2672 		c = '$';
2673 		goto yankeol;
2674 
2675 	case 'y':		/** yank thru motion **/
2676 		if( mode )
2677 			c = vp->lastmotion;
2678 		else
2679 			c = getcount(vp,ed_getchar(vp->ed,-1));
2680 yankeol:
2681 		vp->lastmotion = c;
2682 		if( c == 'y' )
2683 		{
2684 			gencpy(yankbuf, virtual);
2685 		}
2686 		else if(!delmotion(vp, c, 'y'))
2687 		{
2688 			return(BAD);
2689 		}
2690 		break;
2691 
2692 	case 'x':		/** delete repeat chars forward - dl **/
2693 		c = 'l';
2694 		goto deleol;
2695 
2696 	case 'X':		/** delete repeat chars backward - dh **/
2697 		c = 'h';
2698 		goto deleol;
2699 
2700 	case '~':		/** invert case and advance **/
2701 		if( cur_virt != INVALID )
2702 		{
2703 			save_v(vp);
2704 			i = INVALID;
2705 			while(trepeat-->0 && i!=cur_virt)
2706 			{
2707 				i = cur_virt;
2708 				c = virtual[cur_virt];
2709 #if SHOPT_MULTIBYTE
2710 				if((c&~STRIP)==0)
2711 #endif /* SHOPT_MULTIBYTE */
2712 				if( isupper(c) )
2713 					c = tolower(c);
2714 				else if( islower(c) )
2715 					c = toupper(c);
2716 				replace(vp,c, 1);
2717 			}
2718 			return(GOOD);
2719 		}
2720 		else
2721 			return(BAD);
2722 
2723 	default:
2724 		return(BAD);
2725 	}
2726 	refresh(vp,CONTROL);
2727 	return(GOOD);
2728 }
2729 
2730 
2731 #if SHOPT_MULTIBYTE
_isalph(register int v)2732     static int _isalph(register int v)
2733     {
2734 #ifdef _lib_iswalnum
2735 	return(iswalnum(v) || v=='_');
2736 #else
2737 	return((v&~STRIP) || isalnum(v) || v=='_');
2738 #endif
2739     }
2740 
2741 
_isblank(register int v)2742     static int _isblank(register int v)
2743     {
2744 	return((v&~STRIP)==0 && isspace(v));
2745     }
2746 
_ismetach(register int v)2747     static int _ismetach(register int v)
2748     {
2749 	return((v&~STRIP)==0 && ismeta(v));
2750     }
2751 
2752 #endif	/* SHOPT_MULTIBYTE */
2753 
2754 /*
2755  * get a character, after ^V processing
2756  */
getrchar(register Vi_t * vp)2757 static int getrchar(register Vi_t *vp)
2758 {
2759 	register int c;
2760 	if((c=ed_getchar(vp->ed,1))== usrlnext)
2761 		c = ed_getchar(vp->ed,2);
2762 	return(c);
2763 }
2764