1 /* $NetBSD: vi.c,v 1.6 2001/08/20 12:20:01 wiz Exp $ */ 2 3 /* 4 * vi command editing 5 * written by John Rochester (initially for nsh) 6 * bludgeoned to fit pdksh by Larry Bouzane, Jeff Sparkes & Eric Gisin 7 * 8 */ 9 #include "config.h" 10 #ifdef VI 11 12 #include "sh.h" 13 #include <ctype.h> 14 #include "ksh_stat.h" /* completion */ 15 #include "edit.h" 16 17 #define CMDLEN 1024 18 #define Ctrl(c) (c&0x1f) 19 #define is_wordch(c) (letnum(c)) 20 21 struct edstate { 22 int winleft; 23 char *cbuf; 24 int cbufsize; 25 int linelen; 26 int cursor; 27 }; 28 29 30 static int vi_hook ARGS((int ch)); 31 static void vi_reset ARGS((char *buf, size_t len)); 32 static int nextstate ARGS((int ch)); 33 static int vi_insert ARGS((int ch)); 34 static int vi_cmd ARGS((int argcnt, const char *cmd)); 35 static int domove ARGS((int argcnt, const char *cmd, int sub)); 36 static int redo_insert ARGS((int count)); 37 static void yank_range ARGS((int a, int b)); 38 static int bracktype ARGS((int ch)); 39 static void save_cbuf ARGS((void)); 40 static void restore_cbuf ARGS((void)); 41 static void edit_reset ARGS((char *buf, size_t len)); 42 static int putbuf ARGS((const char *buf, int len, int repl)); 43 static void del_range ARGS((int a, int b)); 44 static int findch ARGS((int ch, int cnt, int forw, int incl)); 45 static int forwword ARGS((int argcnt)); 46 static int backword ARGS((int argcnt)); 47 static int endword ARGS((int argcnt)); 48 static int Forwword ARGS((int argcnt)); 49 static int Backword ARGS((int argcnt)); 50 static int Endword ARGS((int argcnt)); 51 static int grabhist ARGS((int save, int n)); 52 static int grabsearch ARGS((int save, int start, int fwd, char *pat)); 53 static void redraw_line ARGS((int newline)); 54 static void refresh ARGS((int leftside)); 55 static int outofwin ARGS((void)); 56 static void rewindow ARGS((void)); 57 static int newcol ARGS((int ch, int col)); 58 static void display ARGS((char *wb1, char *wb2, int leftside)); 59 static void ed_mov_opt ARGS((int col, char *wb)); 60 static int expand_word ARGS((int command)); 61 static int complete_word ARGS((int command, int count)); 62 static int print_expansions ARGS((struct edstate *e, int command)); 63 static int char_len ARGS((int c)); 64 static void x_vi_zotc ARGS((int c)); 65 static void vi_pprompt ARGS((int full)); 66 static void vi_error ARGS((void)); 67 static void vi_macro_reset ARGS((void)); 68 static int x_vi_putbuf ARGS((const char *s, size_t len)); 69 70 #define C_ 0x1 /* a valid command that isn't a M_, E_, U_ */ 71 #define M_ 0x2 /* movement command (h, l, etc.) */ 72 #define E_ 0x4 /* extended command (c, d, y) */ 73 #define X_ 0x8 /* long command (@, f, F, t, T, etc.) */ 74 #define U_ 0x10 /* an UN-undoable command (that isn't a M_) */ 75 #define B_ 0x20 /* bad command (^@) */ 76 #define Z_ 0x40 /* repeat count defaults to 0 (not 1) */ 77 #define S_ 0x80 /* search (/, ?) */ 78 79 #define is_bad(c) (classify[(c)&0x7f]&B_) 80 #define is_cmd(c) (classify[(c)&0x7f]&(M_|E_|C_|U_)) 81 #define is_move(c) (classify[(c)&0x7f]&M_) 82 #define is_extend(c) (classify[(c)&0x7f]&E_) 83 #define is_long(c) (classify[(c)&0x7f]&X_) 84 #define is_undoable(c) (!(classify[(c)&0x7f]&U_)) 85 #define is_srch(c) (classify[(c)&0x7f]&S_) 86 #define is_zerocount(c) (classify[(c)&0x7f]&Z_) 87 88 const unsigned char classify[128] = { 89 /* 0 1 2 3 4 5 6 7 */ 90 /* 0 ^@ ^A ^B ^C ^D ^E ^F ^G */ 91 B_, 0, 0, 0, 0, C_|U_, C_|Z_, 0, 92 /* 01 ^H ^I ^J ^K ^L ^M ^N ^O */ 93 M_, C_|Z_, 0, 0, C_|U_, 0, C_, 0, 94 /* 02 ^P ^Q ^R ^S ^T ^U ^V ^W */ 95 C_, 0, C_|U_, 0, 0, 0, C_, 0, 96 /* 03 ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */ 97 C_, 0, 0, C_|Z_, 0, 0, 0, 0, 98 /* 04 <space> ! " # $ % & ' */ 99 M_, 0, 0, C_, M_, M_, 0, 0, 100 /* 05 ( ) * + , - . / */ 101 0, 0, C_, C_, M_, C_, 0, C_|S_, 102 /* 06 0 1 2 3 4 5 6 7 */ 103 M_, 0, 0, 0, 0, 0, 0, 0, 104 /* 07 8 9 : ; < = > ? */ 105 0, 0, 0, M_, 0, C_, 0, C_|S_, 106 /* 010 @ A B C D E F G */ 107 C_|X_, C_, M_, C_, C_, M_, M_|X_, C_|U_|Z_, 108 /* 011 H I J K L M N O */ 109 0, C_, 0, 0, 0, 0, C_|U_, 0, 110 /* 012 P Q R S T U V W */ 111 C_, 0, C_, C_, M_|X_, C_, 0, M_, 112 /* 013 X Y Z [ \ ] ^ _ */ 113 C_, C_|U_, 0, 0, C_|Z_, 0, M_, C_|Z_, 114 /* 014 ` a b c d e f g */ 115 0, C_, M_, E_, E_, M_, M_|X_, C_|Z_, 116 /* 015 h i j k l m n o */ 117 M_, C_, C_|U_, C_|U_, M_, 0, C_|U_, 0, 118 /* 016 p q r s t u v w */ 119 C_, 0, X_, C_, M_|X_, C_|U_, C_|U_|Z_,M_, 120 /* 017 x y z { | } ~ ^? */ 121 C_, E_|U_, 0, 0, M_|Z_, 0, C_, 0 122 }; 123 124 #define MAXVICMD 3 125 #define SRCHLEN 40 126 127 #define INSERT 1 128 #define REPLACE 2 129 130 #define VNORMAL 0 /* command, insert or replace mode */ 131 #define VARG1 1 /* digit prefix (first, eg, 5l) */ 132 #define VEXTCMD 2 /* cmd + movement (eg, cl) */ 133 #define VARG2 3 /* digit prefix (second, eg, 2c3l) */ 134 #define VXCH 4 /* f, F, t, T, @ */ 135 #define VFAIL 5 /* bad command */ 136 #define VCMD 6 /* single char command (eg, X) */ 137 #define VREDO 7 /* . */ 138 #define VLIT 8 /* ^V */ 139 #define VSEARCH 9 /* /, ? */ 140 #define VVERSION 10 /* <ESC> ^V */ 141 142 static char undocbuf[CMDLEN]; 143 144 static struct edstate *save_edstate ARGS((struct edstate *old)); 145 static void restore_edstate ARGS((struct edstate *old, struct edstate *new)); 146 static void free_edstate ARGS((struct edstate *old)); 147 148 static struct edstate ebuf; 149 static struct edstate undobuf = { 0, undocbuf, CMDLEN, 0, 0 }; 150 151 static struct edstate *es; /* current editor state */ 152 static struct edstate *undo; 153 154 static char ibuf[CMDLEN]; /* input buffer */ 155 static int first_insert; /* set when starting in insert mode */ 156 static int saved_inslen; /* saved inslen for first insert */ 157 static int inslen; /* length of input buffer */ 158 static int srchlen; /* length of current search pattern */ 159 static char ybuf[CMDLEN]; /* yank buffer */ 160 static int yanklen; /* length of yank buffer */ 161 static int fsavecmd = ' '; /* last find command */ 162 static int fsavech; /* character to find */ 163 static char lastcmd[MAXVICMD]; /* last non-move command */ 164 static int lastac; /* argcnt for lastcmd */ 165 static int lastsearch = ' '; /* last search command */ 166 static char srchpat[SRCHLEN]; /* last search pattern */ 167 static int insert; /* non-zero in insert mode */ 168 static int hnum; /* position in history */ 169 static int ohnum; /* history line copied (after mod) */ 170 static int hlast; /* 1 past last position in history */ 171 static int modified; /* buffer has been "modified" */ 172 static int state; 173 174 /* Information for keeping track of macros that are being expanded. 175 * The format of buf is the alias contents followed by a null byte followed 176 * by the name (letter) of the alias. The end of the buffer is marked by 177 * a double null. The name of the alias is stored so recursive macros can 178 * be detected. 179 */ 180 struct macro_state { 181 unsigned char *p; /* current position in buf */ 182 unsigned char *buf; /* pointer to macro(s) being expanded */ 183 int len; /* how much data in buffer */ 184 }; 185 static struct macro_state macro; 186 187 enum expand_mode { NONE, EXPAND, COMPLETE, PRINT }; 188 static enum expand_mode expanded = NONE;/* last input was expanded */ 189 190 int 191 x_vi(buf, len) 192 char *buf; 193 size_t len; 194 { 195 int c; 196 197 vi_reset(buf, len > CMDLEN ? CMDLEN : len); 198 vi_pprompt(1); 199 x_flush(); 200 while (1) { 201 if (macro.p) { 202 c = *macro.p++; 203 /* end of current macro? */ 204 if (!c) { 205 /* more macros left to finish? */ 206 if (*macro.p++) 207 continue; 208 /* must be the end of all the macros */ 209 vi_macro_reset(); 210 c = x_getc(); 211 } 212 } else { 213 c = x_getc(); 214 } 215 if (c == -1) 216 break; 217 if (state != VLIT) { 218 if (c == edchars.intr || c == edchars.quit) { 219 /* pretend we got an interrupt */ 220 x_vi_zotc(c); 221 x_flush(); 222 trapsig(c == edchars.intr ? SIGINT : SIGQUIT); 223 x_mode(FALSE); 224 unwind(LSHELL); 225 } else if (c == edchars.eof && state != VVERSION) { 226 if (es->linelen == 0) { 227 x_vi_zotc(edchars.eof); 228 c = -1; 229 break; 230 } 231 continue; 232 } 233 } 234 if (vi_hook(c)) 235 break; 236 x_flush(); 237 } 238 239 x_putc('\r'); x_putc('\n'); x_flush(); 240 241 if (c == -1) 242 return -1; 243 244 if (es->cbuf != buf) 245 memmove(buf, es->cbuf, es->linelen); 246 247 buf[es->linelen++] = '\n'; 248 249 return es->linelen; 250 } 251 252 static int 253 vi_hook(ch) 254 int ch; 255 { 256 static char curcmd[MAXVICMD]; 257 static char locpat[SRCHLEN]; 258 static int cmdlen; 259 static int argc1, argc2; 260 261 switch (state) { 262 263 case VNORMAL: 264 if (insert != 0) { 265 if (ch == Ctrl('v')) { 266 state = VLIT; 267 ch = '^'; 268 } 269 switch (vi_insert(ch)) { 270 case -1: 271 #ifdef OS2 272 /* Arrow keys generate 0xe0X, where X is H.. */ 273 state = VCMD; 274 argc1 = 1; 275 switch (x_getc()) { 276 case 'H': 277 *curcmd='k'; 278 break; 279 case 'K': 280 *curcmd='h'; 281 break; 282 case 'P': 283 *curcmd='j'; 284 break; 285 case 'M': 286 *curcmd='l'; 287 break; 288 default: 289 vi_error(); 290 state = VNORMAL; 291 } 292 break; 293 #else /* OS2 */ 294 vi_error(); 295 state = VNORMAL; 296 #endif /* OS2 */ 297 break; 298 case 0: 299 if (state == VLIT) { 300 es->cursor--; 301 refresh(0); 302 } else 303 refresh(insert != 0); 304 break; 305 case 1: 306 return 1; 307 } 308 } else { 309 if (ch == '\r' || ch == '\n') 310 return 1; 311 cmdlen = 0; 312 argc1 = 0; 313 if (ch >= '1' && ch <= '9') { 314 argc1 = ch - '0'; 315 state = VARG1; 316 } else { 317 curcmd[cmdlen++] = ch; 318 state = nextstate(ch); 319 if (state == VSEARCH) { 320 save_cbuf(); 321 es->cursor = 0; 322 es->linelen = 0; 323 if (ch == '/') { 324 if (putbuf("/", 1, 0) != 0) { 325 return -1; 326 } 327 } else if (putbuf("?", 1, 0) != 0) 328 return -1; 329 refresh(0); 330 } 331 if (state == VVERSION) { 332 save_cbuf(); 333 es->cursor = 0; 334 es->linelen = 0; 335 putbuf(ksh_version + 4, 336 strlen(ksh_version + 4), 0); 337 refresh(0); 338 } 339 } 340 } 341 break; 342 343 case VLIT: 344 if (is_bad(ch)) { 345 del_range(es->cursor, es->cursor + 1); 346 vi_error(); 347 } else 348 es->cbuf[es->cursor++] = ch; 349 refresh(1); 350 state = VNORMAL; 351 break; 352 353 case VVERSION: 354 restore_cbuf(); 355 state = VNORMAL; 356 refresh(0); 357 break; 358 359 case VARG1: 360 if (isdigit(ch)) 361 argc1 = argc1 * 10 + ch - '0'; 362 else { 363 curcmd[cmdlen++] = ch; 364 state = nextstate(ch); 365 } 366 break; 367 368 case VEXTCMD: 369 argc2 = 0; 370 if (ch >= '1' && ch <= '9') { 371 argc2 = ch - '0'; 372 state = VARG2; 373 return 0; 374 } else { 375 curcmd[cmdlen++] = ch; 376 if (ch == curcmd[0]) 377 state = VCMD; 378 else if (is_move(ch)) 379 state = nextstate(ch); 380 else 381 state = VFAIL; 382 } 383 break; 384 385 case VARG2: 386 if (isdigit(ch)) 387 argc2 = argc2 * 10 + ch - '0'; 388 else { 389 if (argc1 == 0) 390 argc1 = argc2; 391 else 392 argc1 *= argc2; 393 curcmd[cmdlen++] = ch; 394 if (ch == curcmd[0]) 395 state = VCMD; 396 else if (is_move(ch)) 397 state = nextstate(ch); 398 else 399 state = VFAIL; 400 } 401 break; 402 403 case VXCH: 404 if (ch == Ctrl('[')) 405 state = VNORMAL; 406 else { 407 curcmd[cmdlen++] = ch; 408 state = VCMD; 409 } 410 break; 411 412 case VSEARCH: 413 if (ch == '\r' || ch == '\n' /*|| ch == Ctrl('[')*/ ) { 414 restore_cbuf(); 415 /* Repeat last search? */ 416 if (srchlen == 0) { 417 if (!srchpat[0]) { 418 vi_error(); 419 state = VNORMAL; 420 refresh(0); 421 return 0; 422 } 423 } else { 424 locpat[srchlen] = '\0'; 425 (void) strcpy(srchpat, locpat); 426 } 427 state = VCMD; 428 } else if (ch == edchars.erase || ch == Ctrl('h')) { 429 if (srchlen != 0) { 430 srchlen--; 431 es->linelen -= char_len((unsigned char) locpat[srchlen]); 432 es->cursor = es->linelen; 433 refresh(0); 434 return 0; 435 } 436 restore_cbuf(); 437 state = VNORMAL; 438 refresh(0); 439 } else if (ch == edchars.kill) { 440 srchlen = 0; 441 es->linelen = 1; 442 es->cursor = 1; 443 refresh(0); 444 return 0; 445 } else if (ch == edchars.werase) { 446 int i; 447 int n = srchlen; 448 449 while (n > 0 && isspace((unsigned char)locpat[n - 1])) 450 n--; 451 while (n > 0 && !isspace((unsigned char)locpat[n - 1])) 452 n--; 453 for (i = srchlen; --i >= n; ) 454 es->linelen -= char_len((unsigned char) locpat[i]); 455 srchlen = n; 456 es->cursor = es->linelen; 457 refresh(0); 458 return 0; 459 } else { 460 if (srchlen == SRCHLEN - 1) 461 vi_error(); 462 else { 463 locpat[srchlen++] = ch; 464 if ((ch & 0x80) && Flag(FVISHOW8)) { 465 es->cbuf[es->linelen++] = 'M'; 466 es->cbuf[es->linelen++] = '-'; 467 ch &= 0x7f; 468 } 469 if (ch < ' ' || ch == 0x7f) { 470 es->cbuf[es->linelen++] = '^'; 471 es->cbuf[es->linelen++] = ch ^ '@'; 472 } else 473 es->cbuf[es->linelen++] = ch; 474 es->cursor = es->linelen; 475 refresh(0); 476 } 477 return 0; 478 } 479 break; 480 } 481 482 switch (state) { 483 case VCMD: 484 state = VNORMAL; 485 switch (vi_cmd(argc1, curcmd)) { 486 case -1: 487 vi_error(); 488 refresh(0); 489 break; 490 case 0: 491 if (insert != 0) 492 inslen = 0; 493 refresh(insert != 0); 494 break; 495 case 1: 496 refresh(0); 497 return 1; 498 case 2: 499 /* back from a 'v' command - don't redraw the screen */ 500 return 1; 501 } 502 break; 503 504 case VREDO: 505 state = VNORMAL; 506 if (argc1 != 0) 507 lastac = argc1; 508 switch (vi_cmd(lastac, lastcmd)) { 509 case -1: 510 vi_error(); 511 refresh(0); 512 break; 513 case 0: 514 if (insert != 0) { 515 if (lastcmd[0] == 's' || lastcmd[0] == 'c' || 516 lastcmd[0] == 'C') { 517 if (redo_insert(1) != 0) 518 vi_error(); 519 } else { 520 if (redo_insert(lastac) != 0) 521 vi_error(); 522 } 523 } 524 refresh(0); 525 break; 526 case 1: 527 refresh(0); 528 return 1; 529 case 2: 530 /* back from a 'v' command - can't happen */ 531 break; 532 } 533 break; 534 535 case VFAIL: 536 state = VNORMAL; 537 vi_error(); 538 break; 539 } 540 return 0; 541 } 542 543 static void 544 vi_reset(buf, len) 545 char *buf; 546 size_t len; 547 { 548 state = VNORMAL; 549 ohnum = hnum = hlast = histnum(-1) + 1; 550 insert = INSERT; 551 saved_inslen = inslen; 552 first_insert = 1; 553 inslen = 0; 554 modified = 1; 555 vi_macro_reset(); 556 edit_reset(buf, len); 557 } 558 559 static int 560 nextstate(ch) 561 int ch; 562 { 563 if (is_extend(ch)) 564 return VEXTCMD; 565 else if (is_srch(ch)) 566 return VSEARCH; 567 else if (is_long(ch)) 568 return VXCH; 569 else if (ch == '.') 570 return VREDO; 571 else if (ch == Ctrl('v')) 572 return VVERSION; 573 else if (is_cmd(ch)) 574 return VCMD; 575 else 576 return VFAIL; 577 } 578 579 static int 580 vi_insert(ch) 581 int ch; 582 { 583 int tcursor; 584 585 if (ch == edchars.erase || ch == Ctrl('h')) { 586 if (insert == REPLACE) { 587 if (es->cursor == undo->cursor) { 588 vi_error(); 589 return 0; 590 } 591 if (inslen > 0) 592 inslen--; 593 es->cursor--; 594 if (es->cursor >= undo->linelen) 595 es->linelen--; 596 else 597 es->cbuf[es->cursor] = undo->cbuf[es->cursor]; 598 } else { 599 if (es->cursor == 0) { 600 /* x_putc(BEL); no annoying bell here */ 601 return 0; 602 } 603 if (inslen > 0) 604 inslen--; 605 es->cursor--; 606 es->linelen--; 607 memmove(&es->cbuf[es->cursor], &es->cbuf[es->cursor+1], 608 es->linelen - es->cursor + 1); 609 } 610 expanded = NONE; 611 return 0; 612 } 613 if (ch == edchars.kill) { 614 if (es->cursor != 0) { 615 inslen = 0; 616 memmove(es->cbuf, &es->cbuf[es->cursor], 617 es->linelen - es->cursor); 618 es->linelen -= es->cursor; 619 es->cursor = 0; 620 } 621 expanded = NONE; 622 return 0; 623 } 624 if (ch == edchars.werase) { 625 if (es->cursor != 0) { 626 tcursor = Backword(1); 627 memmove(&es->cbuf[tcursor], &es->cbuf[es->cursor], 628 es->linelen - es->cursor); 629 es->linelen -= es->cursor - tcursor; 630 if (inslen < es->cursor - tcursor) 631 inslen = 0; 632 else 633 inslen -= es->cursor - tcursor; 634 es->cursor = tcursor; 635 } 636 expanded = NONE; 637 return 0; 638 } 639 /* If any chars are entered before escape, trash the saved insert 640 * buffer (if user inserts & deletes char, ibuf gets trashed and 641 * we don't want to use it) 642 */ 643 if (first_insert && ch != Ctrl('[')) 644 saved_inslen = 0; 645 switch (ch) { 646 647 #ifdef OS2 648 case 224: /* function key prefix */ 649 #endif /* OS2 */ 650 case '\0': 651 return -1; 652 653 case '\r': 654 case '\n': 655 return 1; 656 657 case Ctrl('['): 658 expanded = NONE; 659 if (first_insert) { 660 first_insert = 0; 661 if (inslen == 0) { 662 inslen = saved_inslen; 663 return redo_insert(0); 664 } 665 lastcmd[0] = 'a'; 666 lastac = 1; 667 } 668 if (lastcmd[0] == 's' || lastcmd[0] == 'c' || 669 lastcmd[0] == 'C') 670 return redo_insert(0); 671 else 672 return redo_insert(lastac - 1); 673 674 /* { Begin nonstandard vi commands */ 675 case Ctrl('x'): 676 expand_word(0); 677 break; 678 679 case Ctrl('f'): 680 complete_word(0, 0); 681 break; 682 683 case Ctrl('e'): 684 print_expansions(es, 0); 685 break; 686 687 case Ctrl('i'): 688 if (Flag(FVITABCOMPLETE)) { 689 complete_word(0, 0); 690 break; 691 } 692 /* FALLTHROUGH */ 693 /* End nonstandard vi commands } */ 694 695 default: 696 if (es->linelen == es->cbufsize - 1) 697 return -1; 698 ibuf[inslen++] = ch; 699 if (insert == INSERT) { 700 memmove(&es->cbuf[es->cursor+1], &es->cbuf[es->cursor], 701 es->linelen - es->cursor); 702 es->linelen++; 703 } 704 es->cbuf[es->cursor++] = ch; 705 if (insert == REPLACE && es->cursor > es->linelen) 706 es->linelen++; 707 expanded = NONE; 708 } 709 return 0; 710 } 711 712 static int 713 vi_cmd(argcnt, cmd) 714 int argcnt; 715 const char *cmd; 716 { 717 int ncursor; 718 int cur, c1, c2, c3 = 0; 719 int any; 720 struct edstate *t; 721 722 if (argcnt == 0 && !is_zerocount(*cmd)) 723 argcnt = 1; 724 725 if (is_move(*cmd)) { 726 if ((cur = domove(argcnt, cmd, 0)) >= 0) { 727 if (cur == es->linelen && cur != 0) 728 cur--; 729 es->cursor = cur; 730 } else 731 return -1; 732 } else { 733 /* Don't save state in middle of macro.. */ 734 if (is_undoable(*cmd) && !macro.p) { 735 undo->winleft = es->winleft; 736 memmove(undo->cbuf, es->cbuf, es->linelen); 737 undo->linelen = es->linelen; 738 undo->cursor = es->cursor; 739 lastac = argcnt; 740 memmove(lastcmd, cmd, MAXVICMD); 741 } 742 switch (*cmd) { 743 744 case Ctrl('l'): 745 case Ctrl('r'): 746 redraw_line(1); 747 break; 748 749 case '@': 750 { 751 static char alias[] = "_\0"; 752 struct tbl *ap; 753 int olen, nlen; 754 char *p, *nbuf; 755 756 /* lookup letter in alias list... */ 757 alias[1] = cmd[1]; 758 ap = tsearch(&aliases, alias, hash(alias)); 759 if (!cmd[1] || !ap || !(ap->flag & ISSET)) 760 return -1; 761 /* check if this is a recursive call... */ 762 if ((p = (char *) macro.p)) 763 while ((p = strchr(p, '\0')) && p[1]) 764 if (*++p == cmd[1]) 765 return -1; 766 /* insert alias into macro buffer */ 767 nlen = strlen(ap->val.s) + 1; 768 olen = !macro.p ? 2 769 : macro.len - (macro.p - macro.buf); 770 nbuf = alloc(nlen + 1 + olen, APERM); 771 memcpy(nbuf, ap->val.s, nlen); 772 nbuf[nlen++] = cmd[1]; 773 if (macro.p) { 774 memcpy(nbuf + nlen, macro.p, olen); 775 afree(macro.buf, APERM); 776 nlen += olen; 777 } else { 778 nbuf[nlen++] = '\0'; 779 nbuf[nlen++] = '\0'; 780 } 781 macro.p = macro.buf = (unsigned char *) nbuf; 782 macro.len = nlen; 783 } 784 break; 785 786 case 'a': 787 modified = 1; hnum = hlast; 788 if (es->linelen != 0) 789 es->cursor++; 790 insert = INSERT; 791 break; 792 793 case 'A': 794 modified = 1; hnum = hlast; 795 del_range(0, 0); 796 es->cursor = es->linelen; 797 insert = INSERT; 798 break; 799 800 case 'S': 801 es->cursor = domove(1, "^", 1); 802 del_range(es->cursor, es->linelen); 803 modified = 1; hnum = hlast; 804 insert = INSERT; 805 break; 806 807 case 'Y': 808 cmd = "y$"; 809 /* ahhhhhh... */ 810 case 'c': 811 case 'd': 812 case 'y': 813 if (*cmd == cmd[1]) { 814 c1 = *cmd == 'c' ? domove(1, "^", 1) : 0; 815 c2 = es->linelen; 816 } else if (!is_move(cmd[1])) 817 return -1; 818 else { 819 if ((ncursor = domove(argcnt, &cmd[1], 1)) < 0) 820 return -1; 821 if (*cmd == 'c' && 822 (cmd[1]=='w' || cmd[1]=='W') && 823 !isspace((unsigned char)es->cbuf[es->cursor])) { 824 while (isspace((unsigned char)es->cbuf[--ncursor])) 825 ; 826 ncursor++; 827 } 828 if (ncursor > es->cursor) { 829 c1 = es->cursor; 830 c2 = ncursor; 831 } else { 832 c1 = ncursor; 833 c2 = es->cursor; 834 if (cmd[1] == '%') 835 c2++; 836 } 837 } 838 if (*cmd != 'c' && c1 != c2) 839 yank_range(c1, c2); 840 if (*cmd != 'y') { 841 del_range(c1, c2); 842 es->cursor = c1; 843 } 844 if (*cmd == 'c') { 845 modified = 1; hnum = hlast; 846 insert = INSERT; 847 } 848 break; 849 850 case 'p': 851 modified = 1; hnum = hlast; 852 if (es->linelen != 0) 853 es->cursor++; 854 while (putbuf(ybuf, yanklen, 0) == 0 && --argcnt > 0) 855 ; 856 if (es->cursor != 0) 857 es->cursor--; 858 if (argcnt != 0) 859 return -1; 860 break; 861 862 case 'P': 863 modified = 1; hnum = hlast; 864 any = 0; 865 while (putbuf(ybuf, yanklen, 0) == 0 && --argcnt > 0) 866 any = 1; 867 if (any && es->cursor != 0) 868 es->cursor--; 869 if (argcnt != 0) 870 return -1; 871 break; 872 873 case 'C': 874 modified = 1; hnum = hlast; 875 del_range(es->cursor, es->linelen); 876 insert = INSERT; 877 break; 878 879 case 'D': 880 yank_range(es->cursor, es->linelen); 881 del_range(es->cursor, es->linelen); 882 if (es->cursor != 0) 883 es->cursor--; 884 break; 885 886 case 'g': 887 if (!argcnt) 888 argcnt = hlast + 1; 889 /* fall through */ 890 case 'G': 891 if (!argcnt) 892 argcnt = 1; 893 else 894 argcnt = hlast - (source->line - argcnt); 895 if (grabhist(modified, argcnt - 1) < 0) 896 return -1; 897 else { 898 modified = 0; 899 hnum = argcnt - 1; 900 } 901 break; 902 903 case 'i': 904 modified = 1; hnum = hlast; 905 insert = INSERT; 906 break; 907 908 case 'I': 909 modified = 1; hnum = hlast; 910 es->cursor = domove(1, "^", 1); 911 insert = INSERT; 912 break; 913 914 case 'j': 915 case '+': 916 case Ctrl('n'): 917 if (grabhist(modified, hnum + argcnt) < 0) 918 return -1; 919 else { 920 modified = 0; 921 hnum += argcnt; 922 } 923 break; 924 925 case 'k': 926 case '-': 927 case Ctrl('p'): 928 if (grabhist(modified, hnum - argcnt) < 0) 929 return -1; 930 else { 931 modified = 0; 932 hnum -= argcnt; 933 } 934 break; 935 936 case 'r': 937 if (es->linelen == 0) 938 return -1; 939 modified = 1; hnum = hlast; 940 if (cmd[1] == 0) 941 vi_error(); 942 else 943 es->cbuf[es->cursor] = cmd[1]; 944 break; 945 946 case 'R': 947 modified = 1; hnum = hlast; 948 insert = REPLACE; 949 break; 950 951 case 's': 952 if (es->linelen == 0) 953 return -1; 954 modified = 1; hnum = hlast; 955 if (es->cursor + argcnt > es->linelen) 956 argcnt = es->linelen - es->cursor; 957 del_range(es->cursor, es->cursor + argcnt); 958 insert = INSERT; 959 break; 960 961 case 'v': 962 if (es->linelen == 0) 963 return -1; 964 if (!argcnt) { 965 if (modified) { 966 es->cbuf[es->linelen] = '\0'; 967 source->line++; 968 histsave(source->line, es->cbuf, 1); 969 } else 970 argcnt = source->line + 1 971 - (hlast - hnum); 972 } 973 shf_snprintf(es->cbuf, es->cbufsize, 974 argcnt ? "%s %d" : "%s", 975 "fc -e ${VISUAL:-${EDITOR:-vi}} --", 976 argcnt); 977 es->linelen = strlen(es->cbuf); 978 return 2; 979 980 case 'x': 981 if (es->linelen == 0) 982 return -1; 983 modified = 1; hnum = hlast; 984 if (es->cursor + argcnt > es->linelen) 985 argcnt = es->linelen - es->cursor; 986 yank_range(es->cursor, es->cursor + argcnt); 987 del_range(es->cursor, es->cursor + argcnt); 988 break; 989 990 case 'X': 991 if (es->cursor > 0) { 992 modified = 1; hnum = hlast; 993 if (es->cursor < argcnt) 994 argcnt = es->cursor; 995 yank_range(es->cursor - argcnt, es->cursor); 996 del_range(es->cursor - argcnt, es->cursor); 997 es->cursor -= argcnt; 998 } else 999 return -1; 1000 break; 1001 1002 case 'u': 1003 t = es; 1004 es = undo; 1005 undo = t; 1006 break; 1007 1008 case 'U': 1009 if (!modified) 1010 return -1; 1011 if (grabhist(modified, ohnum) < 0) 1012 return -1; 1013 modified = 0; 1014 hnum = ohnum; 1015 break; 1016 1017 case '?': 1018 if (hnum == hlast) 1019 hnum = -1; 1020 /* ahhh */ 1021 case '/': 1022 c3 = 1; 1023 srchlen = 0; 1024 lastsearch = *cmd; 1025 /* fall through */ 1026 case 'n': 1027 case 'N': 1028 if (lastsearch == ' ') 1029 return -1; 1030 if (lastsearch == '?') 1031 c1 = 1; 1032 else 1033 c1 = 0; 1034 if (*cmd == 'N') 1035 c1 = !c1; 1036 if ((c2 = grabsearch(modified, hnum, 1037 c1, srchpat)) < 0) { 1038 if (c3) { 1039 restore_cbuf(); 1040 refresh(0); 1041 } 1042 return -1; 1043 } else { 1044 modified = 0; 1045 hnum = c2; 1046 ohnum = hnum; 1047 } 1048 break; 1049 case '_': { 1050 int inspace; 1051 char *p, *sp; 1052 1053 if (histnum(-1) < 0) 1054 return -1; 1055 p = *histpos(); 1056 #define issp(c) (isspace((unsigned char)(c)) || (c) == '\n') 1057 if (argcnt) { 1058 while (*p && issp(*p)) 1059 p++; 1060 while (*p && --argcnt) { 1061 while (*p && !issp(*p)) 1062 p++; 1063 while (*p && issp(*p)) 1064 p++; 1065 } 1066 if (!*p) 1067 return -1; 1068 sp = p; 1069 } else { 1070 sp = p; 1071 inspace = 0; 1072 while (*p) { 1073 if (issp(*p)) 1074 inspace = 1; 1075 else if (inspace) { 1076 inspace = 0; 1077 sp = p; 1078 } 1079 p++; 1080 } 1081 p = sp; 1082 } 1083 modified = 1; hnum = hlast; 1084 if (es->cursor != es->linelen) 1085 es->cursor++; 1086 while (*p && !issp(*p)) { 1087 argcnt++; 1088 p++; 1089 } 1090 if (putbuf(space, 1, 0) != 0) 1091 argcnt = -1; 1092 else if (putbuf(sp, argcnt, 0) != 0) 1093 argcnt = -1; 1094 if (argcnt < 0) { 1095 if (es->cursor != 0) 1096 es->cursor--; 1097 return -1; 1098 } 1099 insert = INSERT; 1100 } 1101 break; 1102 1103 case '~': { 1104 char *p; 1105 int i; 1106 1107 if (es->linelen == 0) 1108 return -1; 1109 for (i = 0; i < argcnt; i++) { 1110 p = &es->cbuf[es->cursor]; 1111 if (islower((unsigned char)*p)) { 1112 modified = 1; hnum = hlast; 1113 *p = toupper(*p); 1114 } else if (isupper((unsigned char)*p)) { 1115 modified = 1; hnum = hlast; 1116 *p = tolower(*p); 1117 } 1118 if (es->cursor < es->linelen - 1) 1119 es->cursor++; 1120 } 1121 break; 1122 } 1123 1124 case '#': 1125 { 1126 int ret = x_do_comment(es->cbuf, es->cbufsize, 1127 &es->linelen); 1128 if (ret >= 0) 1129 es->cursor = 0; 1130 return ret; 1131 } 1132 1133 case '=': /* at&t ksh */ 1134 case Ctrl('e'): /* Nonstandard vi/ksh */ 1135 print_expansions(es, 1); 1136 break; 1137 1138 1139 case Ctrl('i'): /* Nonstandard vi/ksh */ 1140 if (!Flag(FVITABCOMPLETE)) 1141 return -1; 1142 complete_word(1, argcnt); 1143 break; 1144 1145 case Ctrl('['): /* some annoying at&t ksh's */ 1146 if (!Flag(FVIESCCOMPLETE)) 1147 return -1; 1148 case '\\': /* at&t ksh */ 1149 case Ctrl('f'): /* Nonstandard vi/ksh */ 1150 complete_word(1, argcnt); 1151 break; 1152 1153 1154 case '*': /* at&t ksh */ 1155 case Ctrl('x'): /* Nonstandard vi/ksh */ 1156 expand_word(1); 1157 break; 1158 } 1159 if (insert == 0 && es->cursor != 0 && es->cursor >= es->linelen) 1160 es->cursor--; 1161 } 1162 return 0; 1163 } 1164 1165 static int 1166 domove(argcnt, cmd, sub) 1167 int argcnt; 1168 const char *cmd; 1169 int sub; 1170 { 1171 int bcount, UNINITIALIZED(i), t; 1172 int UNINITIALIZED(ncursor); 1173 1174 switch (*cmd) { 1175 1176 case 'b': 1177 if (!sub && es->cursor == 0) 1178 return -1; 1179 ncursor = backword(argcnt); 1180 break; 1181 1182 case 'B': 1183 if (!sub && es->cursor == 0) 1184 return -1; 1185 ncursor = Backword(argcnt); 1186 break; 1187 1188 case 'e': 1189 if (!sub && es->cursor + 1 >= es->linelen) 1190 return -1; 1191 ncursor = endword(argcnt); 1192 if (sub && ncursor < es->linelen) 1193 ncursor++; 1194 break; 1195 1196 case 'E': 1197 if (!sub && es->cursor + 1 >= es->linelen) 1198 return -1; 1199 ncursor = Endword(argcnt); 1200 if (sub && ncursor < es->linelen) 1201 ncursor++; 1202 break; 1203 1204 case 'f': 1205 case 'F': 1206 case 't': 1207 case 'T': 1208 fsavecmd = *cmd; 1209 fsavech = cmd[1]; 1210 /* drop through */ 1211 1212 case ',': 1213 case ';': 1214 if (fsavecmd == ' ') 1215 return -1; 1216 i = fsavecmd == 'f' || fsavecmd == 'F'; 1217 t = fsavecmd > 'a'; 1218 if (*cmd == ',') 1219 t = !t; 1220 if ((ncursor = findch(fsavech, argcnt, t, i)) < 0) 1221 return -1; 1222 if (sub && t) 1223 ncursor++; 1224 break; 1225 1226 case 'h': 1227 case Ctrl('h'): 1228 if (!sub && es->cursor == 0) 1229 return -1; 1230 ncursor = es->cursor - argcnt; 1231 if (ncursor < 0) 1232 ncursor = 0; 1233 break; 1234 1235 case ' ': 1236 case 'l': 1237 if (!sub && es->cursor + 1 >= es->linelen) 1238 return -1; 1239 if (es->linelen != 0) { 1240 ncursor = es->cursor + argcnt; 1241 if (ncursor > es->linelen) 1242 ncursor = es->linelen; 1243 } 1244 break; 1245 1246 case 'w': 1247 if (!sub && es->cursor + 1 >= es->linelen) 1248 return -1; 1249 ncursor = forwword(argcnt); 1250 break; 1251 1252 case 'W': 1253 if (!sub && es->cursor + 1 >= es->linelen) 1254 return -1; 1255 ncursor = Forwword(argcnt); 1256 break; 1257 1258 case '0': 1259 ncursor = 0; 1260 break; 1261 1262 case '^': 1263 ncursor = 0; 1264 while (ncursor < es->linelen - 1 && isspace((unsigned char)es->cbuf[ncursor])) 1265 ncursor++; 1266 break; 1267 1268 case '|': 1269 ncursor = argcnt; 1270 if (ncursor > es->linelen) 1271 ncursor = es->linelen; 1272 if (ncursor) 1273 ncursor--; 1274 break; 1275 1276 case '$': 1277 if (es->linelen != 0) 1278 ncursor = es->linelen; 1279 else 1280 ncursor = 0; 1281 break; 1282 1283 case '%': 1284 ncursor = es->cursor; 1285 while (ncursor < es->linelen && 1286 (i = bracktype(es->cbuf[ncursor])) == 0) 1287 ncursor++; 1288 if (ncursor == es->linelen) 1289 return -1; 1290 bcount = 1; 1291 do { 1292 if (i > 0) { 1293 if (++ncursor >= es->linelen) 1294 return -1; 1295 } else { 1296 if (--ncursor < 0) 1297 return -1; 1298 } 1299 t = bracktype(es->cbuf[ncursor]); 1300 if (t == i) 1301 bcount++; 1302 else if (t == -i) 1303 bcount--; 1304 } while (bcount != 0); 1305 if (sub && i > 0) 1306 ncursor++; 1307 break; 1308 1309 default: 1310 return -1; 1311 } 1312 return ncursor; 1313 } 1314 1315 static int 1316 redo_insert(count) 1317 int count; 1318 { 1319 while (count-- > 0) 1320 if (putbuf(ibuf, inslen, insert==REPLACE) != 0) 1321 return -1; 1322 if (es->cursor > 0) 1323 es->cursor--; 1324 insert = 0; 1325 return 0; 1326 } 1327 1328 static void 1329 yank_range(a, b) 1330 int a, b; 1331 { 1332 yanklen = b - a; 1333 if (yanklen != 0) 1334 memmove(ybuf, &es->cbuf[a], yanklen); 1335 } 1336 1337 static int 1338 bracktype(ch) 1339 int ch; 1340 { 1341 switch (ch) { 1342 1343 case '(': 1344 return 1; 1345 1346 case '[': 1347 return 2; 1348 1349 case '{': 1350 return 3; 1351 1352 case ')': 1353 return -1; 1354 1355 case ']': 1356 return -2; 1357 1358 case '}': 1359 return -3; 1360 1361 default: 1362 return 0; 1363 } 1364 } 1365 1366 /* 1367 * Non user interface editor routines below here 1368 */ 1369 1370 static int cur_col; /* current column on line */ 1371 static int pwidth; /* width of prompt */ 1372 static int prompt_trunc; /* how much of prompt to truncate */ 1373 static int prompt_skip; /* how much of prompt to skip */ 1374 static int winwidth; /* width of window */ 1375 static char *wbuf[2]; /* window buffers */ 1376 static int wbuf_len; /* length of window buffers (x_cols-3)*/ 1377 static int win; /* window buffer in use */ 1378 static char morec; /* more character at right of window */ 1379 static int lastref; /* argument to last refresh() */ 1380 static char holdbuf[CMDLEN]; /* place to hold last edit buffer */ 1381 static int holdlen; /* length of holdbuf */ 1382 1383 static void 1384 save_cbuf() 1385 { 1386 memmove(holdbuf, es->cbuf, es->linelen); 1387 holdlen = es->linelen; 1388 holdbuf[holdlen] = '\0'; 1389 } 1390 1391 static void 1392 restore_cbuf() 1393 { 1394 es->cursor = 0; 1395 es->linelen = holdlen; 1396 memmove(es->cbuf, holdbuf, holdlen); 1397 } 1398 1399 /* return a new edstate */ 1400 static struct edstate * 1401 save_edstate(old) 1402 struct edstate *old; 1403 { 1404 struct edstate *new; 1405 1406 new = (struct edstate *)alloc(sizeof(struct edstate), APERM); 1407 new->cbuf = alloc(old->cbufsize, APERM); 1408 new->cbufsize = old->cbufsize; 1409 strcpy(new->cbuf, old->cbuf); 1410 new->linelen = old->linelen; 1411 new->cursor = old->cursor; 1412 new->winleft = old->winleft; 1413 return new; 1414 } 1415 1416 static void 1417 restore_edstate(new, old) 1418 struct edstate *old, *new; 1419 { 1420 strncpy(new->cbuf, old->cbuf, old->linelen); 1421 new->linelen = old->linelen; 1422 new->cursor = old->cursor; 1423 new->winleft = old->winleft; 1424 free_edstate(old); 1425 } 1426 1427 static void 1428 free_edstate(old) 1429 struct edstate *old; 1430 { 1431 afree(old->cbuf, APERM); 1432 afree((char *)old, APERM); 1433 } 1434 1435 1436 1437 static void 1438 edit_reset(buf, len) 1439 char *buf; 1440 size_t len; 1441 { 1442 const char *p; 1443 1444 es = &ebuf; 1445 es->cbuf = buf; 1446 es->cbufsize = len; 1447 undo = &undobuf; 1448 undo->cbufsize = len; 1449 1450 es->linelen = undo->linelen = 0; 1451 es->cursor = undo->cursor = 0; 1452 es->winleft = undo->winleft = 0; 1453 1454 cur_col = pwidth = promptlen(prompt, &p); 1455 prompt_skip = p - prompt; 1456 if (pwidth > x_cols - 3 - MIN_EDIT_SPACE) { 1457 cur_col = x_cols - 3 - MIN_EDIT_SPACE; 1458 prompt_trunc = pwidth - cur_col; 1459 pwidth -= prompt_trunc; 1460 } else 1461 prompt_trunc = 0; 1462 if (!wbuf_len || wbuf_len != x_cols - 3) { 1463 wbuf_len = x_cols - 3; 1464 wbuf[0] = aresize(wbuf[0], wbuf_len, APERM); 1465 wbuf[1] = aresize(wbuf[1], wbuf_len, APERM); 1466 } 1467 (void) memset(wbuf[0], ' ', wbuf_len); 1468 (void) memset(wbuf[1], ' ', wbuf_len); 1469 winwidth = x_cols - pwidth - 3; 1470 win = 0; 1471 morec = ' '; 1472 lastref = 1; 1473 holdlen = 0; 1474 } 1475 1476 /* 1477 * this is used for calling x_escape() in complete_word() 1478 */ 1479 static int 1480 x_vi_putbuf(s, len) 1481 const char *s; 1482 size_t len; 1483 { 1484 return putbuf(s, len, 0); 1485 } 1486 1487 static int 1488 putbuf(buf, len, repl) 1489 const char *buf; 1490 int len; 1491 int repl; 1492 { 1493 if (len == 0) 1494 return 0; 1495 if (repl) { 1496 if (es->cursor + len >= es->cbufsize) 1497 return -1; 1498 if (es->cursor + len > es->linelen) 1499 es->linelen = es->cursor + len; 1500 } else { 1501 if (es->linelen + len >= es->cbufsize) 1502 return -1; 1503 memmove(&es->cbuf[es->cursor + len], &es->cbuf[es->cursor], 1504 es->linelen - es->cursor); 1505 es->linelen += len; 1506 } 1507 memmove(&es->cbuf[es->cursor], buf, len); 1508 es->cursor += len; 1509 return 0; 1510 } 1511 1512 static void 1513 del_range(a, b) 1514 int a, b; 1515 { 1516 if (es->linelen != b) 1517 memmove(&es->cbuf[a], &es->cbuf[b], es->linelen - b); 1518 es->linelen -= b - a; 1519 } 1520 1521 static int 1522 findch(ch, cnt, forw, incl) 1523 int ch; 1524 int cnt; 1525 int forw; 1526 int incl; 1527 { 1528 int ncursor; 1529 1530 if (es->linelen == 0) 1531 return -1; 1532 ncursor = es->cursor; 1533 while (cnt--) { 1534 do { 1535 if (forw) { 1536 if (++ncursor == es->linelen) 1537 return -1; 1538 } else { 1539 if (--ncursor < 0) 1540 return -1; 1541 } 1542 } while (es->cbuf[ncursor] != ch); 1543 } 1544 if (!incl) { 1545 if (forw) 1546 ncursor--; 1547 else 1548 ncursor++; 1549 } 1550 return ncursor; 1551 } 1552 1553 static int 1554 forwword(argcnt) 1555 int argcnt; 1556 { 1557 int ncursor; 1558 1559 ncursor = es->cursor; 1560 while (ncursor < es->linelen && argcnt--) { 1561 if (is_wordch(es->cbuf[ncursor])) 1562 while (is_wordch(es->cbuf[ncursor]) && 1563 ncursor < es->linelen) 1564 ncursor++; 1565 else if (!isspace((unsigned char)es->cbuf[ncursor])) 1566 while (!is_wordch(es->cbuf[ncursor]) && 1567 !isspace((unsigned char)es->cbuf[ncursor]) && 1568 ncursor < es->linelen) 1569 ncursor++; 1570 while (isspace((unsigned char)es->cbuf[ncursor]) && ncursor < es->linelen) 1571 ncursor++; 1572 } 1573 return ncursor; 1574 } 1575 1576 static int 1577 backword(argcnt) 1578 int argcnt; 1579 { 1580 int ncursor; 1581 1582 ncursor = es->cursor; 1583 while (ncursor > 0 && argcnt--) { 1584 while (--ncursor > 0 && isspace((unsigned char)es->cbuf[ncursor])) 1585 ; 1586 if (ncursor > 0) { 1587 if (is_wordch(es->cbuf[ncursor])) 1588 while (--ncursor >= 0 && 1589 is_wordch(es->cbuf[ncursor])) 1590 ; 1591 else 1592 while (--ncursor >= 0 && 1593 !is_wordch(es->cbuf[ncursor]) && 1594 !isspace((unsigned char)es->cbuf[ncursor])) 1595 ; 1596 ncursor++; 1597 } 1598 } 1599 return ncursor; 1600 } 1601 1602 static int 1603 endword(argcnt) 1604 int argcnt; 1605 { 1606 int ncursor; 1607 1608 ncursor = es->cursor; 1609 while (ncursor < es->linelen && argcnt--) { 1610 while (++ncursor < es->linelen - 1 && 1611 isspace((unsigned char)es->cbuf[ncursor])) 1612 ; 1613 if (ncursor < es->linelen - 1) { 1614 if (is_wordch(es->cbuf[ncursor])) 1615 while (++ncursor < es->linelen && 1616 is_wordch(es->cbuf[ncursor])) 1617 ; 1618 else 1619 while (++ncursor < es->linelen && 1620 !is_wordch(es->cbuf[ncursor]) && 1621 !isspace((unsigned char)es->cbuf[ncursor])) 1622 ; 1623 ncursor--; 1624 } 1625 } 1626 return ncursor; 1627 } 1628 1629 static int 1630 Forwword(argcnt) 1631 int argcnt; 1632 { 1633 int ncursor; 1634 1635 ncursor = es->cursor; 1636 while (ncursor < es->linelen && argcnt--) { 1637 while (!isspace((unsigned char)es->cbuf[ncursor]) && ncursor < es->linelen) 1638 ncursor++; 1639 while (isspace((unsigned char)es->cbuf[ncursor]) && ncursor < es->linelen) 1640 ncursor++; 1641 } 1642 return ncursor; 1643 } 1644 1645 static int 1646 Backword(argcnt) 1647 int argcnt; 1648 { 1649 int ncursor; 1650 1651 ncursor = es->cursor; 1652 while (ncursor > 0 && argcnt--) { 1653 while (--ncursor >= 0 && isspace((unsigned char)es->cbuf[ncursor])) 1654 ; 1655 while (ncursor >= 0 && !isspace((unsigned char)es->cbuf[ncursor])) 1656 ncursor--; 1657 ncursor++; 1658 } 1659 return ncursor; 1660 } 1661 1662 static int 1663 Endword(argcnt) 1664 int argcnt; 1665 { 1666 int ncursor; 1667 1668 ncursor = es->cursor; 1669 while (ncursor < es->linelen - 1 && argcnt--) { 1670 while (++ncursor < es->linelen - 1 && 1671 isspace((unsigned char)es->cbuf[ncursor])) 1672 ; 1673 if (ncursor < es->linelen - 1) { 1674 while (++ncursor < es->linelen && 1675 !isspace((unsigned char)es->cbuf[ncursor])) 1676 ; 1677 ncursor--; 1678 } 1679 } 1680 return ncursor; 1681 } 1682 1683 static int 1684 grabhist(save, n) 1685 int save; 1686 int n; 1687 { 1688 char *hptr; 1689 1690 if (n < 0 || n > hlast) 1691 return -1; 1692 if (n == hlast) { 1693 restore_cbuf(); 1694 ohnum = n; 1695 return 0; 1696 } 1697 (void) histnum(n); 1698 if ((hptr = *histpos()) == NULL) { 1699 internal_errorf(0, "grabhist: bad history array"); 1700 return -1; 1701 } 1702 if (save) 1703 save_cbuf(); 1704 if ((es->linelen = strlen(hptr)) >= es->cbufsize) 1705 es->linelen = es->cbufsize - 1; 1706 memmove(es->cbuf, hptr, es->linelen); 1707 es->cursor = 0; 1708 ohnum = n; 1709 return 0; 1710 } 1711 1712 static int 1713 grabsearch(save, start, fwd, pat) 1714 int save, start, fwd; 1715 char *pat; 1716 { 1717 char *hptr; 1718 int hist; 1719 int anchored; 1720 1721 if ((start == 0 && fwd == 0) || (start >= hlast-1 && fwd == 1)) 1722 return -1; 1723 if (fwd) 1724 start++; 1725 else 1726 start--; 1727 anchored = *pat == '^' ? (++pat, 1) : 0; 1728 if ((hist = findhist(start, fwd, pat, anchored)) < 0) { 1729 /* if (start != 0 && fwd && match(holdbuf, pat) >= 0) { */ 1730 /* XXX should FILECMP be strncmp? */ 1731 if (start != 0 && fwd && FILECMP(holdbuf, pat) >= 0) { 1732 restore_cbuf(); 1733 return 0; 1734 } else 1735 return -1; 1736 } 1737 if (save) 1738 save_cbuf(); 1739 histnum(hist); 1740 hptr = *histpos(); 1741 if ((es->linelen = strlen(hptr)) >= es->cbufsize) 1742 es->linelen = es->cbufsize - 1; 1743 memmove(es->cbuf, hptr, es->linelen); 1744 es->cursor = 0; 1745 return hist; 1746 } 1747 1748 static void 1749 redraw_line(newline) 1750 int newline; 1751 { 1752 (void) memset(wbuf[win], ' ', wbuf_len); 1753 if (newline) { 1754 x_putc('\r'); 1755 x_putc('\n'); 1756 } 1757 vi_pprompt(0); 1758 cur_col = pwidth; 1759 morec = ' '; 1760 } 1761 1762 static void 1763 refresh(leftside) 1764 int leftside; 1765 { 1766 if (leftside < 0) 1767 leftside = lastref; 1768 else 1769 lastref = leftside; 1770 if (outofwin()) 1771 rewindow(); 1772 display(wbuf[1 - win], wbuf[win], leftside); 1773 win = 1 - win; 1774 } 1775 1776 static int 1777 outofwin() 1778 { 1779 int cur, col; 1780 1781 if (es->cursor < es->winleft) 1782 return 1; 1783 col = 0; 1784 cur = es->winleft; 1785 while (cur < es->cursor) 1786 col = newcol((unsigned char) es->cbuf[cur++], col); 1787 if (col >= winwidth) 1788 return 1; 1789 return 0; 1790 } 1791 1792 static void 1793 rewindow() 1794 { 1795 register int tcur, tcol; 1796 int holdcur1, holdcol1; 1797 int holdcur2, holdcol2; 1798 1799 holdcur1 = holdcur2 = tcur = 0; 1800 holdcol1 = holdcol2 = tcol = 0; 1801 while (tcur < es->cursor) { 1802 if (tcol - holdcol2 > winwidth / 2) { 1803 holdcur1 = holdcur2; 1804 holdcol1 = holdcol2; 1805 holdcur2 = tcur; 1806 holdcol2 = tcol; 1807 } 1808 tcol = newcol((unsigned char) es->cbuf[tcur++], tcol); 1809 } 1810 while (tcol - holdcol1 > winwidth / 2) 1811 holdcol1 = newcol((unsigned char) es->cbuf[holdcur1++], 1812 holdcol1); 1813 es->winleft = holdcur1; 1814 } 1815 1816 static int 1817 newcol(ch, col) 1818 int ch, col; 1819 { 1820 if (ch == '\t') 1821 return (col | 7) + 1; 1822 return col + char_len(ch); 1823 } 1824 1825 static void 1826 display(wb1, wb2, leftside) 1827 char *wb1, *wb2; 1828 int leftside; 1829 { 1830 unsigned char ch; 1831 char *twb1, *twb2, mc; 1832 int cur, col, cnt; 1833 int UNINITIALIZED(ncol); 1834 int moreright; 1835 1836 col = 0; 1837 cur = es->winleft; 1838 moreright = 0; 1839 twb1 = wb1; 1840 while (col < winwidth && cur < es->linelen) { 1841 if (cur == es->cursor && leftside) 1842 ncol = col + pwidth; 1843 if ((ch = es->cbuf[cur]) == '\t') { 1844 do { 1845 *twb1++ = ' '; 1846 } while (++col < winwidth && (col & 7) != 0); 1847 } else { 1848 if ((ch & 0x80) && Flag(FVISHOW8)) { 1849 *twb1++ = 'M'; 1850 if (++col < winwidth) { 1851 *twb1++ = '-'; 1852 col++; 1853 } 1854 ch &= 0x7f; 1855 } 1856 if (col < winwidth) { 1857 if (ch < ' ' || ch == 0x7f) { 1858 *twb1++ = '^'; 1859 if (++col < winwidth) { 1860 *twb1++ = ch ^ '@'; 1861 col++; 1862 } 1863 } else { 1864 *twb1++ = ch; 1865 col++; 1866 } 1867 } 1868 } 1869 if (cur == es->cursor && !leftside) 1870 ncol = col + pwidth - 1; 1871 cur++; 1872 } 1873 if (cur == es->cursor) 1874 ncol = col + pwidth; 1875 if (col < winwidth) { 1876 while (col < winwidth) { 1877 *twb1++ = ' '; 1878 col++; 1879 } 1880 } else 1881 moreright++; 1882 *twb1 = ' '; 1883 1884 col = pwidth; 1885 cnt = winwidth; 1886 twb1 = wb1; 1887 twb2 = wb2; 1888 while (cnt--) { 1889 if (*twb1 != *twb2) { 1890 if (cur_col != col) 1891 ed_mov_opt(col, wb1); 1892 x_putc(*twb1); 1893 cur_col++; 1894 } 1895 twb1++; 1896 twb2++; 1897 col++; 1898 } 1899 if (es->winleft > 0 && moreright) 1900 /* POSIX says to use * for this but that is a globbing 1901 * character and may confuse people; + is more innocuous 1902 */ 1903 mc = '+'; 1904 else if (es->winleft > 0) 1905 mc = '<'; 1906 else if (moreright) 1907 mc = '>'; 1908 else 1909 mc = ' '; 1910 if (mc != morec) { 1911 ed_mov_opt(pwidth + winwidth + 1, wb1); 1912 x_putc(mc); 1913 cur_col++; 1914 morec = mc; 1915 } 1916 if (cur_col != ncol) 1917 ed_mov_opt(ncol, wb1); 1918 } 1919 1920 static void 1921 ed_mov_opt(col, wb) 1922 int col; 1923 char *wb; 1924 { 1925 if (col < cur_col) { 1926 if (col + 1 < cur_col - col) { 1927 x_putc('\r'); 1928 vi_pprompt(0); 1929 cur_col = pwidth; 1930 while (cur_col++ < col) 1931 x_putc(*wb++); 1932 } else { 1933 while (cur_col-- > col) 1934 x_putc('\b'); 1935 } 1936 } else { 1937 wb = &wb[cur_col - pwidth]; 1938 while (cur_col++ < col) 1939 x_putc(*wb++); 1940 } 1941 cur_col = col; 1942 } 1943 1944 1945 /* replace word with all expansions (ie, expand word*) */ 1946 static int 1947 expand_word(command) 1948 int command; 1949 { 1950 static struct edstate *buf; 1951 int rval = 0; 1952 int nwords; 1953 int start, end; 1954 char **words; 1955 int i; 1956 1957 /* Undo previous expansion */ 1958 if (command == 0 && expanded == EXPAND && buf) { 1959 restore_edstate(es, buf); 1960 buf = 0; 1961 expanded = NONE; 1962 return 0; 1963 } 1964 if (buf) { 1965 free_edstate(buf); 1966 buf = 0; 1967 } 1968 1969 nwords = x_cf_glob(XCF_COMMAND_FILE|XCF_FULLPATH, 1970 es->cbuf, es->linelen, es->cursor, 1971 &start, &end, &words, (int *) 0); 1972 if (nwords == 0) { 1973 vi_error(); 1974 return -1; 1975 } 1976 1977 buf = save_edstate(es); 1978 expanded = EXPAND; 1979 del_range(start, end); 1980 es->cursor = start; 1981 for (i = 0; i < nwords; ) { 1982 if (putbuf(words[i], (int) strlen(words[i]), 0) != 0) { 1983 rval = -1; 1984 break; 1985 } 1986 if (++i < nwords && putbuf(space, 1, 0) != 0) { 1987 rval = -1; 1988 break; 1989 } 1990 } 1991 i = buf->cursor - end; 1992 if (rval == 0 && i > 0) 1993 es->cursor += i; 1994 modified = 1; hnum = hlast; 1995 insert = INSERT; 1996 lastac = 0; 1997 refresh(0); 1998 return rval; 1999 } 2000 2001 static int 2002 complete_word(command, count) 2003 int command; 2004 int count; 2005 { 2006 static struct edstate *buf; 2007 int rval = 0; 2008 int nwords; 2009 int start, end; 2010 char **words; 2011 char *match; 2012 int match_len; 2013 int is_unique; 2014 int is_command; 2015 2016 /* Undo previous completion */ 2017 if (command == 0 && expanded == COMPLETE && buf) { 2018 print_expansions(buf, 0); 2019 expanded = PRINT; 2020 return 0; 2021 } 2022 if (command == 0 && expanded == PRINT && buf) { 2023 restore_edstate(es, buf); 2024 buf = 0; 2025 expanded = NONE; 2026 return 0; 2027 } 2028 if (buf) { 2029 free_edstate(buf); 2030 buf = 0; 2031 } 2032 2033 /* XCF_FULLPATH for count 'cause the menu printed by print_expansions() 2034 * was done this way. 2035 */ 2036 nwords = x_cf_glob(XCF_COMMAND_FILE | (count ? XCF_FULLPATH : 0), 2037 es->cbuf, es->linelen, es->cursor, 2038 &start, &end, &words, &is_command); 2039 if (nwords == 0) { 2040 vi_error(); 2041 return -1; 2042 } 2043 if (count) { 2044 int i; 2045 2046 count--; 2047 if (count >= nwords) { 2048 vi_error(); 2049 x_print_expansions(nwords, words, is_command); 2050 x_free_words(nwords, words); 2051 redraw_line(0); 2052 return -1; 2053 } 2054 /* 2055 * Expand the count'th word to its basename 2056 */ 2057 if (is_command) { 2058 match = words[count] 2059 + x_basename(words[count], (char *) 0); 2060 /* If more than one possible match, use full path */ 2061 for (i = 0; i < nwords; i++) 2062 if (i != count && 2063 FILECMP(words[i] 2064 + x_basename(words[i], (char *) 0), 2065 match) == 0) 2066 { 2067 match = words[count]; 2068 break; 2069 } 2070 } else 2071 match = words[count]; 2072 match_len = strlen(match); 2073 is_unique = 1; 2074 /* expanded = PRINT; next call undo */ 2075 } else { 2076 match = words[0]; 2077 match_len = x_longest_prefix(nwords, words); 2078 expanded = COMPLETE; /* next call will list completions */ 2079 is_unique = nwords == 1; 2080 } 2081 2082 buf = save_edstate(es); 2083 del_range(start, end); 2084 es->cursor = start; 2085 2086 /* escape all shell-sensitive characters and put the result into 2087 * command buffer */ 2088 rval = x_escape(match, match_len, x_vi_putbuf); 2089 2090 if (rval == 0 && is_unique) { 2091 /* If exact match, don't undo. Allows directory completions 2092 * to be used (ie, complete the next portion of the path). 2093 */ 2094 expanded = NONE; 2095 2096 /* If not a directory, add a space to the end... */ 2097 if (match_len > 0 && !ISDIRSEP(match[match_len - 1])) 2098 rval = putbuf(space, 1, 0); 2099 } 2100 x_free_words(nwords, words); 2101 2102 modified = 1; hnum = hlast; 2103 insert = INSERT; 2104 lastac = 0; /* prevent this from being redone... */ 2105 refresh(0); 2106 2107 return rval; 2108 } 2109 2110 static int 2111 print_expansions(e, command) 2112 struct edstate *e; 2113 int command; 2114 { 2115 int nwords; 2116 int start, end; 2117 char **words; 2118 int is_command; 2119 2120 nwords = x_cf_glob(XCF_COMMAND_FILE|XCF_FULLPATH, 2121 e->cbuf, e->linelen, e->cursor, 2122 &start, &end, &words, &is_command); 2123 if (nwords == 0) { 2124 vi_error(); 2125 return -1; 2126 } 2127 x_print_expansions(nwords, words, is_command); 2128 x_free_words(nwords, words); 2129 redraw_line(0); 2130 return 0; 2131 } 2132 2133 /* How long is char when displayed (not counting tabs) */ 2134 static int 2135 char_len(c) 2136 int c; 2137 { 2138 int len = 1; 2139 2140 if ((c & 0x80) && Flag(FVISHOW8)) { 2141 len += 2; 2142 c &= 0x7f; 2143 } 2144 if (c < ' ' || c == 0x7f) 2145 len++; 2146 return len; 2147 } 2148 2149 /* Similar to x_zotc(emacs.c), but no tab weirdness */ 2150 static void 2151 x_vi_zotc(c) 2152 int c; 2153 { 2154 if (Flag(FVISHOW8) && (c & 0x80)) { 2155 x_puts("M-"); 2156 c &= 0x7f; 2157 } 2158 if (c < ' ' || c == 0x7f) { 2159 x_putc('^'); 2160 c ^= '@'; 2161 } 2162 x_putc(c); 2163 } 2164 2165 static void 2166 vi_pprompt(full) 2167 int full; 2168 { 2169 pprompt(prompt + (full ? 0 : prompt_skip), prompt_trunc); 2170 } 2171 2172 static void 2173 vi_error() 2174 { 2175 /* Beem out of any macros as soon as an error occurs */ 2176 vi_macro_reset(); 2177 x_putc(BEL); 2178 x_flush(); 2179 } 2180 2181 static void 2182 vi_macro_reset() 2183 { 2184 if (macro.p) { 2185 afree(macro.buf, APERM); 2186 memset((char *) ¯o, 0, sizeof(macro)); 2187 } 2188 } 2189 2190 #endif /* VI */ 2191