1 /* $NetBSD: vi.c,v 1.13 2016/02/03 05:26:16 christos 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.13 2016/02/03 05:26:16 christos 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 continue; 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 continue; 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 (ncursor < es->linelen && 1576 is_wordch(es->cbuf[ncursor])) 1577 ncursor++; 1578 else if (!isspace((unsigned char)es->cbuf[ncursor])) 1579 while (ncursor < es->linelen && 1580 !is_wordch(es->cbuf[ncursor]) && 1581 !isspace((unsigned char)es->cbuf[ncursor])) 1582 ncursor++; 1583 while (ncursor < es->linelen && 1584 isspace((unsigned char)es->cbuf[ncursor])) 1585 ncursor++; 1586 } 1587 return ncursor; 1588 } 1589 1590 static int 1591 backword(argcnt) 1592 int argcnt; 1593 { 1594 int ncursor; 1595 1596 ncursor = es->cursor; 1597 while (ncursor > 0 && argcnt--) { 1598 while (--ncursor > 0 && isspace((unsigned char)es->cbuf[ncursor])) 1599 continue; 1600 if (ncursor > 0) { 1601 if (is_wordch(es->cbuf[ncursor])) 1602 while (--ncursor >= 0 && 1603 is_wordch(es->cbuf[ncursor])) 1604 continue; 1605 else 1606 while (--ncursor >= 0 && 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 endword(argcnt) 1618 int argcnt; 1619 { 1620 int ncursor; 1621 1622 ncursor = es->cursor; 1623 while (ncursor < es->linelen && argcnt--) { 1624 while (++ncursor < es->linelen - 1 && 1625 isspace((unsigned char)es->cbuf[ncursor])) 1626 continue; 1627 if (ncursor < es->linelen - 1) { 1628 if (is_wordch(es->cbuf[ncursor])) 1629 while (++ncursor < es->linelen && 1630 is_wordch(es->cbuf[ncursor])) 1631 continue; 1632 else 1633 while (++ncursor < es->linelen && 1634 !is_wordch(es->cbuf[ncursor]) && 1635 !isspace((unsigned char)es->cbuf[ncursor])) 1636 continue; 1637 ncursor--; 1638 } 1639 } 1640 return ncursor; 1641 } 1642 1643 static int 1644 Forwword(argcnt) 1645 int argcnt; 1646 { 1647 int ncursor; 1648 1649 ncursor = es->cursor; 1650 while (ncursor < es->linelen && argcnt--) { 1651 while (ncursor < es->linelen && 1652 !isspace((unsigned char)es->cbuf[ncursor])) 1653 ncursor++; 1654 while (ncursor < es->linelen && 1655 isspace((unsigned char)es->cbuf[ncursor])) 1656 ncursor++; 1657 } 1658 return ncursor; 1659 } 1660 1661 static int 1662 Backword(argcnt) 1663 int argcnt; 1664 { 1665 int ncursor; 1666 1667 ncursor = es->cursor; 1668 while (ncursor > 0 && argcnt--) { 1669 while (--ncursor >= 0 && isspace((unsigned char)es->cbuf[ncursor])) 1670 continue; 1671 while (ncursor >= 0 && !isspace((unsigned char)es->cbuf[ncursor])) 1672 ncursor--; 1673 ncursor++; 1674 } 1675 return ncursor; 1676 } 1677 1678 static int 1679 Endword(argcnt) 1680 int argcnt; 1681 { 1682 int ncursor; 1683 1684 ncursor = es->cursor; 1685 while (ncursor < es->linelen - 1 && argcnt--) { 1686 while (++ncursor < es->linelen - 1 && 1687 isspace((unsigned char)es->cbuf[ncursor])) 1688 continue; 1689 if (ncursor < es->linelen - 1) { 1690 while (++ncursor < es->linelen && 1691 !isspace((unsigned char)es->cbuf[ncursor])) 1692 continue; 1693 ncursor--; 1694 } 1695 } 1696 return ncursor; 1697 } 1698 1699 static int 1700 grabhist(save, n) 1701 int save; 1702 int n; 1703 { 1704 char *hptr; 1705 1706 if (n < 0 || n > hlast) 1707 return -1; 1708 if (n == hlast) { 1709 restore_cbuf(); 1710 ohnum = n; 1711 return 0; 1712 } 1713 (void) histnum(n); 1714 if ((hptr = *histpos()) == NULL) { 1715 internal_errorf(0, "grabhist: bad history array"); 1716 return -1; 1717 } 1718 if (save) 1719 save_cbuf(); 1720 if ((es->linelen = strlen(hptr)) >= es->cbufsize) 1721 es->linelen = es->cbufsize - 1; 1722 memmove(es->cbuf, hptr, es->linelen); 1723 es->cursor = 0; 1724 ohnum = n; 1725 return 0; 1726 } 1727 1728 static int 1729 grabsearch(save, start, fwd, pat) 1730 int save, start, fwd; 1731 char *pat; 1732 { 1733 char *hptr; 1734 int hist; 1735 int anchored; 1736 1737 if ((start == 0 && fwd == 0) || (start >= hlast-1 && fwd == 1)) 1738 return -1; 1739 if (fwd) 1740 start++; 1741 else 1742 start--; 1743 anchored = *pat == '^' ? (++pat, 1) : 0; 1744 if ((hist = findhist(start, fwd, pat, anchored)) < 0) { 1745 /* if (start != 0 && fwd && match(holdbuf, pat) >= 0) { */ 1746 /* XXX should FILECMP be strncmp? */ 1747 if (start != 0 && fwd && FILECMP(holdbuf, pat) >= 0) { 1748 restore_cbuf(); 1749 return 0; 1750 } else 1751 return -1; 1752 } 1753 if (save) 1754 save_cbuf(); 1755 histnum(hist); 1756 hptr = *histpos(); 1757 if ((es->linelen = strlen(hptr)) >= es->cbufsize) 1758 es->linelen = es->cbufsize - 1; 1759 memmove(es->cbuf, hptr, es->linelen); 1760 es->cursor = 0; 1761 return hist; 1762 } 1763 1764 static void 1765 redraw_line(newlinex) 1766 int newlinex; 1767 { 1768 (void) memset(wbuf[win], ' ', wbuf_len); 1769 if (newlinex) { 1770 x_putc('\r'); 1771 x_putc('\n'); 1772 } 1773 vi_pprompt(0); 1774 cur_col = pwidth; 1775 morec = ' '; 1776 } 1777 1778 static void 1779 refresh(leftside) 1780 int leftside; 1781 { 1782 if (leftside < 0) 1783 leftside = lastref; 1784 else 1785 lastref = leftside; 1786 if (outofwin()) 1787 rewindow(); 1788 display(wbuf[1 - win], wbuf[win], leftside); 1789 win = 1 - win; 1790 } 1791 1792 static int 1793 outofwin() 1794 { 1795 int cur, col; 1796 1797 if (es->cursor < es->winleft) 1798 return 1; 1799 col = 0; 1800 cur = es->winleft; 1801 while (cur < es->cursor) 1802 col = newcol((unsigned char) es->cbuf[cur++], col); 1803 if (col >= winwidth) 1804 return 1; 1805 return 0; 1806 } 1807 1808 static void 1809 rewindow() 1810 { 1811 register int tcur, tcol; 1812 int holdcur1, holdcol1; 1813 int holdcur2, holdcol2; 1814 1815 holdcur1 = holdcur2 = tcur = 0; 1816 holdcol1 = holdcol2 = tcol = 0; 1817 while (tcur < es->cursor) { 1818 if (tcol - holdcol2 > winwidth / 2) { 1819 holdcur1 = holdcur2; 1820 holdcol1 = holdcol2; 1821 holdcur2 = tcur; 1822 holdcol2 = tcol; 1823 } 1824 tcol = newcol((unsigned char) es->cbuf[tcur++], tcol); 1825 } 1826 while (tcol - holdcol1 > winwidth / 2) 1827 holdcol1 = newcol((unsigned char) es->cbuf[holdcur1++], 1828 holdcol1); 1829 es->winleft = holdcur1; 1830 } 1831 1832 static int 1833 newcol(ch, col) 1834 int ch, col; 1835 { 1836 if (ch == '\t') 1837 return (col | 7) + 1; 1838 return col + char_len(ch); 1839 } 1840 1841 static void 1842 display(wb1, wb2, leftside) 1843 char *wb1, *wb2; 1844 int leftside; 1845 { 1846 unsigned char ch; 1847 char *twb1, *twb2, mc; 1848 int cur, col, cnt; 1849 int UNINITIALIZED(ncol); 1850 int moreright; 1851 1852 col = 0; 1853 cur = es->winleft; 1854 moreright = 0; 1855 twb1 = wb1; 1856 while (col < winwidth && cur < es->linelen) { 1857 if (cur == es->cursor && leftside) 1858 ncol = col + pwidth; 1859 if ((ch = es->cbuf[cur]) == '\t') { 1860 do { 1861 *twb1++ = ' '; 1862 } while (++col < winwidth && (col & 7) != 0); 1863 } else { 1864 if ((ch & 0x80) && Flag(FVISHOW8)) { 1865 *twb1++ = 'M'; 1866 if (++col < winwidth) { 1867 *twb1++ = '-'; 1868 col++; 1869 } 1870 ch &= 0x7f; 1871 } 1872 if (col < winwidth) { 1873 if (ch < ' ' || ch == 0x7f) { 1874 *twb1++ = '^'; 1875 if (++col < winwidth) { 1876 *twb1++ = ch ^ '@'; 1877 col++; 1878 } 1879 } else { 1880 *twb1++ = ch; 1881 col++; 1882 } 1883 } 1884 } 1885 if (cur == es->cursor && !leftside) 1886 ncol = col + pwidth - 1; 1887 cur++; 1888 } 1889 if (cur == es->cursor) 1890 ncol = col + pwidth; 1891 if (col < winwidth) { 1892 while (col < winwidth) { 1893 *twb1++ = ' '; 1894 col++; 1895 } 1896 } else 1897 moreright++; 1898 *twb1 = ' '; 1899 1900 col = pwidth; 1901 cnt = winwidth; 1902 twb1 = wb1; 1903 twb2 = wb2; 1904 while (cnt--) { 1905 if (*twb1 != *twb2) { 1906 if (cur_col != col) 1907 ed_mov_opt(col, wb1); 1908 x_putc(*twb1); 1909 cur_col++; 1910 } 1911 twb1++; 1912 twb2++; 1913 col++; 1914 } 1915 if (es->winleft > 0 && moreright) 1916 /* POSIX says to use * for this but that is a globbing 1917 * character and may confuse people; + is more innocuous 1918 */ 1919 mc = '+'; 1920 else if (es->winleft > 0) 1921 mc = '<'; 1922 else if (moreright) 1923 mc = '>'; 1924 else 1925 mc = ' '; 1926 if (mc != morec) { 1927 ed_mov_opt(pwidth + winwidth + 1, wb1); 1928 x_putc(mc); 1929 cur_col++; 1930 morec = mc; 1931 } 1932 if (cur_col != ncol) 1933 ed_mov_opt(ncol, wb1); 1934 } 1935 1936 static void 1937 ed_mov_opt(col, wb) 1938 int col; 1939 char *wb; 1940 { 1941 if (col < cur_col) { 1942 if (col + 1 < cur_col - col) { 1943 x_putc('\r'); 1944 vi_pprompt(0); 1945 cur_col = pwidth; 1946 while (cur_col++ < col) 1947 x_putc(*wb++); 1948 } else { 1949 while (cur_col-- > col) 1950 x_putc('\b'); 1951 } 1952 } else { 1953 wb = &wb[cur_col - pwidth]; 1954 while (cur_col++ < col) 1955 x_putc(*wb++); 1956 } 1957 cur_col = col; 1958 } 1959 1960 1961 /* replace word with all expansions (ie, expand word*) */ 1962 static int 1963 expand_word(commandx) 1964 int commandx; 1965 { 1966 static struct edstate *buf; 1967 int rval = 0; 1968 int nwords; 1969 int start, end; 1970 char **words; 1971 int i; 1972 1973 /* Undo previous expansion */ 1974 if (commandx == 0 && expanded == EXPAND && buf) { 1975 restore_edstate(es, buf); 1976 buf = 0; 1977 expanded = NONE; 1978 return 0; 1979 } 1980 if (buf) { 1981 free_edstate(buf); 1982 buf = 0; 1983 } 1984 1985 nwords = x_cf_glob(XCF_COMMAND_FILE|XCF_FULLPATH, 1986 es->cbuf, es->linelen, es->cursor, 1987 &start, &end, &words, (int *) 0); 1988 if (nwords == 0) { 1989 vi_error(); 1990 return -1; 1991 } 1992 1993 buf = save_edstate(es); 1994 expanded = EXPAND; 1995 del_range(start, end); 1996 es->cursor = start; 1997 for (i = 0; i < nwords; ) { 1998 if (x_escape(words[i], strlen(words[i]), x_vi_putbuf) != 0) { 1999 rval = -1; 2000 break; 2001 } 2002 if (++i < nwords && putbuf(space, 1, 0) != 0) { 2003 rval = -1; 2004 break; 2005 } 2006 } 2007 i = buf->cursor - end; 2008 if (rval == 0 && i > 0) 2009 es->cursor += i; 2010 modified = 1; hnum = hlast; 2011 insert = INSERT; 2012 lastac = 0; 2013 refresh(0); 2014 return rval; 2015 } 2016 2017 static int 2018 complete_word(commandx, count) 2019 int commandx; 2020 int count; 2021 { 2022 static struct edstate *buf; 2023 int rval = 0; 2024 int nwords; 2025 int start, end; 2026 char **words; 2027 char *match; 2028 int match_len; 2029 int is_unique; 2030 int is_command; 2031 2032 /* Undo previous completion */ 2033 if (commandx == 0 && expanded == COMPLETE && buf) { 2034 print_expansions(buf, 0); 2035 expanded = PRINT; 2036 return 0; 2037 } 2038 if (commandx == 0 && expanded == PRINT && buf) { 2039 restore_edstate(es, buf); 2040 buf = 0; 2041 expanded = NONE; 2042 return 0; 2043 } 2044 if (buf) { 2045 free_edstate(buf); 2046 buf = 0; 2047 } 2048 2049 /* XCF_FULLPATH for count 'cause the menu printed by print_expansions() 2050 * was done this way. 2051 */ 2052 nwords = x_cf_glob(XCF_COMMAND_FILE | (count ? XCF_FULLPATH : 0), 2053 es->cbuf, es->linelen, es->cursor, 2054 &start, &end, &words, &is_command); 2055 if (nwords == 0) { 2056 vi_error(); 2057 return -1; 2058 } 2059 if (count) { 2060 int i; 2061 2062 count--; 2063 if (count >= nwords) { 2064 vi_error(); 2065 x_print_expansions(nwords, words, is_command); 2066 x_free_words(nwords, words); 2067 redraw_line(0); 2068 return -1; 2069 } 2070 /* 2071 * Expand the count'th word to its basename 2072 */ 2073 if (is_command) { 2074 match = words[count] 2075 + x_basename(words[count], (char *) 0); 2076 /* If more than one possible match, use full path */ 2077 for (i = 0; i < nwords; i++) 2078 if (i != count && 2079 FILECMP(words[i] 2080 + x_basename(words[i], (char *) 0), 2081 match) == 0) 2082 { 2083 match = words[count]; 2084 break; 2085 } 2086 } else 2087 match = words[count]; 2088 match_len = strlen(match); 2089 is_unique = 1; 2090 /* expanded = PRINT; next call undo */ 2091 } else { 2092 match = words[0]; 2093 match_len = x_longest_prefix(nwords, words); 2094 expanded = COMPLETE; /* next call will list completions */ 2095 is_unique = nwords == 1; 2096 } 2097 2098 buf = save_edstate(es); 2099 del_range(start, end); 2100 es->cursor = start; 2101 2102 /* escape all shell-sensitive characters and put the result into 2103 * command buffer */ 2104 rval = x_escape(match, match_len, x_vi_putbuf); 2105 2106 if (rval == 0 && is_unique) { 2107 /* If exact match, don't undo. Allows directory completions 2108 * to be used (ie, complete the next portion of the path). 2109 */ 2110 expanded = NONE; 2111 2112 /* If not a directory, add a space to the end... */ 2113 if (match_len > 0 && !ISDIRSEP(match[match_len - 1])) 2114 rval = putbuf(space, 1, 0); 2115 } 2116 x_free_words(nwords, words); 2117 2118 modified = 1; hnum = hlast; 2119 insert = INSERT; 2120 lastac = 0; /* prevent this from being redone... */ 2121 refresh(0); 2122 2123 return rval; 2124 } 2125 2126 static int 2127 print_expansions(ex, commandx) 2128 struct edstate *ex; 2129 int commandx; 2130 { 2131 int nwords; 2132 int start, end; 2133 char **words; 2134 int is_command; 2135 2136 nwords = x_cf_glob(XCF_COMMAND_FILE|XCF_FULLPATH, 2137 ex->cbuf, ex->linelen, ex->cursor, 2138 &start, &end, &words, &is_command); 2139 if (nwords == 0) { 2140 vi_error(); 2141 return -1; 2142 } 2143 x_print_expansions(nwords, words, is_command); 2144 x_free_words(nwords, words); 2145 redraw_line(0); 2146 return 0; 2147 } 2148 2149 /* How long is char when displayed (not counting tabs) */ 2150 static int 2151 char_len(c) 2152 int c; 2153 { 2154 int len = 1; 2155 2156 if ((c & 0x80) && Flag(FVISHOW8)) { 2157 len += 2; 2158 c &= 0x7f; 2159 } 2160 if (c < ' ' || c == 0x7f) 2161 len++; 2162 return len; 2163 } 2164 2165 /* Similar to x_zotc(emacs.c), but no tab weirdness */ 2166 static void 2167 x_vi_zotc(c) 2168 int c; 2169 { 2170 if (Flag(FVISHOW8) && (c & 0x80)) { 2171 x_puts("M-"); 2172 c &= 0x7f; 2173 } 2174 if (c < ' ' || c == 0x7f) { 2175 x_putc('^'); 2176 c ^= '@'; 2177 } 2178 x_putc(c); 2179 } 2180 2181 static void 2182 vi_pprompt(full) 2183 int full; 2184 { 2185 pprompt(prompt + (full ? 0 : prompt_skip), prompt_trunc); 2186 } 2187 2188 static void 2189 vi_error() 2190 { 2191 /* Beem out of any macros as soon as an error occurs */ 2192 vi_macro_reset(); 2193 x_putc(BEL); 2194 x_flush(); 2195 } 2196 2197 static void 2198 vi_macro_reset() 2199 { 2200 if (macro.p) { 2201 afree(macro.buf, APERM); 2202 memset((char *) ¯o, 0, sizeof(macro)); 2203 } 2204 } 2205 2206 #endif /* VI */ 2207