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