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