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