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/*
22 *  edit.c - common routines for vi and emacs one line editors in shell
23 *
24 *   David Korn				P.D. Sullivan
25 *   AT&T Labs
26 *
27 *   Coded April 1983.
28 */
29
30#include	<ast.h>
31#include	<errno.h>
32#include	<ccode.h>
33#include	"FEATURE/options"
34#include	"FEATURE/time"
35#include	"FEATURE/cmds"
36#ifdef _hdr_utime
37#   include	<utime.h>
38#   include	<ls.h>
39#endif
40
41#if KSHELL
42#   include	"defs.h"
43#   include	"variables.h"
44#else
45#   include	<ctype.h>
46    extern char ed_errbuf[];
47    char e_version[] = "\n@(#)$Id: Editlib version 1993-12-28 r $\0\n";
48#endif	/* KSHELL */
49#include	"io.h"
50#include	"terminal.h"
51#include	"history.h"
52#include	"edit.h"
53
54static char CURSOR_UP[20] = { ESC, '[', 'A', 0 };
55
56
57
58#if SHOPT_MULTIBYTE
59#   define is_cntrl(c)	((c<=STRIP) && iscntrl(c))
60#   define is_print(c)	((c&~STRIP) || isprint(c))
61#else
62#   define is_cntrl(c)	iscntrl(c)
63#   define is_print(c)	isprint(c)
64#endif
65
66#if	(CC_NATIVE == CC_ASCII)
67#   define printchar(c)	((c) ^ ('A'-cntl('A')))
68#else
69    static int printchar(int c)
70    {
71	switch(c)
72	{
73
74	    case cntl('A'): return('A');
75	    case cntl('B'): return('B');
76	    case cntl('C'): return('C');
77	    case cntl('D'): return('D');
78	    case cntl('E'): return('E');
79	    case cntl('F'): return('F');
80	    case cntl('G'): return('G');
81	    case cntl('H'): return('H');
82	    case cntl('I'): return('I');
83	    case cntl('J'): return('J');
84	    case cntl('K'): return('K');
85	    case cntl('L'): return('L');
86	    case cntl('M'): return('M');
87	    case cntl('N'): return('N');
88	    case cntl('O'): return('O');
89	    case cntl('P'): return('P');
90	    case cntl('Q'): return('Q');
91	    case cntl('R'): return('R');
92	    case cntl('S'): return('S');
93	    case cntl('T'): return('T');
94	    case cntl('U'): return('U');
95	    case cntl('V'): return('V');
96	    case cntl('W'): return('W');
97	    case cntl('X'): return('X');
98	    case cntl('Y'): return('Y');
99	    case cntl('Z'): return('Z');
100	    case cntl(']'): return(']');
101	    case cntl('['): return('[');
102	}
103	return('?');
104    }
105#endif
106#define MINWINDOW	15	/* minimum width window */
107#define DFLTWINDOW	80	/* default window width */
108#define RAWMODE		1
109#define ALTMODE		2
110#define ECHOMODE	3
111#define	SYSERR	-1
112
113#if SHOPT_OLDTERMIO
114#   undef tcgetattr
115#   undef tcsetattr
116#endif /* SHOPT_OLDTERMIO */
117
118#ifdef RT
119#   define VENIX 1
120#endif	/* RT */
121
122
123#ifdef _hdr_sgtty
124#   ifdef TIOCGETP
125	static int l_mask;
126	static struct tchars l_ttychars;
127	static struct ltchars l_chars;
128	static  char  l_changed;	/* set if mode bits changed */
129#	define L_CHARS	4
130#	define T_CHARS	2
131#	define L_MASK	1
132#   endif /* TIOCGETP */
133#endif /* _hdr_sgtty */
134
135#if KSHELL
136     static int keytrap(Edit_t *,char*, int, int, int);
137#else
138     Edit_t editb;
139#endif	/* KSHELL */
140
141
142#ifndef _POSIX_DISABLE
143#   define _POSIX_DISABLE	0
144#endif
145
146#ifdef future
147    static int compare(const char*, const char*, int);
148#endif  /* future */
149#if SHOPT_VSH || SHOPT_ESH
150#   define ttyparm	(ep->e_ttyparm)
151#   define nttyparm	(ep->e_nttyparm)
152    static const char bellchr[] = "\a";	/* bell char */
153#endif /* SHOPT_VSH || SHOPT_ESH */
154
155
156/*
157 * This routine returns true if fd refers to a terminal
158 * This should be equivalent to isatty
159 */
160int tty_check(int fd)
161{
162	register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context);
163	struct termios tty;
164	ep->e_savefd = -1;
165	return(tty_get(fd,&tty)==0);
166}
167
168/*
169 * Get the current terminal attributes
170 * This routine remembers the attributes and just returns them if it
171 *   is called again without an intervening tty_set()
172 */
173
174int tty_get(register int fd, register struct termios *tty)
175{
176	register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context);
177	if(fd == ep->e_savefd)
178		*tty = ep->e_savetty;
179	else
180	{
181		while(tcgetattr(fd,tty) == SYSERR)
182		{
183			if(errno !=EINTR)
184				return(SYSERR);
185			errno = 0;
186		}
187		/* save terminal settings if in cannonical state */
188		if(ep->e_raw==0)
189		{
190			ep->e_savetty = *tty;
191			ep->e_savefd = fd;
192		}
193	}
194	return(0);
195}
196
197/*
198 * Set the terminal attributes
199 * If fd<0, then current attributes are invalidated
200 */
201
202int tty_set(int fd, int action, struct termios *tty)
203{
204	register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context);
205	if(fd >=0)
206	{
207#ifdef future
208		if(ep->e_savefd>=0 && compare(&ep->e_savetty,tty,sizeof(struct termios)))
209			return(0);
210#endif
211		while(tcsetattr(fd, action, tty) == SYSERR)
212		{
213			if(errno !=EINTR)
214				return(SYSERR);
215			errno = 0;
216		}
217		ep->e_savetty = *tty;
218	}
219	ep->e_savefd = fd;
220	return(0);
221}
222
223#if SHOPT_ESH || SHOPT_VSH
224/*{	TTY_COOKED( fd )
225 *
226 *	This routine will set the tty in cooked mode.
227 *	It is also called by error.done().
228 *
229}*/
230
231void tty_cooked(register int fd)
232{
233	register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context);
234	if(ep->e_raw==0)
235		return;
236	if(fd < 0)
237		fd = ep->e_savefd;
238#ifdef L_MASK
239	/* restore flags */
240	if(l_changed&L_MASK)
241		ioctl(fd,TIOCLSET,&l_mask);
242	if(l_changed&T_CHARS)
243		/* restore alternate break character */
244		ioctl(fd,TIOCSETC,&l_ttychars);
245	if(l_changed&L_CHARS)
246		/* restore alternate break character */
247		ioctl(fd,TIOCSLTC,&l_chars);
248	l_changed = 0;
249#endif	/* L_MASK */
250	/*** don't do tty_set unless ttyparm has valid data ***/
251	if(tty_set(fd, TCSANOW, &ttyparm) == SYSERR)
252		return;
253	ep->e_raw = 0;
254	return;
255}
256
257/*{	TTY_RAW( fd )
258 *
259 *	This routine will set the tty in raw mode.
260 *
261}*/
262
263int tty_raw(register int fd, int echomode)
264{
265	int echo = echomode;
266#ifdef L_MASK
267	struct ltchars lchars;
268#endif	/* L_MASK */
269	register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context);
270	if(ep->e_raw==RAWMODE)
271		return(echo?-1:0);
272	else if(ep->e_raw==ECHOMODE)
273		return(echo?0:-1);
274#if !SHOPT_RAWONLY
275	if(ep->e_raw != ALTMODE)
276#endif /* SHOPT_RAWONLY */
277	{
278		if(tty_get(fd,&ttyparm) == SYSERR)
279			return(-1);
280	}
281#if  L_MASK || VENIX
282	if(ttyparm.sg_flags&LCASE)
283		return(-1);
284	if(!(ttyparm.sg_flags&ECHO))
285	{
286		if(!echomode)
287			return(-1);
288		echo = 0;
289	}
290	nttyparm = ttyparm;
291	if(!echo)
292		nttyparm.sg_flags &= ~(ECHO | TBDELAY);
293#   ifdef CBREAK
294	nttyparm.sg_flags |= CBREAK;
295#   else
296	nttyparm.sg_flags |= RAW;
297#   endif /* CBREAK */
298	ep->e_erase = ttyparm.sg_erase;
299	ep->e_kill = ttyparm.sg_kill;
300	ep->e_eof = cntl('D');
301	ep->e_werase = cntl('W');
302	ep->e_lnext = cntl('V');
303	if( tty_set(fd, TCSADRAIN, &nttyparm) == SYSERR )
304		return(-1);
305	ep->e_ttyspeed = (ttyparm.sg_ospeed>=B1200?FAST:SLOW);
306#   ifdef TIOCGLTC
307	/* try to remove effect of ^V  and ^Y and ^O */
308	if(ioctl(fd,TIOCGLTC,&l_chars) != SYSERR)
309	{
310		lchars = l_chars;
311		lchars.t_lnextc = -1;
312		lchars.t_flushc = -1;
313		lchars.t_dsuspc = -1;	/* no delayed stop process signal */
314		if(ioctl(fd,TIOCSLTC,&lchars) != SYSERR)
315			l_changed |= L_CHARS;
316	}
317#   endif	/* TIOCGLTC */
318#else
319	if (!(ttyparm.c_lflag & ECHO ))
320	{
321		if(!echomode)
322			return(-1);
323		echo = 0;
324	}
325#   ifdef FLUSHO
326	ttyparm.c_lflag &= ~FLUSHO;
327#   endif /* FLUSHO */
328	nttyparm = ttyparm;
329#  ifndef u370
330	nttyparm.c_iflag &= ~(IGNPAR|PARMRK|INLCR|IGNCR|ICRNL);
331	nttyparm.c_iflag |= BRKINT;
332#   else
333	nttyparm.c_iflag &=
334			~(IGNBRK|PARMRK|INLCR|IGNCR|ICRNL|INPCK);
335	nttyparm.c_iflag |= (BRKINT|IGNPAR);
336#   endif	/* u370 */
337	if(echo)
338		nttyparm.c_lflag &= ~ICANON;
339	else
340		nttyparm.c_lflag &= ~(ICANON|ECHO|ECHOK);
341	nttyparm.c_cc[VTIME] = 0;
342	nttyparm.c_cc[VMIN] = 1;
343#   ifdef VREPRINT
344	nttyparm.c_cc[VREPRINT] = _POSIX_DISABLE;
345#   endif /* VREPRINT */
346#   ifdef VDISCARD
347	nttyparm.c_cc[VDISCARD] = _POSIX_DISABLE;
348#   endif /* VDISCARD */
349#   ifdef VDSUSP
350	nttyparm.c_cc[VDSUSP] = _POSIX_DISABLE;
351#   endif /* VDSUSP */
352#   ifdef VWERASE
353	if(ttyparm.c_cc[VWERASE] == _POSIX_DISABLE)
354		ep->e_werase = cntl('W');
355	else
356		ep->e_werase = nttyparm.c_cc[VWERASE];
357	nttyparm.c_cc[VWERASE] = _POSIX_DISABLE;
358#   else
359	    ep->e_werase = cntl('W');
360#   endif /* VWERASE */
361#   ifdef VLNEXT
362	if(ttyparm.c_cc[VLNEXT] == _POSIX_DISABLE )
363		ep->e_lnext = cntl('V');
364	else
365		ep->e_lnext = nttyparm.c_cc[VLNEXT];
366	nttyparm.c_cc[VLNEXT] = _POSIX_DISABLE;
367#   else
368	ep->e_lnext = cntl('V');
369#   endif /* VLNEXT */
370	ep->e_eof = ttyparm.c_cc[VEOF];
371	ep->e_erase = ttyparm.c_cc[VERASE];
372	ep->e_kill = ttyparm.c_cc[VKILL];
373	if( tty_set(fd, TCSADRAIN, &nttyparm) == SYSERR )
374		return(-1);
375	ep->e_ttyspeed = (cfgetospeed(&ttyparm)>=B1200?FAST:SLOW);
376#endif
377	ep->e_raw = (echomode?ECHOMODE:RAWMODE);
378	return(0);
379}
380
381#if !SHOPT_RAWONLY
382
383/*
384 *
385 *	Get tty parameters and make ESC and '\r' wakeup characters.
386 *
387 */
388
389#   ifdef TIOCGETC
390int tty_alt(register int fd)
391{
392	register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context);
393	int mask;
394	struct tchars ttychars;
395	switch(ep->e_raw)
396	{
397	    case ECHOMODE:
398		return(-1);
399	    case ALTMODE:
400		return(0);
401	    case RAWMODE:
402		tty_cooked(fd);
403	}
404	l_changed = 0;
405	if( ep->e_ttyspeed == 0)
406	{
407		if((tty_get(fd,&ttyparm) != SYSERR))
408			ep->e_ttyspeed = (ttyparm.sg_ospeed>=B1200?FAST:SLOW);
409		ep->e_raw = ALTMODE;
410	}
411	if(ioctl(fd,TIOCGETC,&l_ttychars) == SYSERR)
412		return(-1);
413	if(ioctl(fd,TIOCLGET,&l_mask)==SYSERR)
414		return(-1);
415	ttychars = l_ttychars;
416	mask =  LCRTBS|LCRTERA|LCTLECH|LPENDIN|LCRTKIL;
417	if((l_mask|mask) != l_mask)
418		l_changed = L_MASK;
419	if(ioctl(fd,TIOCLBIS,&mask)==SYSERR)
420		return(-1);
421	if(ttychars.t_brkc!=ESC)
422	{
423		ttychars.t_brkc = ESC;
424		l_changed |= T_CHARS;
425		if(ioctl(fd,TIOCSETC,&ttychars) == SYSERR)
426			return(-1);
427	}
428	return(0);
429}
430#   else
431#	ifndef PENDIN
432#	    define PENDIN	0
433#	endif /* PENDIN */
434#	ifndef IEXTEN
435#	    define IEXTEN	0
436#	endif /* IEXTEN */
437
438int tty_alt(register int fd)
439{
440	register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context);
441	switch(ep->e_raw)
442	{
443	    case ECHOMODE:
444		return(-1);
445	    case ALTMODE:
446		return(0);
447	    case RAWMODE:
448		tty_cooked(fd);
449	}
450	if((tty_get(fd, &ttyparm)==SYSERR) || (!(ttyparm.c_lflag&ECHO)))
451		return(-1);
452#	ifdef FLUSHO
453	    ttyparm.c_lflag &= ~FLUSHO;
454#	endif /* FLUSHO */
455	nttyparm = ttyparm;
456	ep->e_eof = ttyparm.c_cc[VEOF];
457#	ifdef ECHOCTL
458	    /* escape character echos as ^[ */
459	    nttyparm.c_lflag |= (ECHOE|ECHOK|ECHOCTL|PENDIN|IEXTEN);
460	    nttyparm.c_cc[VEOL] = ESC;
461#	else
462	    /* switch VEOL2 and EOF, since EOF isn't echo'd by driver */
463	    nttyparm.c_lflag |= (ECHOE|ECHOK);
464	    nttyparm.c_cc[VEOF] = ESC;	/* make ESC the eof char */
465#	    ifdef VEOL2
466		nttyparm.c_iflag &= ~(IGNCR|ICRNL);
467		nttyparm.c_iflag |= INLCR;
468		nttyparm.c_cc[VEOL] = '\r';	/* make CR an eol char */
469		nttyparm.c_cc[VEOL2] = ep->e_eof; /* make EOF an eol char */
470#	    else
471		nttyparm.c_cc[VEOL] = ep->e_eof; /* make EOF an eol char */
472#	    endif /* VEOL2 */
473#	endif /* ECHOCTL */
474#	ifdef VREPRINT
475		nttyparm.c_cc[VREPRINT] = _POSIX_DISABLE;
476#	endif /* VREPRINT */
477#	ifdef VDISCARD
478		nttyparm.c_cc[VDISCARD] = _POSIX_DISABLE;
479#	endif /* VDISCARD */
480#	ifdef VWERASE
481	    if(ttyparm.c_cc[VWERASE] == _POSIX_DISABLE)
482		    nttyparm.c_cc[VWERASE] = cntl('W');
483	    ep->e_werase = nttyparm.c_cc[VWERASE];
484#	else
485	    ep->e_werase = cntl('W');
486#	endif /* VWERASE */
487#	ifdef VLNEXT
488	    if(ttyparm.c_cc[VLNEXT] == _POSIX_DISABLE )
489		    nttyparm.c_cc[VLNEXT] = cntl('V');
490	    ep->e_lnext = nttyparm.c_cc[VLNEXT];
491#	else
492	    ep->e_lnext = cntl('V');
493#	endif /* VLNEXT */
494	ep->e_erase = ttyparm.c_cc[VERASE];
495	ep->e_kill = ttyparm.c_cc[VKILL];
496	if( tty_set(fd, TCSADRAIN, &nttyparm) == SYSERR )
497		return(-1);
498	ep->e_ttyspeed = (cfgetospeed(&ttyparm)>=B1200?FAST:SLOW);
499	ep->e_raw = ALTMODE;
500	return(0);
501}
502
503#   endif /* TIOCGETC */
504#endif	/* SHOPT_RAWONLY */
505
506/*
507 *	ED_WINDOW()
508 *
509 *	return the window size
510 */
511int ed_window(void)
512{
513	int	rows,cols;
514	register char *cp = nv_getval(COLUMNS);
515	if(cp)
516		cols = (int)strtol(cp, (char**)0, 10)-1;
517	else
518	{
519		astwinsize(2,&rows,&cols);
520		if(--cols <0)
521			cols = DFLTWINDOW-1;
522	}
523	if(cols < MINWINDOW)
524		cols = MINWINDOW;
525	else if(cols > MAXWINDOW)
526		cols = MAXWINDOW;
527	return(cols);
528}
529
530/*	E_FLUSH()
531 *
532 *	Flush the output buffer.
533 *
534 */
535
536void ed_flush(Edit_t *ep)
537{
538	register int n = ep->e_outptr-ep->e_outbase;
539	register int fd = ERRIO;
540	if(n<=0)
541		return;
542	write(fd,ep->e_outbase,(unsigned)n);
543	ep->e_outptr = ep->e_outbase;
544}
545
546/*
547 * send the bell character ^G to the terminal
548 */
549
550void ed_ringbell(void)
551{
552	write(ERRIO,bellchr,1);
553}
554
555/*
556 * send a carriage return line feed to the terminal
557 */
558
559void ed_crlf(register Edit_t *ep)
560{
561#ifdef cray
562	ed_putchar(ep,'\r');
563#endif /* cray */
564#ifdef u370
565	ed_putchar(ep,'\r');
566#endif	/* u370 */
567#ifdef VENIX
568	ed_putchar(ep,'\r');
569#endif /* VENIX */
570	ed_putchar(ep,'\n');
571	ed_flush(ep);
572}
573
574/*	ED_SETUP( max_prompt_size )
575 *
576 *	This routine sets up the prompt string
577 *	The following is an unadvertised feature.
578 *	  Escape sequences in the prompt can be excluded from the calculated
579 *	  prompt length.  This is accomplished as follows:
580 *	  - if the prompt string starts with "%\r, or contains \r%\r", where %
581 *	    represents any char, then % is taken to be the quote character.
582 *	  - strings enclosed by this quote character, and the quote character,
583 *	    are not counted as part of the prompt length.
584 */
585
586void	ed_setup(register Edit_t *ep, int fd, int reedit)
587{
588	Shell_t *shp = ep->sh;
589	register char *pp;
590	register char *last, *prev;
591	char *ppmax;
592	int myquote = 0, n;
593	register int qlen = 1, qwid;
594	char inquote = 0;
595	ep->e_fd = fd;
596	ep->e_multiline = sh_isoption(SH_MULTILINE)!=0;
597#ifdef SIGWINCH
598	if(!(shp->sigflag[SIGWINCH]&SH_SIGFAULT))
599	{
600		signal(SIGWINCH,sh_fault);
601		shp->sigflag[SIGWINCH] |= SH_SIGFAULT;
602	}
603	pp = shp->st.trapcom[SIGWINCH];
604	shp->st.trapcom[SIGWINCH] = 0;
605	sh_fault(SIGWINCH);
606	shp->st.trapcom[SIGWINCH] = pp;
607	ep->sh->winch = 0;
608#endif
609#if KSHELL
610	ep->e_stkptr = stakptr(0);
611	ep->e_stkoff = staktell();
612	if(!(last = shp->prompt))
613		last = "";
614	shp->prompt = 0;
615#else
616	last = ep->e_prbuff;
617#endif /* KSHELL */
618	if(shp->hist_ptr)
619	{
620		register History_t *hp = shp->hist_ptr;
621		ep->e_hismax = hist_max(hp);
622		ep->e_hismin = hist_min(hp);
623	}
624	else
625	{
626		ep->e_hismax = ep->e_hismin = ep->e_hloff = 0;
627	}
628	ep->e_hline = ep->e_hismax;
629	if(!sh_isoption(SH_VI) && !sh_isoption(SH_EMACS) && !sh_isoption(SH_GMACS))
630		ep->e_wsize = MAXLINE;
631	else
632		ep->e_wsize = ed_window()-2;
633	ep->e_winsz = ep->e_wsize+2;
634	ep->e_crlf = 1;
635	ep->e_plen = 0;
636	pp = ep->e_prompt;
637	ppmax = pp+PRSIZE-1;
638	*pp++ = '\r';
639	{
640		register int c;
641		while(prev = last, c = mbchar(last)) switch(c)
642		{
643			case ESC:
644			{
645				int skip=0;
646				ep->e_crlf = 0;
647				*pp++ = c;
648				for(n=1; c = *last++; n++)
649				{
650					if(pp < ppmax)
651						*pp++ = c;
652					if(c=='\a' || c==ESC || c=='\r')
653						break;
654					if(skip || (c>='0' && c<='9'))
655						continue;
656					if(n>1 && c==';')
657						skip = 1;
658					else if(n>2 || (c!= '[' &&  c!= ']'))
659						break;
660				}
661				if(c==0 || c==ESC || c=='\r')
662					last--;
663				qlen += (n+1);
664				break;
665			}
666			case '\b':
667				if(pp>ep->e_prompt+1)
668					pp--;
669				break;
670			case '\r':
671				if(pp == (ep->e_prompt+2)) /* quote char */
672					myquote = *(pp-1);
673				/*FALLTHROUGH*/
674
675			case '\n':
676				/* start again */
677				ep->e_crlf = 1;
678				qlen = 1;
679				inquote = 0;
680				pp = ep->e_prompt+1;
681				break;
682
683			case '\t':
684				/* expand tabs */
685				while((pp-ep->e_prompt)%TABSIZE)
686				{
687					if(pp >= ppmax)
688						break;
689					*pp++ = ' ';
690				}
691				break;
692
693			case '\a':
694				/* cut out bells */
695				break;
696
697			default:
698				if(c==myquote)
699				{
700					qlen += inquote;
701					inquote ^= 1;
702				}
703				if(pp < ppmax)
704				{
705					if(inquote)
706						qlen++;
707					else if(!is_print(c))
708						ep->e_crlf = 0;
709					if((qwid = last - prev) > 1)
710						qlen += qwid - mbwidth(c);
711					while(prev < last && pp < ppmax)
712						*pp++ = *prev++;
713				}
714				break;
715		}
716	}
717	if(pp-ep->e_prompt > qlen)
718		ep->e_plen = pp - ep->e_prompt - qlen;
719	*pp = 0;
720	if(!ep->e_multiline && (ep->e_wsize -= ep->e_plen) < 7)
721	{
722		register int shift = 7-ep->e_wsize;
723		ep->e_wsize = 7;
724		pp = ep->e_prompt+1;
725		strcpy(pp,pp+shift);
726		ep->e_plen -= shift;
727		last[-ep->e_plen-2] = '\r';
728	}
729	sfsync(sfstderr);
730	if(fd == sffileno(sfstderr))
731	{
732		/* can't use output buffer when reading from stderr */
733		static char *buff;
734		if(!buff)
735			buff = (char*)malloc(MAXLINE);
736		ep->e_outbase = ep->e_outptr = buff;
737		ep->e_outlast = ep->e_outptr + MAXLINE;
738		return;
739	}
740	qlen = sfset(sfstderr,SF_READ,0);
741	/* make sure SF_READ not on */
742	ep->e_outbase = ep->e_outptr = (char*)sfreserve(sfstderr,SF_UNBOUND,SF_LOCKR);
743	ep->e_outlast = ep->e_outptr + sfvalue(sfstderr);
744	if(qlen)
745		sfset(sfstderr,SF_READ,1);
746	sfwrite(sfstderr,ep->e_outptr,0);
747	ep->e_eol = reedit;
748	if(ep->e_multiline)
749	{
750#ifdef _cmd_tput
751		char *term;
752		if(!ep->e_term)
753			ep->e_term = nv_search("TERM",shp->var_tree,0);
754		if(ep->e_term && (term=nv_getval(ep->e_term)) && strlen(term)<sizeof(ep->e_termname) && strcmp(term,ep->e_termname))
755		{
756			sh_trap(".sh.subscript=$(tput cuu1 2>/dev/null)",0);
757			if(pp=nv_getval(SH_SUBSCRNOD))
758				strncpy(CURSOR_UP,pp,sizeof(CURSOR_UP)-1);
759			nv_unset(SH_SUBSCRNOD);
760			strcpy(ep->e_termname,term);
761		}
762#endif
763		ep->e_wsize = MAXLINE - (ep->e_plen+1);
764	}
765	if(ep->e_default && (pp = nv_getval(ep->e_default)))
766	{
767		n = strlen(pp);
768		if(n > LOOKAHEAD)
769			n = LOOKAHEAD;
770		ep->e_lookahead = n;
771		while(n-- > 0)
772			ep->e_lbuf[n] = *pp++;
773		ep->e_default = 0;
774	}
775}
776
777static void ed_putstring(register Edit_t *ep, const char *str)
778{
779	register int c;
780	while(c = *str++)
781		ed_putchar(ep,c);
782}
783
784static void ed_nputchar(register Edit_t *ep, int n, int c)
785{
786	while(n-->0)
787		ed_putchar(ep,c);
788}
789
790/*
791 * Do read, restart on interrupt unless SH_SIGSET or SH_SIGTRAP is set
792 * Use sfpkrd() to poll() or select() to wait for input if possible
793 * Unfortunately, systems that get interrupted from slow reads update
794 * this access time for for the terminal (in violation of POSIX).
795 * The fixtime() macro, resets the time to the time at entry in
796 * this case.  This is not necessary for systems that can handle
797 * sfpkrd() correctly (i,e., those that support poll() or select()
798 */
799int ed_read(void *context, int fd, char *buff, int size, int reedit)
800{
801	register Edit_t *ep = (Edit_t*)context;
802	register int rv= -1;
803	register int delim = (ep->e_raw==RAWMODE?'\r':'\n');
804	Shell_t *shp = ep->sh;
805	int mode = -1;
806	int (*waitevent)(int,long,int) = shp->waitevent;
807	if(ep->e_raw==ALTMODE)
808		mode = 1;
809	if(size < 0)
810	{
811		mode = 1;
812		size = -size;
813	}
814	sh_onstate(SH_TTYWAIT);
815	errno = EINTR;
816	shp->waitevent = 0;
817	while(rv<0 && errno==EINTR)
818	{
819		if(shp->trapnote&(SH_SIGSET|SH_SIGTRAP))
820			goto done;
821		if(ep->sh->winch && sh_isstate(SH_INTERACTIVE) && (sh_isoption(SH_VI) || sh_isoption(SH_EMACS)))
822		{
823			Edpos_t	lastpos;
824			int	n, rows, newsize;
825			/* move cursor to start of first line */
826			ed_putchar(ep,'\r');
827			ed_flush(ep);
828			astwinsize(2,&rows,&newsize);
829			n = (ep->e_plen+ep->e_cur)/++ep->e_winsz;
830			while(n--)
831				ed_putstring(ep,CURSOR_UP);
832			if(ep->e_multiline && newsize>ep->e_winsz && (lastpos.line=(ep->e_plen+ep->e_peol)/ep->e_winsz))
833			{
834				/* clear the current command line */
835				n = lastpos.line;
836				while(lastpos.line--)
837				{
838					ed_nputchar(ep,ep->e_winsz,' ');
839					ed_putchar(ep,'\n');
840				}
841				ed_nputchar(ep,ep->e_winsz,' ');
842				while(n--)
843					ed_putstring(ep,CURSOR_UP);
844			}
845	                ep->sh->winch = 0;
846			ed_flush(ep);
847			sh_delay(.05);
848			astwinsize(2,&rows,&newsize);
849			ep->e_winsz = newsize-1;
850			if(!ep->e_multiline && ep->e_wsize < MAXLINE)
851				ep->e_wsize = ep->e_winsz-2;
852			ep->e_nocrnl=1;
853			if(*ep->e_vi_insert)
854			{
855				buff[0] = ESC;
856				buff[1] = cntl('L');
857				buff[2] = 'a';
858				return(3);
859			}
860			if(sh_isoption(SH_EMACS) || sh_isoption(SH_VI))
861				buff[0] = cntl('L');
862			return(1);
863		}
864		else
865			ep->sh->winch = 0;
866		/* an interrupt that should be ignored */
867		errno = 0;
868		if(!waitevent || (rv=(*waitevent)(fd,-1L,0))>=0)
869			rv = sfpkrd(fd,buff,size,delim,-1L,mode);
870	}
871	if(rv < 0)
872	{
873#ifdef _hdr_utime
874#		define fixtime()	if(isdevtty)utime(ep->e_tty,&utimes)
875		int	isdevtty=0;
876		struct stat statb;
877		struct utimbuf utimes;
878	 	if(errno==0 && !ep->e_tty)
879		{
880			if((ep->e_tty=ttyname(fd)) && stat(ep->e_tty,&statb)>=0)
881			{
882				ep->e_tty_ino = statb.st_ino;
883				ep->e_tty_dev = statb.st_dev;
884			}
885		}
886		if(ep->e_tty_ino && fstat(fd,&statb)>=0 && statb.st_ino==ep->e_tty_ino && statb.st_dev==ep->e_tty_dev)
887		{
888			utimes.actime = statb.st_atime;
889			utimes.modtime = statb.st_mtime;
890			isdevtty=1;
891		}
892#else
893#		define fixtime()
894#endif /* _hdr_utime */
895		while(1)
896		{
897			rv = read(fd,buff,size);
898			if(rv>=0 || errno!=EINTR)
899				break;
900			if(shp->trapnote&(SH_SIGSET|SH_SIGTRAP))
901				goto done;
902			/* an interrupt that should be ignored */
903			fixtime();
904		}
905	}
906	else if(rv>=0 && mode>0)
907		rv = read(fd,buff,rv>0?rv:1);
908done:
909	shp->waitevent = waitevent;
910	sh_offstate(SH_TTYWAIT);
911	return(rv);
912}
913
914
915/*
916 * put <string> of length <nbyte> onto lookahead stack
917 * if <type> is non-zero,  the negation of the character is put
918 *    onto the stack so that it can be checked for KEYTRAP
919 * putstack() returns 1 except when in the middle of a multi-byte char
920 */
921static int putstack(Edit_t *ep,char string[], register int nbyte, int type)
922{
923	register int c;
924#if SHOPT_MULTIBYTE
925	char *endp, *p=string;
926	int size, offset = ep->e_lookahead + nbyte;
927	*(endp = &p[nbyte]) = 0;
928	endp = &p[nbyte];
929	do
930	{
931		c = (int)((*p) & STRIP);
932		if(c< 0x80 && c!='<')
933		{
934			if (type)
935				c = -c;
936#   ifndef CBREAK
937			if(c == '\0')
938			{
939				/*** user break key ***/
940				ep->e_lookahead = 0;
941#	if KSHELL
942				sh_fault(SIGINT);
943				siglongjmp(ep->e_env, UINTR);
944#	endif   /* KSHELL */
945			}
946#   endif /* CBREAK */
947
948		}
949		else
950		{
951		again:
952			if((c=mbchar(p)) >=0)
953			{
954				p--;	/* incremented below */
955				if(type)
956					c = -c;
957			}
958#ifdef EILSEQ
959			else if(errno == EILSEQ)
960				errno = 0;
961#endif
962			else if((endp-p) < mbmax())
963			{
964				if ((c=ed_read(ep,ep->e_fd,endp, 1,0)) == 1)
965				{
966					*++endp = 0;
967					goto again;
968				}
969				return(c);
970			}
971			else
972			{
973				ed_ringbell();
974				c = -(int)((*p) & STRIP);
975				offset += mbmax()-1;
976			}
977		}
978		ep->e_lbuf[--offset] = c;
979		p++;
980	}
981	while (p < endp);
982	/* shift lookahead buffer if necessary */
983	if(offset -= ep->e_lookahead)
984	{
985		for(size=offset;size < nbyte;size++)
986			ep->e_lbuf[ep->e_lookahead+size-offset] = ep->e_lbuf[ep->e_lookahead+size];
987	}
988	ep->e_lookahead += nbyte-offset;
989#else
990	while (nbyte > 0)
991	{
992		c = string[--nbyte] & STRIP;
993		ep->e_lbuf[ep->e_lookahead++] = (type?-c:c);
994#   ifndef CBREAK
995		if( c == '\0' )
996		{
997			/*** user break key ***/
998			ep->e_lookahead = 0;
999#	if KSHELL
1000			sh_fault(SIGINT);
1001			siglongjmp(ep->e_env, UINTR);
1002#	endif	/* KSHELL */
1003		}
1004#   endif /* CBREAK */
1005	}
1006#endif /* SHOPT_MULTIBYTE */
1007	return(1);
1008}
1009
1010/*
1011 * routine to perform read from terminal for vi and emacs mode
1012 * <mode> can be one of the following:
1013 *   -2		vi insert mode - key binding is in effect
1014 *   -1		vi control mode - key binding is in effect
1015 *   0		normal command mode - key binding is in effect
1016 *   1		edit keys not mapped
1017 *   2		Next key is literal
1018 */
1019int ed_getchar(register Edit_t *ep,int mode)
1020{
1021	register int n, c;
1022	char readin[LOOKAHEAD+1];
1023	if(!ep->e_lookahead)
1024	{
1025		ed_flush(ep);
1026		ep->e_inmacro = 0;
1027		/* The while is necessary for reads of partial multbyte chars */
1028		*ep->e_vi_insert = (mode==-2);
1029		if((n=ed_read(ep,ep->e_fd,readin,-LOOKAHEAD,0)) > 0)
1030			n = putstack(ep,readin,n,1);
1031		*ep->e_vi_insert = 0;
1032	}
1033	if(ep->e_lookahead)
1034	{
1035		/* check for possible key mapping */
1036		if((c = ep->e_lbuf[--ep->e_lookahead]) < 0)
1037		{
1038			if(mode<=0 && ep->sh->st.trap[SH_KEYTRAP])
1039			{
1040				n=1;
1041				if((readin[0]= -c) == ESC)
1042				{
1043					while(1)
1044					{
1045						if(!ep->e_lookahead)
1046						{
1047							if((c=sfpkrd(ep->e_fd,readin+n,1,'\r',(mode?400L:-1L),0))>0)
1048								putstack(ep,readin+n,c,1);
1049						}
1050						if(!ep->e_lookahead)
1051							break;
1052						if((c=ep->e_lbuf[--ep->e_lookahead])>=0)
1053						{
1054							ep->e_lookahead++;
1055							break;
1056						}
1057						c = -c;
1058						readin[n++] = c;
1059						if(c>='0' && c<='9' && n>2)
1060							continue;
1061						if(n>2 || (c!= '['  &&  c!= 'O'))
1062							break;
1063					}
1064				}
1065				if(n=keytrap(ep,readin,n,LOOKAHEAD-n,mode))
1066				{
1067					putstack(ep,readin,n,0);
1068					c = ep->e_lbuf[--ep->e_lookahead];
1069				}
1070				else
1071					c = ed_getchar(ep,mode);
1072			}
1073			else
1074				c = -c;
1075		}
1076		/*** map '\r' to '\n' ***/
1077		if(c == '\r' && mode!=2)
1078			c = '\n';
1079		if(ep->e_tabcount && !(c=='\t'||c==ESC || c=='\\' || c=='=' || c==cntl('L') || isdigit(c)))
1080			ep->e_tabcount = 0;
1081	}
1082	else
1083		siglongjmp(ep->e_env,(n==0?UEOF:UINTR));
1084	return(c);
1085}
1086
1087void ed_ungetchar(Edit_t *ep,register int c)
1088{
1089	if (ep->e_lookahead < LOOKAHEAD)
1090		ep->e_lbuf[ep->e_lookahead++] = c;
1091	return;
1092}
1093
1094/*
1095 * put a character into the output buffer
1096 */
1097
1098void	ed_putchar(register Edit_t *ep,register int c)
1099{
1100	char buf[8];
1101	register char *dp = ep->e_outptr;
1102	register int i,size=1;
1103	if(!dp)
1104		return;
1105	buf[0] = c;
1106#if SHOPT_MULTIBYTE
1107	/* check for place holder */
1108	if(c == MARKER)
1109		return;
1110	if((size = mbconv(buf, (wchar_t)c)) > 1)
1111	{
1112		for (i = 0; i < (size-1); i++)
1113			*dp++ = buf[i];
1114		c = buf[i];
1115	}
1116	else
1117	{
1118		buf[0] = c;
1119		size = 1;
1120	}
1121#endif	/* SHOPT_MULTIBYTE */
1122	if (buf[0] == '_' && size==1)
1123	{
1124		*dp++ = ' ';
1125		*dp++ = '\b';
1126	}
1127	*dp++ = c;
1128	*dp = '\0';
1129	if(dp >= ep->e_outlast)
1130		ed_flush(ep);
1131	else
1132		ep->e_outptr = dp;
1133}
1134
1135/*
1136 * returns the line and column corresponding to offset <off> in the physical buffer
1137 * if <cur> is non-zero and <= <off>, then correspodning <curpos> will start the search
1138 */
1139Edpos_t ed_curpos(Edit_t *ep,genchar *phys, int off, int cur, Edpos_t curpos)
1140{
1141	register genchar *sp=phys;
1142	register int c=1, col=ep->e_plen;
1143	Edpos_t pos;
1144#if SHOPT_MULTIBYTE
1145	char p[16];
1146#endif /* SHOPT_MULTIBYTE */
1147	if(cur && off>=cur)
1148	{
1149		sp += cur;
1150		off -= cur;
1151		pos = curpos;
1152		col = pos.col;
1153	}
1154	else
1155	{
1156		pos.line = 0;
1157		while(col > ep->e_winsz)
1158		{
1159			pos.line++;
1160			col -= (ep->e_winsz+1);
1161		}
1162	}
1163	while(off-->0)
1164	{
1165		if(c)
1166			c = *sp++;
1167#if SHOPT_MULTIBYTE
1168		if(c && (mbconv(p, (wchar_t)c))==1 && p[0]=='\n')
1169#else
1170		if(c=='\n')
1171#endif /* SHOPT_MULTIBYTE */
1172			col = 0;
1173		else
1174			col++;
1175		if(col >  ep->e_winsz)
1176			col = 0;
1177		if(col==0)
1178			pos.line++;
1179	}
1180	pos.col = col;
1181	return(pos);
1182}
1183
1184int ed_setcursor(register Edit_t *ep,genchar *physical,register int old,register int new,int first)
1185{
1186	static int oldline;
1187	register int delta;
1188	int clear = 0;
1189	Edpos_t newpos;
1190
1191	delta = new - old;
1192	if(first < 0)
1193	{
1194		first = 0;
1195		clear = 1;
1196	}
1197	if( delta == 0  &&  !clear)
1198		return(new);
1199	if(ep->e_multiline)
1200	{
1201		ep->e_curpos = ed_curpos(ep, physical, old,0,ep->e_curpos);
1202		if(clear && old>=ep->e_peol && (clear=ep->e_winsz-ep->e_curpos.col)>0)
1203		{
1204			ed_nputchar(ep,clear,' ');
1205			ed_nputchar(ep,clear,'\b');
1206			return(new);
1207		}
1208		newpos =     ed_curpos(ep, physical, new,old,ep->e_curpos);
1209		if(ep->e_curpos.col==0 && ep->e_curpos.line>0 && oldline<ep->e_curpos.line && delta<0)
1210			ed_putstring(ep,"\r\n");
1211		oldline = newpos.line;
1212		if(ep->e_curpos.line > newpos.line)
1213		{
1214			int n,pline,plen=ep->e_plen;
1215			for(;ep->e_curpos.line > newpos.line; ep->e_curpos.line--)
1216				ed_putstring(ep,CURSOR_UP);
1217			pline = plen/(ep->e_winsz+1);
1218			if(newpos.line <= pline)
1219				plen -= pline*(ep->e_winsz+1);
1220			else
1221				plen = 0;
1222			if((n=plen- ep->e_curpos.col)>0)
1223			{
1224				ep->e_curpos.col += n;
1225				ed_putchar(ep,'\r');
1226				if(!ep->e_crlf && pline==0)
1227					ed_putstring(ep,ep->e_prompt);
1228				else
1229				{
1230					int m = ep->e_winsz+1-plen;
1231					ed_putchar(ep,'\n');
1232					n = plen;
1233					if(m < ed_genlen(physical))
1234					{
1235						while(physical[m] && n-->0)
1236							ed_putchar(ep,physical[m++]);
1237					}
1238					ed_nputchar(ep,n,' ');
1239					ed_putstring(ep,CURSOR_UP);
1240				}
1241			}
1242		}
1243		else if(ep->e_curpos.line < newpos.line)
1244		{
1245			ed_nputchar(ep, newpos.line-ep->e_curpos.line,'\n');
1246			ep->e_curpos.line = newpos.line;
1247			ed_putchar(ep,'\r');
1248			ep->e_curpos.col = 0;
1249		}
1250		delta = newpos.col - ep->e_curpos.col;
1251		old   =  new - delta;
1252	}
1253	else
1254		newpos.line=0;
1255	if(delta<0)
1256	{
1257		int bs= newpos.line && ep->e_plen>ep->e_winsz;
1258		/*** move to left ***/
1259		delta = -delta;
1260		/*** attempt to optimize cursor movement ***/
1261		if(!ep->e_crlf || bs || (2*delta <= ((old-first)+(newpos.line?0:ep->e_plen))) )
1262		{
1263			ed_nputchar(ep,delta,'\b');
1264			delta = 0;
1265		}
1266		else
1267		{
1268			if(newpos.line==0)
1269				ed_putstring(ep,ep->e_prompt);
1270			else
1271			{
1272				first = 1+(newpos.line*ep->e_winsz - ep->e_plen);
1273				ed_putchar(ep,'\r');
1274			}
1275			old = first;
1276			delta = new-first;
1277		}
1278	}
1279	while(delta-->0)
1280		ed_putchar(ep,physical[old++]);
1281	return(new);
1282}
1283
1284/*
1285 * copy virtual to physical and return the index for cursor in physical buffer
1286 */
1287int ed_virt_to_phys(Edit_t *ep,genchar *virt,genchar *phys,int cur,int voff,int poff)
1288{
1289	register genchar *sp = virt;
1290	register genchar *dp = phys;
1291	register int c;
1292	genchar *curp = sp + cur;
1293	genchar *dpmax = phys+MAXLINE;
1294	int d, r;
1295	sp += voff;
1296	dp += poff;
1297	for(r=poff;c= *sp;sp++)
1298	{
1299		if(curp == sp)
1300			r = dp - phys;
1301#if SHOPT_MULTIBYTE
1302		d = mbwidth((wchar_t)c);
1303		if(d==1 && is_cntrl(c))
1304			d = -1;
1305		if(d>1)
1306		{
1307			/* multiple width character put in place holders */
1308			*dp++ = c;
1309			while(--d >0)
1310				*dp++ = MARKER;
1311			/* in vi mode the cursor is at the last character */
1312			if(dp>=dpmax)
1313				break;
1314			continue;
1315		}
1316		else
1317#else
1318		d = (is_cntrl(c)?-1:1);
1319#endif	/* SHOPT_MULTIBYTE */
1320		if(d<0)
1321		{
1322			if(c=='\t')
1323			{
1324				c = dp-phys;
1325				if(sh_isoption(SH_VI))
1326					c += ep->e_plen;
1327				c = TABSIZE - c%TABSIZE;
1328				while(--c>0)
1329					*dp++ = ' ';
1330				c = ' ';
1331			}
1332			else
1333			{
1334				*dp++ = '^';
1335				c = printchar(c);
1336			}
1337			/* in vi mode the cursor is at the last character */
1338			if(curp == sp && sh_isoption(SH_VI))
1339				r = dp - phys;
1340		}
1341		*dp++ = c;
1342		if(dp>=dpmax)
1343			break;
1344	}
1345	*dp = 0;
1346	ep->e_peol = dp-phys;
1347	return(r);
1348}
1349
1350#if SHOPT_MULTIBYTE
1351/*
1352 * convert external representation <src> to an array of genchars <dest>
1353 * <src> and <dest> can be the same
1354 * returns number of chars in dest
1355 */
1356
1357int	ed_internal(const char *src, genchar *dest)
1358{
1359	register const unsigned char *cp = (unsigned char *)src;
1360	register int c;
1361	register wchar_t *dp = (wchar_t*)dest;
1362	if(dest == (genchar*)roundof(cp-(unsigned char*)0,sizeof(genchar)))
1363	{
1364		genchar buffer[MAXLINE];
1365		c = ed_internal(src,buffer);
1366		ed_gencpy((genchar*)dp,buffer);
1367		return(c);
1368	}
1369	while(*cp)
1370		*dp++ = mbchar(cp);
1371	*dp = 0;
1372	return(dp-(wchar_t*)dest);
1373}
1374
1375/*
1376 * convert internal representation <src> into character array <dest>.
1377 * The <src> and <dest> may be the same.
1378 * returns number of chars in dest.
1379 */
1380
1381int	ed_external(const genchar *src, char *dest)
1382{
1383	register genchar wc;
1384	register int c,size;
1385	register char *dp = dest;
1386	char *dpmax = dp+sizeof(genchar)*MAXLINE-2;
1387	if((char*)src == dp)
1388	{
1389		char buffer[MAXLINE*sizeof(genchar)];
1390		c = ed_external(src,buffer);
1391
1392#ifdef _lib_wcscpy
1393		wcscpy((wchar_t *)dest,(const wchar_t *)buffer);
1394#else
1395		strcpy(dest,buffer);
1396#endif
1397		return(c);
1398	}
1399	while((wc = *src++) && dp<dpmax)
1400	{
1401		if((size = mbconv(dp, wc)) < 0)
1402		{
1403			/* copy the character as is */
1404			size = 1;
1405			*dp = wc;
1406		}
1407		dp += size;
1408	}
1409	*dp = 0;
1410	return(dp-dest);
1411}
1412
1413/*
1414 * copy <sp> to <dp>
1415 */
1416
1417void	ed_gencpy(genchar *dp,const genchar *sp)
1418{
1419	dp = (genchar*)roundof((char*)dp-(char*)0,sizeof(genchar));
1420	sp = (const genchar*)roundof((char*)sp-(char*)0,sizeof(genchar));
1421	while(*dp++ = *sp++);
1422}
1423
1424/*
1425 * copy at most <n> items from <sp> to <dp>
1426 */
1427
1428void	ed_genncpy(register genchar *dp,register const genchar *sp, int n)
1429{
1430	dp = (genchar*)roundof((char*)dp-(char*)0,sizeof(genchar));
1431	sp = (const genchar*)roundof((char*)sp-(char*)0,sizeof(genchar));
1432	while(n-->0 && (*dp++ = *sp++));
1433}
1434
1435/*
1436 * find the string length of <str>
1437 */
1438
1439int	ed_genlen(register const genchar *str)
1440{
1441	register const genchar *sp = str;
1442	sp = (const genchar*)roundof((char*)sp-(char*)0,sizeof(genchar));
1443	while(*sp++);
1444	return(sp-str-1);
1445}
1446#endif /* SHOPT_MULTIBYTE */
1447#endif /* SHOPT_ESH || SHOPT_VSH */
1448
1449#ifdef future
1450/*
1451 * returns 1 when <n> bytes starting at <a> and <b> are equal
1452 */
1453static int compare(register const char *a,register const char *b,register int n)
1454{
1455	while(n-->0)
1456	{
1457		if(*a++ != *b++)
1458			return(0);
1459	}
1460	return(1);
1461}
1462#endif
1463
1464#if SHOPT_OLDTERMIO
1465
1466#   include	<sys/termio.h>
1467
1468#ifndef ECHOCTL
1469#   define ECHOCTL	0
1470#endif /* !ECHOCTL */
1471#define ott	ep->e_ott
1472
1473/*
1474 * For backward compatibility only
1475 * This version will use termios when possible, otherwise termio
1476 */
1477
1478int tcgetattr(int fd, struct termios *tt)
1479{
1480	register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context);
1481	register int r,i;
1482	ep->e_tcgeta = 0;
1483	ep->e_echoctl = (ECHOCTL!=0);
1484	if((r=ioctl(fd,TCGETS,tt))>=0 ||  errno!=EINVAL)
1485		return(r);
1486	if((r=ioctl(fd,TCGETA,&ott)) >= 0)
1487	{
1488		tt->c_lflag = ott.c_lflag;
1489		tt->c_oflag = ott.c_oflag;
1490		tt->c_iflag = ott.c_iflag;
1491		tt->c_cflag = ott.c_cflag;
1492		for(i=0; i<NCC; i++)
1493			tt->c_cc[i] = ott.c_cc[i];
1494		ep->e_tcgeta++;
1495		ep->e_echoctl = 0;
1496	}
1497	return(r);
1498}
1499
1500int tcsetattr(int fd,int mode,struct termios *tt)
1501{
1502	register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context);
1503	register int r;
1504	if(ep->e_tcgeta)
1505	{
1506		register int i;
1507		ott.c_lflag = tt->c_lflag;
1508		ott.c_oflag = tt->c_oflag;
1509		ott.c_iflag = tt->c_iflag;
1510		ott.c_cflag = tt->c_cflag;
1511		for(i=0; i<NCC; i++)
1512			ott.c_cc[i] = tt->c_cc[i];
1513		if(tt->c_lflag&ECHOCTL)
1514		{
1515			ott.c_lflag &= ~(ECHOCTL|IEXTEN);
1516			ott.c_iflag &= ~(IGNCR|ICRNL);
1517			ott.c_iflag |= INLCR;
1518			ott.c_cc[VEOF]= ESC;  /* ESC -> eof char */
1519			ott.c_cc[VEOL] = '\r'; /* CR -> eol char */
1520			ott.c_cc[VEOL2] = tt->c_cc[VEOF]; /* EOF -> eol char */
1521		}
1522		switch(mode)
1523		{
1524			case TCSANOW:
1525				mode = TCSETA;
1526				break;
1527			case TCSADRAIN:
1528				mode = TCSETAW;
1529				break;
1530			case TCSAFLUSH:
1531				mode = TCSETAF;
1532		}
1533		return(ioctl(fd,mode,&ott));
1534	}
1535	return(ioctl(fd,mode,tt));
1536}
1537#endif /* SHOPT_OLDTERMIO */
1538
1539#if KSHELL
1540/*
1541 * Execute keyboard trap on given buffer <inbuff> of given size <isize>
1542 * <mode> < 0 for vi insert mode
1543 */
1544static int keytrap(Edit_t *ep,char *inbuff,register int insize, int bufsize, int mode)
1545{
1546	register char *cp;
1547	int savexit;
1548	Shell_t *shp = ep->sh;
1549#if SHOPT_MULTIBYTE
1550	char buff[MAXLINE];
1551	ed_external(ep->e_inbuf,cp=buff);
1552#else
1553	cp = ep->e_inbuf;
1554#endif /* SHOPT_MULTIBYTE */
1555	inbuff[insize] = 0;
1556	ep->e_col = ep->e_cur;
1557	if(mode== -2)
1558	{
1559		ep->e_col++;
1560		*ep->e_vi_insert = ESC;
1561	}
1562	else
1563		*ep->e_vi_insert = 0;
1564	nv_putval(ED_CHRNOD,inbuff,NV_NOFREE);
1565	nv_putval(ED_COLNOD,(char*)&ep->e_col,NV_NOFREE|NV_INTEGER);
1566	nv_putval(ED_TXTNOD,(char*)cp,NV_NOFREE);
1567	nv_putval(ED_MODENOD,ep->e_vi_insert,NV_NOFREE);
1568	savexit = shp->savexit;
1569	sh_trap(shp->st.trap[SH_KEYTRAP],0);
1570	shp->savexit = savexit;
1571	if((cp = nv_getval(ED_CHRNOD)) == inbuff)
1572		nv_unset(ED_CHRNOD);
1573	else if(bufsize>0)
1574	{
1575		strncpy(inbuff,cp,bufsize);
1576		inbuff[bufsize-1]='\0';
1577		insize = strlen(inbuff);
1578	}
1579	else
1580		insize = 0;
1581	nv_unset(ED_TXTNOD);
1582	return(insize);
1583}
1584#endif /* KSHELL */
1585
1586void	*ed_open(Shell_t *shp)
1587{
1588	Edit_t *ed = newof(0,Edit_t,1,0);
1589	ed->sh = shp;
1590	strcpy(ed->e_macro,"_??");
1591	return((void*)ed);
1592}
1593