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