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