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
printchar(int c)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 */
tty_check(int fd)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
tty_get(register int fd,register struct termios * tty)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
tty_set(int fd,int action,struct termios * tty)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
tty_cooked(register int fd)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
tty_raw(register int fd,int echomode)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
tty_alt(register int fd)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
tty_alt(register int fd)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 */
ed_window(void)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
ed_flush(Edit_t * ep)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
ed_ringbell(void)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
ed_crlf(register Edit_t * ep)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
ed_setup(register Edit_t * ep,int fd,int reedit)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
ed_putstring(register Edit_t * ep,const char * str)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
ed_nputchar(register Edit_t * ep,int n,int c)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 */
ed_read(void * context,int fd,char * buff,int size,int reedit)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