1 /* 2 3 * Copyright (c) 1984, 1985, 1986 AT&T 4 * All Rights Reserved 5 6 * THIS IS UNPUBLISHED PROPRIETARY SOURCE 7 * CODE OF AT&T. 8 * The copyright notice above does not 9 * evidence any actual or intended 10 * publication of such source code. 11 12 */ 13 /* @(#)vi.c 1.1 */ 14 /* Adapted for ksh by David Korn */ 15 /*+ VI.C P.D. Sullivan 16 * 17 * One line editor for the shell based on the vi editor. 18 * 19 * Questions to: 20 * P.D. Sullivan 21 * cbosg!pds 22 -*/ 23 24 25 #include <errno.h> 26 27 #ifdef KSHELL 28 #include "flags.h" 29 #include "defs.h" 30 #include "shtype.h" 31 #include "io.h" 32 #include "brkincr.h" 33 34 #else 35 #include <stdio.h> 36 #include <signal.h> 37 #include <setjmp.h> 38 #include <ctype.h> 39 #endif /* KSHELL */ 40 41 #include "history.h" 42 #include "edit.h" 43 #ifdef BSD 44 #include <sys/ioctl.h> 45 #endif /* BSD */ 46 47 #define NTICKS 5 /* number of ticks for typeahead */ 48 #define MAXCHAR MAXLINE-2 /* max char per line */ 49 #define PRSIZE 80 /* max prompt size */ 50 #define WINDOW MAXWINDOW /* max char in window of which */ 51 /* WINDOW-2 are available to user */ 52 /* actual window size may be smaller */ 53 54 55 #ifndef KSHELL 56 extern char trapnote; 57 #define output stderr 58 #endif /* KSHELL */ 59 60 #ifdef MULTIBYTE 61 static int bigvi; 62 #define gencpy(a,b) e_gencpy(a,b) 63 #define genncpy(a,b,n) e_genncpy(a,b,n) 64 #define genlen(str) e_genlen(str) 65 #define digit(c) ((c&~STRIP)==0 && isdigit(c)) 66 #define is_print(c) ((c&~STRIP) || isprint(c)) 67 #else 68 #define gencpy(a,b) strcpy((char*)(a),(char*)(b)) 69 #define genncpy(a,b,n) strncpy((char*)(a),(char*)(b),n) 70 #define genlen(str) strlen(str) 71 #define isalph(v) isalnum(virtual[v]) 72 #define isblank(v) isspace(virtual[v]) 73 #define ismetach(v) ismeta(virtual[v]) 74 #define digit(c) isdigit(c) 75 #define is_print(c) isprint(c) 76 #endif /* MULTIBYTE */ 77 #define fold(c) ((c)&~040) /* lower and uppercase equivalent */ 78 #ifdef INT16 79 /* save space by defining functions for these */ 80 # undef isalph 81 # undef isblank 82 # undef ismetach 83 #endif /* INT16 */ 84 85 #undef putchar 86 #undef getchar 87 #define getchar() e_getchar() 88 #define putchar(c) e_putchar(c) 89 #define bell e_ringbell() /* ring terminal's bell */ 90 #define crlf e_crlf() /* return and linefeed */ 91 92 #define addnl editb.e_addnl /* boolean - add newline flag */ 93 #define crallowed editb.e_crlf 94 #define cur_virt editb.e_cur /* current virtual column */ 95 #define cur_phys editb.e_pcur /* current phys column cursor is at */ 96 #define curhline editb.e_hline /* current history line */ 97 #define env editb.e_env 98 #define fildes editb.e_fd 99 #define findchar editb.e_fchar /* last find char */ 100 #define first_virt editb.e_fcol /* first allowable column */ 101 #define first_wind editb.e_globals[0] /* first column of window */ 102 #define globals editb.e_globals /* local global variables */ 103 #define histmin editb.e_hismin 104 #define histmax editb.e_hismax 105 #define last_phys editb.e_peol /* last column in physical */ 106 #define last_virt editb.e_eol /* last column */ 107 #define last_wind editb.e_globals[1] /* last column in window */ 108 #define lastline editb.e_tmp /* last line entered */ 109 #define lastmotion editb.e_globals[2] /* last motion */ 110 #define lastrepeat editb.e_mode /* last repeat count for motion cmds */ 111 #define long_char editb.e_globals[3] /* line bigger than window */ 112 #define long_line editb.e_globals[4] /* line bigger than window */ 113 #define lsearch editb.e_search /* last search string */ 114 #define lookahead editb.e_index /* characters in buffer */ 115 #define previous editb.e_lbuf /* lookahead buffer */ 116 #define max_col editb.e_llimit /* maximum column */ 117 #define ocur_phys editb.e_globals[5] /* old current physical position */ 118 #define ocur_virt editb.e_globals[6] /* old last virtual position */ 119 #define ofirst_wind editb.e_globals[7] /* old window first col */ 120 #define o_v_char editb.e_globals[8] /* prev virtual[ocur_virt] */ 121 #define Prompt editb.e_prompt /* pointer to prompt */ 122 #define plen editb.e_plen /* length of prompt */ 123 #define physical editb.e_physbuf /* physical image */ 124 #define repeat editb.e_repeat /* repeat count for motion cmds */ 125 #define ttyspeed editb.e_ttyspeed /* tty speed */ 126 #define u_column editb.e_ucol /* undo current column */ 127 #define U_saved editb.e_saved /* original virtual saved */ 128 #define U_space editb.e_Ubuf /* used for U command */ 129 #define u_space editb.e_ubuf /* used for u command */ 130 #define usreof editb.e_eof /* user defined eof char */ 131 #define usrerase editb.e_erase /* user defined erase char */ 132 #define usrintr editb.e_intr /* user defined intr char */ 133 #define usrkill editb.e_kill /* user defined kill char */ 134 #define usrquit editb.e_quit /* user defined quit char */ 135 #define virtual editb.e_inbuf /* pointer to virtual image buffer */ 136 #define window editb.e_window /* window buffer */ 137 #define w_size editb.e_wsize /* window size */ 138 #define inmacro editb.e_inmacro /* true when in macro */ 139 genchar *yankbuf; /* yank/delete buffer */ 140 141 extern histloc hist_find(); 142 extern char *hist_word(); 143 extern void hist_flush(); 144 extern char *movstr(); 145 extern char *itos(); 146 extern char *strchr(); 147 extern char *strcpy(); 148 extern char *strncpy(); 149 extern char *malloc(); 150 extern long times(); 151 extern void e_flush(); 152 153 #define ABORT -2 /* user abort */ 154 #define APPEND -10 /* append chars */ 155 #define BAD -1 /* failure flag */ 156 #define BIGVI -15 /* user wants real vi */ 157 #define CONTROL -20 /* control mode */ 158 #define ENTER -25 /* enter flag */ 159 #define GOOD 0 /* success flag */ 160 #define INPUT -30 /* input mode */ 161 #define INSERT -35 /* insert mode */ 162 #define REPLACE -40 /* replace chars */ 163 #define SEARCH -45 /* search flag */ 164 #define TRANSLATE -50 /* translate virt to phys only */ 165 166 #define DEL '\177' /* interrupt char */ 167 168 #define TRUE 1 169 #define FALSE 0 170 171 #define INVALID (-1) /* invalid column */ 172 #define QUIT_C '\34' /* quit char */ 173 #define SYSERR (-1) /* system error */ 174 175 static char last_cmd = '\0'; /* last command */ 176 static char repeat_set; 177 static char nonewline; 178 179 #ifdef BSD 180 static long typeahead; /* typeahead occurred */ 181 #else 182 static int typeahead; /* typeahead occurred */ 183 #endif /* BSD */ 184 185 static void pr_prompt(); 186 static void pr_string(); 187 188 /*+ VREAD( fd, shbuf, nchar ) 189 * 190 * This routine implements a one line version of vi and is 191 * called by _filbuf.c 192 * 193 -*/ 194 195 vread(fd, shbuf, nchar) 196 int fd; /* input file descriptor */ 197 unsigned char *shbuf; /* shell line buffer */ 198 int nchar; /* number of chars to read */ 199 { 200 register int c; /* general variable */ 201 register int i; /* general variable */ 202 register int term_char; /* read() termination character */ 203 char prompt[PRSIZE]; /* prompt */ 204 char Lsearch[MAXCHAR+2]; /* last search string */ 205 genchar Physical[2*MAXCHAR+2]; /* physical image */ 206 genchar Ubuf[MAXCHAR+2]; /* used for U command */ 207 genchar ubuf[MAXCHAR+2]; /* used for u command */ 208 genchar Window[WINDOW+10]; /* window image */ 209 #ifndef BSD 210 char cntl_char; /* TRUE if control character present */ 211 #endif /* BSD */ 212 int Globals[9]; /* local global variables */ 213 #ifndef BSD 214 long oldtime, newtime; 215 struct tbuffer 216 { 217 long utime; 218 long stime; 219 long cutime; 220 long cstime; 221 } dummy; 222 #endif /* BSD */ 223 224 /*** setup prompt ***/ 225 226 Prompt = prompt; 227 e_setup(fd,PRSIZE-2); 228 229 #ifndef RAWONLY 230 if( !is_option(VIRAW) ) 231 { 232 /*** Change the eol characters to '\r' and eof ***/ 233 /* in addition to '\n' and make eof an ESC */ 234 235 if( setalt(fd) == BAD ) 236 { 237 return(read(fd, (char*)shbuf, nchar)); 238 } 239 240 # ifdef BSD 241 ioctl(fd,FIONREAD,&typeahead); 242 243 /*** Read the line ***/ 244 trapnote = 0; 245 i = read(fd, (char*)shbuf, nchar); 246 if( i <= 0 ) 247 { 248 /*** read error or eof typed ***/ 249 setcooked(fd); 250 return(i); 251 } 252 term_char = shbuf[--i]; 253 if( term_char == '\r' ) 254 term_char = '\n'; 255 if( term_char=='\n' || term_char==ESC ) 256 shbuf[i--] = '\0'; 257 else 258 shbuf[i+1] = '\0'; 259 # else 260 261 /*** save the current time to determine typeahead ***/ 262 /* if typeahead occurs, want to write user's line */ 263 264 oldtime = times(&dummy); 265 266 /*** Read the line ***/ 267 trapnote = 0; 268 i = read(fd, (char*)shbuf, nchar); 269 newtime = times(&dummy); 270 typeahead = ((newtime-oldtime) < NTICKS); 271 272 c = shbuf[0]; 273 if( i<0 || c==usreof ) 274 { 275 /*** read error or eof typed ***/ 276 setcooked(fd); 277 if( c == usreof ) 278 i = 0; 279 return(i); 280 } 281 282 /*** Save and remove the last character if its an eol, ***/ 283 /* changing '\r' to '\n' */ 284 285 if( i == 0 ) 286 { 287 /*** ESC was typed as first char of line ***/ 288 term_char = ESC; 289 shbuf[i--] = '\0'; /* null terminate line */ 290 } 291 else 292 { 293 term_char = shbuf[--i]; 294 if( term_char == '\r' ) 295 term_char = '\n'; 296 if( term_char=='\n' || term_char==usreof ) 297 { 298 /*** remove terminator & null terminate ***/ 299 shbuf[i--] = '\0'; 300 } 301 else 302 { 303 /** terminator was ESC, which is not xmitted **/ 304 term_char = ESC; 305 shbuf[i+1] = '\0'; 306 } 307 } 308 # endif /* BSD */ 309 } 310 else 311 #endif /* RAWONLY */ 312 { 313 /*** Set raw mode ***/ 314 315 #ifndef RAWONLY 316 if( ttyspeed == 0 ) 317 { 318 /*** never did TCGETA, so do it ***/ 319 /* avoids problem if user does 'sh -o viraw' */ 320 setalt(fd); 321 } 322 #endif 323 if( setraw(fd) == BAD ) 324 { 325 return(read(fd, (char*)shbuf, nchar)); 326 } 327 i = INVALID; 328 } 329 330 /*** Initialize some things ***/ 331 332 virtual = (genchar*)shbuf; 333 #ifdef MULTIBYTE 334 shbuf[i+1] = 0; 335 i = e_internal(shbuf,virtual)-1; 336 #endif /* MULTIBYTE */ 337 globals = Globals; 338 cur_phys = i + 1; 339 cur_virt = i; 340 fildes = fd; 341 first_virt = 0; 342 first_wind = 0; 343 last_virt = i; 344 last_phys = i; 345 last_wind = i; 346 lsearch = Lsearch; 347 lsearch[0] = 0; 348 long_line = ' '; 349 long_char = ' '; 350 o_v_char = '\0'; 351 ocur_phys = 0; 352 ocur_virt = MAXCHAR; 353 ofirst_wind = 0; 354 physical = Physical; 355 u_column = INVALID - 1; 356 U_space = Ubuf; 357 u_space = ubuf; 358 window = Window; 359 window[0] = '\0'; 360 361 if( last_cmd == '\0' ) 362 { 363 /*** first time for this shell ***/ 364 365 last_cmd = 'i'; 366 findchar = INVALID; 367 lastmotion = '\0'; 368 lastrepeat = 1; 369 repeat = 1; 370 #if KSHELL && (CHARSISE*(MAXLINE+MAXCHAR+2))<BUFSIZE 371 lastline = shbuf + MAXLINE*sizeof(genchar); 372 #else 373 lastline = (genchar*)malloc(sizeof(genchar)*(MAXCHAR+2)); 374 #endif 375 #if KSHELL && (CHARSISE*(MAXLINE+MAXCHAR+2+MAXCHAR+2))<BUFSIZE 376 yankbuf = shbuf + (MAXLINE+MAXCHAR+2)*sizeof(genchar); 377 #else 378 yankbuf = (genchar*)malloc(sizeof(genchar)*(MAXCHAR+2)); 379 #endif 380 } 381 382 /*** fiddle around with prompt length ***/ 383 if( nchar+plen > MAXCHAR ) 384 nchar = MAXCHAR - plen; 385 max_col = nchar - 2; 386 w_size -= plen; 387 388 #ifndef RAWONLY 389 if( !is_option(VIRAW) ) 390 { 391 # ifndef BSD 392 cntl_char = FALSE; 393 # endif /* BSD */ 394 for(i=0; i<=last_virt; ++i ) 395 { 396 /*** change \r to \n, check for control characters, ***/ 397 /* delete appropriate ^Vs, */ 398 /* and estimate last physical column */ 399 400 if( virtual[i] == '\r' ) 401 virtual[i] = '\n'; 402 c = virtual[i]; 403 404 # ifndef BSD 405 if( c==usrerase || c==usrkill ) 406 { 407 /*** user typed escaped erase or kill char ***/ 408 cntl_char = TRUE; 409 } 410 else if( !is_print(c) ) 411 { 412 cntl_char = TRUE; 413 414 if( c == cntl(V) ) 415 { 416 if( i == last_virt ) 417 { 418 /*** eol/eof was escaped ***/ 419 /* so replace ^V with it */ 420 virtual[i] = term_char; 421 break; 422 } 423 424 /*** delete ^V ***/ 425 gencpy((&virtual[i]), (&virtual[i+1])); 426 --cur_virt; 427 --last_virt; 428 } 429 } 430 # endif /* BSD */ 431 } 432 433 /*** copy virtual image to window ***/ 434 if(last_virt > 0) 435 last_phys = e_virt_to_phys(virtual,physical,last_virt,0,0); 436 if( last_phys >= w_size ) 437 { 438 /*** line longer than window ***/ 439 last_wind = w_size - 1; 440 } 441 else 442 last_wind = last_phys; 443 genncpy(window, virtual, last_wind+1); 444 445 if( term_char!=ESC && (last_virt==INVALID 446 || virtual[last_virt]!=term_char) ) 447 { 448 /*** Line not terminated with ESC or escaped (^V) ***/ 449 /* eol, so return after doing a total update */ 450 /* if( (speed is greater or equal to 1200 */ 451 /* and something was typed) and */ 452 /* (control character present */ 453 /* or typeahead occurred) ) */ 454 455 setcooked(fd); 456 if( ttyspeed==FAST && last_virt!=INVALID 457 # ifdef BSD 458 && typeahead) 459 # else 460 && (typeahead || cntl_char==TRUE) ) 461 # endif 462 { 463 refresh(TRANSLATE); 464 pr_prompt(); 465 putstring(0, last_phys+1); 466 # ifdef BSD 467 crlf; 468 # endif /* BSD */ 469 } 470 471 if( term_char=='\n' ) 472 { 473 # ifndef BSD 474 crlf; 475 # endif /* BSD */ 476 virtual[++last_virt] = '\n'; 477 } 478 last_cmd = 'i'; 479 save_last(); 480 #ifdef MULTIBYTE 481 virtual[last_virt+1] = 0; 482 last_virt = e_external(virtual,shbuf); 483 return(last_virt); 484 #else 485 return(++last_virt); 486 #endif /* MULTIBYTE */ 487 } 488 489 /*** Line terminated with escape, or escaped eol/eof, ***/ 490 /* so set raw mode */ 491 492 if( setraw(fd) == BAD ) 493 { 494 setcooked(fd); 495 virtual[++last_virt] = '\n'; 496 #ifdef MULTIBYTE 497 virtual[last_virt+1] = 0; 498 last_virt = e_external(virtual,shbuf); 499 return(last_virt); 500 #else 501 return(++last_virt); 502 #endif /* MULTIBYTE */ 503 } 504 505 # ifdef BSD 506 /*** for BSD erase the ^[ ***/ 507 pr_string("\b\b \b\b"); 508 # endif /* BSD */ 509 510 511 if( crallowed == YES ) 512 { 513 /*** start over since there may be ***/ 514 /*** a control char, or cursor might not ***/ 515 /*** be at left margin (this lets us know ***/ 516 /*** where we are ***/ 517 cur_phys = 0; 518 window[0] = '\0'; 519 pr_prompt(); 520 if( term_char==ESC && virtual[last_virt]!=ESC ) 521 refresh(CONTROL); 522 else 523 refresh(INPUT); 524 } 525 else 526 { 527 /*** just update everything internally ***/ 528 refresh(TRANSLATE); 529 } 530 } 531 else 532 #endif /* RAWONLY */ 533 virtual[0] = '\0'; 534 535 /*** Handle usrintr, usrquit, or EOF ***/ 536 537 if( (i=setjmp(env)) != 0 ) 538 { 539 virtual[0] = '\0'; 540 setcooked(fd); 541 542 switch(i) 543 { 544 case UEOF: 545 /*** EOF ***/ 546 return(0); 547 548 case UINTR: 549 /** interrupt **/ 550 return(SYSERR); 551 } 552 return(SYSERR); 553 } 554 555 /*** Get a line from the terminal ***/ 556 557 U_saved = FALSE; 558 559 #ifdef RAWONLY 560 getline(APPEND); 561 #else 562 if( is_option(VIRAW) || virtual[last_virt]==term_char ) 563 getline(APPEND); 564 else 565 getline(ESC); 566 #endif /* RAWONLY */ 567 568 /*** add a new line if user typed unescaped \n ***/ 569 /* to cause the shell to process the line */ 570 if( addnl ) 571 { 572 virtual[++last_virt] = '\n'; 573 crlf; 574 } 575 setcooked(fd); 576 if( ++last_virt >= 0 ) 577 { 578 #ifdef MULTIBYTE 579 if(bigvi) 580 { 581 bigvi = 0; 582 shbuf[last_virt-1] = '\n'; 583 } 584 else 585 { 586 virtual[last_virt] = 0; 587 last_virt = e_external(virtual,shbuf); 588 } 589 #endif /* MULTIBYTE */ 590 return(last_virt); 591 } 592 else 593 return(SYSERR); 594 } 595 596 /*{ APPEND( char, mode ) 597 * 598 * This routine will append char after cur_virt in the virtual image. 599 * mode = APPEND, shift chars right before appending 600 * REPLACE, replace char if possible 601 * 602 }*/ 603 604 static int 605 append(c, mode) 606 int c; 607 int mode; 608 { 609 register int i; 610 611 if( last_virt<max_col && last_phys<max_col ) 612 { 613 if( mode==APPEND || cur_virt==last_virt ) 614 { 615 for(i = ++last_virt; i > cur_virt; --i) 616 { 617 virtual[i] = virtual[i-1]; 618 } 619 } 620 virtual[++cur_virt] = c; 621 } 622 return; 623 } 624 625 /*{ BACKWORD( nwords, cmd ) 626 * 627 * This routine will position cur_virt at the nth previous word. 628 * 629 }*/ 630 631 static int 632 backword(nwords, cmd) 633 int nwords; 634 register int cmd; 635 { 636 register int tcur_virt = cur_virt; 637 while( nwords-- && tcur_virt > first_virt ) 638 { 639 if( !isblank(tcur_virt) && isblank(tcur_virt-1) 640 && tcur_virt>first_virt ) 641 --tcur_virt; 642 else if(cmd != 'B') 643 { 644 register int last = isalph(tcur_virt-1); 645 if((!isalph(tcur_virt) && last) 646 || (isalph(tcur_virt) && !last)) 647 --tcur_virt; 648 } 649 while( isblank(tcur_virt) && tcur_virt>=first_virt ) 650 --tcur_virt; 651 if( cmd == 'B' ) 652 { 653 while( !isblank(tcur_virt) && tcur_virt>=first_virt ) 654 --tcur_virt; 655 } 656 else 657 { 658 if( isalph(tcur_virt) ) 659 while( isalph(tcur_virt) && tcur_virt>=first_virt ) 660 --tcur_virt; 661 else 662 while( !isalph(tcur_virt) && !isblank(tcur_virt) 663 && tcur_virt>=first_virt ) 664 --tcur_virt; 665 } 666 cur_virt = ++tcur_virt; 667 } 668 return; 669 } 670 671 /*{ CNTLMODE() 672 * 673 * This routine implements the vi command subset. 674 * The cursor will always be positioned at the char of interest. 675 * 676 }*/ 677 678 static int 679 cntlmode() 680 { 681 register int c; 682 register int i; 683 genchar tmp_u_space[MAXCHAR+2]; /* temporary u_space */ 684 genchar *real_u_space; /* points to real u_space */ 685 int tmp_u_column; /* temporary u_column */ 686 687 if( U_saved == FALSE ) 688 { 689 /*** save virtual image if never done before ***/ 690 virtual[last_virt+1] = '\0'; 691 gencpy(U_space, virtual); 692 U_saved = TRUE; 693 } 694 695 save_last(); 696 697 real_u_space = u_space; 698 curhline = histmax; 699 first_virt = 0; 700 repeat = 1; 701 if( cur_virt > INVALID ) 702 { 703 /*** make sure cursor is at the last char ***/ 704 sync_cursor(); 705 } 706 707 /*** Read control char until something happens to cause a ***/ 708 /* return to APPEND/REPLACE mode */ 709 710 while( c=getchar() ) 711 { 712 repeat_set = 0; 713 if( c == '0' ) 714 { 715 /*** move to leftmost column ***/ 716 cur_virt = 0; 717 sync_cursor(); 718 continue; 719 } 720 721 if( digit(c) ) 722 { 723 lastrepeat = repeat; 724 c = getcount(c); 725 if( c == '.' ) 726 lastrepeat = repeat; 727 } 728 729 /*** see if it's a move cursor command ***/ 730 731 if( mvcursor(c) == GOOD ) 732 { 733 sync_cursor(); 734 repeat = 1; 735 continue; 736 } 737 738 /*** see if it's a repeat of the last command ***/ 739 740 if( c == '.' ) 741 { 742 c = last_cmd; 743 repeat = lastrepeat; 744 i = textmod(c, c); 745 } 746 else 747 { 748 i = textmod(c, 0); 749 } 750 751 /*** see if it's a text modification command ***/ 752 753 switch(i) 754 { 755 case BAD: 756 break; 757 758 default: /** input mode **/ 759 last_cmd = c; 760 lastrepeat = repeat; 761 repeat = 1; 762 if( i == GOOD ) 763 continue; 764 return(i); 765 } 766 767 switch( c ) 768 { 769 /***** Other stuff *****/ 770 771 case cntl(L): /** Redraw line **/ 772 /*** print the prompt and ***/ 773 /* force a total refresh */ 774 if(nonewline==0) 775 putchar('\n'); 776 nonewline = 0; 777 pr_prompt(); 778 window[0] = '\0'; 779 cur_phys = first_wind; 780 ofirst_wind = INVALID; 781 long_line = ' '; 782 break; 783 784 case '/': /** Search **/ 785 case '?': 786 case 'N': 787 case 'n': 788 save_v(); 789 switch( search(c) ) 790 { 791 case GOOD: 792 /*** force a total refresh ***/ 793 window[0] = '\0'; 794 goto newhist; 795 796 case BAD: 797 /*** no match ***/ 798 bell; 799 800 default: 801 if( u_column == INVALID ) 802 del_line(BAD); 803 else 804 restore_v(); 805 break; 806 } 807 break; 808 809 case 'j': /** get next command **/ 810 case '+': /** get next command **/ 811 curhline += repeat; 812 if( curhline > histmax ) 813 { 814 curhline = histmax; 815 goto ringbell; 816 } 817 else if(curhline==histmax && tmp_u_column!=INVALID ) 818 { 819 u_space = tmp_u_space; 820 u_column = tmp_u_column; 821 restore_v(); 822 u_space = real_u_space; 823 break; 824 } 825 save_v(); 826 goto newhist; 827 828 case 'k': /** get previous command **/ 829 case '-': /** get previous command **/ 830 if( curhline == histmax ) 831 { 832 u_space = tmp_u_space; 833 i = u_column; 834 save_v(); 835 u_space = real_u_space; 836 tmp_u_column = u_column; 837 u_column = i; 838 } 839 840 curhline -= repeat; 841 if( curhline <= histmin ) 842 { 843 curhline = histmin + 1; 844 goto ringbell; 845 } 846 save_v(); 847 newhist: 848 hist_copy((char*)virtual, curhline, -1); 849 #ifdef MULTIBYTE 850 e_internal((char*)virtual,virtual); 851 #endif /* MULTIBYTE */ 852 if( (last_virt = genlen((char*)virtual) - 1) > 0 ) 853 cur_virt = 0; 854 else 855 cur_virt = INVALID; 856 break; 857 858 859 case 'u': /** undo the last thing done **/ 860 restore_v(); 861 break; 862 863 case 'U': /** Undo everything **/ 864 save_v(); 865 if( virtual[0] == '\0' ) 866 goto ringbell; 867 else 868 { 869 gencpy(virtual, U_space); 870 last_virt = genlen(U_space) - 1; 871 cur_virt = 0; 872 } 873 break; 874 875 #ifdef KSHELL 876 case 'v': 877 if(repeat_set==0) 878 goto vcommand; 879 #endif /* KSHELL */ 880 881 case 'G': /** goto command repeat **/ 882 if(repeat_set==0) 883 repeat = histmin+1; 884 if( repeat <= histmin || repeat > histmax ) 885 { 886 goto ringbell; 887 } 888 curhline = repeat; 889 save_v(); 890 if(c == 'G') 891 goto newhist; 892 893 #ifdef KSHELL 894 vcommand: 895 { 896 /* use EDITOR on current command */ 897 register char *cp; 898 if(curhline == histmax) 899 { 900 if(last_virt<=0) 901 goto ringbell; 902 virtual[last_virt+1] = 0; 903 fputs((char*)virtual,fc_fix->fixfd); 904 states |= FIXFLG; 905 hist_flush(); 906 } 907 cp = movstr(big_vi, (char*)virtual); 908 cp = movstr(itos(curhline), cp); 909 last_virt = (unsigned char*)cp - (unsigned char*)virtual; 910 return(BIGVI); 911 } 912 #endif /* KSHELL */ 913 914 case '#': /** insert # to no-op command **/ 915 if( cur_virt != INVALID ) 916 { 917 /*** something was typed, so no-op it ***/ 918 cur_virt = INVALID; 919 append('#', APPEND); 920 refresh(INPUT); 921 } 922 923 case '\n': /** send to shell **/ 924 return(ENTER); 925 926 default: 927 ringbell: 928 bell; 929 repeat = 1; 930 continue; 931 } 932 933 refresh(CONTROL); 934 repeat = 1; 935 } 936 /* NOTREACHED */ 937 } 938 939 /*{ CURSOR( new_current_physical ) 940 * 941 * This routine will position the virtual cursor at 942 * physical column x in the window. 943 * 944 }*/ 945 946 static int 947 cursor(x) 948 register int x; 949 { 950 register int delta; 951 952 #ifdef MULTIBYTE 953 while(physical[x]==MARKER) 954 x++; 955 #endif /* MULTIBYTE */ 956 delta = x - cur_phys; 957 958 if( delta == 0 ) 959 return; 960 961 if( delta > 0 ) 962 { 963 /*** move to right ***/ 964 putstring(cur_phys, delta); 965 } 966 else 967 { 968 /*** move to left ***/ 969 970 delta = -delta; 971 972 /*** attempt to optimize cursor movement ***/ 973 if( crallowed==NO 974 || (delta <= ((cur_phys-first_wind)+plen)>>1) ) 975 { 976 while( delta-- ) 977 putchar('\b'); 978 } 979 else 980 { 981 pr_prompt(); 982 putstring(first_wind, x - first_wind); 983 } 984 } 985 cur_phys = x; 986 return; 987 } 988 989 /*{ DELETE( nchars, mode ) 990 * 991 * Delete nchars from the virtual space and leave cur_virt positioned 992 * at cur_virt-1. 993 * 994 * If mode = 'c', do not save the characters deleted 995 * = 'd', save them in yankbuf and delete. 996 * = 'y', save them in yankbuf but do not delete. 997 * 998 }*/ 999 1000 static int 1001 delete(nchars, mode) 1002 register int nchars; 1003 char mode; 1004 { 1005 register int i; 1006 register genchar *vp; 1007 1008 if( cur_virt < first_virt ) 1009 { 1010 bell; 1011 return; 1012 } 1013 if( nchars > 0 ) 1014 { 1015 vp = virtual+cur_virt; 1016 if( (cur_virt-- + nchars) > last_virt ) 1017 { 1018 /*** set nchars to number actually deleted ***/ 1019 nchars = last_virt - cur_virt; 1020 } 1021 1022 /*** save characters to be deleted ***/ 1023 1024 if( mode != 'c' ) 1025 { 1026 i = vp[nchars]; 1027 vp[nchars] = 0; 1028 gencpy(yankbuf,vp); 1029 vp[nchars] = i; 1030 } 1031 1032 /*** now delete these characters ***/ 1033 1034 if( mode != 'y' ) 1035 { 1036 gencpy(vp,vp+nchars); 1037 last_virt -= nchars; 1038 } 1039 } 1040 return; 1041 } 1042 1043 /*{ DEL_LINE( mode ) 1044 * 1045 * This routine will delete the line. 1046 * mode = GOOD, do a save_v() 1047 * 1048 }*/ 1049 1050 static int 1051 del_line(mode) 1052 int mode; 1053 { 1054 if( last_virt == INVALID ) 1055 return; 1056 1057 if( mode == GOOD ) 1058 save_v(); 1059 1060 cur_virt = 0; 1061 first_virt = 0; 1062 delete(last_virt+1, BAD); 1063 refresh(CONTROL); 1064 1065 cur_virt = INVALID; 1066 cur_phys = 0; 1067 findchar = INVALID; 1068 last_phys = INVALID; 1069 last_virt = INVALID; 1070 last_wind = INVALID; 1071 first_wind = 0; 1072 o_v_char = '\0'; 1073 ocur_phys = 0; 1074 ocur_virt = MAXCHAR; 1075 ofirst_wind = 0; 1076 window[0] = '\0'; 1077 return; 1078 } 1079 1080 /*{ DELMOTION( motion, mode ) 1081 * 1082 * Delete thru motion. 1083 * 1084 * mode = 'd', save deleted characters, delete 1085 * = 'c', do not save characters, change 1086 * = 'y', save characters, yank 1087 * 1088 * Returns GOOD if operation successful; else BAD. 1089 * 1090 }*/ 1091 1092 static int 1093 delmotion(motion, mode) 1094 register int motion; 1095 char mode; 1096 { 1097 register int i; 1098 register int j; 1099 1100 if( cur_virt == INVALID ) 1101 return(BAD); 1102 if( mode != 'y' ) 1103 save_v(); 1104 i = cur_virt; 1105 1106 /*** fake out the motion routines by appending a blank ***/ 1107 1108 virtual[++last_virt] = ' '; 1109 if( mvcursor(motion)==BAD && strchr(";,TtFf", motion) ) 1110 { 1111 --last_virt; 1112 return(BAD); 1113 } 1114 --last_virt; 1115 1116 j = cur_virt; 1117 if( mode=='c' && j>i ) 1118 { 1119 /*** called by change operation ***/ 1120 while( j>i && isblank(j-1) ) 1121 --j; 1122 } 1123 1124 if( j > i ) 1125 { 1126 cur_virt = i; 1127 j -= i; 1128 } 1129 else 1130 { 1131 j = i - j; 1132 } 1133 1134 if( !strchr("wWh\bl ", motion) ) 1135 ++j; 1136 delete(j, mode); 1137 if( mode == 'y' ) 1138 cur_virt = i; 1139 return(GOOD); 1140 } 1141 1142 /*{ ENDWORD( nwords, cmd ) 1143 * 1144 * This routine will move cur_virt to the end of the nth word. 1145 * 1146 }*/ 1147 1148 static int 1149 endword(nwords, cmd) 1150 int nwords; 1151 register int cmd; 1152 { 1153 register int tcur_virt = cur_virt; 1154 while( nwords-- ) 1155 { 1156 if( !isblank(tcur_virt) && tcur_virt<=last_virt ) 1157 ++tcur_virt; 1158 while( isblank(tcur_virt) && tcur_virt<=last_virt ) 1159 ++tcur_virt; 1160 if( cmd == 'E' ) 1161 { 1162 while( !isblank(tcur_virt) && tcur_virt<=last_virt ) 1163 ++tcur_virt; 1164 } 1165 else 1166 { 1167 if( isalph(tcur_virt) ) 1168 while( isalph(tcur_virt) && tcur_virt<=last_virt ) 1169 ++tcur_virt; 1170 else 1171 while( !isalph(tcur_virt) && !isblank(tcur_virt) 1172 && tcur_virt<=last_virt ) 1173 ++tcur_virt; 1174 } 1175 if( tcur_virt > first_virt ) 1176 tcur_virt--; 1177 } 1178 cur_virt = tcur_virt; 1179 return; 1180 } 1181 1182 /*{ FORWARD( nwords, cmd ) 1183 * 1184 * This routine will move cur_virt forward to the next nth word. 1185 * 1186 }*/ 1187 1188 static int 1189 forward(nwords, cmd) 1190 register int nwords; 1191 char cmd; 1192 { 1193 register tcur_virt = cur_virt; 1194 while( nwords-- ) 1195 { 1196 if( cmd == 'W' ) 1197 { 1198 while( !isblank(tcur_virt) && tcur_virt < last_virt ) 1199 ++tcur_virt; 1200 } 1201 else 1202 { 1203 if( isalph(tcur_virt) ) 1204 { 1205 while( isalph(tcur_virt) && tcur_virt<last_virt ) 1206 ++tcur_virt; 1207 } 1208 else 1209 { 1210 while( !isalph(tcur_virt) && !isblank(tcur_virt) 1211 && tcur_virt < last_virt ) 1212 ++tcur_virt; 1213 } 1214 } 1215 while( isblank(tcur_virt) && tcur_virt < last_virt ) 1216 ++tcur_virt; 1217 } 1218 cur_virt = tcur_virt; 1219 return; 1220 } 1221 1222 1223 1224 /*{ GETCOUNT(c) 1225 * 1226 * Set repeat to the user typed number and return the terminating 1227 * character. 1228 * 1229 }*/ 1230 1231 static int 1232 getcount(c) 1233 register int c; 1234 { 1235 register int i; 1236 1237 /*** get any repeat count ***/ 1238 1239 repeat_set++; 1240 i = 0; 1241 while( digit(c) ) 1242 { 1243 i = i*10 + c - '0'; 1244 c = getchar(); 1245 } 1246 if( i <= 0 ) i= 1; 1247 repeat *= i; 1248 return(c); 1249 } 1250 1251 /*{ GETLINE( mode ) 1252 * 1253 * This routine will fetch a line. 1254 * mode = APPEND, allow escape to cntlmode subroutine 1255 * appending characters. 1256 * = REPLACE, allow escape to cntlmode subroutine 1257 * replacing characters. 1258 * = SEARCH, no escape allowed 1259 * = ESC, enter control mode immediately 1260 * 1261 * The cursor will always be positioned after the last 1262 * char printed. 1263 * 1264 * This routine returns when cr, nl, or (eof in column 0) is 1265 * received (column 0 is the first char position). 1266 * 1267 }*/ 1268 1269 static int 1270 getline(mode) 1271 register int mode; 1272 { 1273 register int c; 1274 register int tmp; 1275 1276 addnl = TRUE; 1277 1278 if( mode == ESC ) 1279 { 1280 /*** go directly to control mode ***/ 1281 goto escape; 1282 } 1283 1284 for(;;) 1285 { 1286 if( last_virt >= max_col || last_phys >= max_col ) 1287 { 1288 if( virtual[last_virt] != '\\' ) 1289 virtual[++last_virt] = '\\'; 1290 refresh(INPUT); 1291 return; 1292 } 1293 1294 if( (c = getchar()) == cntl(V) ) 1295 { 1296 /*** implement ^V to escape next char ***/ 1297 c = getchar(); 1298 append(c, mode); 1299 refresh(INPUT); 1300 continue; 1301 } 1302 1303 if( c == usreof ) 1304 c = UEOF; 1305 else if( c == usrerase ) 1306 c = UERASE; 1307 else if( c == usrkill ) 1308 c = UKILL; 1309 1310 switch( c ) 1311 { 1312 case ESC: /** enter control mode **/ 1313 if( mode == SEARCH ) 1314 { 1315 bell; 1316 continue; 1317 } 1318 else 1319 { 1320 escape: 1321 if( mode == REPLACE ) 1322 --cur_virt; 1323 tmp = cntlmode(); 1324 if( tmp == ENTER || tmp == BIGVI ) 1325 { 1326 #ifdef MULTIBYTE 1327 bigvi = (tmp==BIGVI); 1328 #endif /* MULTIBYTE */ 1329 return; 1330 } 1331 if( tmp == INSERT ) 1332 { 1333 mode = APPEND; 1334 continue; 1335 } 1336 mode = tmp; 1337 } 1338 break; 1339 1340 case UERASE: /** user erase char **/ 1341 /*** treat as backspace ***/ 1342 1343 case '\b': /** backspace **/ 1344 if( virtual[cur_virt] == '\\' ) 1345 { 1346 delete(1, BAD); 1347 append(usrerase, mode); 1348 } 1349 else 1350 { 1351 if( mode==SEARCH && cur_virt==0 ) 1352 { 1353 first_virt = 0; 1354 delete(1, BAD); 1355 return; 1356 } 1357 delete(1, BAD); 1358 } 1359 break; 1360 1361 case cntl(W): /** delete back word **/ 1362 if( cur_virt > first_virt && isblank(cur_virt-1) ) 1363 { 1364 delete(1, BAD); 1365 } 1366 else 1367 { 1368 tmp = cur_virt; 1369 backword(1, 'b'); 1370 delete(tmp - cur_virt + 1, BAD); 1371 } 1372 break; 1373 1374 case UKILL: /** user kill line char **/ 1375 if( virtual[cur_virt] == '\\' ) 1376 { 1377 delete(1, BAD); 1378 append(usrkill, mode); 1379 } 1380 else 1381 { 1382 if( mode == SEARCH ) 1383 { 1384 cur_virt = 1; 1385 delmotion('$', BAD); 1386 } 1387 else 1388 del_line(GOOD); 1389 } 1390 break; 1391 1392 case UEOF: /** eof char **/ 1393 if( cur_virt != INVALID ) 1394 continue; 1395 addnl = FALSE; 1396 1397 case '\n': /** newline or return **/ 1398 if( mode != SEARCH ) 1399 save_last(); 1400 return; 1401 1402 default: 1403 if( mode == REPLACE ) 1404 { 1405 if( cur_virt < last_virt ) 1406 { 1407 replace(c, TRUE); 1408 continue; 1409 } 1410 delete(1, BAD); 1411 mode = APPEND; 1412 } 1413 append(c, mode); 1414 break; 1415 } 1416 refresh(INPUT); 1417 1418 } 1419 } 1420 1421 /*{ MVCURSOR( motion ) 1422 * 1423 * This routine will move the virtual cursor according to motion 1424 * for repeat times. 1425 * 1426 * It returns GOOD if successful; else BAD. 1427 * 1428 }*/ 1429 1430 static int 1431 mvcursor(motion) 1432 register int motion; 1433 { 1434 register int count; 1435 register int tcur_virt; 1436 register int incr = -1; 1437 register int bound = 0; 1438 static int last_find = 0; /* last find command */ 1439 1440 switch(motion) 1441 { 1442 /***** Cursor move commands *****/ 1443 1444 case '0': /** First column **/ 1445 tcur_virt = 0; 1446 break; 1447 1448 case '^': /** First nonblank character **/ 1449 tcur_virt = first_virt; 1450 while( isblank(tcur_virt) && tcur_virt < last_virt ) 1451 ++tcur_virt; 1452 break; 1453 1454 case '$': /** End of line **/ 1455 tcur_virt = last_virt; 1456 break; 1457 1458 case 'h': /** Left one **/ 1459 case '\b': 1460 motion = first_virt; 1461 goto walk; 1462 1463 case ' ': 1464 case 'l': /** Right one **/ 1465 motion = last_virt; 1466 incr = 1; 1467 walk: 1468 tcur_virt = cur_virt; 1469 if( incr*tcur_virt < motion) 1470 { 1471 tcur_virt += repeat*incr; 1472 if( incr*tcur_virt > motion) 1473 tcur_virt = motion; 1474 } 1475 else 1476 { 1477 return(BAD); 1478 } 1479 break; 1480 1481 case 'B': 1482 case 'b': /** back word **/ 1483 tcur_virt = cur_virt; 1484 backword(repeat, motion); 1485 if( cur_virt == tcur_virt ) 1486 return(BAD); 1487 return(GOOD); 1488 1489 case 'E': 1490 case 'e': /** end of word **/ 1491 tcur_virt = cur_virt; 1492 endword(repeat, motion); 1493 if( cur_virt == tcur_virt ) 1494 return(BAD); 1495 return(GOOD); 1496 1497 case ',': /** reverse find old char **/ 1498 case ';': /** find old char **/ 1499 switch(last_find) 1500 { 1501 case 't': 1502 case 'f': 1503 if(motion==';') 1504 { 1505 bound = last_virt; 1506 incr = 1; 1507 } 1508 goto find_b; 1509 1510 case 'T': 1511 case 'F': 1512 if(motion==',') 1513 { 1514 bound = last_virt; 1515 incr = 1; 1516 } 1517 goto find_b; 1518 1519 default: 1520 return(BAD); 1521 } 1522 1523 1524 case 't': /** find up to new char forward **/ 1525 case 'f': /** find new char forward **/ 1526 bound = last_virt; 1527 incr = 1; 1528 1529 case 'T': /** find up to new char backward **/ 1530 case 'F': /** find new char backward **/ 1531 last_find = motion; 1532 findchar = getchar(); 1533 find_b: 1534 tcur_virt = cur_virt; 1535 count = repeat; 1536 while( count-- ) 1537 { 1538 while( incr*(tcur_virt+=incr) <= bound 1539 && virtual[tcur_virt] != findchar ); 1540 if( incr*tcur_virt > bound ) 1541 { 1542 return(BAD); 1543 } 1544 } 1545 if( fold(last_find) == 'T' ) 1546 tcur_virt -= incr; 1547 break; 1548 1549 case 'W': 1550 case 'w': /** forward word **/ 1551 tcur_virt = cur_virt; 1552 forward(repeat, motion); 1553 if( tcur_virt == cur_virt ) 1554 return(BAD); 1555 return(GOOD); 1556 1557 default: 1558 return(BAD); 1559 } 1560 cur_virt = tcur_virt; 1561 1562 return(GOOD); 1563 } 1564 1565 /*{ PR_PROMPT() 1566 * 1567 * Print the prompt. 1568 * 1569 }*/ 1570 1571 static void 1572 pr_prompt() 1573 { 1574 pr_string(Prompt); 1575 return; 1576 } 1577 1578 /* 1579 * print a string 1580 */ 1581 1582 static void 1583 pr_string(s) 1584 register char *s; 1585 { 1586 /*** copy string s ***/ 1587 register char *ptr = (char*)editb.e_outptr; 1588 while(*s) 1589 *ptr++ = *s++; 1590 editb.e_outptr = (unsigned char*)ptr; 1591 return; 1592 } 1593 1594 /*{ PUTSTRING( column, nchars ) 1595 * 1596 * Put nchars starting at column of physical into the workspace 1597 * to be printed. 1598 * 1599 }*/ 1600 1601 static int 1602 putstring(col, nchars) 1603 register int col; 1604 register int nchars; 1605 { 1606 while( nchars-- ) 1607 putchar(physical[col++]); 1608 return; 1609 } 1610 1611 /*{ REFRESH( mode ) 1612 * 1613 * This routine will refresh the crt so the physical image matches 1614 * the virtual image and display the proper window. 1615 * 1616 * mode = CONTROL, refresh in control mode, ie. leave cursor 1617 * positioned at last char printed. 1618 * = INPUT, refresh in input mode; leave cursor positioned 1619 * after last char printed. 1620 * = TRANSLATE, perform virtual to physical translation 1621 * and adjust left margin only. 1622 * 1623 * +-------------------------------+ 1624 * | | | virtual | | | 1625 * +-------------------------------+ 1626 * cur_virt last_virt 1627 * 1628 * +-----------------------------------------------+ 1629 * | | | physical | | | 1630 * +-----------------------------------------------+ 1631 * cur_phys last_phys 1632 * 1633 * 0 w_size - 1 1634 * +-----------------------+ 1635 * | | | window | 1636 * +-----------------------+ 1637 * cur_window = cur_phys - first_wind 1638 }*/ 1639 1640 static int 1641 refresh(mode) 1642 int mode; 1643 { 1644 register int p; 1645 register int regb; 1646 register int first_w = first_wind; 1647 int p_differ; 1648 int new_lw; 1649 int ncur_phys; 1650 int opflag; /* search optimize flag */ 1651 1652 # define w regb 1653 # define v regb 1654 1655 /*** find out if it's necessary to start translating at beginning ***/ 1656 1657 if(lookahead>0) 1658 { 1659 p = previous[lookahead-1]; 1660 if(p != ESC && p != '\n' && p != '\r') 1661 { 1662 ocur_virt = INVALID; 1663 return; 1664 } 1665 } 1666 v = cur_virt; 1667 if( v<ocur_virt || ocur_virt==INVALID 1668 || ( v==ocur_virt 1669 && (!is_print(virtual[v]) || !is_print(o_v_char))) ) 1670 { 1671 opflag = FALSE; 1672 p = 0; 1673 v = 0; 1674 } 1675 else 1676 { 1677 opflag = TRUE; 1678 p = ocur_phys; 1679 v = ocur_virt; 1680 if( !is_print(virtual[v]) ) 1681 { 1682 /*** avoid double ^'s ***/ 1683 ++p; 1684 ++v; 1685 } 1686 } 1687 virtual[last_virt+1] = 0; 1688 ncur_phys = e_virt_to_phys(virtual,physical,cur_virt,v,p); 1689 p = genlen(physical); 1690 if( --p < 0 ) 1691 last_phys = 0; 1692 else 1693 last_phys = p; 1694 1695 /*** see if this was a translate only ***/ 1696 1697 if( mode == TRANSLATE ) 1698 return; 1699 1700 /*** adjust left margin if necessary ***/ 1701 1702 if( ncur_phys<first_w || ncur_phys>=(first_w + w_size) ) 1703 { 1704 cursor(first_w); 1705 first_w = ncur_phys - (w_size>>1); 1706 if( first_w < 0 ) 1707 first_w = 0; 1708 first_wind = cur_phys = first_w; 1709 } 1710 1711 /*** attempt to optimize search somewhat to find ***/ 1712 /*** out where physical and window images differ ***/ 1713 1714 if( first_w==ofirst_wind && ncur_phys>=ocur_phys && opflag==TRUE ) 1715 { 1716 p = ocur_phys; 1717 w = p - first_w; 1718 } 1719 else 1720 { 1721 p = first_w; 1722 w = 0; 1723 } 1724 1725 for(; (p<=last_phys && w<=last_wind); ++p, ++w) 1726 { 1727 if( window[w] != physical[p] ) 1728 break; 1729 } 1730 p_differ = p; 1731 1732 if( (p>last_phys || p>=first_w+w_size) && w>last_wind 1733 && cur_virt==ocur_virt ) 1734 { 1735 /*** images are identical ***/ 1736 return; 1737 } 1738 1739 /*** copy the physical image to the window image ***/ 1740 1741 if( last_virt != INVALID ) 1742 { 1743 while( p <= last_phys && w < w_size ) 1744 window[w++] = physical[p++]; 1745 } 1746 new_lw = w; 1747 1748 /*** erase trailing characters if needed ***/ 1749 1750 while( w <= last_wind ) 1751 window[w++] = ' '; 1752 last_wind = --w; 1753 1754 p = p_differ; 1755 1756 /*** move cursor to start of difference ***/ 1757 1758 cursor(p); 1759 1760 /*** and output difference ***/ 1761 1762 w = p - first_w; 1763 while( w <= last_wind ) 1764 putchar(window[w++]); 1765 1766 cur_phys = w + first_w; 1767 last_wind = --new_lw; 1768 1769 if( last_phys >= w_size ) 1770 { 1771 if( first_w == 0 ) 1772 long_char = '>'; 1773 else if( last_phys < (first_w+w_size) ) 1774 long_char = '<'; 1775 else 1776 long_char = '*'; 1777 } 1778 else 1779 long_char = ' '; 1780 1781 if( long_line != long_char ) 1782 { 1783 /*** indicate lines longer than window ***/ 1784 while( w++ < w_size ) 1785 { 1786 putchar(' '); 1787 ++cur_phys; 1788 } 1789 putchar(long_char); 1790 ++cur_phys; 1791 long_line = long_char; 1792 } 1793 1794 ocur_phys = ncur_phys; 1795 ocur_virt = cur_virt; 1796 ofirst_wind = first_w; 1797 1798 if( mode==INPUT && cur_virt>INVALID ) 1799 ++ncur_phys; 1800 1801 cursor(ncur_phys); 1802 e_flush(); 1803 return; 1804 } 1805 1806 /*{ REPLACE( char, increment ) 1807 * 1808 * Replace the cur_virt character with char. This routine attempts 1809 * to avoid using refresh(). 1810 * 1811 * increment = TRUE, increment cur_virt after replacement. 1812 * = FALSE, leave cur_virt where it is. 1813 * 1814 }*/ 1815 1816 static int 1817 replace(c, increment) 1818 register int c; 1819 register int increment; 1820 { 1821 register int cur_window; 1822 1823 cur_window = cur_phys - first_wind; 1824 1825 if( ocur_virt == INVALID || (!is_print(c) || !is_print(o_v_char)) 1826 #ifdef MULTIBYTE 1827 || icharset(c) || out_csize(icharset(o_v_char))>1 1828 #endif /* MULTIBYTE */ 1829 || (increment==TRUE && (cur_window==w_size-1) 1830 || !is_print(virtual[cur_virt+1])) ) 1831 { 1832 /*** must use standard refresh routine ***/ 1833 1834 delete(1, BAD); 1835 append(c, APPEND); 1836 if( increment==TRUE && cur_virt<last_virt ) 1837 ++cur_virt; 1838 refresh(CONTROL); 1839 } 1840 else 1841 { 1842 virtual[cur_virt] = c; 1843 physical[cur_phys] = c; 1844 window[cur_window] = c; 1845 putchar(c); 1846 if( increment == TRUE ) 1847 { 1848 c = virtual[++cur_virt]; 1849 ++cur_phys; 1850 } 1851 else 1852 { 1853 putchar('\b'); 1854 } 1855 o_v_char = c; 1856 e_flush(); 1857 } 1858 return; 1859 } 1860 1861 /*{ RESTORE_V() 1862 * 1863 * Restore the contents of virtual space from u_space. 1864 * 1865 }*/ 1866 1867 static int 1868 restore_v() 1869 { 1870 register int tmpcol; 1871 genchar tmpspace[MAXCHAR+2]; 1872 1873 if( u_column == INVALID-1 ) 1874 { 1875 /*** never saved anything ***/ 1876 bell; 1877 return; 1878 } 1879 gencpy(tmpspace, u_space); 1880 tmpcol = u_column; 1881 save_v(); 1882 gencpy(virtual, tmpspace); 1883 cur_virt = tmpcol; 1884 last_virt = genlen(tmpspace) - 1; 1885 ocur_virt = MAXCHAR; /** invalidate refresh optimization **/ 1886 return; 1887 } 1888 1889 /*{ SAVE_LAST() 1890 * 1891 * If the user has typed something, save it in last line. 1892 * 1893 }*/ 1894 1895 static int 1896 save_last() 1897 { 1898 register int i; 1899 1900 if( (i = cur_virt - first_virt + 1) > 0 ) 1901 { 1902 /*** save last thing user typed ***/ 1903 genncpy(lastline, (&virtual[first_virt]), i); 1904 lastline[i] = '\0'; 1905 } 1906 return; 1907 } 1908 1909 /*{ SAVE_V() 1910 * 1911 * This routine will save the contents of virtual in u_space. 1912 * 1913 }*/ 1914 1915 static int 1916 save_v() 1917 { 1918 if(!inmacro) 1919 { 1920 virtual[last_virt + 1] = '\0'; 1921 gencpy(u_space, virtual); 1922 u_column = cur_virt; 1923 } 1924 return; 1925 } 1926 1927 /*{ SEARCH( mode ) 1928 * 1929 * Search history file for regular expression. 1930 * 1931 * mode = '/' require search string and search new to old 1932 * mode = '?' require search string and search old to new 1933 * mode = 'N' repeat last search in reverse direction 1934 * mode = 'n' repeat last search 1935 * 1936 }*/ 1937 1938 static int 1939 search(mode) 1940 register char mode; 1941 { 1942 register int new_direction; 1943 register int oldcurhline; 1944 static int direction = -1; 1945 histloc location; 1946 1947 if( mode == '/' || mode == '?' ) 1948 { 1949 /*** new search expression ***/ 1950 del_line(BAD); 1951 append(mode, APPEND); 1952 refresh(INPUT); 1953 first_virt = 1; 1954 getline(SEARCH); 1955 first_virt = 0; 1956 virtual[last_virt + 1] = '\0'; /*** make null terminated ***/ 1957 direction = mode=='/' ? -1 : 1; 1958 } 1959 1960 if( cur_virt == INVALID ) 1961 { 1962 /*** no operation ***/ 1963 return(ABORT); 1964 } 1965 1966 if( cur_virt==0 || fold(mode)=='N' ) 1967 { 1968 /*** user wants repeat of last search ***/ 1969 del_line(BAD); 1970 strcpy( ((char*)virtual)+1, lsearch); 1971 #ifdef MULTIBYTE 1972 *((char*)virtual) = '/'; 1973 e_internal((char*)virtual,virtual); 1974 #endif /* MULTIBYTE */ 1975 } 1976 1977 if( mode == 'N' ) 1978 new_direction = -direction; 1979 else 1980 new_direction = direction; 1981 1982 if( new_direction==1 && curhline >= histmax ) 1983 curhline = histmin + 1; 1984 1985 /*** now search ***/ 1986 1987 oldcurhline = curhline; 1988 #ifdef MULTIBYTE 1989 e_external(virtual,(char*)virtual); 1990 #endif /* MULTIBYTE */ 1991 location = hist_find(((char*)virtual)+1, curhline, 1, new_direction); 1992 if( (curhline=location.his_command) >=0 ) 1993 { 1994 strcpy(lsearch, ((char*)virtual)+1); 1995 return(GOOD); 1996 } 1997 1998 /*** could not find matching line ***/ 1999 2000 curhline = oldcurhline; 2001 return(BAD); 2002 } 2003 2004 /*{ SYNC_CURSOR() 2005 * 2006 * This routine will move the physical cursor to the same 2007 * column as the virtual cursor. 2008 * 2009 }*/ 2010 2011 static int 2012 sync_cursor() 2013 { 2014 register int p; 2015 register int v; 2016 register int c; 2017 int new_phys; 2018 2019 if( cur_virt == INVALID ) 2020 return; 2021 2022 /*** find physical col that corresponds to virtual col ***/ 2023 2024 new_phys = 0; 2025 if( first_wind == ofirst_wind && cur_virt > ocur_virt ) 2026 { 2027 /*** try to optimize search a little ***/ 2028 p = ocur_phys + 1; 2029 #ifdef MULTIBYTE 2030 while(physical[p]==MARKER) 2031 p++; 2032 #endif /* MULTIBYTE */ 2033 v = ocur_virt + 1; 2034 } 2035 else 2036 { 2037 p = 0; 2038 v = 0; 2039 } 2040 for(; v <= last_virt; ++p, ++v) 2041 { 2042 #ifdef MULTIBYTE 2043 int d; 2044 c = virtual[v]; 2045 if(d = icharset(c)) 2046 { 2047 if( v != cur_virt ) 2048 p += (out_csize(d)-1); 2049 } 2050 else 2051 #else 2052 c = virtual[v]; 2053 #endif /* MULTIBYTE */ 2054 if( !isprint(c) ) 2055 { 2056 if( c == '\t' ) 2057 { 2058 p = (p + 8) & ~07; 2059 --p; 2060 } 2061 else 2062 { 2063 ++p; 2064 } 2065 } 2066 if( v == cur_virt ) 2067 { 2068 new_phys = p; 2069 break; 2070 } 2071 } 2072 2073 if( new_phys < first_wind || new_phys >= first_wind + w_size ) 2074 { 2075 /*** asked to move outside of window ***/ 2076 2077 window[0] = '\0'; 2078 refresh(CONTROL); 2079 return; 2080 } 2081 2082 cursor(new_phys); 2083 e_flush(); 2084 ocur_phys = cur_phys; 2085 ocur_virt = cur_virt; 2086 o_v_char = virtual[ocur_virt]; 2087 2088 return; 2089 } 2090 2091 /*{ TEXTMOD( command, mode ) 2092 * 2093 * Modify text operations. 2094 * 2095 * mode != 0, repeat previous operation 2096 * 2097 }*/ 2098 2099 static int 2100 textmod(c, mode) 2101 register int c; 2102 int mode; 2103 { 2104 register int i; 2105 register genchar *p = lastline; 2106 register int trepeat = repeat; 2107 genchar *savep; 2108 2109 if(mode && (fold(lastmotion)=='F' || fold(lastmotion)=='T')) 2110 lastmotion = ';'; 2111 2112 if( fold(c) == 'P' ) 2113 { 2114 /*** change p from lastline to yankbuf ***/ 2115 p = yankbuf; 2116 } 2117 2118 addin: 2119 switch( c ) 2120 { 2121 /***** Input commands *****/ 2122 2123 #ifdef KSHELL 2124 case '*': /** do file name expansion in place **/ 2125 if( cur_virt == INVALID ) 2126 return(BAD); 2127 case '=': /** list file name expansions **/ 2128 save_v(); 2129 i = last_virt; 2130 ++last_virt; 2131 virtual[last_virt] = 0; 2132 if( q_expand((char*)virtual, &cur_virt, &last_virt, 0, c) ) 2133 { 2134 last_virt = i; 2135 bell; 2136 } 2137 else if(c == '=') 2138 { 2139 last_virt = i; 2140 nonewline++; 2141 ungetchar(cntl(L)); 2142 return(GOOD); 2143 } 2144 else 2145 { 2146 --cur_virt; 2147 --last_virt; 2148 ocur_virt = MAXCHAR; 2149 return(APPEND); 2150 } 2151 break; 2152 2153 case '@': /** macro expansion **/ 2154 if( mode ) 2155 c = *p; 2156 else 2157 c = getchar(); 2158 *p = c; 2159 if(e_macro(c)) 2160 { 2161 save_v(); 2162 inmacro++; 2163 return(GOOD); 2164 } 2165 bell; 2166 return(BAD); 2167 2168 #endif /* KSHELL */ 2169 case '_': /** append last argument of prev command **/ 2170 save_v(); 2171 { 2172 genchar tmpbuf[MAXLINE]; 2173 if(repeat_set==0) 2174 repeat = -1; 2175 p = (genchar*)hist_word(tmpbuf,repeat); 2176 #ifndef KSHELL 2177 if(p==NULL) 2178 { 2179 bell; 2180 break; 2181 } 2182 #endif /* KSHELL */ 2183 #ifdef MULTIBYTE 2184 e_internal((char*)p,tmpbuf); 2185 p = tmpbuf; 2186 #endif /* MULTIBYTE */ 2187 i = ' '; 2188 do 2189 { 2190 append(i,APPEND); 2191 } 2192 while(i = *p++); 2193 return(APPEND); 2194 } 2195 2196 case 'A': /** append to end of line **/ 2197 cur_virt = last_virt; 2198 sync_cursor(); 2199 2200 case 'a': /** append **/ 2201 if( fold(mode) == 'A' ) 2202 { 2203 c = 'p'; 2204 goto addin; 2205 } 2206 save_v(); 2207 if( cur_virt != INVALID ) 2208 { 2209 first_virt = cur_virt + 1; 2210 cursor(cur_phys + 1); 2211 e_flush(); 2212 } 2213 return(APPEND); 2214 2215 case 'I': /** insert at beginning of line **/ 2216 cur_virt = first_virt; 2217 sync_cursor(); 2218 2219 case 'i': /** insert **/ 2220 if( fold(mode) == 'I' ) 2221 { 2222 c = 'P'; 2223 goto addin; 2224 } 2225 save_v(); 2226 if( cur_virt != INVALID ) 2227 first_virt = cur_virt--; 2228 return(INSERT); 2229 2230 case 'C': /** change to eol **/ 2231 c = '$'; 2232 goto chgeol; 2233 2234 case 'c': /** change **/ 2235 if( mode ) 2236 c = lastmotion; 2237 else 2238 c = getcount(getchar()); 2239 chgeol: 2240 lastmotion = c; 2241 if( c == 'c' ) 2242 { 2243 del_line(GOOD); 2244 return(APPEND); 2245 } 2246 2247 if( delmotion(c, 'c') == BAD ) 2248 return(BAD); 2249 2250 if( mode == 'c' ) 2251 { 2252 c = 'p'; 2253 trepeat = 1; 2254 goto addin; 2255 } 2256 first_virt = cur_virt + 1; 2257 return(APPEND); 2258 2259 case 'D': /** delete to eol **/ 2260 c = '$'; 2261 goto deleol; 2262 2263 case 'd': /** delete **/ 2264 if( mode ) 2265 c = lastmotion; 2266 else 2267 c = getcount(getchar()); 2268 deleol: 2269 lastmotion = c; 2270 if( c == 'd' ) 2271 { 2272 del_line(GOOD); 2273 break; 2274 } 2275 if( delmotion(c, 'd') == BAD ) 2276 return(BAD); 2277 if( cur_virt < last_virt ) 2278 ++cur_virt; 2279 break; 2280 2281 case 'P': 2282 if( p[0] == '\0' ) 2283 return(BAD); 2284 if( cur_virt != INVALID ) 2285 --cur_virt; 2286 2287 case 'p': /** print **/ 2288 if( p[0] == '\0' ) 2289 return(BAD); 2290 2291 if( mode != 's' && mode != 'c' ) 2292 { 2293 save_v(); 2294 if( c == 'P' ) 2295 { 2296 /*** fix stored cur_virt ***/ 2297 ++u_column; 2298 } 2299 } 2300 if( mode == 'R' ) 2301 mode = REPLACE; 2302 else 2303 mode = APPEND; 2304 savep = p; 2305 for(i=0; i<trepeat; ++i) 2306 { 2307 while(c= *p++) 2308 append(c,APPEND); 2309 p = savep; 2310 } 2311 break; 2312 2313 case 'R': /* Replace many chars **/ 2314 if( mode == 'R' ) 2315 { 2316 c = 'P'; 2317 goto addin; 2318 } 2319 save_v(); 2320 if( cur_virt != INVALID ) 2321 first_virt = cur_virt; 2322 return(REPLACE); 2323 2324 case 'r': /** replace **/ 2325 if( mode ) 2326 c = *p; 2327 else 2328 c = getchar(); 2329 *p = c; 2330 save_v(); 2331 replace(c, FALSE); 2332 return(GOOD); 2333 2334 case 'S': /** Substitute line - cc **/ 2335 c = 'c'; 2336 goto chgeol; 2337 2338 case 's': /** substitute **/ 2339 save_v(); 2340 delete(repeat, BAD); 2341 if( mode ) 2342 { 2343 c = 'p'; 2344 trepeat = 1; 2345 goto addin; 2346 } 2347 first_virt = cur_virt + 1; 2348 return(APPEND); 2349 2350 case 'Y': /** Yank to end of line **/ 2351 c = '$'; 2352 goto yankeol; 2353 2354 case 'y': /** yank thru motion **/ 2355 if( mode ) 2356 c = lastmotion; 2357 else 2358 c = getcount(getchar()); 2359 yankeol: 2360 lastmotion = c; 2361 if( c == 'y' ) 2362 { 2363 gencpy(yankbuf, virtual); 2364 } 2365 else if( delmotion(c, 'y') == BAD ) 2366 { 2367 return(BAD); 2368 } 2369 break; 2370 2371 case 'x': /** delete repeat chars forward - dl **/ 2372 c = 'l'; 2373 goto deleol; 2374 2375 case 'X': /** delete repeat chars backward - dh **/ 2376 c = 'h'; 2377 goto deleol; 2378 2379 case '~': /** invert case and advance **/ 2380 if( cur_virt != INVALID ) 2381 { 2382 save_v(); 2383 c = virtual[cur_virt]; 2384 #ifdef MULTIBYTE 2385 if((c&~STRIP)==0) 2386 #endif /* MULTIBYTE */ 2387 if( isupper(c) ) 2388 c = tolower(c); 2389 else if( islower(c) ) 2390 c = toupper(c); 2391 replace(c, TRUE); 2392 return(GOOD); 2393 } 2394 else 2395 return(BAD); 2396 2397 default: 2398 return(BAD); 2399 } 2400 refresh(CONTROL); 2401 return(GOOD); 2402 } 2403 2404 #ifdef INT16 2405 2406 /* making these functions reduces the size of the text region */ 2407 2408 int isalph(c) 2409 register int c; 2410 { 2411 register int v = virtual[c]; 2412 return(isalnum(v)); 2413 } 2414 2415 2416 int isblank(c) 2417 register int c; 2418 { 2419 register int v = virtual[c]; 2420 return(isspace(v)); 2421 } 2422 2423 int ismetach(c) 2424 register int c; 2425 { 2426 register int v = virtual[c]; 2427 return(ismeta(v)); 2428 } 2429 2430 #endif /* INT16 */ 2431 2432 2433 #ifdef MULTIBYTE 2434 int isalph(c) 2435 register int c; 2436 { 2437 register int v = virtual[c]; 2438 return((v&~STRIP) || isalnum(v)); 2439 } 2440 2441 2442 int isblank(c) 2443 register int c; 2444 { 2445 register int v = virtual[c]; 2446 return((v&~STRIP)==0 && isspace(v)); 2447 } 2448 2449 int ismetach(c) 2450 register int c; 2451 { 2452 register int v = virtual[c]; 2453 return((v&~STRIP)==0 && ismeta(v)); 2454 } 2455 2456 #endif /* MULTIBYTE */ 2457