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