1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1982-2008 AT&T Intellectual Property * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Intellectual Property * 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 if(vp->ed->e_multiline) 560 { 561 cur_virt = last_virt; 562 sync_cursor(vp); 563 } 564 virtual[0] = '\0'; 565 tty_cooked(ERRIO); 566 567 switch(i) 568 { 569 case UEOF: 570 /*** EOF ***/ 571 return(0); 572 573 case UINTR: 574 /** interrupt **/ 575 return(-1); 576 } 577 return(-1); 578 } 579 580 /*** Get a line from the terminal ***/ 581 582 vp->U_saved = 0; 583 if(reedit) 584 refresh(vp,INPUT); 585 if(viraw) 586 getline(vp,APPEND); 587 else if(last_virt>=0 && virtual[last_virt]==term_char) 588 getline(vp,APPEND); 589 else 590 getline(vp,ESC); 591 if(vp->ed->e_multiline) 592 cursor(vp, last_phys); 593 /*** add a new line if user typed unescaped \n ***/ 594 /* to cause the shell to process the line */ 595 tty_cooked(ERRIO); 596 if(ed->e_nlist) 597 { 598 ed->e_nlist = 0; 599 stakset(ed->e_stkptr,ed->e_stkoff); 600 } 601 if( vp->addnl ) 602 { 603 virtual[++last_virt] = '\n'; 604 ed_crlf(vp->ed); 605 } 606 if( ++last_virt >= 0 ) 607 { 608 #if SHOPT_MULTIBYTE 609 if(vp->bigvi) 610 { 611 vp->bigvi = 0; 612 shbuf[last_virt-1] = '\n'; 613 } 614 else 615 { 616 virtual[last_virt] = 0; 617 last_virt = ed_external(virtual,shbuf); 618 } 619 #endif /* SHOPT_MULTIBYTE */ 620 return(last_virt); 621 } 622 else 623 return(-1); 624 } 625 626 627 /*{ APPEND( char, mode ) 628 * 629 * This routine will append char after cur_virt in the virtual image. 630 * mode = APPEND, shift chars right before appending 631 * REPLACE, replace char if possible 632 * 633 }*/ 634 635 static void append(Vi_t *vp,int c, int mode) 636 { 637 register int i,j; 638 639 if( last_virt<max_col && last_phys<max_col ) 640 { 641 if( mode==APPEND || (cur_virt==last_virt && last_virt>=0)) 642 { 643 j = (cur_virt>=0?cur_virt:0); 644 for(i = ++last_virt; i > j; --i) 645 virtual[i] = virtual[i-1]; 646 } 647 virtual[++cur_virt] = c; 648 } 649 else 650 ed_ringbell(); 651 return; 652 } 653 654 /*{ BACKWORD( nwords, cmd ) 655 * 656 * This routine will position cur_virt at the nth previous word. 657 * 658 }*/ 659 660 static void backword(Vi_t *vp,int nwords, register int cmd) 661 { 662 register int tcur_virt = cur_virt; 663 while( nwords-- && tcur_virt > first_virt ) 664 { 665 if( !isblank(tcur_virt) && isblank(tcur_virt-1) 666 && tcur_virt>first_virt ) 667 --tcur_virt; 668 else if(cmd != 'B') 669 { 670 register int last = isalph(tcur_virt-1); 671 register int cur = isalph(tcur_virt); 672 if((!cur && last) || (cur && !last)) 673 --tcur_virt; 674 } 675 while( isblank(tcur_virt) && tcur_virt>=first_virt ) 676 --tcur_virt; 677 if( cmd == 'B' ) 678 { 679 while( !isblank(tcur_virt) && tcur_virt>=first_virt ) 680 --tcur_virt; 681 } 682 else 683 { 684 if(isalph(tcur_virt)) 685 while( isalph(tcur_virt) && tcur_virt>=first_virt ) 686 --tcur_virt; 687 else 688 while( !isalph(tcur_virt) && !isblank(tcur_virt) 689 && tcur_virt>=first_virt ) 690 --tcur_virt; 691 } 692 cur_virt = ++tcur_virt; 693 } 694 return; 695 } 696 697 /*{ CNTLMODE() 698 * 699 * This routine implements the vi command subset. 700 * The cursor will always be positioned at the char of interest. 701 * 702 }*/ 703 704 static int cntlmode(Vi_t *vp) 705 { 706 register int c; 707 register int i; 708 genchar tmp_u_space[MAXLINE]; /* temporary u_space */ 709 genchar *real_u_space; /* points to real u_space */ 710 int tmp_u_column = INVALID; /* temporary u_column */ 711 int was_inmacro; 712 713 if(!vp->U_saved) 714 { 715 /*** save virtual image if never done before ***/ 716 virtual[last_virt+1] = '\0'; 717 gencpy(vp->U_space, virtual); 718 vp->U_saved = 1; 719 } 720 721 save_last(vp); 722 723 real_u_space = vp->u_space; 724 curhline = histmax; 725 first_virt = 0; 726 vp->repeat = 1; 727 if( cur_virt > INVALID ) 728 { 729 /*** make sure cursor is at the last char ***/ 730 sync_cursor(vp); 731 } 732 733 /*** Read control char until something happens to cause a ***/ 734 /* return to APPEND/REPLACE mode */ 735 736 while( c=ed_getchar(vp->ed,-1) ) 737 { 738 vp->repeat_set = 0; 739 was_inmacro = inmacro; 740 if( c == '0' ) 741 { 742 /*** move to leftmost column ***/ 743 cur_virt = 0; 744 sync_cursor(vp); 745 continue; 746 } 747 748 if( digit(c) ) 749 { 750 c = getcount(vp,c); 751 if( c == '.' ) 752 vp->lastrepeat = vp->repeat; 753 } 754 755 /*** see if it's a move cursor command ***/ 756 757 if(mvcursor(vp,c)) 758 { 759 sync_cursor(vp); 760 vp->repeat = 1; 761 continue; 762 } 763 764 /*** see if it's a repeat of the last command ***/ 765 766 if( c == '.' ) 767 { 768 c = vp->last_cmd; 769 vp->repeat = vp->lastrepeat; 770 i = textmod(vp,c, c); 771 } 772 else 773 { 774 i = textmod(vp,c, 0); 775 } 776 777 /*** see if it's a text modification command ***/ 778 779 switch(i) 780 { 781 case BAD: 782 break; 783 784 default: /** input mode **/ 785 if(!was_inmacro) 786 { 787 vp->last_cmd = c; 788 vp->lastrepeat = vp->repeat; 789 } 790 vp->repeat = 1; 791 if( i == GOOD ) 792 continue; 793 return(i); 794 } 795 796 switch( c ) 797 { 798 /***** Other stuff *****/ 799 800 case cntl('L'): /** Redraw line **/ 801 /*** print the prompt and ***/ 802 /* force a total refresh */ 803 if(vp->nonewline==0 && !vp->ed->e_nocrnl) 804 putchar('\n'); 805 vp->nonewline = 0; 806 pr_string(vp,Prompt); 807 window[0] = '\0'; 808 cur_phys = vp->first_wind; 809 vp->ofirst_wind = INVALID; 810 vp->long_line = ' '; 811 break; 812 813 case cntl('V'): 814 { 815 register const char *p = fmtident(e_version); 816 save_v(vp); 817 del_line(vp,BAD); 818 while(c = *p++) 819 append(vp,c,APPEND); 820 refresh(vp,CONTROL); 821 ed_getchar(vp->ed,-1); 822 restore_v(vp); 823 break; 824 } 825 826 case '/': /** Search **/ 827 case '?': 828 case 'N': 829 case 'n': 830 save_v(vp); 831 switch( search(vp,c) ) 832 { 833 case GOOD: 834 /*** force a total refresh ***/ 835 window[0] = '\0'; 836 goto newhist; 837 838 case BAD: 839 /*** no match ***/ 840 ed_ringbell(); 841 842 default: 843 if( vp->u_column == INVALID ) 844 del_line(vp,BAD); 845 else 846 restore_v(vp); 847 break; 848 } 849 break; 850 851 case 'j': /** get next command **/ 852 case '+': /** get next command **/ 853 curhline += vp->repeat; 854 if( curhline > histmax ) 855 { 856 curhline = histmax; 857 goto ringbell; 858 } 859 else if(curhline==histmax && tmp_u_column!=INVALID ) 860 { 861 vp->u_space = tmp_u_space; 862 vp->u_column = tmp_u_column; 863 restore_v(vp); 864 vp->u_space = real_u_space; 865 break; 866 } 867 save_v(vp); 868 cur_virt = INVALID; 869 goto newhist; 870 871 case 'k': /** get previous command **/ 872 case '-': /** get previous command **/ 873 if( curhline == histmax ) 874 { 875 vp->u_space = tmp_u_space; 876 i = vp->u_column; 877 save_v(vp); 878 vp->u_space = real_u_space; 879 tmp_u_column = vp->u_column; 880 vp->u_column = i; 881 } 882 883 curhline -= vp->repeat; 884 if( curhline <= histmin ) 885 { 886 curhline += vp->repeat; 887 goto ringbell; 888 } 889 save_v(vp); 890 cur_virt = INVALID; 891 newhist: 892 if(curhline!=histmax || cur_virt==INVALID) 893 hist_copy((char*)virtual, MAXLINE, curhline,-1); 894 else 895 { 896 strcpy((char*)virtual,(char*)vp->u_space); 897 #if SHOPT_MULTIBYTE 898 ed_internal((char*)vp->u_space,vp->u_space); 899 #endif /* SHOPT_MULTIBYTE */ 900 } 901 #if SHOPT_MULTIBYTE 902 ed_internal((char*)virtual,virtual); 903 #endif /* SHOPT_MULTIBYTE */ 904 if((last_virt=genlen(virtual)-1) >= 0 && cur_virt == INVALID) 905 cur_virt = 0; 906 break; 907 908 909 case 'u': /** undo the last thing done **/ 910 restore_v(vp); 911 break; 912 913 case 'U': /** Undo everything **/ 914 save_v(vp); 915 if( virtual[0] == '\0' ) 916 goto ringbell; 917 else 918 { 919 gencpy(virtual, vp->U_space); 920 last_virt = genlen(vp->U_space) - 1; 921 cur_virt = 0; 922 } 923 break; 924 925 #if KSHELL 926 case 'v': 927 if(vp->repeat_set==0) 928 goto vcommand; 929 #endif /* KSHELL */ 930 931 case 'G': /** goto command repeat **/ 932 if(vp->repeat_set==0) 933 vp->repeat = histmin+1; 934 if( vp->repeat <= histmin || vp->repeat > histmax ) 935 { 936 goto ringbell; 937 } 938 curhline = vp->repeat; 939 save_v(vp); 940 if(c == 'G') 941 { 942 cur_virt = INVALID; 943 goto newhist; 944 } 945 946 #if KSHELL 947 vcommand: 948 if(ed_fulledit(vp->ed)==GOOD) 949 return(BIGVI); 950 else 951 goto ringbell; 952 #endif /* KSHELL */ 953 954 case '#': /** insert(delete) # to (no)comment command **/ 955 if( cur_virt != INVALID ) 956 { 957 register genchar *p = &virtual[last_virt+1]; 958 *p = 0; 959 /*** see whether first char is comment char ***/ 960 c = (virtual[0]=='#'); 961 while(p-- >= virtual) 962 { 963 if(*p=='\n' || p<virtual) 964 { 965 if(c) /* delete '#' */ 966 { 967 if(p[1]=='#') 968 { 969 last_virt--; 970 gencpy(p+1,p+2); 971 } 972 } 973 else 974 { 975 cur_virt = p-virtual; 976 append(vp,'#', APPEND); 977 } 978 } 979 } 980 if(c) 981 { 982 curhline = histmax; 983 cur_virt = 0; 984 break; 985 } 986 refresh(vp,INPUT); 987 } 988 989 case '\n': /** send to shell **/ 990 return(ENTER); 991 992 case ESC: 993 /* don't ring bell if next char is '[' */ 994 if(!lookahead) 995 { 996 char x; 997 if(sfpkrd(editb.e_fd,&x,1,'\r',400L,-1)>0) 998 ed_ungetchar(vp->ed,x); 999 } 1000 if(lookahead) 1001 { 1002 ed_ungetchar(vp->ed,c=ed_getchar(vp->ed,1)); 1003 if(c=='[') 1004 { 1005 vp->repeat = 1; 1006 continue; 1007 } 1008 } 1009 default: 1010 ringbell: 1011 ed_ringbell(); 1012 vp->repeat = 1; 1013 continue; 1014 } 1015 1016 refresh(vp,CONTROL); 1017 vp->repeat = 1; 1018 } 1019 /* NOTREACHED */ 1020 return(0); 1021 } 1022 1023 /*{ CURSOR( new_current_physical ) 1024 * 1025 * This routine will position the virtual cursor at 1026 * physical column x in the window. 1027 * 1028 }*/ 1029 1030 static void cursor(Vi_t *vp,register int x) 1031 { 1032 #if SHOPT_MULTIBYTE 1033 while(physical[x]==MARKER) 1034 x++; 1035 #endif /* SHOPT_MULTIBYTE */ 1036 cur_phys = ed_setcursor(vp->ed, physical, cur_phys,x,vp->first_wind); 1037 } 1038 1039 /*{ DELETE( nchars, mode ) 1040 * 1041 * Delete nchars from the virtual space and leave cur_virt positioned 1042 * at cur_virt-1. 1043 * 1044 * If mode = 'c', do not save the characters deleted 1045 * = 'd', save them in yankbuf and delete. 1046 * = 'y', save them in yankbuf but do not delete. 1047 * 1048 }*/ 1049 1050 static void cdelete(Vi_t *vp,register int nchars, int mode) 1051 { 1052 register int i; 1053 register genchar *cp; 1054 1055 if( cur_virt < first_virt ) 1056 { 1057 ed_ringbell(); 1058 return; 1059 } 1060 if( nchars > 0 ) 1061 { 1062 cp = virtual+cur_virt; 1063 vp->o_v_char = cp[0]; 1064 if( (cur_virt-- + nchars) > last_virt ) 1065 { 1066 /*** set nchars to number actually deleted ***/ 1067 nchars = last_virt - cur_virt; 1068 } 1069 1070 /*** save characters to be deleted ***/ 1071 1072 if( mode != 'c' ) 1073 { 1074 i = cp[nchars]; 1075 cp[nchars] = 0; 1076 gencpy(yankbuf,cp); 1077 cp[nchars] = i; 1078 } 1079 1080 /*** now delete these characters ***/ 1081 1082 if( mode != 'y' ) 1083 { 1084 gencpy(cp,cp+nchars); 1085 last_virt -= nchars; 1086 } 1087 } 1088 return; 1089 } 1090 1091 /*{ DEL_LINE( mode ) 1092 * 1093 * This routine will delete the line. 1094 * mode = GOOD, do a save_v() 1095 * 1096 }*/ 1097 static void del_line(register Vi_t *vp, int mode) 1098 { 1099 if( last_virt == INVALID ) 1100 return; 1101 1102 if( mode == GOOD ) 1103 save_v(vp); 1104 1105 cur_virt = 0; 1106 first_virt = 0; 1107 cdelete(vp,last_virt+1, BAD); 1108 refresh(vp,CONTROL); 1109 1110 cur_virt = INVALID; 1111 cur_phys = 0; 1112 vp->findchar = INVALID; 1113 last_phys = INVALID; 1114 last_virt = INVALID; 1115 vp->last_wind = INVALID; 1116 vp->first_wind = 0; 1117 vp->o_v_char = '\0'; 1118 vp->ocur_phys = 0; 1119 vp->ocur_virt = MAXCHAR; 1120 vp->ofirst_wind = 0; 1121 window[0] = '\0'; 1122 return; 1123 } 1124 1125 /*{ DELMOTION( motion, mode ) 1126 * 1127 * Delete thru motion. 1128 * 1129 * mode = 'd', save deleted characters, delete 1130 * = 'c', do not save characters, change 1131 * = 'y', save characters, yank 1132 * 1133 * Returns 1 if operation successful; else 0. 1134 * 1135 }*/ 1136 1137 static int delmotion(Vi_t *vp,int motion, int mode) 1138 { 1139 register int begin, end, delta; 1140 /* the following saves a register */ 1141 1142 if( cur_virt == INVALID ) 1143 return(0); 1144 if( mode != 'y' ) 1145 save_v(vp); 1146 begin = cur_virt; 1147 1148 /*** fake out the motion routines by appending a blank ***/ 1149 1150 virtual[++last_virt] = ' '; 1151 end = mvcursor(vp,motion); 1152 virtual[last_virt--] = 0; 1153 if(!end) 1154 return(0); 1155 1156 end = cur_virt; 1157 if( mode=='c' && end>begin && strchr("wW", motion) ) 1158 { 1159 /*** called by change operation, user really expects ***/ 1160 /* the effect of the eE commands, so back up to end of word */ 1161 while( end>begin && isblank(end-1) ) 1162 --end; 1163 if( end == begin ) 1164 ++end; 1165 } 1166 1167 delta = end - begin; 1168 if( delta >= 0 ) 1169 { 1170 cur_virt = begin; 1171 if( strchr("eE;,TtFf%", motion) ) 1172 ++delta; 1173 } 1174 else 1175 { 1176 delta = -delta + (motion=='%'); 1177 } 1178 1179 cdelete(vp,delta, mode); 1180 if( mode == 'y' ) 1181 cur_virt = begin; 1182 return(1); 1183 } 1184 1185 1186 /*{ ENDWORD( nwords, cmd ) 1187 * 1188 * This routine will move cur_virt to the end of the nth word. 1189 * 1190 }*/ 1191 1192 static void endword(Vi_t *vp, int nwords, register int cmd) 1193 { 1194 register int tcur_virt = cur_virt; 1195 while( nwords-- ) 1196 { 1197 if( !isblank(tcur_virt) && tcur_virt<=last_virt ) 1198 ++tcur_virt; 1199 while( isblank(tcur_virt) && tcur_virt<=last_virt ) 1200 ++tcur_virt; 1201 if( cmd == 'E' ) 1202 { 1203 while( !isblank(tcur_virt) && tcur_virt<=last_virt ) 1204 ++tcur_virt; 1205 } 1206 else 1207 { 1208 if( isalph(tcur_virt) ) 1209 while( isalph(tcur_virt) && tcur_virt<=last_virt ) 1210 ++tcur_virt; 1211 else 1212 while( !isalph(tcur_virt) && !isblank(tcur_virt) 1213 && tcur_virt<=last_virt ) 1214 ++tcur_virt; 1215 } 1216 if( tcur_virt > first_virt ) 1217 tcur_virt--; 1218 } 1219 cur_virt = tcur_virt; 1220 return; 1221 } 1222 1223 /*{ FORWARD( nwords, cmd ) 1224 * 1225 * This routine will move cur_virt forward to the next nth word. 1226 * 1227 }*/ 1228 1229 static void forward(Vi_t *vp,register int nwords, int cmd) 1230 { 1231 register int tcur_virt = cur_virt; 1232 while( nwords-- ) 1233 { 1234 if( cmd == 'W' ) 1235 { 1236 while( !isblank(tcur_virt) && tcur_virt < last_virt ) 1237 ++tcur_virt; 1238 } 1239 else 1240 { 1241 if( isalph(tcur_virt) ) 1242 { 1243 while( isalph(tcur_virt) && tcur_virt<last_virt ) 1244 ++tcur_virt; 1245 } 1246 else 1247 { 1248 while( !isalph(tcur_virt) && !isblank(tcur_virt) 1249 && tcur_virt < last_virt ) 1250 ++tcur_virt; 1251 } 1252 } 1253 while( isblank(tcur_virt) && tcur_virt < last_virt ) 1254 ++tcur_virt; 1255 } 1256 cur_virt = tcur_virt; 1257 return; 1258 } 1259 1260 1261 1262 /*{ GETCOUNT(c) 1263 * 1264 * Set repeat to the user typed number and return the terminating 1265 * character. 1266 * 1267 }*/ 1268 1269 static int getcount(register Vi_t *vp,register int c) 1270 { 1271 register int i; 1272 1273 /*** get any repeat count ***/ 1274 1275 if( c == '0' ) 1276 return(c); 1277 1278 vp->repeat_set++; 1279 i = 0; 1280 while( digit(c) ) 1281 { 1282 i = i*10 + c - '0'; 1283 c = ed_getchar(vp->ed,-1); 1284 } 1285 1286 if( i > 0 ) 1287 vp->repeat *= i; 1288 return(c); 1289 } 1290 1291 1292 /*{ GETLINE( mode ) 1293 * 1294 * This routine will fetch a line. 1295 * mode = APPEND, allow escape to cntlmode subroutine 1296 * appending characters. 1297 * = REPLACE, allow escape to cntlmode subroutine 1298 * replacing characters. 1299 * = SEARCH, no escape allowed 1300 * = ESC, enter control mode immediately 1301 * 1302 * The cursor will always be positioned after the last 1303 * char printed. 1304 * 1305 * This routine returns when cr, nl, or (eof in column 0) is 1306 * received (column 0 is the first char position). 1307 * 1308 }*/ 1309 1310 static void getline(register Vi_t* vp,register int mode) 1311 { 1312 register int c; 1313 register int tmp; 1314 int max_virt=0, last_save=0; 1315 genchar saveline[MAXLINE]; 1316 1317 vp->addnl = 1; 1318 1319 if( mode == ESC ) 1320 { 1321 /*** go directly to control mode ***/ 1322 goto escape; 1323 } 1324 1325 for(;;) 1326 { 1327 if( (c=ed_getchar(vp->ed,mode==SEARCH?1:-2)) == usreof ) 1328 c = UEOF; 1329 else if( c == usrerase ) 1330 c = UERASE; 1331 else if( c == usrkill ) 1332 c = UKILL; 1333 else if( c == editb.e_werase ) 1334 c = UWERASE; 1335 else if( c == usrlnext ) 1336 c = ULNEXT; 1337 1338 if( c == ULNEXT) 1339 { 1340 /*** implement ^V to escape next char ***/ 1341 c = ed_getchar(vp->ed,2); 1342 append(vp,c, mode); 1343 refresh(vp,INPUT); 1344 continue; 1345 } 1346 1347 switch( c ) 1348 { 1349 case ESC: /** enter control mode **/ 1350 if(!sh_isoption(SH_VI)) 1351 { 1352 append(vp,c, mode); 1353 break; 1354 } 1355 if( mode == SEARCH ) 1356 { 1357 ed_ringbell(); 1358 continue; 1359 } 1360 else 1361 { 1362 escape: 1363 if( mode == REPLACE ) 1364 { 1365 c = max_virt-cur_virt; 1366 if(c > 0 && last_save>=cur_virt) 1367 { 1368 genncpy((&virtual[cur_virt]),&saveline[cur_virt],c); 1369 if(last_virt>=last_save) 1370 last_virt=last_save-1; 1371 refresh(vp,INPUT); 1372 } 1373 --cur_virt; 1374 } 1375 tmp = cntlmode(vp); 1376 if( tmp == ENTER || tmp == BIGVI ) 1377 { 1378 #if SHOPT_MULTIBYTE 1379 vp->bigvi = (tmp==BIGVI); 1380 #endif /* SHOPT_MULTIBYTE */ 1381 return; 1382 } 1383 if( tmp == INSERT ) 1384 { 1385 mode = APPEND; 1386 continue; 1387 } 1388 mode = tmp; 1389 if(mode==REPLACE) 1390 { 1391 c = last_save = last_virt+1; 1392 if(c >= MAXLINE) 1393 c = MAXLINE-1; 1394 genncpy(saveline, virtual, c); 1395 } 1396 } 1397 break; 1398 1399 case UERASE: /** user erase char **/ 1400 /*** treat as backspace ***/ 1401 1402 case '\b': /** backspace **/ 1403 if( virtual[cur_virt] == '\\' ) 1404 { 1405 cdelete(vp,1, BAD); 1406 append(vp,usrerase, mode); 1407 } 1408 else 1409 { 1410 if( mode==SEARCH && cur_virt==0 ) 1411 { 1412 first_virt = 0; 1413 cdelete(vp,1, BAD); 1414 return; 1415 } 1416 if(mode==REPLACE || (last_save>0 && last_virt<=last_save)) 1417 { 1418 if(cur_virt<=first_virt) 1419 ed_ringbell(); 1420 else if(mode==REPLACE) 1421 --cur_virt; 1422 mode = REPLACE; 1423 sync_cursor(vp); 1424 continue; 1425 } 1426 else 1427 cdelete(vp,1, BAD); 1428 } 1429 break; 1430 1431 case UWERASE: /** delete back word **/ 1432 if( cur_virt > first_virt && 1433 !isblank(cur_virt) && 1434 !ispunct(virtual[cur_virt]) && 1435 isblank(cur_virt-1) ) 1436 { 1437 cdelete(vp,1, BAD); 1438 } 1439 else 1440 { 1441 tmp = cur_virt; 1442 backword(vp,1, 'W'); 1443 cdelete(vp,tmp - cur_virt + 1, BAD); 1444 } 1445 break; 1446 1447 case UKILL: /** user kill line char **/ 1448 if( virtual[cur_virt] == '\\' ) 1449 { 1450 cdelete(vp,1, BAD); 1451 append(vp,usrkill, mode); 1452 } 1453 else 1454 { 1455 if( mode == SEARCH ) 1456 { 1457 cur_virt = 1; 1458 delmotion(vp, '$', BAD); 1459 } 1460 else if(first_virt) 1461 { 1462 tmp = cur_virt; 1463 cur_virt = first_virt; 1464 cdelete(vp,tmp - cur_virt + 1, BAD); 1465 } 1466 else 1467 del_line(vp,GOOD); 1468 } 1469 break; 1470 1471 case UEOF: /** eof char **/ 1472 if( cur_virt != INVALID ) 1473 continue; 1474 vp->addnl = 0; 1475 1476 case '\n': /** newline or return **/ 1477 if( mode != SEARCH ) 1478 save_last(vp); 1479 refresh(vp,INPUT); 1480 last_phys++; 1481 return; 1482 1483 case '\t': /** command completion **/ 1484 if(mode!=SEARCH && last_virt>=0 && (vp->ed->e_tabcount|| !isblank(cur_virt)) && vp->ed->sh->nextprompt) 1485 { 1486 if(vp->ed->e_tabcount==0) 1487 { 1488 ed_ungetchar(vp->ed,'\\'); 1489 vp->ed->e_tabcount=1; 1490 goto escape; 1491 } 1492 else if(vp->ed->e_tabcount==1) 1493 { 1494 ed_ungetchar(vp->ed,'='); 1495 goto escape; 1496 } 1497 vp->ed->e_tabcount = 0; 1498 } 1499 /* FALL THRU*/ 1500 default: 1501 if( mode == REPLACE ) 1502 { 1503 if( cur_virt < last_virt ) 1504 { 1505 replace(vp,c, 1); 1506 if(cur_virt>max_virt) 1507 max_virt = cur_virt; 1508 continue; 1509 } 1510 cdelete(vp,1, BAD); 1511 mode = APPEND; 1512 max_virt = last_virt+3; 1513 } 1514 append(vp,c, mode); 1515 break; 1516 } 1517 refresh(vp,INPUT); 1518 1519 } 1520 } 1521 1522 /*{ MVCURSOR( motion ) 1523 * 1524 * This routine will move the virtual cursor according to motion 1525 * for repeat times. 1526 * 1527 * It returns GOOD if successful; else BAD. 1528 * 1529 }*/ 1530 1531 static int mvcursor(register Vi_t* vp,register int motion) 1532 { 1533 register int count; 1534 register int tcur_virt; 1535 register int incr = -1; 1536 register int bound = 0; 1537 1538 switch(motion) 1539 { 1540 /***** Cursor move commands *****/ 1541 1542 case '0': /** First column **/ 1543 tcur_virt = 0; 1544 break; 1545 1546 case '^': /** First nonblank character **/ 1547 tcur_virt = first_virt; 1548 while( isblank(tcur_virt) && tcur_virt < last_virt ) 1549 ++tcur_virt; 1550 break; 1551 1552 case '|': 1553 tcur_virt = vp->repeat-1; 1554 if(tcur_virt <= last_virt) 1555 break; 1556 /* fall through */ 1557 1558 case '$': /** End of line **/ 1559 tcur_virt = last_virt; 1560 break; 1561 1562 case '[': 1563 switch(motion=getcount(vp,ed_getchar(vp->ed,-1))) 1564 { 1565 case 'A': 1566 if(cur_virt>=0 && cur_virt<(SEARCHSIZE-2) && cur_virt == last_virt) 1567 { 1568 virtual[last_virt + 1] = '\0'; 1569 gencpy(&((genchar*)lsearch)[1], virtual); 1570 #if SHOPT_MULTIBYTE 1571 ed_external(&((genchar*)lsearch)[1],lsearch+1); 1572 #endif /* SHOPT_MULTIBYTE */ 1573 *lsearch = '^'; 1574 vp->direction = -2; 1575 ed_ungetchar(vp->ed,'n'); 1576 } 1577 else if(cur_virt==0 && vp->direction == -2) 1578 ed_ungetchar(vp->ed,'n'); 1579 else 1580 ed_ungetchar(vp->ed,'k'); 1581 return(1); 1582 case 'B': 1583 ed_ungetchar(vp->ed,'j'); 1584 return(1); 1585 case 'C': 1586 motion = last_virt; 1587 incr = 1; 1588 goto walk; 1589 case 'D': 1590 motion = first_virt; 1591 goto walk; 1592 case 'H': 1593 tcur_virt = 0; 1594 break; 1595 case 'Y': 1596 tcur_virt = last_virt; 1597 break; 1598 default: 1599 ed_ungetchar(vp->ed,motion); 1600 return(0); 1601 } 1602 break; 1603 1604 case 'h': /** Left one **/ 1605 case '\b': 1606 motion = first_virt; 1607 goto walk; 1608 1609 case ' ': 1610 case 'l': /** Right one **/ 1611 motion = last_virt; 1612 incr = 1; 1613 walk: 1614 tcur_virt = cur_virt; 1615 if( incr*tcur_virt < motion) 1616 { 1617 tcur_virt += vp->repeat*incr; 1618 if( incr*tcur_virt > motion) 1619 tcur_virt = motion; 1620 } 1621 else 1622 return(0); 1623 break; 1624 1625 case 'B': 1626 case 'b': /** back word **/ 1627 tcur_virt = cur_virt; 1628 backword(vp,vp->repeat, motion); 1629 if( cur_virt == tcur_virt ) 1630 return(0); 1631 return(1); 1632 1633 case 'E': 1634 case 'e': /** end of word **/ 1635 tcur_virt = cur_virt; 1636 if(tcur_virt >=0) 1637 endword(vp, vp->repeat, motion); 1638 if( cur_virt == tcur_virt ) 1639 return(0); 1640 return(1); 1641 1642 case ',': /** reverse find old char **/ 1643 case ';': /** find old char **/ 1644 switch(vp->last_find) 1645 { 1646 case 't': 1647 case 'f': 1648 if(motion==';') 1649 { 1650 bound = last_virt; 1651 incr = 1; 1652 } 1653 goto find_b; 1654 1655 case 'T': 1656 case 'F': 1657 if(motion==',') 1658 { 1659 bound = last_virt; 1660 incr = 1; 1661 } 1662 goto find_b; 1663 1664 default: 1665 return(0); 1666 } 1667 1668 1669 case 't': /** find up to new char forward **/ 1670 case 'f': /** find new char forward **/ 1671 bound = last_virt; 1672 incr = 1; 1673 1674 case 'T': /** find up to new char backward **/ 1675 case 'F': /** find new char backward **/ 1676 vp->last_find = motion; 1677 if((vp->findchar=getrchar(vp))==ESC) 1678 return(1); 1679 find_b: 1680 tcur_virt = cur_virt; 1681 count = vp->repeat; 1682 while( count-- ) 1683 { 1684 while( incr*(tcur_virt+=incr) <= bound 1685 && virtual[tcur_virt] != vp->findchar ); 1686 if( incr*tcur_virt > bound ) 1687 { 1688 return(0); 1689 } 1690 } 1691 if( fold(vp->last_find) == 'T' ) 1692 tcur_virt -= incr; 1693 break; 1694 1695 case '%': 1696 { 1697 int nextmotion; 1698 int nextc; 1699 tcur_virt = cur_virt; 1700 while( tcur_virt <= last_virt 1701 && strchr(paren_chars,virtual[tcur_virt])==(char*)0) 1702 tcur_virt++; 1703 if(tcur_virt > last_virt ) 1704 return(0); 1705 nextc = virtual[tcur_virt]; 1706 count = strchr(paren_chars,nextc)-paren_chars; 1707 if(count < 3) 1708 { 1709 incr = 1; 1710 bound = last_virt; 1711 nextmotion = paren_chars[count+3]; 1712 } 1713 else 1714 nextmotion = paren_chars[count-3]; 1715 count = 1; 1716 while(count >0 && incr*(tcur_virt+=incr) <= bound) 1717 { 1718 if(virtual[tcur_virt] == nextmotion) 1719 count--; 1720 else if(virtual[tcur_virt]==nextc) 1721 count++; 1722 } 1723 if(count) 1724 return(0); 1725 break; 1726 } 1727 1728 case 'W': 1729 case 'w': /** forward word **/ 1730 tcur_virt = cur_virt; 1731 forward(vp,vp->repeat, motion); 1732 if( tcur_virt == cur_virt ) 1733 return(0); 1734 return(1); 1735 1736 default: 1737 return(0); 1738 } 1739 cur_virt = tcur_virt; 1740 1741 return(1); 1742 } 1743 1744 /* 1745 * print a string 1746 */ 1747 1748 static void pr_string(register Vi_t *vp, register const char *sp) 1749 { 1750 /*** copy string sp ***/ 1751 register char *ptr = editb.e_outptr; 1752 while(*sp) 1753 *ptr++ = *sp++; 1754 editb.e_outptr = ptr; 1755 return; 1756 } 1757 1758 /*{ PUTSTRING( column, nchars ) 1759 * 1760 * Put nchars starting at column of physical into the workspace 1761 * to be printed. 1762 * 1763 }*/ 1764 1765 static void putstring(register Vi_t *vp,register int col, register int nchars) 1766 { 1767 while( nchars-- ) 1768 putchar(physical[col++]); 1769 return; 1770 } 1771 1772 /*{ REFRESH( mode ) 1773 * 1774 * This routine will refresh the crt so the physical image matches 1775 * the virtual image and display the proper window. 1776 * 1777 * mode = CONTROL, refresh in control mode, ie. leave cursor 1778 * positioned at last char printed. 1779 * = INPUT, refresh in input mode; leave cursor positioned 1780 * after last char printed. 1781 * = TRANSLATE, perform virtual to physical translation 1782 * and adjust left margin only. 1783 * 1784 * +-------------------------------+ 1785 * | | | virtual | | | 1786 * +-------------------------------+ 1787 * cur_virt last_virt 1788 * 1789 * +-----------------------------------------------+ 1790 * | | | physical | | | 1791 * +-----------------------------------------------+ 1792 * cur_phys last_phys 1793 * 1794 * 0 w_size - 1 1795 * +-----------------------+ 1796 * | | | window | 1797 * +-----------------------+ 1798 * cur_window = cur_phys - first_wind 1799 }*/ 1800 1801 static void refresh(register Vi_t* vp, int mode) 1802 { 1803 register int p; 1804 register int regb; 1805 register int first_w = vp->first_wind; 1806 int p_differ; 1807 int new_lw; 1808 int ncur_phys; 1809 int opflag; /* search optimize flag */ 1810 1811 # define w regb 1812 # define v regb 1813 1814 /*** find out if it's necessary to start translating at beginning ***/ 1815 1816 if(lookahead>0) 1817 { 1818 p = previous[lookahead-1]; 1819 if(p != ESC && p != '\n' && p != '\r') 1820 mode = TRANSLATE; 1821 } 1822 v = cur_virt; 1823 if( v<vp->ocur_virt || vp->ocur_virt==INVALID 1824 || ( v==vp->ocur_virt 1825 && (!is_print(virtual[v]) || !is_print(vp->o_v_char))) ) 1826 { 1827 opflag = 0; 1828 p = 0; 1829 v = 0; 1830 } 1831 else 1832 { 1833 opflag = 1; 1834 p = vp->ocur_phys; 1835 v = vp->ocur_virt; 1836 if( !is_print(virtual[v]) ) 1837 { 1838 /*** avoid double ^'s ***/ 1839 ++p; 1840 ++v; 1841 } 1842 } 1843 virtual[last_virt+1] = 0; 1844 ncur_phys = ed_virt_to_phys(vp->ed,virtual,physical,cur_virt,v,p); 1845 p = genlen(physical); 1846 if( --p < 0 ) 1847 last_phys = 0; 1848 else 1849 last_phys = p; 1850 1851 /*** see if this was a translate only ***/ 1852 1853 if( mode == TRANSLATE ) 1854 return; 1855 1856 /*** adjust left margin if necessary ***/ 1857 1858 if( ncur_phys<first_w || ncur_phys>=(first_w + w_size) ) 1859 { 1860 cursor(vp,first_w); 1861 first_w = ncur_phys - (w_size>>1); 1862 if( first_w < 0 ) 1863 first_w = 0; 1864 vp->first_wind = cur_phys = first_w; 1865 } 1866 1867 /*** attempt to optimize search somewhat to find ***/ 1868 /*** out where physical and window images differ ***/ 1869 1870 if( first_w==vp->ofirst_wind && ncur_phys>=vp->ocur_phys && opflag==1 ) 1871 { 1872 p = vp->ocur_phys; 1873 w = p - first_w; 1874 } 1875 else 1876 { 1877 p = first_w; 1878 w = 0; 1879 } 1880 1881 for(; (p<=last_phys && w<=vp->last_wind); ++p, ++w) 1882 { 1883 if( window[w] != physical[p] ) 1884 break; 1885 } 1886 p_differ = p; 1887 1888 if( (p>last_phys || p>=first_w+w_size) && w>vp->last_wind 1889 && cur_virt==vp->ocur_virt ) 1890 { 1891 /*** images are identical ***/ 1892 return; 1893 } 1894 1895 /*** copy the physical image to the window image ***/ 1896 1897 if( last_virt != INVALID ) 1898 { 1899 while( p <= last_phys && w < w_size ) 1900 window[w++] = physical[p++]; 1901 } 1902 new_lw = w; 1903 1904 /*** erase trailing characters if needed ***/ 1905 1906 while( w <= vp->last_wind ) 1907 window[w++] = ' '; 1908 vp->last_wind = --w; 1909 1910 p = p_differ; 1911 1912 /*** move cursor to start of difference ***/ 1913 1914 cursor(vp,p); 1915 1916 /*** and output difference ***/ 1917 1918 w = p - first_w; 1919 while( w <= vp->last_wind ) 1920 putchar(window[w++]); 1921 1922 cur_phys = w + first_w; 1923 vp->last_wind = --new_lw; 1924 1925 if( last_phys >= w_size ) 1926 { 1927 if( first_w == 0 ) 1928 vp->long_char = '>'; 1929 else if( last_phys < (first_w+w_size) ) 1930 vp->long_char = '<'; 1931 else 1932 vp->long_char = '*'; 1933 } 1934 else 1935 vp->long_char = ' '; 1936 1937 if( vp->long_line != vp->long_char ) 1938 { 1939 /*** indicate lines longer than window ***/ 1940 while( w++ < w_size ) 1941 { 1942 putchar(' '); 1943 ++cur_phys; 1944 } 1945 putchar(vp->long_char); 1946 ++cur_phys; 1947 vp->long_line = vp->long_char; 1948 } 1949 1950 if(vp->ed->e_multiline && vp->ofirst_wind==INVALID && !vp->ed->e_nocrnl) 1951 ed_setcursor(vp->ed, physical, last_phys+1, last_phys+1, -1); 1952 vp->ed->e_nocrnl = 0; 1953 vp->ocur_phys = ncur_phys; 1954 vp->ocur_virt = cur_virt; 1955 vp->ofirst_wind = first_w; 1956 1957 if( mode==INPUT && cur_virt>INVALID ) 1958 ++ncur_phys; 1959 1960 cursor(vp,ncur_phys); 1961 ed_flush(vp->ed); 1962 return; 1963 } 1964 1965 /*{ REPLACE( char, increment ) 1966 * 1967 * Replace the cur_virt character with char. This routine attempts 1968 * to avoid using refresh(). 1969 * 1970 * increment = 1, increment cur_virt after replacement. 1971 * = 0, leave cur_virt where it is. 1972 * 1973 }*/ 1974 1975 static void replace(register Vi_t *vp, register int c, register int increment) 1976 { 1977 register int cur_window; 1978 1979 if( cur_virt == INVALID ) 1980 { 1981 /*** can't replace invalid cursor ***/ 1982 ed_ringbell(); 1983 return; 1984 } 1985 cur_window = cur_phys - vp->first_wind; 1986 if( vp->ocur_virt == INVALID || !is_print(c) 1987 || !is_print(virtual[cur_virt]) 1988 || !is_print(vp->o_v_char) 1989 #if SHOPT_MULTIBYTE 1990 || !iswascii(c) || mbwidth(vp->o_v_char)>1 1991 || !iswascii(virtual[cur_virt]) 1992 #endif /* SHOPT_MULTIBYTE */ 1993 || (increment && (cur_window==w_size-1) 1994 || !is_print(virtual[cur_virt+1])) ) 1995 { 1996 /*** must use standard refresh routine ***/ 1997 1998 cdelete(vp,1, BAD); 1999 append(vp,c, APPEND); 2000 if( increment && cur_virt<last_virt ) 2001 ++cur_virt; 2002 refresh(vp,CONTROL); 2003 } 2004 else 2005 { 2006 virtual[cur_virt] = c; 2007 physical[cur_phys] = c; 2008 window[cur_window] = c; 2009 putchar(c); 2010 if(increment) 2011 { 2012 c = virtual[++cur_virt]; 2013 ++cur_phys; 2014 } 2015 else 2016 { 2017 putchar('\b'); 2018 } 2019 vp->o_v_char = c; 2020 ed_flush(vp->ed); 2021 } 2022 return; 2023 } 2024 2025 /*{ RESTORE_V() 2026 * 2027 * Restore the contents of virtual space from u_space. 2028 * 2029 }*/ 2030 2031 static void restore_v(register Vi_t *vp) 2032 { 2033 register int tmpcol; 2034 genchar tmpspace[MAXLINE]; 2035 2036 if( vp->u_column == INVALID-1 ) 2037 { 2038 /*** never saved anything ***/ 2039 ed_ringbell(); 2040 return; 2041 } 2042 gencpy(tmpspace, vp->u_space); 2043 tmpcol = vp->u_column; 2044 save_v(vp); 2045 gencpy(virtual, tmpspace); 2046 cur_virt = tmpcol; 2047 last_virt = genlen(tmpspace) - 1; 2048 vp->ocur_virt = MAXCHAR; /** invalidate refresh optimization **/ 2049 return; 2050 } 2051 2052 /*{ SAVE_LAST() 2053 * 2054 * If the user has typed something, save it in last line. 2055 * 2056 }*/ 2057 2058 static void save_last(register Vi_t* vp) 2059 { 2060 register int i; 2061 2062 if( (i = cur_virt - first_virt + 1) > 0 ) 2063 { 2064 /*** save last thing user typed ***/ 2065 if(i >= MAXLINE) 2066 i = MAXLINE-1; 2067 genncpy(vp->lastline, (&virtual[first_virt]), i); 2068 vp->lastline[i] = '\0'; 2069 } 2070 return; 2071 } 2072 2073 /*{ SAVE_V() 2074 * 2075 * This routine will save the contents of virtual in u_space. 2076 * 2077 }*/ 2078 2079 static void save_v(register Vi_t *vp) 2080 { 2081 if(!inmacro) 2082 { 2083 virtual[last_virt + 1] = '\0'; 2084 gencpy(vp->u_space, virtual); 2085 vp->u_column = cur_virt; 2086 } 2087 return; 2088 } 2089 2090 /*{ SEARCH( mode ) 2091 * 2092 * Search history file for regular expression. 2093 * 2094 * mode = '/' require search string and search new to old 2095 * mode = '?' require search string and search old to new 2096 * mode = 'N' repeat last search in reverse direction 2097 * mode = 'n' repeat last search 2098 * 2099 }*/ 2100 2101 /* 2102 * search for <string> in the current command 2103 */ 2104 static int curline_search(Vi_t *vp, const char *string) 2105 { 2106 register int len=strlen(string); 2107 register const char *dp,*cp=string, *dpmax; 2108 #if SHOPT_MULTIBYTE 2109 ed_external(vp->u_space,(char*)vp->u_space); 2110 #endif /* SHOPT_MULTIBYTE */ 2111 for(dp=(char*)vp->u_space,dpmax=dp+strlen(dp)-len; dp<=dpmax; dp++) 2112 { 2113 if(*dp==*cp && memcmp(cp,dp,len)==0) 2114 return(dp-(char*)vp->u_space); 2115 } 2116 #if SHOPT_MULTIBYTE 2117 ed_internal((char*)vp->u_space,vp->u_space); 2118 #endif /* SHOPT_MULTIBYTE */ 2119 return(-1); 2120 } 2121 2122 static int search(register Vi_t* vp,register int mode) 2123 { 2124 register int new_direction; 2125 register int oldcurhline; 2126 register int i; 2127 Histloc_t location; 2128 2129 if( vp->direction == -2 && mode != 'n') 2130 vp->direction = -1; 2131 if( mode == '/' || mode == '?') 2132 { 2133 /*** new search expression ***/ 2134 del_line(vp,BAD); 2135 append(vp,mode, APPEND); 2136 refresh(vp,INPUT); 2137 first_virt = 1; 2138 getline(vp,SEARCH); 2139 first_virt = 0; 2140 virtual[last_virt + 1] = '\0'; /*** make null terminated ***/ 2141 vp->direction = mode=='/' ? -1 : 1; 2142 } 2143 2144 if( cur_virt == INVALID ) 2145 { 2146 /*** no operation ***/ 2147 return(ABORT); 2148 } 2149 2150 if( cur_virt==0 || fold(mode)=='N' ) 2151 { 2152 /*** user wants repeat of last search ***/ 2153 del_line(vp,BAD); 2154 strcpy( ((char*)virtual)+1, lsearch); 2155 #if SHOPT_MULTIBYTE 2156 *((char*)virtual) = '/'; 2157 ed_internal((char*)virtual,virtual); 2158 #endif /* SHOPT_MULTIBYTE */ 2159 } 2160 2161 if( mode == 'N' ) 2162 new_direction = -vp->direction; 2163 else 2164 new_direction = vp->direction; 2165 2166 2167 /*** now search ***/ 2168 2169 oldcurhline = curhline; 2170 #if SHOPT_MULTIBYTE 2171 ed_external(virtual,(char*)virtual); 2172 #endif /* SHOPT_MULTIBYTE */ 2173 if(mode=='?' && (i=curline_search(vp,((char*)virtual)+1))>=0) 2174 { 2175 location.hist_command = curhline; 2176 location.hist_char = i; 2177 } 2178 else 2179 { 2180 i = INVALID; 2181 if( new_direction==1 && curhline >= histmax ) 2182 curhline = histmin + 1; 2183 location = hist_find(sh.hist_ptr,((char*)virtual)+1, curhline, 1, new_direction); 2184 } 2185 cur_virt = i; 2186 strncpy(lsearch, ((char*)virtual)+1, SEARCHSIZE); 2187 if( (curhline=location.hist_command) >=0 ) 2188 { 2189 vp->ocur_virt = INVALID; 2190 return(GOOD); 2191 } 2192 2193 /*** could not find matching line ***/ 2194 2195 curhline = oldcurhline; 2196 return(BAD); 2197 } 2198 2199 /*{ SYNC_CURSOR() 2200 * 2201 * This routine will move the physical cursor to the same 2202 * column as the virtual cursor. 2203 * 2204 }*/ 2205 2206 static void sync_cursor(register Vi_t *vp) 2207 { 2208 register int p; 2209 register int v; 2210 register int c; 2211 int new_phys; 2212 2213 if( cur_virt == INVALID ) 2214 return; 2215 2216 /*** find physical col that corresponds to virtual col ***/ 2217 2218 new_phys = 0; 2219 if(vp->first_wind==vp->ofirst_wind && cur_virt>vp->ocur_virt && vp->ocur_virt!=INVALID) 2220 { 2221 /*** try to optimize search a little ***/ 2222 p = vp->ocur_phys + 1; 2223 #if SHOPT_MULTIBYTE 2224 while(physical[p]==MARKER) 2225 p++; 2226 #endif /* SHOPT_MULTIBYTE */ 2227 v = vp->ocur_virt + 1; 2228 } 2229 else 2230 { 2231 p = 0; 2232 v = 0; 2233 } 2234 for(; v <= last_virt; ++p, ++v) 2235 { 2236 #if SHOPT_MULTIBYTE 2237 int d; 2238 c = virtual[v]; 2239 if((d = mbwidth(c)) > 1) 2240 { 2241 if( v != cur_virt ) 2242 p += (d-1); 2243 } 2244 else if(!iswprint(c)) 2245 #else 2246 c = virtual[v]; 2247 if(!isprint(c)) 2248 #endif /* SHOPT_MULTIBYTE */ 2249 { 2250 if( c == '\t' ) 2251 { 2252 p -= ((p+editb.e_plen)%TABSIZE); 2253 p += (TABSIZE-1); 2254 } 2255 else 2256 { 2257 ++p; 2258 } 2259 } 2260 if( v == cur_virt ) 2261 { 2262 new_phys = p; 2263 break; 2264 } 2265 } 2266 2267 if( new_phys < vp->first_wind || new_phys >= vp->first_wind + w_size ) 2268 { 2269 /*** asked to move outside of window ***/ 2270 2271 window[0] = '\0'; 2272 refresh(vp,CONTROL); 2273 return; 2274 } 2275 2276 cursor(vp,new_phys); 2277 ed_flush(vp->ed); 2278 vp->ocur_phys = cur_phys; 2279 vp->ocur_virt = cur_virt; 2280 vp->o_v_char = virtual[vp->ocur_virt]; 2281 2282 return; 2283 } 2284 2285 /*{ TEXTMOD( command, mode ) 2286 * 2287 * Modify text operations. 2288 * 2289 * mode != 0, repeat previous operation 2290 * 2291 }*/ 2292 2293 static int textmod(register Vi_t *vp,register int c, int mode) 2294 { 2295 register int i; 2296 register genchar *p = vp->lastline; 2297 register int trepeat = vp->repeat; 2298 genchar *savep; 2299 2300 if(mode && (fold(vp->lastmotion)=='F' || fold(vp->lastmotion)=='T')) 2301 vp->lastmotion = ';'; 2302 2303 if( fold(c) == 'P' ) 2304 { 2305 /*** change p from lastline to yankbuf ***/ 2306 p = yankbuf; 2307 } 2308 2309 addin: 2310 switch( c ) 2311 { 2312 /***** Input commands *****/ 2313 2314 #if KSHELL 2315 case '\t': 2316 if(vp->ed->e_tabcount!=1) 2317 return(BAD); 2318 c = '='; 2319 case '*': /** do file name expansion in place **/ 2320 case '\\': /** do file name completion in place **/ 2321 if( cur_virt == INVALID ) 2322 return(BAD); 2323 case '=': /** list file name expansions **/ 2324 save_v(vp); 2325 i = last_virt; 2326 ++last_virt; 2327 mode = cur_virt-1; 2328 virtual[last_virt] = 0; 2329 if(ed_expand(vp->ed,(char*)virtual, &cur_virt, &last_virt, c, vp->repeat_set?vp->repeat:-1)<0) 2330 { 2331 if(vp->ed->e_tabcount) 2332 { 2333 vp->ed->e_tabcount=2; 2334 ed_ungetchar(vp->ed,'\t'); 2335 --last_virt; 2336 return(APPEND); 2337 } 2338 last_virt = i; 2339 ed_ringbell(); 2340 } 2341 else if(c == '=' && !vp->repeat_set) 2342 { 2343 last_virt = i; 2344 vp->nonewline++; 2345 ed_ungetchar(vp->ed,cntl('L')); 2346 return(GOOD); 2347 } 2348 else 2349 { 2350 --cur_virt; 2351 --last_virt; 2352 vp->ocur_virt = MAXCHAR; 2353 if(c=='=' || (mode<cur_virt && (virtual[cur_virt]==' ' || virtual[cur_virt]=='/'))) 2354 vp->ed->e_tabcount = 0; 2355 return(APPEND); 2356 } 2357 break; 2358 2359 case '@': /** macro expansion **/ 2360 if( mode ) 2361 c = vp->lastmacro; 2362 else 2363 if((c=getrchar(vp))==ESC) 2364 return(GOOD); 2365 if(!inmacro) 2366 vp->lastmacro = c; 2367 if(ed_macro(vp->ed,c)) 2368 { 2369 save_v(vp); 2370 inmacro++; 2371 return(GOOD); 2372 } 2373 ed_ringbell(); 2374 return(BAD); 2375 2376 #endif /* KSHELL */ 2377 case '_': /** append last argument of prev command **/ 2378 save_v(vp); 2379 { 2380 genchar tmpbuf[MAXLINE]; 2381 if(vp->repeat_set==0) 2382 vp->repeat = -1; 2383 p = (genchar*)hist_word((char*)tmpbuf,MAXLINE,vp->repeat); 2384 #if !KSHELL 2385 if(p==0) 2386 { 2387 ed_ringbell(); 2388 break; 2389 } 2390 #endif /* KSHELL */ 2391 #if SHOPT_MULTIBYTE 2392 ed_internal((char*)p,tmpbuf); 2393 p = tmpbuf; 2394 #endif /* SHOPT_MULTIBYTE */ 2395 i = ' '; 2396 do 2397 { 2398 append(vp,i,APPEND); 2399 } 2400 while(i = *p++); 2401 return(APPEND); 2402 } 2403 2404 case 'A': /** append to end of line **/ 2405 cur_virt = last_virt; 2406 sync_cursor(vp); 2407 2408 case 'a': /** append **/ 2409 if( fold(mode) == 'A' ) 2410 { 2411 c = 'p'; 2412 goto addin; 2413 } 2414 save_v(vp); 2415 if( cur_virt != INVALID ) 2416 { 2417 first_virt = cur_virt + 1; 2418 cursor(vp,cur_phys + 1); 2419 ed_flush(vp->ed); 2420 } 2421 return(APPEND); 2422 2423 case 'I': /** insert at beginning of line **/ 2424 cur_virt = first_virt; 2425 sync_cursor(vp); 2426 2427 case 'i': /** insert **/ 2428 if( fold(mode) == 'I' ) 2429 { 2430 c = 'P'; 2431 goto addin; 2432 } 2433 save_v(vp); 2434 if( cur_virt != INVALID ) 2435 { 2436 vp->o_v_char = virtual[cur_virt]; 2437 first_virt = cur_virt--; 2438 } 2439 return(INSERT); 2440 2441 case 'C': /** change to eol **/ 2442 c = '$'; 2443 goto chgeol; 2444 2445 case 'c': /** change **/ 2446 if( mode ) 2447 c = vp->lastmotion; 2448 else 2449 c = getcount(vp,ed_getchar(vp->ed,-1)); 2450 chgeol: 2451 vp->lastmotion = c; 2452 if( c == 'c' ) 2453 { 2454 del_line(vp,GOOD); 2455 return(APPEND); 2456 } 2457 2458 if(!delmotion(vp, c, 'c')) 2459 return(BAD); 2460 2461 if( mode == 'c' ) 2462 { 2463 c = 'p'; 2464 trepeat = 1; 2465 goto addin; 2466 } 2467 first_virt = cur_virt + 1; 2468 return(APPEND); 2469 2470 case 'D': /** delete to eol **/ 2471 c = '$'; 2472 goto deleol; 2473 2474 case 'd': /** delete **/ 2475 if( mode ) 2476 c = vp->lastmotion; 2477 else 2478 c = getcount(vp,ed_getchar(vp->ed,-1)); 2479 deleol: 2480 vp->lastmotion = c; 2481 if( c == 'd' ) 2482 { 2483 del_line(vp,GOOD); 2484 break; 2485 } 2486 if(!delmotion(vp, c, 'd')) 2487 return(BAD); 2488 if( cur_virt < last_virt ) 2489 ++cur_virt; 2490 break; 2491 2492 case 'P': 2493 if( p[0] == '\0' ) 2494 return(BAD); 2495 if( cur_virt != INVALID ) 2496 { 2497 i = virtual[cur_virt]; 2498 if(!is_print(i)) 2499 vp->ocur_virt = INVALID; 2500 --cur_virt; 2501 } 2502 2503 case 'p': /** print **/ 2504 if( p[0] == '\0' ) 2505 return(BAD); 2506 2507 if( mode != 's' && mode != 'c' ) 2508 { 2509 save_v(vp); 2510 if( c == 'P' ) 2511 { 2512 /*** fix stored cur_virt ***/ 2513 ++vp->u_column; 2514 } 2515 } 2516 if( mode == 'R' ) 2517 mode = REPLACE; 2518 else 2519 mode = APPEND; 2520 savep = p; 2521 for(i=0; i<trepeat; ++i) 2522 { 2523 while(c= *p++) 2524 append(vp,c,mode); 2525 p = savep; 2526 } 2527 break; 2528 2529 case 'R': /* Replace many chars **/ 2530 if( mode == 'R' ) 2531 { 2532 c = 'P'; 2533 goto addin; 2534 } 2535 save_v(vp); 2536 if( cur_virt != INVALID ) 2537 first_virt = cur_virt; 2538 return(REPLACE); 2539 2540 case 'r': /** replace **/ 2541 if( mode ) 2542 c = *p; 2543 else 2544 if((c=getrchar(vp))==ESC) 2545 return(GOOD); 2546 *p = c; 2547 save_v(vp); 2548 while(trepeat--) 2549 replace(vp,c, trepeat!=0); 2550 return(GOOD); 2551 2552 case 'S': /** Substitute line - cc **/ 2553 c = 'c'; 2554 goto chgeol; 2555 2556 case 's': /** substitute **/ 2557 save_v(vp); 2558 cdelete(vp,vp->repeat, BAD); 2559 if( mode ) 2560 { 2561 c = 'p'; 2562 trepeat = 1; 2563 goto addin; 2564 } 2565 first_virt = cur_virt + 1; 2566 return(APPEND); 2567 2568 case 'Y': /** Yank to end of line **/ 2569 c = '$'; 2570 goto yankeol; 2571 2572 case 'y': /** yank thru motion **/ 2573 if( mode ) 2574 c = vp->lastmotion; 2575 else 2576 c = getcount(vp,ed_getchar(vp->ed,-1)); 2577 yankeol: 2578 vp->lastmotion = c; 2579 if( c == 'y' ) 2580 { 2581 gencpy(yankbuf, virtual); 2582 } 2583 else if(!delmotion(vp, c, 'y')) 2584 { 2585 return(BAD); 2586 } 2587 break; 2588 2589 case 'x': /** delete repeat chars forward - dl **/ 2590 c = 'l'; 2591 goto deleol; 2592 2593 case 'X': /** delete repeat chars backward - dh **/ 2594 c = 'h'; 2595 goto deleol; 2596 2597 case '~': /** invert case and advance **/ 2598 if( cur_virt != INVALID ) 2599 { 2600 save_v(vp); 2601 i = INVALID; 2602 while(trepeat-->0 && i!=cur_virt) 2603 { 2604 i = cur_virt; 2605 c = virtual[cur_virt]; 2606 #if SHOPT_MULTIBYTE 2607 if((c&~STRIP)==0) 2608 #endif /* SHOPT_MULTIBYTE */ 2609 if( isupper(c) ) 2610 c = tolower(c); 2611 else if( islower(c) ) 2612 c = toupper(c); 2613 replace(vp,c, 1); 2614 } 2615 return(GOOD); 2616 } 2617 else 2618 return(BAD); 2619 2620 default: 2621 return(BAD); 2622 } 2623 refresh(vp,CONTROL); 2624 return(GOOD); 2625 } 2626 2627 2628 #if SHOPT_MULTIBYTE 2629 static int _isalph(register int v) 2630 { 2631 #ifdef _lib_iswalnum 2632 return(iswalnum(v) || v=='_'); 2633 #else 2634 return((v&~STRIP) || isalnum(v) || v=='_'); 2635 #endif 2636 } 2637 2638 2639 static int _isblank(register int v) 2640 { 2641 return((v&~STRIP)==0 && isspace(v)); 2642 } 2643 2644 static int _ismetach(register int v) 2645 { 2646 return((v&~STRIP)==0 && ismeta(v)); 2647 } 2648 2649 #endif /* SHOPT_MULTIBYTE */ 2650 2651 /* 2652 * get a character, after ^V processing 2653 */ 2654 static int getrchar(register Vi_t *vp) 2655 { 2656 register int c; 2657 if((c=ed_getchar(vp->ed,1))== usrlnext) 2658 c = ed_getchar(vp->ed,2); 2659 return(c); 2660 } 2661