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