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