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