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