1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1982-2008 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                  Common Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *            http://www.opensource.org/licenses/cpl1.0.txt             *
11 *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *                  David Korn <dgk@research.att.com>                   *
18 *                                                                      *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
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 
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  */
160 int 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 
174 int 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 
202 int 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 
231 void 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 
263 int 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
390 int 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 
438 int 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  */
511 int 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 
536 void 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 
550 void ed_ringbell(void)
551 {
552 	write(ERRIO,bellchr,1);
553 }
554 
555 /*
556  * send a carriage return line feed to the terminal
557  */
558 
559 void 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 
586 void	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 
777 static 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 
784 static 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  */
799 int 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)
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 			buff[0] = cntl('L');
861 			return(1);
862 		}
863 		/* an interrupt that should be ignored */
864 		errno = 0;
865 		if(!waitevent || (rv=(*waitevent)(fd,-1L,0))>=0)
866 			rv = sfpkrd(fd,buff,size,delim,-1L,mode);
867 	}
868 	if(rv < 0)
869 	{
870 #ifdef _hdr_utime
871 #		define fixtime()	if(isdevtty)utime(ep->e_tty,&utimes)
872 		int	isdevtty=0;
873 		struct stat statb;
874 		struct utimbuf utimes;
875 	 	if(errno==0 && !ep->e_tty)
876 		{
877 			if((ep->e_tty=ttyname(fd)) && stat(ep->e_tty,&statb)>=0)
878 			{
879 				ep->e_tty_ino = statb.st_ino;
880 				ep->e_tty_dev = statb.st_dev;
881 			}
882 		}
883 		if(ep->e_tty_ino && fstat(fd,&statb)>=0 && statb.st_ino==ep->e_tty_ino && statb.st_dev==ep->e_tty_dev)
884 		{
885 			utimes.actime = statb.st_atime;
886 			utimes.modtime = statb.st_mtime;
887 			isdevtty=1;
888 		}
889 #else
890 #		define fixtime()
891 #endif /* _hdr_utime */
892 		while(1)
893 		{
894 			rv = read(fd,buff,size);
895 			if(rv>=0 || errno!=EINTR)
896 				break;
897 			if(shp->trapnote&(SH_SIGSET|SH_SIGTRAP))
898 				goto done;
899 			/* an interrupt that should be ignored */
900 			fixtime();
901 		}
902 	}
903 	else if(rv>=0 && mode>0)
904 		rv = read(fd,buff,rv>0?rv:1);
905 done:
906 	shp->waitevent = waitevent;
907 	sh_offstate(SH_TTYWAIT);
908 	return(rv);
909 }
910 
911 
912 /*
913  * put <string> of length <nbyte> onto lookahead stack
914  * if <type> is non-zero,  the negation of the character is put
915  *    onto the stack so that it can be checked for KEYTRAP
916  * putstack() returns 1 except when in the middle of a multi-byte char
917  */
918 static int putstack(Edit_t *ep,char string[], register int nbyte, int type)
919 {
920 	register int c;
921 #if SHOPT_MULTIBYTE
922 	char *endp, *p=string;
923 	int size, offset = ep->e_lookahead + nbyte;
924 	*(endp = &p[nbyte]) = 0;
925 	endp = &p[nbyte];
926 	do
927 	{
928 		c = (int)((*p) & STRIP);
929 		if(c< 0x80 && c!='<')
930 		{
931 			if (type)
932 				c = -c;
933 #   ifndef CBREAK
934 			if(c == '\0')
935 			{
936 				/*** user break key ***/
937 				ep->e_lookahead = 0;
938 #	if KSHELL
939 				sh_fault(SIGINT);
940 				siglongjmp(ep->e_env, UINTR);
941 #	endif   /* KSHELL */
942 			}
943 #   endif /* CBREAK */
944 
945 		}
946 		else
947 		{
948 		again:
949 			if((c=mbchar(p)) >=0)
950 			{
951 				p--;	/* incremented below */
952 				if(type)
953 					c = -c;
954 			}
955 #ifdef EILSEQ
956 			else if(errno == EILSEQ)
957 				errno = 0;
958 #endif
959 			else if((endp-p) < mbmax())
960 			{
961 				if ((c=ed_read(ep,ep->e_fd,endp, 1,0)) == 1)
962 				{
963 					*++endp = 0;
964 					goto again;
965 				}
966 				return(c);
967 			}
968 			else
969 			{
970 				ed_ringbell();
971 				c = -(int)((*p) & STRIP);
972 				offset += mbmax()-1;
973 			}
974 		}
975 		ep->e_lbuf[--offset] = c;
976 		p++;
977 	}
978 	while (p < endp);
979 	/* shift lookahead buffer if necessary */
980 	if(offset -= ep->e_lookahead)
981 	{
982 		for(size=offset;size < nbyte;size++)
983 			ep->e_lbuf[ep->e_lookahead+size-offset] = ep->e_lbuf[ep->e_lookahead+size];
984 	}
985 	ep->e_lookahead += nbyte-offset;
986 #else
987 	while (nbyte > 0)
988 	{
989 		c = string[--nbyte] & STRIP;
990 		ep->e_lbuf[ep->e_lookahead++] = (type?-c:c);
991 #   ifndef CBREAK
992 		if( c == '\0' )
993 		{
994 			/*** user break key ***/
995 			ep->e_lookahead = 0;
996 #	if KSHELL
997 			sh_fault(SIGINT);
998 			siglongjmp(ep->e_env, UINTR);
999 #	endif	/* KSHELL */
1000 		}
1001 #   endif /* CBREAK */
1002 	}
1003 #endif /* SHOPT_MULTIBYTE */
1004 	return(1);
1005 }
1006 
1007 /*
1008  * routine to perform read from terminal for vi and emacs mode
1009  * <mode> can be one of the following:
1010  *   -2		vi insert mode - key binding is in effect
1011  *   -1		vi control mode - key binding is in effect
1012  *   0		normal command mode - key binding is in effect
1013  *   1		edit keys not mapped
1014  *   2		Next key is literal
1015  */
1016 int ed_getchar(register Edit_t *ep,int mode)
1017 {
1018 	register int n, c;
1019 	char readin[LOOKAHEAD+1];
1020 	if(!ep->e_lookahead)
1021 	{
1022 		ed_flush(ep);
1023 		ep->e_inmacro = 0;
1024 		/* The while is necessary for reads of partial multbyte chars */
1025 		*ep->e_vi_insert = (mode==-2);
1026 		if((n=ed_read(ep,ep->e_fd,readin,-LOOKAHEAD,0)) > 0)
1027 			n = putstack(ep,readin,n,1);
1028 		*ep->e_vi_insert = 0;
1029 	}
1030 	if(ep->e_lookahead)
1031 	{
1032 		/* check for possible key mapping */
1033 		if((c = ep->e_lbuf[--ep->e_lookahead]) < 0)
1034 		{
1035 			if(mode<=0 && ep->sh->st.trap[SH_KEYTRAP])
1036 			{
1037 				n=1;
1038 				if((readin[0]= -c) == ESC)
1039 				{
1040 					while(1)
1041 					{
1042 						if(!ep->e_lookahead)
1043 						{
1044 							if((c=sfpkrd(ep->e_fd,readin+n,1,'\r',(mode?400L:-1L),0))>0)
1045 								putstack(ep,readin+n,c,1);
1046 						}
1047 						if(!ep->e_lookahead)
1048 							break;
1049 						if((c=ep->e_lbuf[--ep->e_lookahead])>=0)
1050 						{
1051 							ep->e_lookahead++;
1052 							break;
1053 						}
1054 						c = -c;
1055 						readin[n++] = c;
1056 						if(c>='0' && c<='9' && n>2)
1057 							continue;
1058 						if(n>2 || (c!= '['  &&  c!= 'O'))
1059 							break;
1060 					}
1061 				}
1062 				if(n=keytrap(ep,readin,n,LOOKAHEAD-n,mode))
1063 				{
1064 					putstack(ep,readin,n,0);
1065 					c = ep->e_lbuf[--ep->e_lookahead];
1066 				}
1067 				else
1068 					c = ed_getchar(ep,mode);
1069 			}
1070 			else
1071 				c = -c;
1072 		}
1073 		/*** map '\r' to '\n' ***/
1074 		if(c == '\r' && mode!=2)
1075 			c = '\n';
1076 		if(ep->e_tabcount && !(c=='\t'||c==ESC || c=='\\' || c=='=' || c==cntl('L') || isdigit(c)))
1077 			ep->e_tabcount = 0;
1078 	}
1079 	else
1080 		siglongjmp(ep->e_env,(n==0?UEOF:UINTR));
1081 	return(c);
1082 }
1083 
1084 void ed_ungetchar(Edit_t *ep,register int c)
1085 {
1086 	if (ep->e_lookahead < LOOKAHEAD)
1087 		ep->e_lbuf[ep->e_lookahead++] = c;
1088 	return;
1089 }
1090 
1091 /*
1092  * put a character into the output buffer
1093  */
1094 
1095 void	ed_putchar(register Edit_t *ep,register int c)
1096 {
1097 	char buf[8];
1098 	register char *dp = ep->e_outptr;
1099 	register int i,size=1;
1100 	if(!dp)
1101 		return;
1102 	buf[0] = c;
1103 #if SHOPT_MULTIBYTE
1104 	/* check for place holder */
1105 	if(c == MARKER)
1106 		return;
1107 	if((size = mbconv(buf, (wchar_t)c)) > 1)
1108 	{
1109 		for (i = 0; i < (size-1); i++)
1110 			*dp++ = buf[i];
1111 		c = buf[i];
1112 	}
1113 	else
1114 	{
1115 		buf[0] = c;
1116 		size = 1;
1117 	}
1118 #endif	/* SHOPT_MULTIBYTE */
1119 	if (buf[0] == '_' && size==1)
1120 	{
1121 		*dp++ = ' ';
1122 		*dp++ = '\b';
1123 	}
1124 	*dp++ = c;
1125 	*dp = '\0';
1126 	if(dp >= ep->e_outlast)
1127 		ed_flush(ep);
1128 	else
1129 		ep->e_outptr = dp;
1130 }
1131 
1132 /*
1133  * returns the line and column corresponding to offset <off> in the physical buffer
1134  * if <cur> is non-zero and <= <off>, then correspodning <curpos> will start the search
1135  */
1136 Edpos_t ed_curpos(Edit_t *ep,genchar *phys, int off, int cur, Edpos_t curpos)
1137 {
1138 	register genchar *sp=phys;
1139 	register int c=1, col=ep->e_plen;
1140 	Edpos_t pos;
1141 #if SHOPT_MULTIBYTE
1142 	char p[16];
1143 #endif /* SHOPT_MULTIBYTE */
1144 	if(cur && off>=cur)
1145 	{
1146 		sp += cur;
1147 		off -= cur;
1148 		pos = curpos;
1149 		col = pos.col;
1150 	}
1151 	else
1152 	{
1153 		pos.line = 0;
1154 		while(col > ep->e_winsz)
1155 		{
1156 			pos.line++;
1157 			col -= (ep->e_winsz+1);
1158 		}
1159 	}
1160 	while(off-->0)
1161 	{
1162 		if(c)
1163 			c = *sp++;
1164 #if SHOPT_MULTIBYTE
1165 		if(c && (mbconv(p, (wchar_t)c))==1 && p[0]=='\n')
1166 #else
1167 		if(c=='\n')
1168 #endif /* SHOPT_MULTIBYTE */
1169 			col = 0;
1170 		else
1171 			col++;
1172 		if(col >  ep->e_winsz)
1173 			col = 0;
1174 		if(col==0)
1175 			pos.line++;
1176 	}
1177 	pos.col = col;
1178 	return(pos);
1179 }
1180 
1181 int ed_setcursor(register Edit_t *ep,genchar *physical,register int old,register int new,int first)
1182 {
1183 	static int oldline;
1184 	register int delta;
1185 	int clear = 0;
1186 	Edpos_t newpos;
1187 
1188 	delta = new - old;
1189 	if(first < 0)
1190 	{
1191 		first = 0;
1192 		clear = 1;
1193 	}
1194 	if( delta == 0  &&  !clear)
1195 		return(new);
1196 	if(ep->e_multiline)
1197 	{
1198 		ep->e_curpos = ed_curpos(ep, physical, old,0,ep->e_curpos);
1199 		if(clear && old>=ep->e_peol && (clear=ep->e_winsz-ep->e_curpos.col)>0)
1200 		{
1201 			ed_nputchar(ep,clear,' ');
1202 			ed_nputchar(ep,clear,'\b');
1203 			return(new);
1204 		}
1205 		newpos =     ed_curpos(ep, physical, new,old,ep->e_curpos);
1206 		if(ep->e_curpos.col==0 && ep->e_curpos.line>0 && oldline<ep->e_curpos.line && delta<0)
1207 			ed_putstring(ep,"\r\n");
1208 		oldline = newpos.line;
1209 		if(ep->e_curpos.line > newpos.line)
1210 		{
1211 			int n,pline,plen=ep->e_plen;
1212 			for(;ep->e_curpos.line > newpos.line; ep->e_curpos.line--)
1213 				ed_putstring(ep,CURSOR_UP);
1214 			pline = plen/(ep->e_winsz+1);
1215 			if(newpos.line <= pline)
1216 				plen -= pline*(ep->e_winsz+1);
1217 			else
1218 				plen = 0;
1219 			if((n=plen- ep->e_curpos.col)>0)
1220 			{
1221 				ep->e_curpos.col += n;
1222 				ed_putchar(ep,'\r');
1223 				if(!ep->e_crlf && pline==0)
1224 					ed_putstring(ep,ep->e_prompt);
1225 				else
1226 				{
1227 					int m = ep->e_winsz+1-plen;
1228 					ed_putchar(ep,'\n');
1229 					n = plen;
1230 					if(m < ed_genlen(physical))
1231 					{
1232 						while(physical[m] && n-->0)
1233 							ed_putchar(ep,physical[m++]);
1234 					}
1235 					ed_nputchar(ep,n,' ');
1236 					ed_putstring(ep,CURSOR_UP);
1237 				}
1238 			}
1239 		}
1240 		else if(ep->e_curpos.line < newpos.line)
1241 		{
1242 			ed_nputchar(ep, newpos.line-ep->e_curpos.line,'\n');
1243 			ep->e_curpos.line = newpos.line;
1244 			ed_putchar(ep,'\r');
1245 			ep->e_curpos.col = 0;
1246 		}
1247 		delta = newpos.col - ep->e_curpos.col;
1248 		old   =  new - delta;
1249 	}
1250 	else
1251 		newpos.line=0;
1252 	if(delta<0)
1253 	{
1254 		int bs= newpos.line && ep->e_plen>ep->e_winsz;
1255 		/*** move to left ***/
1256 		delta = -delta;
1257 		/*** attempt to optimize cursor movement ***/
1258 		if(!ep->e_crlf || bs || (2*delta <= ((old-first)+(newpos.line?0:ep->e_plen))) )
1259 		{
1260 			ed_nputchar(ep,delta,'\b');
1261 			delta = 0;
1262 		}
1263 		else
1264 		{
1265 			if(newpos.line==0)
1266 				ed_putstring(ep,ep->e_prompt);
1267 			else
1268 			{
1269 				first = 1+(newpos.line*ep->e_winsz - ep->e_plen);
1270 				ed_putchar(ep,'\r');
1271 			}
1272 			old = first;
1273 			delta = new-first;
1274 		}
1275 	}
1276 	while(delta-->0)
1277 		ed_putchar(ep,physical[old++]);
1278 	return(new);
1279 }
1280 
1281 /*
1282  * copy virtual to physical and return the index for cursor in physical buffer
1283  */
1284 int ed_virt_to_phys(Edit_t *ep,genchar *virt,genchar *phys,int cur,int voff,int poff)
1285 {
1286 	register genchar *sp = virt;
1287 	register genchar *dp = phys;
1288 	register int c;
1289 	genchar *curp = sp + cur;
1290 	genchar *dpmax = phys+MAXLINE;
1291 	int d, r;
1292 	sp += voff;
1293 	dp += poff;
1294 	for(r=poff;c= *sp;sp++)
1295 	{
1296 		if(curp == sp)
1297 			r = dp - phys;
1298 #if SHOPT_MULTIBYTE
1299 		d = mbwidth((wchar_t)c);
1300 		if(d==1 && is_cntrl(c))
1301 			d = -1;
1302 		if(d>1)
1303 		{
1304 			/* multiple width character put in place holders */
1305 			*dp++ = c;
1306 			while(--d >0)
1307 				*dp++ = MARKER;
1308 			/* in vi mode the cursor is at the last character */
1309 			if(dp>=dpmax)
1310 				break;
1311 			continue;
1312 		}
1313 		else
1314 #else
1315 		d = (is_cntrl(c)?-1:1);
1316 #endif	/* SHOPT_MULTIBYTE */
1317 		if(d<0)
1318 		{
1319 			if(c=='\t')
1320 			{
1321 				c = dp-phys;
1322 				if(sh_isoption(SH_VI))
1323 					c += ep->e_plen;
1324 				c = TABSIZE - c%TABSIZE;
1325 				while(--c>0)
1326 					*dp++ = ' ';
1327 				c = ' ';
1328 			}
1329 			else
1330 			{
1331 				*dp++ = '^';
1332 				c = printchar(c);
1333 			}
1334 			/* in vi mode the cursor is at the last character */
1335 			if(curp == sp && sh_isoption(SH_VI))
1336 				r = dp - phys;
1337 		}
1338 		*dp++ = c;
1339 		if(dp>=dpmax)
1340 			break;
1341 	}
1342 	*dp = 0;
1343 	ep->e_peol = dp-phys;
1344 	return(r);
1345 }
1346 
1347 #if SHOPT_MULTIBYTE
1348 /*
1349  * convert external representation <src> to an array of genchars <dest>
1350  * <src> and <dest> can be the same
1351  * returns number of chars in dest
1352  */
1353 
1354 int	ed_internal(const char *src, genchar *dest)
1355 {
1356 	register const unsigned char *cp = (unsigned char *)src;
1357 	register int c;
1358 	register wchar_t *dp = (wchar_t*)dest;
1359 	if(dest == (genchar*)roundof(cp-(unsigned char*)0,sizeof(genchar)))
1360 	{
1361 		genchar buffer[MAXLINE];
1362 		c = ed_internal(src,buffer);
1363 		ed_gencpy((genchar*)dp,buffer);
1364 		return(c);
1365 	}
1366 	while(*cp)
1367 		*dp++ = mbchar(cp);
1368 	*dp = 0;
1369 	return(dp-(wchar_t*)dest);
1370 }
1371 
1372 /*
1373  * convert internal representation <src> into character array <dest>.
1374  * The <src> and <dest> may be the same.
1375  * returns number of chars in dest.
1376  */
1377 
1378 int	ed_external(const genchar *src, char *dest)
1379 {
1380 	register genchar wc;
1381 	register int c,size;
1382 	register char *dp = dest;
1383 	char *dpmax = dp+sizeof(genchar)*MAXLINE-2;
1384 	if((char*)src == dp)
1385 	{
1386 		char buffer[MAXLINE*sizeof(genchar)];
1387 		c = ed_external(src,buffer);
1388 
1389 #ifdef _lib_wcscpy
1390 		wcscpy((wchar_t *)dest,(const wchar_t *)buffer);
1391 #else
1392 		strcpy(dest,buffer);
1393 #endif
1394 		return(c);
1395 	}
1396 	while((wc = *src++) && dp<dpmax)
1397 	{
1398 		if((size = mbconv(dp, wc)) < 0)
1399 		{
1400 			/* copy the character as is */
1401 			size = 1;
1402 			*dp = wc;
1403 		}
1404 		dp += size;
1405 	}
1406 	*dp = 0;
1407 	return(dp-dest);
1408 }
1409 
1410 /*
1411  * copy <sp> to <dp>
1412  */
1413 
1414 void	ed_gencpy(genchar *dp,const genchar *sp)
1415 {
1416 	dp = (genchar*)roundof((char*)dp-(char*)0,sizeof(genchar));
1417 	sp = (const genchar*)roundof((char*)sp-(char*)0,sizeof(genchar));
1418 	while(*dp++ = *sp++);
1419 }
1420 
1421 /*
1422  * copy at most <n> items from <sp> to <dp>
1423  */
1424 
1425 void	ed_genncpy(register genchar *dp,register const genchar *sp, int n)
1426 {
1427 	dp = (genchar*)roundof((char*)dp-(char*)0,sizeof(genchar));
1428 	sp = (const genchar*)roundof((char*)sp-(char*)0,sizeof(genchar));
1429 	while(n-->0 && (*dp++ = *sp++));
1430 }
1431 
1432 /*
1433  * find the string length of <str>
1434  */
1435 
1436 int	ed_genlen(register const genchar *str)
1437 {
1438 	register const genchar *sp = str;
1439 	sp = (const genchar*)roundof((char*)sp-(char*)0,sizeof(genchar));
1440 	while(*sp++);
1441 	return(sp-str-1);
1442 }
1443 #endif /* SHOPT_MULTIBYTE */
1444 #endif /* SHOPT_ESH || SHOPT_VSH */
1445 
1446 #ifdef future
1447 /*
1448  * returns 1 when <n> bytes starting at <a> and <b> are equal
1449  */
1450 static int compare(register const char *a,register const char *b,register int n)
1451 {
1452 	while(n-->0)
1453 	{
1454 		if(*a++ != *b++)
1455 			return(0);
1456 	}
1457 	return(1);
1458 }
1459 #endif
1460 
1461 #if SHOPT_OLDTERMIO
1462 
1463 #   include	<sys/termio.h>
1464 
1465 #ifndef ECHOCTL
1466 #   define ECHOCTL	0
1467 #endif /* !ECHOCTL */
1468 #define ott	ep->e_ott
1469 
1470 /*
1471  * For backward compatibility only
1472  * This version will use termios when possible, otherwise termio
1473  */
1474 
1475 int tcgetattr(int fd, struct termios *tt)
1476 {
1477 	register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context);
1478 	register int r,i;
1479 	ep->e_tcgeta = 0;
1480 	ep->e_echoctl = (ECHOCTL!=0);
1481 	if((r=ioctl(fd,TCGETS,tt))>=0 ||  errno!=EINVAL)
1482 		return(r);
1483 	if((r=ioctl(fd,TCGETA,&ott)) >= 0)
1484 	{
1485 		tt->c_lflag = ott.c_lflag;
1486 		tt->c_oflag = ott.c_oflag;
1487 		tt->c_iflag = ott.c_iflag;
1488 		tt->c_cflag = ott.c_cflag;
1489 		for(i=0; i<NCC; i++)
1490 			tt->c_cc[i] = ott.c_cc[i];
1491 		ep->e_tcgeta++;
1492 		ep->e_echoctl = 0;
1493 	}
1494 	return(r);
1495 }
1496 
1497 int tcsetattr(int fd,int mode,struct termios *tt)
1498 {
1499 	register Edit_t *ep = (Edit_t*)(sh_getinterp()->ed_context);
1500 	register int r;
1501 	if(ep->e_tcgeta)
1502 	{
1503 		register int i;
1504 		ott.c_lflag = tt->c_lflag;
1505 		ott.c_oflag = tt->c_oflag;
1506 		ott.c_iflag = tt->c_iflag;
1507 		ott.c_cflag = tt->c_cflag;
1508 		for(i=0; i<NCC; i++)
1509 			ott.c_cc[i] = tt->c_cc[i];
1510 		if(tt->c_lflag&ECHOCTL)
1511 		{
1512 			ott.c_lflag &= ~(ECHOCTL|IEXTEN);
1513 			ott.c_iflag &= ~(IGNCR|ICRNL);
1514 			ott.c_iflag |= INLCR;
1515 			ott.c_cc[VEOF]= ESC;  /* ESC -> eof char */
1516 			ott.c_cc[VEOL] = '\r'; /* CR -> eol char */
1517 			ott.c_cc[VEOL2] = tt->c_cc[VEOF]; /* EOF -> eol char */
1518 		}
1519 		switch(mode)
1520 		{
1521 			case TCSANOW:
1522 				mode = TCSETA;
1523 				break;
1524 			case TCSADRAIN:
1525 				mode = TCSETAW;
1526 				break;
1527 			case TCSAFLUSH:
1528 				mode = TCSETAF;
1529 		}
1530 		return(ioctl(fd,mode,&ott));
1531 	}
1532 	return(ioctl(fd,mode,tt));
1533 }
1534 #endif /* SHOPT_OLDTERMIO */
1535 
1536 #if KSHELL
1537 /*
1538  * Execute keyboard trap on given buffer <inbuff> of given size <isize>
1539  * <mode> < 0 for vi insert mode
1540  */
1541 static int keytrap(Edit_t *ep,char *inbuff,register int insize, int bufsize, int mode)
1542 {
1543 	register char *cp;
1544 	int savexit;
1545 	Shell_t *shp = ep->sh;
1546 #if SHOPT_MULTIBYTE
1547 	char buff[MAXLINE];
1548 	ed_external(ep->e_inbuf,cp=buff);
1549 #else
1550 	cp = ep->e_inbuf;
1551 #endif /* SHOPT_MULTIBYTE */
1552 	inbuff[insize] = 0;
1553 	ep->e_col = ep->e_cur;
1554 	if(mode== -2)
1555 	{
1556 		ep->e_col++;
1557 		*ep->e_vi_insert = ESC;
1558 	}
1559 	else
1560 		*ep->e_vi_insert = 0;
1561 	nv_putval(ED_CHRNOD,inbuff,NV_NOFREE);
1562 	nv_putval(ED_COLNOD,(char*)&ep->e_col,NV_NOFREE|NV_INTEGER);
1563 	nv_putval(ED_TXTNOD,(char*)cp,NV_NOFREE);
1564 	nv_putval(ED_MODENOD,ep->e_vi_insert,NV_NOFREE);
1565 	savexit = shp->savexit;
1566 	sh_trap(shp->st.trap[SH_KEYTRAP],0);
1567 	shp->savexit = savexit;
1568 	if((cp = nv_getval(ED_CHRNOD)) == inbuff)
1569 		nv_unset(ED_CHRNOD);
1570 	else if(bufsize>0)
1571 	{
1572 		strncpy(inbuff,cp,bufsize);
1573 		inbuff[bufsize-1]='\0';
1574 		insize = strlen(inbuff);
1575 	}
1576 	else
1577 		insize = 0;
1578 	nv_unset(ED_TXTNOD);
1579 	return(insize);
1580 }
1581 #endif /* KSHELL */
1582 
1583 void	*ed_open(Shell_t *shp)
1584 {
1585 	Edit_t *ed = newof(0,Edit_t,1,0);
1586 	ed->sh = shp;
1587 	strcpy(ed->e_macro,"_??");
1588 	return((void*)ed);
1589 }
1590