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