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