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