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