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