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