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