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