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