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