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