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