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