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