xref: /titanic_41/usr/src/lib/libshell/common/edit/emacs.c (revision 8e50dcc9f00b393d43e6aa42b820bcbf1d3e1ce4)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *           Copyright (c) 1982-2007 AT&T Knowledge Ventures            *
5 *                      and is licensed under the                       *
6 *                  Common Public License, Version 1.0                  *
7 *                      by AT&T Knowledge Ventures                      *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *            http://www.opensource.org/licenses/cpl1.0.txt             *
11 *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *                  David Korn <dgk@research.att.com>                   *
18 *                                                                      *
19 ***********************************************************************/
20 #pragma prototyped
21 /* Original version by Michael T. Veach
22  * Adapted for ksh by David Korn */
23 /* EMACS_MODES: c tabstop=4
24 
25 One line screen editor for any program
26 
27 */
28 
29 
30 /*	The following is provided by:
31  *
32  *			Matthijs N. Melchior
33  *			AT&T Network Systems International
34  *			APT Nederland
35  *			HV BZ335 x2962
36  *			hvlpb!mmelchio
37  *
38  *  These are now on by default
39  *
40  *  ESH_NFIRST
41  *	-  A ^N as first history related command after the prompt will move
42  *	   to the next command relative to the last known history position.
43  *	   It will not start at the position where the last command was entered
44  *	   as is done by the ^P command.  Every history related command will
45  *	   set both the current and last position.  Executing a command will
46  *	   only set the current position.
47  *
48  *  ESH_KAPPEND
49  *	-  Successive kill and delete commands will accumulate their data
50  *	   in the kill buffer, by appending or prepending as appropriate.
51  *	   This mode will be reset by any command not adding something to the
52  *	   kill buffer.
53  *
54  *  ESH_BETTER
55  *	-  Some enhancements:
56  *		- argument for a macro is passed to its replacement
57  *		- ^X^H command to find out about history position (debugging)
58  *		- ^X^D command to show any debugging info
59  *
60  *  I do not pretend these for changes are completely independent,
61  *  but you can use them to seperate features.
62  */
63 
64 #include	<ast.h>
65 #include	<ctype.h>
66 #include	"FEATURE/cmds"
67 #if KSHELL
68 #   include	"defs.h"
69 #endif	/* KSHELL */
70 #include	"io.h"
71 
72 #include	"history.h"
73 #include	"edit.h"
74 #include	"terminal.h"
75 
76 #define ESH_NFIRST
77 #define ESH_KAPPEND
78 #define ESH_BETTER
79 
80 #undef putchar
81 #define putchar(ed,c)	ed_putchar(ed,c)
82 #define beep()		ed_ringbell()
83 
84 
85 #if SHOPT_MULTIBYTE
86 #   define gencpy(a,b)	ed_gencpy(a,b)
87 #   define genncpy(a,b,n)	ed_genncpy(a,b,n)
88 #   define genlen(str)	ed_genlen(str)
89     static int	print(int);
90     static int	_isword(int);
91 #   define  isword(c)	_isword(out[c])
92 
93 #else
94 #   define gencpy(a,b)	strcpy((char*)(a),(char*)(b))
95 #   define genncpy(a,b,n)	strncpy((char*)(a),(char*)(b),n)
96 #   define genlen(str)	strlen(str)
97 #   define print(c)	isprint(c)
98 #   define isword(c)	(isalnum(out[c]) || (out[c]=='_'))
99 #endif /*SHOPT_MULTIBYTE */
100 
101 typedef struct _emacs_
102 {
103 	genchar *screen;	/* pointer to window buffer */
104 	genchar *cursor;	/* Cursor in real screen */
105 	int 	mark;
106 	int 	in_mult;
107 	char	cr_ok;
108 	char	CntrlO;
109 	char	overflow;		/* Screen overflow flag set */
110 	char	scvalid;		/* Screen is up to date */
111 	int	offset;		/* Screen offset */
112 	enum
113 	{
114 		CRT=0,	/* Crt terminal */
115 		PAPER	/* Paper terminal */
116 	} terminal;
117 	Histloc_t _location;
118 	int	prevdirection;
119 	Edit_t	*ed;	/* pointer to edit data */
120 } Emacs_t;
121 
122 #define	editb		(*ep->ed)
123 #define eol		editb.e_eol
124 #define cur		editb.e_cur
125 #define hline		editb.e_hline
126 #define hloff		editb.e_hloff
127 #define hismin		editb.e_hismin
128 #define usrkill		editb.e_kill
129 #define usrlnext	editb.e_lnext
130 #define usreof		editb.e_eof
131 #define usrerase	editb.e_erase
132 #define crallowed	editb.e_crlf
133 #define Prompt		editb.e_prompt
134 #define plen		editb.e_plen
135 #define kstack		editb.e_killbuf
136 #define lstring		editb.e_search
137 #define lookahead	editb.e_lookahead
138 #define env		editb.e_env
139 #define raw		editb.e_raw
140 #define histlines	editb.e_hismax
141 #define w_size		editb.e_wsize
142 #define drawbuff	editb.e_inbuf
143 #define killing		editb.e_mode
144 #define location	ep->_location
145 
146 #define LBUF	100
147 #define KILLCHAR	UKILL
148 #define ERASECHAR	UERASE
149 #define EOFCHAR		UEOF
150 #define LNEXTCHAR		ULNEXT
151 #define DELETE		('a'==97?0177:7)
152 
153 /**********************
154 A large lookahead helps when the user is inserting
155 characters in the middle of the line.
156 ************************/
157 
158 
159 typedef enum
160 {
161 	FIRST,		/* First time thru for logical line, prompt on screen */
162 	REFRESH,	/* Redraw entire screen */
163 	APPEND,		/* Append char before cursor to screen */
164 	UPDATE,		/* Update the screen as need be */
165 	FINAL		/* Update screen even if pending look ahead */
166 } Draw_t;
167 
168 static void draw(Emacs_t*,Draw_t);
169 static int escape(Emacs_t*,genchar*, int);
170 static void putstring(Emacs_t*,char*);
171 static void search(Emacs_t*,genchar*,int);
172 static void setcursor(Emacs_t*,int, int);
173 static void show_info(Emacs_t*,const char*);
174 static void xcommands(Emacs_t*,int);
175 
176 int ed_emacsread(void *context, int fd,char *buff,int scend, int reedit)
177 {
178 	Edit_t *ed = (Edit_t*)context;
179 	register int c;
180 	register int i;
181 	register genchar *out;
182 	register int count;
183 	register Emacs_t *ep = ed->e_emacs;
184 	int adjust,oadjust;
185 	char backslash;
186 	genchar *kptr;
187 	char prompt[PRSIZE];
188 	genchar Screen[MAXLINE];
189 	if(!ep)
190 	{
191 		ep = ed->e_emacs = newof(0,Emacs_t,1,0);
192 		ep->ed = ed;
193 		ep->prevdirection =  1;
194 		location.hist_command =  -5;
195 	}
196 	Prompt = prompt;
197 	ep->screen = Screen;
198 	if(tty_raw(ERRIO,0) < 0)
199 	{
200 		 return(reedit?reedit:ed_read(context, fd,buff,scend,0));
201 	}
202 	raw = 1;
203 	/* This mess in case the read system call fails */
204 
205 	ed_setup(ep->ed,fd,reedit);
206 	out = (genchar*)buff;
207 #if SHOPT_MULTIBYTE
208 	out = (genchar*)roundof((char*)out-(char*)0,sizeof(genchar));
209 	ed_internal(buff,out);
210 #endif /* SHOPT_MULTIBYTE */
211 	if(!kstack)
212 	{
213 		kstack = (genchar*)malloc(CHARSIZE*MAXLINE);
214 		kstack[0] = '\0';
215 	}
216 	drawbuff = out;
217 #ifdef ESH_NFIRST
218 	if (location.hist_command == -5)		/* to be initialized */
219 	{
220 		kstack[0] = '\0';		/* also clear kstack... */
221 		location.hist_command = hline;
222 		location.hist_line = hloff;
223 	}
224 	if (location.hist_command <= hismin)	/* don't start below minimum */
225 	{
226 		location.hist_command = hismin + 1;
227 		location.hist_line = 0;
228 	}
229 	ep->in_mult = hloff;			/* save pos in last command */
230 #endif /* ESH_NFIRST */
231 	i = sigsetjmp(env,0);
232 	if (i !=0)
233 	{
234 		tty_cooked(ERRIO);
235 		if (i == UEOF)
236 		{
237 			return(0); /* EOF */
238 		}
239 		return(-1); /* some other error */
240 	}
241 	out[reedit] = 0;
242 	if(scend+plen > (MAXLINE-2))
243 		scend = (MAXLINE-2)-plen;
244 	ep->mark = 0;
245 	cur = eol;
246 	draw(ep,reedit?REFRESH:FIRST);
247 	adjust = -1;
248 	backslash = 0;
249 	if (ep->CntrlO)
250 	{
251 #ifdef ESH_NFIRST
252 		ed_ungetchar(ep->ed,cntl('N'));
253 #else
254 		location = hist_locate(sh.hist_ptr,location.hist_command,location.hist_line,1);
255 		if (location.hist_command < histlines)
256 		{
257 			hline = location.hist_command;
258 			hloff = location.hist_line;
259 			hist_copy((char*)kstack,MAXLINE, hline,hloff);
260 #   if SHOPT_MULTIBYTE
261 			ed_internal((char*)kstack,kstack);
262 #   endif /* SHOPT_MULTIBYTE */
263 			ed_ungetchar(ep->ed,cntl('Y'));
264 		}
265 #endif /* ESH_NFIRST */
266 	}
267 	ep->CntrlO = 0;
268 	while ((c = ed_getchar(ep->ed,0)) != (-1))
269 	{
270 		if (backslash)
271 		{
272 			backslash = 0;
273 			if (c==usrerase||c==usrkill||(!print(c) &&
274 				(c!='\r'&&c!='\n')))
275 			{
276 				/* accept a backslashed character */
277 				cur--;
278 				out[cur++] = c;
279 				out[eol] = '\0';
280 				draw(ep,APPEND);
281 				continue;
282 			}
283 		}
284 		if (c == usrkill)
285 		{
286 			c = KILLCHAR ;
287 		}
288 		else if (c == usrerase)
289 		{
290 			c = ERASECHAR ;
291 		}
292 		else if (c == usrlnext)
293 		{
294 			c = LNEXTCHAR ;
295 		}
296 		else if ((c == usreof)&&(eol == 0))
297 		{
298 			c = EOFCHAR;
299 		}
300 #ifdef ESH_KAPPEND
301 		if (--killing <= 0)	/* reset killing flag */
302 			killing = 0;
303 #endif
304 		oadjust = count = adjust;
305 		if(count<0)
306 			count = 1;
307 		adjust = -1;
308 		i = cur;
309 		switch(c)
310 		{
311 		case LNEXTCHAR:
312 			c = ed_getchar(ep->ed,2);
313 			goto do_default_processing;
314 		case cntl('V'):
315 			show_info(ep,fmtident(e_version));
316 			continue;
317 		case '\0':
318 			ep->mark = i;
319 			continue;
320 		case cntl('X'):
321 			xcommands(ep,count);
322 			continue;
323 		case EOFCHAR:
324 			ed_flush(ep->ed);
325 			tty_cooked(ERRIO);
326 			return(0);
327 #ifdef u370
328 		case cntl('S') :
329 		case cntl('Q') :
330 			continue;
331 #endif	/* u370 */
332 		case '\t':
333 			if(cur>0  && ep->ed->sh->nextprompt)
334 			{
335 				if(ep->ed->e_tabcount==0)
336 				{
337 					ep->ed->e_tabcount=1;
338 					ed_ungetchar(ep->ed,ESC);
339 					goto do_escape;
340 				}
341 				else if(ep->ed->e_tabcount==1)
342 				{
343 					ed_ungetchar(ep->ed,'=');
344 					goto do_escape;
345 				}
346 				ep->ed->e_tabcount = 0;
347 			}
348 		do_default_processing:
349 		default:
350 
351 			if ((eol+1) >= (scend)) /*  will not fit on line */
352 			{
353 				ed_ungetchar(ep->ed,c); /* save character for next line */
354 				goto process;
355 			}
356 			for(i= ++eol; i>cur; i--)
357 				out[i] = out[i-1];
358 			backslash =  (c == '\\');
359 			out[cur++] = c;
360 			draw(ep,APPEND);
361 			continue;
362 		case cntl('Y') :
363 			{
364 				c = genlen(kstack);
365 				if ((c + eol) > scend)
366 				{
367 					beep();
368 					continue;
369 				}
370 				ep->mark = i;
371 				for(i=eol;i>=cur;i--)
372 					out[c+i] = out[i];
373 				kptr=kstack;
374 				while (i = *kptr++)
375 					out[cur++] = i;
376 				draw(ep,UPDATE);
377 				eol = genlen(out);
378 				continue;
379 			}
380 		case '\n':
381 		case '\r':
382 			c = '\n';
383 			goto process;
384 
385 		case DELETE:	/* delete char 0x7f */
386 		case '\b':	/* backspace, ^h */
387 		case ERASECHAR :
388 			if (count > i)
389 				count = i;
390 #ifdef ESH_KAPPEND
391 			kptr = &kstack[count];	/* move old contents here */
392 			if (killing)		/* prepend to killbuf */
393 			{
394 				c = genlen(kstack) + CHARSIZE; /* include '\0' */
395 				while(c--)	/* copy stuff */
396 					kptr[c] = kstack[c];
397 			}
398 			else
399 				*kptr = 0;	/* this is end of data */
400 			killing = 2;		/* we are killing */
401 			i -= count;
402 			eol -= count;
403 			genncpy(kstack,out+i,cur-i);
404 #else
405 			while ((count--)&&(i>0))
406 			{
407 				i--;
408 				eol--;
409 			}
410 			genncpy(kstack,out+i,cur-i);
411 			kstack[cur-i] = 0;
412 #endif /* ESH_KAPPEND */
413 			gencpy(out+i,out+cur);
414 			ep->mark = i;
415 			goto update;
416 		case cntl('W') :
417 #ifdef ESH_KAPPEND
418 			++killing;		/* keep killing flag */
419 #endif
420 			if (ep->mark > eol )
421 				ep->mark = eol;
422 			if (ep->mark == i)
423 				continue;
424 			if (ep->mark > i)
425 			{
426 				adjust = ep->mark - i;
427 				ed_ungetchar(ep->ed,cntl('D'));
428 				continue;
429 			}
430 			adjust = i - ep->mark;
431 			ed_ungetchar(ep->ed,usrerase);
432 			continue;
433 		case cntl('D') :
434 			ep->mark = i;
435 #ifdef ESH_KAPPEND
436 			if (killing)
437 				kptr = &kstack[genlen(kstack)];	/* append here */
438 			else
439 				kptr = kstack;
440 			killing = 2;			/* we are now killing */
441 #else
442 			kptr = kstack;
443 #endif /* ESH_KAPPEND */
444 			while ((count--)&&(eol>0)&&(i<eol))
445 			{
446 				*kptr++ = out[i];
447 				eol--;
448 				while(1)
449 				{
450 					if ((out[i] = out[(i+1)])==0)
451 						break;
452 					i++;
453 				}
454 				i = cur;
455 			}
456 			*kptr = '\0';
457 			goto update;
458 		case cntl('C') :
459 		case cntl('F') :
460 		{
461 			int cntlC = (c==cntl('C'));
462 			while (count-- && eol>i)
463 			{
464 				if (cntlC)
465 				{
466 					c = out[i];
467 #if SHOPT_MULTIBYTE
468 					if((c&~STRIP)==0 && islower(c))
469 #else
470 					if(islower(c))
471 #endif /* SHOPT_MULTIBYTE */
472 					{
473 						c += 'A' - 'a';
474 						out[i] = c;
475 					}
476 				}
477 				i++;
478 			}
479 			goto update;
480 		}
481 		case cntl(']') :
482 			c = ed_getchar(ep->ed,1);
483 			if ((count == 0) || (count > eol))
484                         {
485                                 beep();
486                                 continue;
487                         }
488 			if (out[i])
489 				i++;
490 			while (i < eol)
491 			{
492 				if (out[i] == c && --count==0)
493 					goto update;
494 				i++;
495 			}
496 			i = 0;
497 			while (i < cur)
498 			{
499 				if (out[i] == c && --count==0)
500 					break;
501 				i++;
502 			};
503 
504 update:
505 			cur = i;
506 			draw(ep,UPDATE);
507 			continue;
508 
509 		case cntl('B') :
510 			if (count > i)
511 				count = i;
512 			i -= count;
513 			goto update;
514 		case cntl('T') :
515 			if ((sh_isoption(SH_EMACS))&& (eol!=i))
516 				i++;
517 			if (i >= 2)
518 			{
519 				c = out[i - 1];
520 				out[i-1] = out[i-2];
521 				out[i-2] = c;
522 			}
523 			else
524 			{
525 				if(sh_isoption(SH_EMACS))
526 					i--;
527 				beep();
528 				continue;
529 			}
530 			goto update;
531 		case cntl('A') :
532 			i = 0;
533 			goto update;
534 		case cntl('E') :
535 			i = eol;
536 			goto update;
537 		case cntl('U') :
538 			adjust = 4*count;
539 			continue;
540 		case KILLCHAR :
541 			cur = 0;
542 			oadjust = -1;
543 		case cntl('K') :
544 			if(oadjust >= 0)
545 			{
546 #ifdef ESH_KAPPEND
547 				killing = 2;		/* set killing signal */
548 #endif
549 				ep->mark = count;
550 				ed_ungetchar(ep->ed,cntl('W'));
551 				continue;
552 			}
553 			i = cur;
554 			eol = i;
555 			ep->mark = i;
556 #ifdef ESH_KAPPEND
557 			if (killing)			/* append to kill buffer */
558 				gencpy(&kstack[genlen(kstack)], &out[i]);
559 			else
560 				gencpy(kstack,&out[i]);
561 			killing = 2;			/* set killing signal */
562 #else
563 			gencpy(kstack,&out[i]);
564 #endif /* ESH_KAPPEND */
565 			out[i] = 0;
566 			draw(ep,UPDATE);
567 			if (c == KILLCHAR)
568 			{
569 				if (ep->terminal == PAPER)
570 				{
571 					putchar(ep->ed,'\n');
572 					putstring(ep,Prompt);
573 				}
574 				c = ed_getchar(ep->ed,0);
575 				if (c != usrkill)
576 				{
577 					ed_ungetchar(ep->ed,c);
578 					continue;
579 				}
580 				if (ep->terminal == PAPER)
581 					ep->terminal = CRT;
582 				else
583 				{
584 					ep->terminal = PAPER;
585 					putchar(ep->ed,'\n');
586 					putstring(ep,Prompt);
587 				}
588 			}
589 			continue;
590 		case cntl('L'):
591 			ed_crlf(ep->ed);
592 			draw(ep,REFRESH);
593 			continue;
594 		case cntl('[') :
595 		do_escape:
596 			adjust = escape(ep,out,oadjust);
597 			continue;
598 		case cntl('R') :
599 			search(ep,out,count);
600 			goto drawline;
601 		case cntl('P') :
602                         if (count <= hloff)
603                                 hloff -= count;
604                         else
605                         {
606                                 hline -= count - hloff;
607                                 hloff = 0;
608                         }
609 #ifdef ESH_NFIRST
610 			if (hline <= hismin)
611 #else
612 			if (hline < hismin)
613 #endif /* ESH_NFIRST */
614 			{
615 				hline = hismin+1;
616 				beep();
617 #ifndef ESH_NFIRST
618 				continue;
619 #endif
620 			}
621 			goto common;
622 
623 		case cntl('O') :
624 			location.hist_command = hline;
625 			location.hist_line = hloff;
626 			ep->CntrlO = 1;
627 			c = '\n';
628 			goto process;
629 		case cntl('N') :
630 #ifdef ESH_NFIRST
631 			hline = location.hist_command;	/* start at saved position */
632 			hloff = location.hist_line;
633 #endif /* ESH_NFIRST */
634 			location = hist_locate(sh.hist_ptr,hline,hloff,count);
635 			if (location.hist_command > histlines)
636 			{
637 				beep();
638 #ifdef ESH_NFIRST
639 				location.hist_command = histlines;
640 				location.hist_line = ep->in_mult;
641 #else
642 				continue;
643 #endif /* ESH_NFIRST */
644 			}
645 			hline = location.hist_command;
646 			hloff = location.hist_line;
647 		common:
648 #ifdef ESH_NFIRST
649 			location.hist_command = hline;	/* save current position */
650 			location.hist_line = hloff;
651 #endif
652 			hist_copy((char*)out,MAXLINE, hline,hloff);
653 #if SHOPT_MULTIBYTE
654 			ed_internal((char*)(out),out);
655 #endif /* SHOPT_MULTIBYTE */
656 		drawline:
657 			eol = genlen(out);
658 			cur = eol;
659 			draw(ep,UPDATE);
660 			continue;
661 		}
662 
663 	}
664 
665 process:
666 
667 	if (c == (-1))
668 	{
669 		lookahead = 0;
670 		beep();
671 		*out = '\0';
672 	}
673 	draw(ep,FINAL);
674 	tty_cooked(ERRIO);
675 	if(ed->e_nlist)
676 	{
677 		ed->e_nlist = 0;
678 		stakset(ed->e_stkptr,ed->e_stkoff);
679 	}
680 	if(c == '\n')
681 	{
682 		out[eol++] = '\n';
683 		out[eol] = '\0';
684 		ed_crlf(ep->ed);
685 	}
686 #if SHOPT_MULTIBYTE
687 	ed_external(out,buff);
688 #endif /* SHOPT_MULTIBYTE */
689 	i = strlen(buff);
690 	if (i)
691 		return(i);
692 	return(-1);
693 }
694 
695 static void show_info(Emacs_t *ep,const char *str)
696 {
697 	register genchar *out = drawbuff;
698 	register int c;
699 	genchar string[LBUF];
700 	int sav_cur = cur;
701 	/* save current line */
702 	genncpy(string,out,sizeof(string)/sizeof(*string));
703 	*out = 0;
704 	cur = 0;
705 #if SHOPT_MULTIBYTE
706 	ed_internal(str,out);
707 #else
708 	gencpy(out,str);
709 #endif	/* SHOPT_MULTIBYTE */
710 	draw(ep,UPDATE);
711 	c = ed_getchar(ep->ed,0);
712 	if(c!=' ')
713 		ed_ungetchar(ep->ed,c);
714 	/* restore line */
715 	cur = sav_cur;
716 	genncpy(out,string,sizeof(string)/sizeof(*string));
717 	draw(ep,UPDATE);
718 }
719 
720 static void putstring(Emacs_t* ep,register char *sp)
721 {
722 	register int c;
723 	while (c= *sp++)
724 		 putchar(ep->ed,c);
725 }
726 
727 
728 static int escape(register Emacs_t* ep,register genchar *out,int count)
729 {
730 	register int i,value;
731 	int digit,ch;
732 	digit = 0;
733 	value = 0;
734 	while ((i=ed_getchar(ep->ed,0)),isdigit(i))
735 	{
736 		value *= 10;
737 		value += (i - '0');
738 		digit = 1;
739 	}
740 	if (digit)
741 	{
742 		ed_ungetchar(ep->ed,i) ;
743 #ifdef ESH_KAPPEND
744 		++killing;		/* don't modify killing signal */
745 #endif
746 		return(value);
747 	}
748 	value = count;
749 	if(value<0)
750 		value = 1;
751 	switch(ch=i)
752 	{
753 		case cntl('V'):
754 			show_info(ep,fmtident(e_version));
755 			return(-1);
756 		case ' ':
757 			ep->mark = cur;
758 			return(-1);
759 
760 #ifdef ESH_KAPPEND
761 		case '+':		/* M-+ = append next kill */
762 			killing = 2;
763 			return -1;	/* no argument for next command */
764 #endif
765 
766 		case 'p':	/* M-p == ^W^Y (copy stack == kill & yank) */
767 			ed_ungetchar(ep->ed,cntl('Y'));
768 			ed_ungetchar(ep->ed,cntl('W'));
769 #ifdef ESH_KAPPEND
770 			killing = 0;	/* start fresh */
771 #endif
772 			return(-1);
773 
774 		case 'l':	/* M-l == lower-case */
775 		case 'd':
776 		case 'c':
777 		case 'f':
778 		{
779 			i = cur;
780 			while(value-- && i<eol)
781 			{
782 				while ((out[i])&&(!isword(i)))
783 					i++;
784 				while ((out[i])&&(isword(i)))
785 					i++;
786 			}
787 			if(ch=='l')
788 			{
789 				value = i-cur;
790 				while (value-- > 0)
791 				{
792 					i = out[cur];
793 #if SHOPT_MULTIBYTE
794 					if((i&~STRIP)==0 && isupper(i))
795 #else
796 					if(isupper(i))
797 #endif /* SHOPT_MULTIBYTE */
798 					{
799 						i += 'a' - 'A';
800 						out[cur] = i;
801 					}
802 					cur++;
803 				}
804 				draw(ep,UPDATE);
805 				return(-1);
806 			}
807 
808 			else if(ch=='f')
809 				goto update;
810 			else if(ch=='c')
811 			{
812 				ed_ungetchar(ep->ed,cntl('C'));
813 				return(i-cur);
814 			}
815 			else
816 			{
817 				if (i-cur)
818 				{
819 					ed_ungetchar(ep->ed,cntl('D'));
820 #ifdef ESH_KAPPEND
821 					++killing;	/* keep killing signal */
822 #endif
823 					return(i-cur);
824 				}
825 				beep();
826 				return(-1);
827 			}
828 		}
829 
830 
831 		case 'b':
832 		case DELETE :
833 		case '\b':
834 		case 'h':
835 		{
836 			i = cur;
837 			while(value-- && i>0)
838 			{
839 				i--;
840 				while ((i>0)&&(!isword(i)))
841 					i--;
842 				while ((i>0)&&(isword(i-1)))
843 					i--;
844 			}
845 			if(ch=='b')
846 				goto update;
847 			else
848 			{
849 				ed_ungetchar(ep->ed,usrerase);
850 #ifdef ESH_KAPPEND
851 				++killing;
852 #endif
853 				return(cur-i);
854 			}
855 		}
856 
857 		case '>':
858 			ed_ungetchar(ep->ed,cntl('N'));
859 #ifdef ESH_NFIRST
860 			if (ep->in_mult)
861 			{
862 				location.hist_command = histlines;
863 				location.hist_line = ep->in_mult - 1;
864 			}
865 			else
866 			{
867 				location.hist_command = histlines - 1;
868 				location.hist_line = 0;
869 			}
870 #else
871 			hline = histlines-1;
872 			hloff = 0;
873 #endif /* ESH_NFIRST */
874 			return(0);
875 
876 		case '<':
877 			ed_ungetchar(ep->ed,cntl('P'));
878 			hloff = 0;
879 #ifdef ESH_NFIRST
880 			hline = hismin + 1;
881 			return 0;
882 #else
883 			return(hline-hismin);
884 #endif /* ESH_NFIRST */
885 
886 
887 		case '#':
888 			ed_ungetchar(ep->ed,'\n');
889 			ed_ungetchar(ep->ed,(out[0]=='#')?cntl('D'):'#');
890 			ed_ungetchar(ep->ed,cntl('A'));
891 			return(-1);
892 		case '_' :
893 		case '.' :
894 		{
895 			genchar name[MAXLINE];
896 			char buf[MAXLINE];
897 			char *ptr;
898 			ptr = hist_word(buf,MAXLINE,(count?count:-1));
899 #if !KSHELL
900 			if(ptr==0)
901 			{
902 				beep();
903 				break;
904 			}
905 #endif	/* KSHELL */
906 			if ((eol - cur) >= sizeof(name))
907 			{
908 				beep();
909 				return(-1);
910 			}
911 			ep->mark = cur;
912 			gencpy(name,&out[cur]);
913 			while(*ptr)
914 			{
915 				out[cur++] = *ptr++;
916 				eol++;
917 			}
918 			gencpy(&out[cur],name);
919 			draw(ep,UPDATE);
920 			return(-1);
921 		}
922 #if KSHELL
923 
924 		/* file name expansion */
925 		case cntl('[') :	/* filename completion */
926 			i = '\\';
927 		case '*':		/* filename expansion */
928 		case '=':	/* escape = - list all matching file names */
929 			ep->mark = cur;
930 			if(ed_expand(ep->ed,(char*)out,&cur,&eol,i,count) < 0)
931 			{
932 				if(ep->ed->e_tabcount==1)
933 				{
934 					ep->ed->e_tabcount=2;
935 					ed_ungetchar(ep->ed,cntl('\t'));
936 					return(-1);
937 				}
938 				beep();
939 			}
940 			else if(i=='=')
941 			{
942 				draw(ep,REFRESH);
943 				if(count>0)
944 					ep->ed->e_tabcount=0;
945 				else
946 				{
947 					i=ed_getchar(ep->ed,0);
948 					ed_ungetchar(ep->ed,i);
949 					if(isdigit(i))
950 						ed_ungetchar(ep->ed,ESC);
951 				}
952 			}
953 			else
954 			{
955 				if(i=='\\' && cur>ep->mark && (out[cur-1]=='/' || out[cur-1]==' '))
956 					ep->ed->e_tabcount=0;
957 				draw(ep,UPDATE);
958 			}
959 			return(-1);
960 
961 		/* search back for character */
962 		case cntl(']'):	/* feature not in book */
963 		{
964 			int c = ed_getchar(ep->ed,1);
965 			if ((value == 0) || (value > eol))
966 			{
967 				beep();
968 				return(-1);
969 			}
970 			i = cur;
971 			if (i > 0)
972 				i--;
973 			while (i >= 0)
974 			{
975 				if (out[i] == c && --value==0)
976 					goto update;
977 				i--;
978 			}
979 			i = eol;
980 			while (i > cur)
981 			{
982 				if (out[i] == c && --value==0)
983 					break;
984 				i--;
985 			};
986 
987 		}
988 		update:
989 			cur = i;
990 			draw(ep,UPDATE);
991 			return(-1);
992 
993 #ifdef _cmd_tput
994 		case cntl('L'): /* clear screen */
995 			sh_trap("tput clear", 0);
996 			draw(ep,REFRESH);
997 			return(-1);
998 #endif
999 		case '[':	/* feature not in book */
1000 			switch(i=ed_getchar(ep->ed,1))
1001 			{
1002 			    case 'A':
1003 				ed_ungetchar(ep->ed,cntl('P'));
1004 				return(-1);
1005 			    case 'B':
1006 				ed_ungetchar(ep->ed,cntl('N'));
1007 				return(-1);
1008 			    case 'C':
1009 				ed_ungetchar(ep->ed,cntl('F'));
1010 				return(-1);
1011 			    case 'D':
1012 				ed_ungetchar(ep->ed,cntl('B'));
1013 				return(-1);
1014 			    case 'H':
1015 				ed_ungetchar(ep->ed,cntl('A'));
1016 				return(-1);
1017 			    case 'Y':
1018 				ed_ungetchar(ep->ed,cntl('E'));
1019 				return(-1);
1020 			    default:
1021 				ed_ungetchar(ep->ed,i);
1022 			}
1023 			i = '_';
1024 
1025 		default:
1026 			/* look for user defined macro definitions */
1027 			if(ed_macro(ep->ed,i))
1028 #   ifdef ESH_BETTER
1029 				return(count);	/* pass argument to macro */
1030 #   else
1031 				return(-1);
1032 #   endif /* ESH_BETTER */
1033 #else
1034 		update:
1035 			cur = i;
1036 			draw(ep,UPDATE);
1037 			return(-1);
1038 
1039 		default:
1040 #endif	/* KSHELL */
1041 		beep();
1042 		return(-1);
1043 	}
1044 }
1045 
1046 
1047 /*
1048  * This routine process all commands starting with ^X
1049  */
1050 
1051 static void xcommands(register Emacs_t *ep,int count)
1052 {
1053         register int i = ed_getchar(ep->ed,0);
1054 	NOT_USED(count);
1055         switch(i)
1056         {
1057                 case cntl('X'):	/* exchange dot and mark */
1058                         if (ep->mark > eol)
1059                                 ep->mark = eol;
1060                         i = ep->mark;
1061                         ep->mark = cur;
1062                         cur = i;
1063                         draw(ep,UPDATE);
1064                         return;
1065 
1066 #if KSHELL
1067 #   ifdef ESH_BETTER
1068                 case cntl('E'):	/* invoke emacs on current command */
1069 			if(ed_fulledit(ep->ed)==-1)
1070 				beep();
1071 			else
1072 			{
1073 #if SHOPT_MULTIBYTE
1074 				ed_internal((char*)drawbuff,drawbuff);
1075 #endif /* SHOPT_MULTIBYTE */
1076 				ed_ungetchar(ep->ed,'\n');
1077 			}
1078 			return;
1079 
1080 #	define itos(i)	fmtbase((long)(i),0,0)/* want signed conversion */
1081 
1082 		case cntl('H'):		/* ^X^H show history info */
1083 			{
1084 				char hbuf[MAXLINE];
1085 
1086 				strcpy(hbuf, "Current command ");
1087 				strcat(hbuf, itos(hline));
1088 				if (hloff)
1089 				{
1090 					strcat(hbuf, " (line ");
1091 					strcat(hbuf, itos(hloff+1));
1092 					strcat(hbuf, ")");
1093 				}
1094 				if ((hline != location.hist_command) ||
1095 				    (hloff != location.hist_line))
1096 				{
1097 					strcat(hbuf, "; Previous command ");
1098 					strcat(hbuf, itos(location.hist_command));
1099 					if (location.hist_line)
1100 					{
1101 						strcat(hbuf, " (line ");
1102 						strcat(hbuf, itos(location.hist_line+1));
1103 						strcat(hbuf, ")");
1104 					}
1105 				}
1106 				show_info(ep,hbuf);
1107 				return;
1108 			}
1109 #	if 0	/* debugging, modify as required */
1110 		case cntl('D'):		/* ^X^D show debugging info */
1111 			{
1112 				char debugbuf[MAXLINE];
1113 
1114 				strcpy(debugbuf, "count=");
1115 				strcat(debugbuf, itos(count));
1116 				strcat(debugbuf, " eol=");
1117 				strcat(debugbuf, itos(eol));
1118 				strcat(debugbuf, " cur=");
1119 				strcat(debugbuf, itos(cur));
1120 				strcat(debugbuf, " crallowed=");
1121 				strcat(debugbuf, itos(crallowed));
1122 				strcat(debugbuf, " plen=");
1123 				strcat(debugbuf, itos(plen));
1124 				strcat(debugbuf, " w_size=");
1125 				strcat(debugbuf, itos(w_size));
1126 
1127 				show_info(ep,debugbuf);
1128 				return;
1129 			}
1130 #	endif /* debugging code */
1131 #   endif /* ESH_BETTER */
1132 #endif /* KSHELL */
1133 
1134                 default:
1135                         beep();
1136                         return;
1137 	}
1138 }
1139 
1140 static void search(Emacs_t* ep,genchar *out,int direction)
1141 {
1142 #ifndef ESH_NFIRST
1143 	Histloc_t location;
1144 #endif
1145 	register int i,sl;
1146 	genchar str_buff[LBUF];
1147 	register genchar *string = drawbuff;
1148 	/* save current line */
1149 	int sav_cur = cur;
1150 	genncpy(str_buff,string,sizeof(str_buff)/sizeof(*str_buff));
1151 	string[0] = '^';
1152 	string[1] = 'R';
1153 	string[2] = '\0';
1154 	sl = 2;
1155 	cur = sl;
1156 	draw(ep,UPDATE);
1157 	while ((i = ed_getchar(ep->ed,1))&&(i != '\r')&&(i != '\n'))
1158 	{
1159 		if (i==usrerase || i==DELETE || i=='\b' || i==ERASECHAR)
1160 		{
1161 			if (sl > 2)
1162 			{
1163 				string[--sl] = '\0';
1164 				cur = sl;
1165 				draw(ep,UPDATE);
1166 			}
1167 			else
1168 				beep();
1169 			continue;
1170 		}
1171 		if (i==usrkill)
1172 		{
1173 			beep();
1174 			goto restore;
1175 		}
1176 		if (i == '\\')
1177 		{
1178 			string[sl++] = '\\';
1179 			string[sl] = '\0';
1180 			cur = sl;
1181 			draw(ep,APPEND);
1182 			i = ed_getchar(ep->ed,1);
1183 			string[--sl] = '\0';
1184 		}
1185 		string[sl++] = i;
1186 		string[sl] = '\0';
1187 		cur = sl;
1188 		draw(ep,APPEND);
1189 	}
1190 	i = genlen(string);
1191 
1192 	if (direction < 1)
1193 	{
1194 		ep->prevdirection = -ep->prevdirection;
1195 		direction = 1;
1196 	}
1197 	else
1198 		direction = -1;
1199 	if (i != 2)
1200 	{
1201 #if SHOPT_MULTIBYTE
1202 		ed_external(string,(char*)string);
1203 #endif /* SHOPT_MULTIBYTE */
1204 		strncpy(lstring,((char*)string)+2,SEARCHSIZE);
1205 		ep->prevdirection = direction;
1206 	}
1207 	else
1208 		direction = ep->prevdirection ;
1209 	location = hist_find(sh.hist_ptr,(char*)lstring,hline,1,direction);
1210 	i = location.hist_command;
1211 	if(i>0)
1212 	{
1213 		hline = i;
1214 #ifdef ESH_NFIRST
1215 		hloff = location.hist_line = 0;	/* display first line of multi line command */
1216 #else
1217 		hloff = location.hist_line;
1218 #endif /* ESH_NFIRST */
1219 		hist_copy((char*)out,MAXLINE, hline,hloff);
1220 #if SHOPT_MULTIBYTE
1221 		ed_internal((char*)out,out);
1222 #endif /* SHOPT_MULTIBYTE */
1223 		return;
1224 	}
1225 	if (i < 0)
1226 	{
1227 		beep();
1228 #ifdef ESH_NFIRST
1229 		location.hist_command = hline;
1230 		location.hist_line = hloff;
1231 #else
1232 		hloff = 0;
1233 		hline = histlines;
1234 #endif /* ESH_NFIRST */
1235 	}
1236 restore:
1237 	genncpy(string,str_buff,sizeof(str_buff)/sizeof(*str_buff));
1238 	cur = sav_cur;
1239 	return;
1240 }
1241 
1242 
1243 /* Adjust screen to agree with inputs: logical line and cursor */
1244 /* If 'first' assume screen is blank */
1245 /* Prompt is always kept on the screen */
1246 
1247 static void draw(register Emacs_t *ep,Draw_t option)
1248 {
1249 #define	NORMAL ' '
1250 #define	LOWER  '<'
1251 #define	BOTH   '*'
1252 #define	UPPER  '>'
1253 
1254 	register genchar *sptr;		/* Pointer within screen */
1255 	genchar nscreen[2*MAXLINE];	/* New entire screen */
1256 	genchar *ncursor;		/* New cursor */
1257 	register genchar *nptr;		/* Pointer to New screen */
1258 	char  longline;			/* Line overflow */
1259 	genchar *logcursor;
1260 	genchar *nscend;		/* end of logical screen */
1261 	register int i;
1262 
1263 	nptr = nscreen;
1264 	sptr = drawbuff;
1265 	logcursor = sptr + cur;
1266 	longline = NORMAL;
1267 
1268 	if (option == FIRST || option == REFRESH)
1269 	{
1270 		ep->overflow = NORMAL;
1271 		ep->cursor = ep->screen;
1272 		ep->offset = 0;
1273 		ep->cr_ok = crallowed;
1274 		if (option == FIRST)
1275 		{
1276 			ep->scvalid = 1;
1277 			return;
1278 		}
1279 		*ep->cursor = '\0';
1280 		putstring(ep,Prompt);	/* start with prompt */
1281 	}
1282 
1283 	/*********************
1284 	 Do not update screen if pending characters
1285 	**********************/
1286 
1287 	if ((lookahead)&&(option != FINAL))
1288 	{
1289 
1290 		ep->scvalid = 0; /* Screen is out of date, APPEND will not work */
1291 
1292 		return;
1293 	}
1294 
1295 	/***************************************
1296 	If in append mode, cursor at end of line, screen up to date,
1297 	the previous character was a 'normal' character,
1298 	and the window has room for another character.
1299 	Then output the character and adjust the screen only.
1300 	*****************************************/
1301 
1302 
1303 	i = *(logcursor-1);	/* last character inserted */
1304 
1305 	if ((option == APPEND)&&(ep->scvalid)&&(*logcursor == '\0')&&
1306 	    print(i)&&((ep->cursor-ep->screen)<(w_size-1)))
1307 	{
1308 		putchar(ep->ed,i);
1309 		*ep->cursor++ = i;
1310 		*ep->cursor = '\0';
1311 		return;
1312 	}
1313 
1314 	/* copy the line */
1315 	ncursor = nptr + ed_virt_to_phys(ep->ed,sptr,nptr,cur,0,0);
1316 	nptr += genlen(nptr);
1317 	sptr += genlen(sptr);
1318 	nscend = nptr - 1;
1319 	if(sptr == logcursor)
1320 		ncursor = nptr;
1321 
1322 	/*********************
1323 	 Does ncursor appear on the screen?
1324 	 If not, adjust the screen offset so it does.
1325 	**********************/
1326 
1327 	i = ncursor - nscreen;
1328 
1329 	if ((ep->offset && i<=ep->offset)||(i >= (ep->offset+w_size)))
1330 	{
1331 		/* Center the cursor on the screen */
1332 		ep->offset = i - (w_size>>1);
1333 		if (--ep->offset < 0)
1334 			ep->offset = 0;
1335 	}
1336 
1337 	/*********************
1338 	 Is the range of screen[0] thru screen[w_size] up-to-date
1339 	 with nscreen[offset] thru nscreen[offset+w_size] ?
1340 	 If not, update as need be.
1341 	***********************/
1342 
1343 	nptr = &nscreen[ep->offset];
1344 	sptr = ep->screen;
1345 
1346 	i = w_size;
1347 
1348 	while (i-- > 0)
1349 	{
1350 
1351 		if (*nptr == '\0')
1352 		{
1353 			*(nptr + 1) = '\0';
1354 			*nptr = ' ';
1355 		}
1356 		if (*sptr == '\0')
1357 		{
1358 			*(sptr + 1) = '\0';
1359 			*sptr = ' ';
1360 		}
1361 		if (*nptr == *sptr)
1362 		{
1363 			nptr++;
1364 			sptr++;
1365 			continue;
1366 		}
1367 		setcursor(ep,sptr-ep->screen,*nptr);
1368 		*sptr++ = *nptr++;
1369 #if SHOPT_MULTIBYTE
1370 		while(*nptr==MARKER)
1371 		{
1372 			if(*sptr=='\0')
1373 				*(sptr + 1) = '\0';
1374 			*sptr++ = *nptr++;
1375 			i--;
1376 			ep->cursor++;
1377 		}
1378 #endif /* SHOPT_MULTIBYTE */
1379 	}
1380 
1381 	/******************
1382 
1383 	Screen overflow checks
1384 
1385 	********************/
1386 
1387 	if (nscend >= &nscreen[ep->offset+w_size])
1388 	{
1389 		if (ep->offset > 0)
1390 			longline = BOTH;
1391 		else
1392 			longline = UPPER;
1393 	}
1394 	else
1395 	{
1396 		if (ep->offset > 0)
1397 			longline = LOWER;
1398 	}
1399 
1400 	/* Update screen overflow indicator if need be */
1401 
1402 	if (longline != ep->overflow)
1403 	{
1404 		setcursor(ep,w_size,longline);
1405 		ep->overflow = longline;
1406 	}
1407 	i = (ncursor-nscreen) - ep->offset;
1408 	setcursor(ep,i,0);
1409 	if(option==FINAL && ep->ed->e_multiline)
1410 		setcursor(ep,nscend-nscreen,0);
1411 	ep->scvalid = 1;
1412 	return;
1413 }
1414 
1415 /*
1416  * put the cursor to the <newp> position within screen buffer
1417  * if <c> is non-zero then output this character
1418  * cursor is set to reflect the change
1419  */
1420 
1421 static void setcursor(register Emacs_t *ep,register int newp,int c)
1422 {
1423 	register int oldp = ep->cursor - ep->screen;
1424 	newp  = ed_setcursor(ep->ed, ep->screen, oldp, newp, 0);
1425 	if(c)
1426 	{
1427 		putchar(ep->ed,c);
1428 		newp++;
1429 	}
1430 	ep->cursor = ep->screen+newp;
1431 	return;
1432 }
1433 
1434 #if SHOPT_MULTIBYTE
1435 static int print(register int c)
1436 {
1437 	return((c&~STRIP)==0 && isprint(c));
1438 }
1439 
1440 static int _isword(register int c)
1441 {
1442 	return((c&~STRIP) || isalnum(c) || c=='_');
1443 }
1444 #endif /* SHOPT_MULTIBYTE */
1445