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