1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1982-2009 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 /* Original version by Michael T. Veach 22 * Adapted for ksh by David Korn */ 23 /* EMACS_MODES: c tabstop=4 24 25 One line screen editor for any program 26 27 */ 28 29 30 /* The following is provided by: 31 * 32 * Matthijs N. Melchior 33 * AT&T Network Systems International 34 * APT Nederland 35 * HV BZ335 x2962 36 * hvlpb!mmelchio 37 * 38 * These are now on by default 39 * 40 * ESH_NFIRST 41 * - A ^N as first history related command after the prompt will move 42 * to the next command relative to the last known history position. 43 * It will not start at the position where the last command was entered 44 * as is done by the ^P command. Every history related command will 45 * set both the current and last position. Executing a command will 46 * only set the current position. 47 * 48 * ESH_KAPPEND 49 * - Successive kill and delete commands will accumulate their data 50 * in the kill buffer, by appending or prepending as appropriate. 51 * This mode will be reset by any command not adding something to the 52 * kill buffer. 53 * 54 * ESH_BETTER 55 * - Some enhancements: 56 * - argument for a macro is passed to its replacement 57 * - ^X^H command to find out about history position (debugging) 58 * - ^X^D command to show any debugging info 59 * 60 * I do not pretend these for changes are completely independent, 61 * but you can use them to seperate features. 62 */ 63 64 #include <ast.h> 65 #include "FEATURE/cmds" 66 #if KSHELL 67 # include "defs.h" 68 #else 69 # include <ctype.h> 70 #endif /* KSHELL */ 71 #include "io.h" 72 73 #include "history.h" 74 #include "edit.h" 75 #include "terminal.h" 76 77 #define ESH_NFIRST 78 #define ESH_KAPPEND 79 #define ESH_BETTER 80 81 #undef putchar 82 #define putchar(ed,c) ed_putchar(ed,c) 83 #define beep() ed_ringbell() 84 85 86 #if SHOPT_MULTIBYTE 87 # define gencpy(a,b) ed_gencpy(a,b) 88 # define genncpy(a,b,n) ed_genncpy(a,b,n) 89 # define genlen(str) ed_genlen(str) 90 static int print(int); 91 static int _isword(int); 92 # define isword(c) _isword(out[c]) 93 94 #else 95 # define gencpy(a,b) strcpy((char*)(a),(char*)(b)) 96 # define genncpy(a,b,n) strncpy((char*)(a),(char*)(b),n) 97 # define genlen(str) strlen(str) 98 # define print(c) isprint(c) 99 # define isword(c) (isalnum(out[c]) || (out[c]=='_')) 100 #endif /*SHOPT_MULTIBYTE */ 101 102 typedef struct _emacs_ 103 { 104 genchar *screen; /* pointer to window buffer */ 105 genchar *cursor; /* Cursor in real screen */ 106 int mark; 107 int in_mult; 108 char cr_ok; 109 char CntrlO; 110 char overflow; /* Screen overflow flag set */ 111 char scvalid; /* Screen is up to date */ 112 char lastdraw; /* last update type */ 113 int offset; /* Screen offset */ 114 enum 115 { 116 CRT=0, /* Crt terminal */ 117 PAPER /* Paper terminal */ 118 } terminal; 119 Histloc_t _location; 120 int prevdirection; 121 Edit_t *ed; /* pointer to edit data */ 122 } Emacs_t; 123 124 #define editb (*ep->ed) 125 #define eol editb.e_eol 126 #define cur editb.e_cur 127 #define hline editb.e_hline 128 #define hloff editb.e_hloff 129 #define hismin editb.e_hismin 130 #define usrkill editb.e_kill 131 #define usrlnext editb.e_lnext 132 #define usreof editb.e_eof 133 #define usrerase editb.e_erase 134 #define crallowed editb.e_crlf 135 #define Prompt editb.e_prompt 136 #define plen editb.e_plen 137 #define kstack editb.e_killbuf 138 #define lstring editb.e_search 139 #define lookahead editb.e_lookahead 140 #define env editb.e_env 141 #define raw editb.e_raw 142 #define histlines editb.e_hismax 143 #define w_size editb.e_wsize 144 #define drawbuff editb.e_inbuf 145 #define killing editb.e_mode 146 #define location ep->_location 147 148 #define LBUF 100 149 #define KILLCHAR UKILL 150 #define ERASECHAR UERASE 151 #define EOFCHAR UEOF 152 #define LNEXTCHAR ULNEXT 153 #define DELETE ('a'==97?0177:7) 154 155 /********************** 156 A large lookahead helps when the user is inserting 157 characters in the middle of the line. 158 ************************/ 159 160 161 typedef enum 162 { 163 FIRST, /* First time thru for logical line, prompt on screen */ 164 REFRESH, /* Redraw entire screen */ 165 APPEND, /* Append char before cursor to screen */ 166 UPDATE, /* Update the screen as need be */ 167 FINAL /* Update screen even if pending look ahead */ 168 } Draw_t; 169 170 static void draw(Emacs_t*,Draw_t); 171 static int escape(Emacs_t*,genchar*, int); 172 static void putstring(Emacs_t*,char*); 173 static void search(Emacs_t*,genchar*,int); 174 static void setcursor(Emacs_t*,int, int); 175 static void show_info(Emacs_t*,const char*); 176 static void xcommands(Emacs_t*,int); 177 178 int ed_emacsread(void *context, int fd,char *buff,int scend, int reedit) 179 { 180 Edit_t *ed = (Edit_t*)context; 181 register int c; 182 register int i; 183 register genchar *out; 184 register int count; 185 register Emacs_t *ep = ed->e_emacs; 186 int adjust,oadjust; 187 char backslash; 188 genchar *kptr; 189 char prompt[PRSIZE]; 190 genchar Screen[MAXLINE]; 191 if(!ep) 192 { 193 ep = ed->e_emacs = newof(0,Emacs_t,1,0); 194 ep->ed = ed; 195 ep->prevdirection = 1; 196 location.hist_command = -5; 197 } 198 Prompt = prompt; 199 ep->screen = Screen; 200 ep->lastdraw = FINAL; 201 if(tty_raw(ERRIO,0) < 0) 202 { 203 return(reedit?reedit:ed_read(context, fd,buff,scend,0)); 204 } 205 raw = 1; 206 /* This mess in case the read system call fails */ 207 208 ed_setup(ep->ed,fd,reedit); 209 out = (genchar*)buff; 210 #if SHOPT_MULTIBYTE 211 out = (genchar*)roundof(buff-(char*)0,sizeof(genchar)); 212 if(reedit) 213 ed_internal(buff,out); 214 #endif /* SHOPT_MULTIBYTE */ 215 if(!kstack) 216 { 217 kstack = (genchar*)malloc(CHARSIZE*MAXLINE); 218 kstack[0] = '\0'; 219 } 220 drawbuff = out; 221 #ifdef ESH_NFIRST 222 if (location.hist_command == -5) /* to be initialized */ 223 { 224 kstack[0] = '\0'; /* also clear kstack... */ 225 location.hist_command = hline; 226 location.hist_line = hloff; 227 } 228 if (location.hist_command <= hismin) /* don't start below minimum */ 229 { 230 location.hist_command = hismin + 1; 231 location.hist_line = 0; 232 } 233 ep->in_mult = hloff; /* save pos in last command */ 234 #endif /* ESH_NFIRST */ 235 i = sigsetjmp(env,0); 236 if (i !=0) 237 { 238 if(ep->ed->e_multiline) 239 { 240 cur = eol; 241 draw(ep,FINAL); 242 ed_flush(ep->ed); 243 } 244 tty_cooked(ERRIO); 245 if (i == UEOF) 246 { 247 return(0); /* EOF */ 248 } 249 return(-1); /* some other error */ 250 } 251 out[reedit] = 0; 252 if(scend+plen > (MAXLINE-2)) 253 scend = (MAXLINE-2)-plen; 254 ep->mark = 0; 255 cur = eol; 256 draw(ep,reedit?REFRESH:FIRST); 257 adjust = -1; 258 backslash = 0; 259 if (ep->CntrlO) 260 { 261 #ifdef ESH_NFIRST 262 ed_ungetchar(ep->ed,cntl('N')); 263 #else 264 location = hist_locate(sh.hist_ptr,location.hist_command,location.hist_line,1); 265 if (location.hist_command < histlines) 266 { 267 hline = location.hist_command; 268 hloff = location.hist_line; 269 hist_copy((char*)kstack,MAXLINE, hline,hloff); 270 # if SHOPT_MULTIBYTE 271 ed_internal((char*)kstack,kstack); 272 # endif /* SHOPT_MULTIBYTE */ 273 ed_ungetchar(ep->ed,cntl('Y')); 274 } 275 #endif /* ESH_NFIRST */ 276 } 277 ep->CntrlO = 0; 278 while ((c = ed_getchar(ep->ed,0)) != (-1)) 279 { 280 if (backslash) 281 { 282 backslash = 0; 283 if (c==usrerase||c==usrkill||(!print(c) && 284 (c!='\r'&&c!='\n'))) 285 { 286 /* accept a backslashed character */ 287 cur--; 288 out[cur++] = c; 289 out[eol] = '\0'; 290 draw(ep,APPEND); 291 continue; 292 } 293 } 294 if (c == usrkill) 295 { 296 c = KILLCHAR ; 297 } 298 else if (c == usrerase) 299 { 300 c = ERASECHAR ; 301 } 302 else if (c == usrlnext) 303 { 304 c = LNEXTCHAR ; 305 } 306 else if ((c == usreof)&&(eol == 0)) 307 { 308 c = EOFCHAR; 309 } 310 #ifdef ESH_KAPPEND 311 if (--killing <= 0) /* reset killing flag */ 312 killing = 0; 313 #endif 314 oadjust = count = adjust; 315 if(count<0) 316 count = 1; 317 adjust = -1; 318 i = cur; 319 switch(c) 320 { 321 case LNEXTCHAR: 322 c = ed_getchar(ep->ed,2); 323 goto do_default_processing; 324 case cntl('V'): 325 show_info(ep,fmtident(e_version)); 326 continue; 327 case '\0': 328 ep->mark = i; 329 continue; 330 case cntl('X'): 331 xcommands(ep,count); 332 continue; 333 case EOFCHAR: 334 ed_flush(ep->ed); 335 tty_cooked(ERRIO); 336 return(0); 337 #ifdef u370 338 case cntl('S') : 339 case cntl('Q') : 340 continue; 341 #endif /* u370 */ 342 case '\t': 343 if(cur>0 && ep->ed->sh->nextprompt) 344 { 345 if(ep->ed->e_tabcount==0) 346 { 347 ep->ed->e_tabcount=1; 348 ed_ungetchar(ep->ed,ESC); 349 goto do_escape; 350 } 351 else if(ep->ed->e_tabcount==1) 352 { 353 ed_ungetchar(ep->ed,'='); 354 goto do_escape; 355 } 356 ep->ed->e_tabcount = 0; 357 } 358 do_default_processing: 359 default: 360 361 if ((eol+1) >= (scend)) /* will not fit on line */ 362 { 363 ed_ungetchar(ep->ed,c); /* save character for next line */ 364 goto process; 365 } 366 for(i= ++eol; i>cur; i--) 367 out[i] = out[i-1]; 368 backslash = (c == '\\'); 369 out[cur++] = c; 370 draw(ep,APPEND); 371 continue; 372 case cntl('Y') : 373 { 374 c = genlen(kstack); 375 if ((c + eol) > scend) 376 { 377 beep(); 378 continue; 379 } 380 ep->mark = i; 381 for(i=eol;i>=cur;i--) 382 out[c+i] = out[i]; 383 kptr=kstack; 384 while (i = *kptr++) 385 out[cur++] = i; 386 draw(ep,UPDATE); 387 eol = genlen(out); 388 continue; 389 } 390 case '\n': 391 case '\r': 392 c = '\n'; 393 goto process; 394 395 case DELETE: /* delete char 0x7f */ 396 case '\b': /* backspace, ^h */ 397 case ERASECHAR : 398 if (count > i) 399 count = i; 400 #ifdef ESH_KAPPEND 401 kptr = &kstack[count]; /* move old contents here */ 402 if (killing) /* prepend to killbuf */ 403 { 404 c = genlen(kstack) + CHARSIZE; /* include '\0' */ 405 while(c--) /* copy stuff */ 406 kptr[c] = kstack[c]; 407 } 408 else 409 *kptr = 0; /* this is end of data */ 410 killing = 2; /* we are killing */ 411 i -= count; 412 eol -= count; 413 genncpy(kstack,out+i,cur-i); 414 #else 415 while ((count--)&&(i>0)) 416 { 417 i--; 418 eol--; 419 } 420 genncpy(kstack,out+i,cur-i); 421 kstack[cur-i] = 0; 422 #endif /* ESH_KAPPEND */ 423 gencpy(out+i,out+cur); 424 ep->mark = i; 425 goto update; 426 case cntl('W') : 427 #ifdef ESH_KAPPEND 428 ++killing; /* keep killing flag */ 429 #endif 430 if (ep->mark > eol ) 431 ep->mark = eol; 432 if (ep->mark == i) 433 continue; 434 if (ep->mark > i) 435 { 436 adjust = ep->mark - i; 437 ed_ungetchar(ep->ed,cntl('D')); 438 continue; 439 } 440 adjust = i - ep->mark; 441 ed_ungetchar(ep->ed,usrerase); 442 continue; 443 case cntl('D') : 444 ep->mark = i; 445 #ifdef ESH_KAPPEND 446 if (killing) 447 kptr = &kstack[genlen(kstack)]; /* append here */ 448 else 449 kptr = kstack; 450 killing = 2; /* we are now killing */ 451 #else 452 kptr = kstack; 453 #endif /* ESH_KAPPEND */ 454 while ((count--)&&(eol>0)&&(i<eol)) 455 { 456 *kptr++ = out[i]; 457 eol--; 458 while(1) 459 { 460 if ((out[i] = out[(i+1)])==0) 461 break; 462 i++; 463 } 464 i = cur; 465 } 466 *kptr = '\0'; 467 goto update; 468 case cntl('C') : 469 case cntl('F') : 470 { 471 int cntlC = (c==cntl('C')); 472 while (count-- && eol>i) 473 { 474 if (cntlC) 475 { 476 c = out[i]; 477 #if SHOPT_MULTIBYTE 478 if((c&~STRIP)==0 && islower(c)) 479 #else 480 if(islower(c)) 481 #endif /* SHOPT_MULTIBYTE */ 482 { 483 c += 'A' - 'a'; 484 out[i] = c; 485 } 486 } 487 i++; 488 } 489 goto update; 490 } 491 case cntl(']') : 492 c = ed_getchar(ep->ed,1); 493 if ((count == 0) || (count > eol)) 494 { 495 beep(); 496 continue; 497 } 498 if (out[i]) 499 i++; 500 while (i < eol) 501 { 502 if (out[i] == c && --count==0) 503 goto update; 504 i++; 505 } 506 i = 0; 507 while (i < cur) 508 { 509 if (out[i] == c && --count==0) 510 break; 511 i++; 512 }; 513 514 update: 515 cur = i; 516 draw(ep,UPDATE); 517 continue; 518 519 case cntl('B') : 520 if (count > i) 521 count = i; 522 i -= count; 523 goto update; 524 case cntl('T') : 525 if ((sh_isoption(SH_EMACS))&& (eol!=i)) 526 i++; 527 if (i >= 2) 528 { 529 c = out[i - 1]; 530 out[i-1] = out[i-2]; 531 out[i-2] = c; 532 } 533 else 534 { 535 if(sh_isoption(SH_EMACS)) 536 i--; 537 beep(); 538 continue; 539 } 540 goto update; 541 case cntl('A') : 542 i = 0; 543 goto update; 544 case cntl('E') : 545 i = eol; 546 goto update; 547 case cntl('U') : 548 adjust = 4*count; 549 continue; 550 case KILLCHAR : 551 cur = 0; 552 oadjust = -1; 553 case cntl('K') : 554 if(oadjust >= 0) 555 { 556 #ifdef ESH_KAPPEND 557 killing = 2; /* set killing signal */ 558 #endif 559 ep->mark = count; 560 ed_ungetchar(ep->ed,cntl('W')); 561 continue; 562 } 563 i = cur; 564 eol = i; 565 ep->mark = i; 566 #ifdef ESH_KAPPEND 567 if (killing) /* append to kill buffer */ 568 gencpy(&kstack[genlen(kstack)], &out[i]); 569 else 570 gencpy(kstack,&out[i]); 571 killing = 2; /* set killing signal */ 572 #else 573 gencpy(kstack,&out[i]); 574 #endif /* ESH_KAPPEND */ 575 out[i] = 0; 576 draw(ep,UPDATE); 577 if (c == KILLCHAR) 578 { 579 if (ep->terminal == PAPER) 580 { 581 putchar(ep->ed,'\n'); 582 putstring(ep,Prompt); 583 } 584 c = ed_getchar(ep->ed,0); 585 if (c != usrkill) 586 { 587 ed_ungetchar(ep->ed,c); 588 continue; 589 } 590 if (ep->terminal == PAPER) 591 ep->terminal = CRT; 592 else 593 { 594 ep->terminal = PAPER; 595 putchar(ep->ed,'\n'); 596 putstring(ep,Prompt); 597 } 598 } 599 continue; 600 case cntl('L'): 601 if(!ep->ed->e_nocrnl) 602 ed_crlf(ep->ed); 603 draw(ep,REFRESH); 604 ep->ed->e_nocrnl = 0; 605 continue; 606 case cntl('[') : 607 do_escape: 608 adjust = escape(ep,out,oadjust); 609 continue; 610 case cntl('R') : 611 search(ep,out,count); 612 goto drawline; 613 case cntl('P') : 614 if (count <= hloff) 615 hloff -= count; 616 else 617 { 618 hline -= count - hloff; 619 hloff = 0; 620 } 621 #ifdef ESH_NFIRST 622 if (hline <= hismin) 623 #else 624 if (hline < hismin) 625 #endif /* ESH_NFIRST */ 626 { 627 hline = hismin+1; 628 beep(); 629 #ifndef ESH_NFIRST 630 continue; 631 #endif 632 } 633 goto common; 634 635 case cntl('O') : 636 location.hist_command = hline; 637 location.hist_line = hloff; 638 ep->CntrlO = 1; 639 c = '\n'; 640 goto process; 641 case cntl('N') : 642 #ifdef ESH_NFIRST 643 hline = location.hist_command; /* start at saved position */ 644 hloff = location.hist_line; 645 #endif /* ESH_NFIRST */ 646 location = hist_locate(sh.hist_ptr,hline,hloff,count); 647 if (location.hist_command > histlines) 648 { 649 beep(); 650 #ifdef ESH_NFIRST 651 location.hist_command = histlines; 652 location.hist_line = ep->in_mult; 653 #else 654 continue; 655 #endif /* ESH_NFIRST */ 656 } 657 hline = location.hist_command; 658 hloff = location.hist_line; 659 common: 660 #ifdef ESH_NFIRST 661 location.hist_command = hline; /* save current position */ 662 location.hist_line = hloff; 663 #endif 664 cur = 0; 665 draw(ep,UPDATE); 666 hist_copy((char*)out,MAXLINE, hline,hloff); 667 #if SHOPT_MULTIBYTE 668 ed_internal((char*)(out),out); 669 #endif /* SHOPT_MULTIBYTE */ 670 drawline: 671 eol = genlen(out); 672 cur = eol; 673 draw(ep,UPDATE); 674 continue; 675 } 676 677 } 678 679 process: 680 681 if (c == (-1)) 682 { 683 lookahead = 0; 684 beep(); 685 *out = '\0'; 686 } 687 draw(ep,FINAL); 688 tty_cooked(ERRIO); 689 if(ed->e_nlist) 690 { 691 ed->e_nlist = 0; 692 stakset(ed->e_stkptr,ed->e_stkoff); 693 } 694 if(c == '\n') 695 { 696 out[eol++] = '\n'; 697 out[eol] = '\0'; 698 ed_crlf(ep->ed); 699 } 700 #if SHOPT_MULTIBYTE 701 ed_external(out,buff); 702 #endif /* SHOPT_MULTIBYTE */ 703 i = strlen(buff); 704 if (i) 705 return(i); 706 return(-1); 707 } 708 709 static void show_info(Emacs_t *ep,const char *str) 710 { 711 register genchar *out = drawbuff; 712 register int c; 713 genchar string[LBUF]; 714 int sav_cur = cur; 715 /* save current line */ 716 genncpy(string,out,sizeof(string)/sizeof(*string)); 717 *out = 0; 718 cur = 0; 719 #if SHOPT_MULTIBYTE 720 ed_internal(str,out); 721 #else 722 gencpy(out,str); 723 #endif /* SHOPT_MULTIBYTE */ 724 draw(ep,UPDATE); 725 c = ed_getchar(ep->ed,0); 726 if(c!=' ') 727 ed_ungetchar(ep->ed,c); 728 /* restore line */ 729 cur = sav_cur; 730 genncpy(out,string,sizeof(string)/sizeof(*string)); 731 draw(ep,UPDATE); 732 } 733 734 static void putstring(Emacs_t* ep,register char *sp) 735 { 736 register int c; 737 while (c= *sp++) 738 putchar(ep->ed,c); 739 } 740 741 742 static int escape(register Emacs_t* ep,register genchar *out,int count) 743 { 744 register int i,value; 745 int digit,ch; 746 digit = 0; 747 value = 0; 748 while ((i=ed_getchar(ep->ed,0)),isdigit(i)) 749 { 750 value *= 10; 751 value += (i - '0'); 752 digit = 1; 753 } 754 if (digit) 755 { 756 ed_ungetchar(ep->ed,i) ; 757 #ifdef ESH_KAPPEND 758 ++killing; /* don't modify killing signal */ 759 #endif 760 return(value); 761 } 762 value = count; 763 if(value<0) 764 value = 1; 765 switch(ch=i) 766 { 767 case cntl('V'): 768 show_info(ep,fmtident(e_version)); 769 return(-1); 770 case ' ': 771 ep->mark = cur; 772 return(-1); 773 774 #ifdef ESH_KAPPEND 775 case '+': /* M-+ = append next kill */ 776 killing = 2; 777 return -1; /* no argument for next command */ 778 #endif 779 780 case 'p': /* M-p == ^W^Y (copy stack == kill & yank) */ 781 ed_ungetchar(ep->ed,cntl('Y')); 782 ed_ungetchar(ep->ed,cntl('W')); 783 #ifdef ESH_KAPPEND 784 killing = 0; /* start fresh */ 785 #endif 786 return(-1); 787 788 case 'l': /* M-l == lower-case */ 789 case 'd': 790 case 'c': 791 case 'f': 792 { 793 i = cur; 794 while(value-- && i<eol) 795 { 796 while ((out[i])&&(!isword(i))) 797 i++; 798 while ((out[i])&&(isword(i))) 799 i++; 800 } 801 if(ch=='l') 802 { 803 value = i-cur; 804 while (value-- > 0) 805 { 806 i = out[cur]; 807 #if SHOPT_MULTIBYTE 808 if((i&~STRIP)==0 && isupper(i)) 809 #else 810 if(isupper(i)) 811 #endif /* SHOPT_MULTIBYTE */ 812 { 813 i += 'a' - 'A'; 814 out[cur] = i; 815 } 816 cur++; 817 } 818 draw(ep,UPDATE); 819 return(-1); 820 } 821 822 else if(ch=='f') 823 goto update; 824 else if(ch=='c') 825 { 826 ed_ungetchar(ep->ed,cntl('C')); 827 return(i-cur); 828 } 829 else 830 { 831 if (i-cur) 832 { 833 ed_ungetchar(ep->ed,cntl('D')); 834 #ifdef ESH_KAPPEND 835 ++killing; /* keep killing signal */ 836 #endif 837 return(i-cur); 838 } 839 beep(); 840 return(-1); 841 } 842 } 843 844 845 case 'b': 846 case DELETE : 847 case '\b': 848 case 'h': 849 { 850 i = cur; 851 while(value-- && i>0) 852 { 853 i--; 854 while ((i>0)&&(!isword(i))) 855 i--; 856 while ((i>0)&&(isword(i-1))) 857 i--; 858 } 859 if(ch=='b') 860 goto update; 861 else 862 { 863 ed_ungetchar(ep->ed,usrerase); 864 #ifdef ESH_KAPPEND 865 ++killing; 866 #endif 867 return(cur-i); 868 } 869 } 870 871 case '>': 872 ed_ungetchar(ep->ed,cntl('N')); 873 #ifdef ESH_NFIRST 874 if (ep->in_mult) 875 { 876 location.hist_command = histlines; 877 location.hist_line = ep->in_mult - 1; 878 } 879 else 880 { 881 location.hist_command = histlines - 1; 882 location.hist_line = 0; 883 } 884 #else 885 hline = histlines-1; 886 hloff = 0; 887 #endif /* ESH_NFIRST */ 888 return(0); 889 890 case '<': 891 ed_ungetchar(ep->ed,cntl('P')); 892 hloff = 0; 893 #ifdef ESH_NFIRST 894 hline = hismin + 1; 895 return 0; 896 #else 897 return(hline-hismin); 898 #endif /* ESH_NFIRST */ 899 900 901 case '#': 902 ed_ungetchar(ep->ed,'\n'); 903 ed_ungetchar(ep->ed,(out[0]=='#')?cntl('D'):'#'); 904 ed_ungetchar(ep->ed,cntl('A')); 905 return(-1); 906 case '_' : 907 case '.' : 908 { 909 genchar name[MAXLINE]; 910 char buf[MAXLINE]; 911 char *ptr; 912 ptr = hist_word(buf,MAXLINE,(count?count:-1)); 913 if(ptr==0) 914 { 915 beep(); 916 break; 917 } 918 if ((eol - cur) >= sizeof(name)) 919 { 920 beep(); 921 return(-1); 922 } 923 ep->mark = cur; 924 gencpy(name,&out[cur]); 925 while(*ptr) 926 { 927 out[cur++] = *ptr++; 928 eol++; 929 } 930 gencpy(&out[cur],name); 931 draw(ep,UPDATE); 932 return(-1); 933 } 934 #if KSHELL 935 936 /* file name expansion */ 937 case cntl('[') : /* filename completion */ 938 i = '\\'; 939 case '*': /* filename expansion */ 940 case '=': /* escape = - list all matching file names */ 941 ep->mark = cur; 942 if(ed_expand(ep->ed,(char*)out,&cur,&eol,i,count) < 0) 943 { 944 if(ep->ed->e_tabcount==1) 945 { 946 ep->ed->e_tabcount=2; 947 ed_ungetchar(ep->ed,cntl('\t')); 948 return(-1); 949 } 950 beep(); 951 } 952 else if(i=='=') 953 { 954 draw(ep,REFRESH); 955 if(count>0) 956 ep->ed->e_tabcount=0; 957 else 958 { 959 i=ed_getchar(ep->ed,0); 960 ed_ungetchar(ep->ed,i); 961 if(isdigit(i)) 962 ed_ungetchar(ep->ed,ESC); 963 } 964 } 965 else 966 { 967 if(i=='\\' && cur>ep->mark && (out[cur-1]=='/' || out[cur-1]==' ')) 968 ep->ed->e_tabcount=0; 969 draw(ep,UPDATE); 970 } 971 return(-1); 972 973 /* search back for character */ 974 case cntl(']'): /* feature not in book */ 975 { 976 int c = ed_getchar(ep->ed,1); 977 if ((value == 0) || (value > eol)) 978 { 979 beep(); 980 return(-1); 981 } 982 i = cur; 983 if (i > 0) 984 i--; 985 while (i >= 0) 986 { 987 if (out[i] == c && --value==0) 988 goto update; 989 i--; 990 } 991 i = eol; 992 while (i > cur) 993 { 994 if (out[i] == c && --value==0) 995 break; 996 i--; 997 }; 998 999 } 1000 update: 1001 cur = i; 1002 draw(ep,UPDATE); 1003 return(-1); 1004 1005 #ifdef _cmd_tput 1006 case cntl('L'): /* clear screen */ 1007 sh_trap("tput clear", 0); 1008 draw(ep,REFRESH); 1009 return(-1); 1010 #endif 1011 case '[': /* feature not in book */ 1012 switch(i=ed_getchar(ep->ed,1)) 1013 { 1014 case 'A': 1015 if(cur>0 && eol==cur && (cur<(SEARCHSIZE-2) || ep->prevdirection == -2)) 1016 { 1017 if(ep->lastdraw==APPEND && ep->prevdirection != -2) 1018 { 1019 out[cur] = 0; 1020 gencpy(&((genchar*)lstring)[1],out); 1021 #if SHOPT_MULTIBYTE 1022 ed_external(&((genchar*)lstring)[1],lstring+1); 1023 #endif /* SHOPT_MULTIBYTE */ 1024 *lstring = '^'; 1025 ep->prevdirection = -2; 1026 } 1027 if(*lstring) 1028 { 1029 ed_ungetchar(ep->ed,'\r'); 1030 ed_ungetchar(ep->ed,cntl('R')); 1031 return(-1); 1032 } 1033 } 1034 *lstring = 0; 1035 ed_ungetchar(ep->ed,cntl('P')); 1036 return(-1); 1037 case 'B': 1038 ed_ungetchar(ep->ed,cntl('N')); 1039 return(-1); 1040 case 'C': 1041 ed_ungetchar(ep->ed,cntl('F')); 1042 return(-1); 1043 case 'D': 1044 ed_ungetchar(ep->ed,cntl('B')); 1045 return(-1); 1046 case 'H': 1047 ed_ungetchar(ep->ed,cntl('A')); 1048 return(-1); 1049 case 'Y': 1050 ed_ungetchar(ep->ed,cntl('E')); 1051 return(-1); 1052 default: 1053 ed_ungetchar(ep->ed,i); 1054 } 1055 i = '_'; 1056 1057 default: 1058 /* look for user defined macro definitions */ 1059 if(ed_macro(ep->ed,i)) 1060 # ifdef ESH_BETTER 1061 return(count); /* pass argument to macro */ 1062 # else 1063 return(-1); 1064 # endif /* ESH_BETTER */ 1065 #else 1066 update: 1067 cur = i; 1068 draw(ep,UPDATE); 1069 return(-1); 1070 1071 default: 1072 #endif /* KSHELL */ 1073 beep(); 1074 return(-1); 1075 } 1076 return(-1); 1077 } 1078 1079 1080 /* 1081 * This routine process all commands starting with ^X 1082 */ 1083 1084 static void xcommands(register Emacs_t *ep,int count) 1085 { 1086 register int i = ed_getchar(ep->ed,0); 1087 NOT_USED(count); 1088 switch(i) 1089 { 1090 case cntl('X'): /* exchange dot and mark */ 1091 if (ep->mark > eol) 1092 ep->mark = eol; 1093 i = ep->mark; 1094 ep->mark = cur; 1095 cur = i; 1096 draw(ep,UPDATE); 1097 return; 1098 1099 #if KSHELL 1100 # ifdef ESH_BETTER 1101 case cntl('E'): /* invoke emacs on current command */ 1102 if(ed_fulledit(ep->ed)==-1) 1103 beep(); 1104 else 1105 { 1106 #if SHOPT_MULTIBYTE 1107 ed_internal((char*)drawbuff,drawbuff); 1108 #endif /* SHOPT_MULTIBYTE */ 1109 ed_ungetchar(ep->ed,'\n'); 1110 } 1111 return; 1112 1113 # define itos(i) fmtbase((long)(i),0,0)/* want signed conversion */ 1114 1115 case cntl('H'): /* ^X^H show history info */ 1116 { 1117 char hbuf[MAXLINE]; 1118 1119 strcpy(hbuf, "Current command "); 1120 strcat(hbuf, itos(hline)); 1121 if (hloff) 1122 { 1123 strcat(hbuf, " (line "); 1124 strcat(hbuf, itos(hloff+1)); 1125 strcat(hbuf, ")"); 1126 } 1127 if ((hline != location.hist_command) || 1128 (hloff != location.hist_line)) 1129 { 1130 strcat(hbuf, "; Previous command "); 1131 strcat(hbuf, itos(location.hist_command)); 1132 if (location.hist_line) 1133 { 1134 strcat(hbuf, " (line "); 1135 strcat(hbuf, itos(location.hist_line+1)); 1136 strcat(hbuf, ")"); 1137 } 1138 } 1139 show_info(ep,hbuf); 1140 return; 1141 } 1142 # if 0 /* debugging, modify as required */ 1143 case cntl('D'): /* ^X^D show debugging info */ 1144 { 1145 char debugbuf[MAXLINE]; 1146 1147 strcpy(debugbuf, "count="); 1148 strcat(debugbuf, itos(count)); 1149 strcat(debugbuf, " eol="); 1150 strcat(debugbuf, itos(eol)); 1151 strcat(debugbuf, " cur="); 1152 strcat(debugbuf, itos(cur)); 1153 strcat(debugbuf, " crallowed="); 1154 strcat(debugbuf, itos(crallowed)); 1155 strcat(debugbuf, " plen="); 1156 strcat(debugbuf, itos(plen)); 1157 strcat(debugbuf, " w_size="); 1158 strcat(debugbuf, itos(w_size)); 1159 1160 show_info(ep,debugbuf); 1161 return; 1162 } 1163 # endif /* debugging code */ 1164 # endif /* ESH_BETTER */ 1165 #endif /* KSHELL */ 1166 1167 default: 1168 beep(); 1169 return; 1170 } 1171 } 1172 1173 static void search(Emacs_t* ep,genchar *out,int direction) 1174 { 1175 #ifndef ESH_NFIRST 1176 Histloc_t location; 1177 #endif 1178 register int i,sl; 1179 genchar str_buff[LBUF]; 1180 register genchar *string = drawbuff; 1181 /* save current line */ 1182 int sav_cur = cur; 1183 genncpy(str_buff,string,sizeof(str_buff)/sizeof(*str_buff)); 1184 string[0] = '^'; 1185 string[1] = 'R'; 1186 string[2] = '\0'; 1187 sl = 2; 1188 cur = sl; 1189 draw(ep,UPDATE); 1190 while ((i = ed_getchar(ep->ed,1))&&(i != '\r')&&(i != '\n')) 1191 { 1192 if (i==usrerase || i==DELETE || i=='\b' || i==ERASECHAR) 1193 { 1194 if (sl > 2) 1195 { 1196 string[--sl] = '\0'; 1197 cur = sl; 1198 draw(ep,UPDATE); 1199 } 1200 else 1201 beep(); 1202 continue; 1203 } 1204 if (i==usrkill) 1205 { 1206 beep(); 1207 goto restore; 1208 } 1209 if (i == '\\') 1210 { 1211 string[sl++] = '\\'; 1212 string[sl] = '\0'; 1213 cur = sl; 1214 draw(ep,APPEND); 1215 i = ed_getchar(ep->ed,1); 1216 string[--sl] = '\0'; 1217 } 1218 string[sl++] = i; 1219 string[sl] = '\0'; 1220 cur = sl; 1221 draw(ep,APPEND); 1222 } 1223 i = genlen(string); 1224 1225 if(ep->prevdirection == -2 && i!=2 || direction!=1) 1226 ep->prevdirection = -1; 1227 if (direction < 1) 1228 { 1229 ep->prevdirection = -ep->prevdirection; 1230 direction = 1; 1231 } 1232 else 1233 direction = -1; 1234 if (i != 2) 1235 { 1236 #if SHOPT_MULTIBYTE 1237 ed_external(string,(char*)string); 1238 #endif /* SHOPT_MULTIBYTE */ 1239 strncpy(lstring,((char*)string)+2,SEARCHSIZE); 1240 ep->prevdirection = direction; 1241 } 1242 else 1243 direction = ep->prevdirection ; 1244 location = hist_find(sh.hist_ptr,(char*)lstring,hline,1,direction); 1245 i = location.hist_command; 1246 if(i>0) 1247 { 1248 hline = i; 1249 #ifdef ESH_NFIRST 1250 hloff = location.hist_line = 0; /* display first line of multi line command */ 1251 #else 1252 hloff = location.hist_line; 1253 #endif /* ESH_NFIRST */ 1254 hist_copy((char*)out,MAXLINE, hline,hloff); 1255 #if SHOPT_MULTIBYTE 1256 ed_internal((char*)out,out); 1257 #endif /* SHOPT_MULTIBYTE */ 1258 return; 1259 } 1260 if (i < 0) 1261 { 1262 beep(); 1263 #ifdef ESH_NFIRST 1264 location.hist_command = hline; 1265 location.hist_line = hloff; 1266 #else 1267 hloff = 0; 1268 hline = histlines; 1269 #endif /* ESH_NFIRST */ 1270 } 1271 restore: 1272 genncpy(string,str_buff,sizeof(str_buff)/sizeof(*str_buff)); 1273 cur = sav_cur; 1274 return; 1275 } 1276 1277 1278 /* Adjust screen to agree with inputs: logical line and cursor */ 1279 /* If 'first' assume screen is blank */ 1280 /* Prompt is always kept on the screen */ 1281 1282 static void draw(register Emacs_t *ep,Draw_t option) 1283 { 1284 #define NORMAL ' ' 1285 #define LOWER '<' 1286 #define BOTH '*' 1287 #define UPPER '>' 1288 1289 register genchar *sptr; /* Pointer within screen */ 1290 genchar nscreen[2*MAXLINE]; /* New entire screen */ 1291 genchar *ncursor; /* New cursor */ 1292 register genchar *nptr; /* Pointer to New screen */ 1293 char longline; /* Line overflow */ 1294 genchar *logcursor; 1295 genchar *nscend; /* end of logical screen */ 1296 register int i; 1297 1298 nptr = nscreen; 1299 sptr = drawbuff; 1300 logcursor = sptr + cur; 1301 longline = NORMAL; 1302 ep->lastdraw = option; 1303 1304 if (option == FIRST || option == REFRESH) 1305 { 1306 ep->overflow = NORMAL; 1307 ep->cursor = ep->screen; 1308 ep->offset = 0; 1309 ep->cr_ok = crallowed; 1310 if (option == FIRST) 1311 { 1312 ep->scvalid = 1; 1313 return; 1314 } 1315 *ep->cursor = '\0'; 1316 putstring(ep,Prompt); /* start with prompt */ 1317 } 1318 1319 /********************* 1320 Do not update screen if pending characters 1321 **********************/ 1322 1323 if ((lookahead)&&(option != FINAL)) 1324 { 1325 1326 ep->scvalid = 0; /* Screen is out of date, APPEND will not work */ 1327 1328 return; 1329 } 1330 1331 /*************************************** 1332 If in append mode, cursor at end of line, screen up to date, 1333 the previous character was a 'normal' character, 1334 and the window has room for another character. 1335 Then output the character and adjust the screen only. 1336 *****************************************/ 1337 1338 1339 i = *(logcursor-1); /* last character inserted */ 1340 1341 if ((option == APPEND)&&(ep->scvalid)&&(*logcursor == '\0')&& 1342 print(i)&&((ep->cursor-ep->screen)<(w_size-1))) 1343 { 1344 putchar(ep->ed,i); 1345 *ep->cursor++ = i; 1346 *ep->cursor = '\0'; 1347 return; 1348 } 1349 1350 /* copy the line */ 1351 ncursor = nptr + ed_virt_to_phys(ep->ed,sptr,nptr,cur,0,0); 1352 nptr += genlen(nptr); 1353 sptr += genlen(sptr); 1354 nscend = nptr - 1; 1355 if(sptr == logcursor) 1356 ncursor = nptr; 1357 1358 /********************* 1359 Does ncursor appear on the screen? 1360 If not, adjust the screen offset so it does. 1361 **********************/ 1362 1363 i = ncursor - nscreen; 1364 1365 if ((ep->offset && i<=ep->offset)||(i >= (ep->offset+w_size))) 1366 { 1367 /* Center the cursor on the screen */ 1368 ep->offset = i - (w_size>>1); 1369 if (--ep->offset < 0) 1370 ep->offset = 0; 1371 } 1372 1373 /********************* 1374 Is the range of screen[0] thru screen[w_size] up-to-date 1375 with nscreen[offset] thru nscreen[offset+w_size] ? 1376 If not, update as need be. 1377 ***********************/ 1378 1379 nptr = &nscreen[ep->offset]; 1380 sptr = ep->screen; 1381 1382 i = w_size; 1383 1384 while (i-- > 0) 1385 { 1386 1387 if (*nptr == '\0') 1388 { 1389 *(nptr + 1) = '\0'; 1390 *nptr = ' '; 1391 } 1392 if (*sptr == '\0') 1393 { 1394 *(sptr + 1) = '\0'; 1395 *sptr = ' '; 1396 } 1397 if (*nptr == *sptr) 1398 { 1399 nptr++; 1400 sptr++; 1401 continue; 1402 } 1403 setcursor(ep,sptr-ep->screen,*nptr); 1404 *sptr++ = *nptr++; 1405 #if SHOPT_MULTIBYTE 1406 while(*nptr==MARKER) 1407 { 1408 if(*sptr=='\0') 1409 *(sptr + 1) = '\0'; 1410 *sptr++ = *nptr++; 1411 i--; 1412 ep->cursor++; 1413 } 1414 #endif /* SHOPT_MULTIBYTE */ 1415 } 1416 if(ep->ed->e_multiline && option == REFRESH && ep->ed->e_nocrnl==0) 1417 ed_setcursor(ep->ed, ep->screen, ep->cursor-ep->screen, ep->ed->e_peol, -1); 1418 1419 1420 /****************** 1421 1422 Screen overflow checks 1423 1424 ********************/ 1425 1426 if (nscend >= &nscreen[ep->offset+w_size]) 1427 { 1428 if (ep->offset > 0) 1429 longline = BOTH; 1430 else 1431 longline = UPPER; 1432 } 1433 else 1434 { 1435 if (ep->offset > 0) 1436 longline = LOWER; 1437 } 1438 1439 /* Update screen overflow indicator if need be */ 1440 1441 if (longline != ep->overflow) 1442 { 1443 setcursor(ep,w_size,longline); 1444 ep->overflow = longline; 1445 } 1446 i = (ncursor-nscreen) - ep->offset; 1447 setcursor(ep,i,0); 1448 if(option==FINAL && ep->ed->e_multiline) 1449 setcursor(ep,nscend+1-nscreen,0); 1450 ep->scvalid = 1; 1451 return; 1452 } 1453 1454 /* 1455 * put the cursor to the <newp> position within screen buffer 1456 * if <c> is non-zero then output this character 1457 * cursor is set to reflect the change 1458 */ 1459 1460 static void setcursor(register Emacs_t *ep,register int newp,int c) 1461 { 1462 register int oldp = ep->cursor - ep->screen; 1463 newp = ed_setcursor(ep->ed, ep->screen, oldp, newp, 0); 1464 if(c) 1465 { 1466 putchar(ep->ed,c); 1467 newp++; 1468 } 1469 ep->cursor = ep->screen+newp; 1470 return; 1471 } 1472 1473 #if SHOPT_MULTIBYTE 1474 static int print(register int c) 1475 { 1476 return((c&~STRIP)==0 && isprint(c)); 1477 } 1478 1479 static int _isword(register int c) 1480 { 1481 return((c&~STRIP) || isalnum(c) || c=='_'); 1482 } 1483 #endif /* SHOPT_MULTIBYTE */ 1484