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