1 /* $OpenBSD: edit.c,v 1.41 2015/09/01 13:12:31 tedu Exp $ */ 2 /* $OpenBSD: edit.h,v 1.9 2011/05/30 17:14:35 martynas Exp $ */ 3 /* $OpenBSD: emacs.c,v 1.52 2015/09/10 22:48:58 nicm Exp $ */ 4 /* $OpenBSD: vi.c,v 1.30 2015/09/10 22:48:58 nicm Exp $ */ 5 6 /*- 7 * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 8 * 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 9 * mirabilos <m (at) mirbsd.org> 10 * 11 * Provided that these terms and disclaimer and all copyright notices 12 * are retained or reproduced in an accompanying document, permission 13 * is granted to deal in this work without restriction, including un- 14 * limited rights to use, publicly perform, distribute, sell, modify, 15 * merge, give away, or sublicence. 16 * 17 * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to 18 * the utmost extent permitted by applicable law, neither express nor 19 * implied; without malicious intent or gross negligence. In no event 20 * may a licensor, author or contributor be held liable for indirect, 21 * direct, other damage, loss, or other issues arising in any way out 22 * of dealing in the work, even if advised of the possibility of such 23 * damage or existence of a defect, except proven that it results out 24 * of said person's immediate fault when using the work as intended. 25 */ 26 27 #include "sh.h" 28 29 #ifndef MKSH_NO_CMDLINE_EDITING 30 31 __RCSID("$MirOS: src/bin/mksh/edit.c,v 1.342 2018/01/14 00:03:00 tg Exp $"); 32 33 /* 34 * in later versions we might use libtermcap for this, but since external 35 * dependencies are problematic, this has not yet been decided on; another 36 * good string is KSH_ESC_STRING "c" except on hardware terminals like the 37 * DEC VT420 which do a full power cycle then... 38 */ 39 #ifndef MKSH_CLS_STRING 40 #define MKSH_CLS_STRING KSH_ESC_STRING "[;H" KSH_ESC_STRING "[J" 41 #endif 42 43 /* tty driver characters we are interested in */ 44 #define EDCHAR_DISABLED 0xFFFFU 45 #define EDCHAR_INITIAL 0xFFFEU 46 static struct { 47 unsigned short erase; 48 unsigned short kill; 49 unsigned short werase; 50 unsigned short intr; 51 unsigned short quit; 52 unsigned short eof; 53 } edchars; 54 55 #define isched(x,e) ((unsigned short)(unsigned char)(x) == (e)) 56 #define isedchar(x) (!((x) & ~0xFF)) 57 #ifndef _POSIX_VDISABLE 58 #define toedchar(x) ((unsigned short)(unsigned char)(x)) 59 #else 60 #define toedchar(x) (((_POSIX_VDISABLE != -1) && ((x) == _POSIX_VDISABLE)) ? \ 61 ((unsigned short)EDCHAR_DISABLED) : \ 62 ((unsigned short)(unsigned char)(x))) 63 #endif 64 65 /* x_cf_glob() flags */ 66 #define XCF_COMMAND BIT(0) /* Do command completion */ 67 #define XCF_FILE BIT(1) /* Do file completion */ 68 #define XCF_FULLPATH BIT(2) /* command completion: store full path */ 69 #define XCF_COMMAND_FILE (XCF_COMMAND | XCF_FILE) 70 #define XCF_IS_COMMAND BIT(3) /* return flag: is command */ 71 #define XCF_IS_NOSPACE BIT(4) /* return flag: do not append a space */ 72 73 static char editmode; 74 static int xx_cols; /* for Emacs mode */ 75 static int modified; /* buffer has been "modified" */ 76 static char *holdbufp; /* place to hold last edit buffer */ 77 78 /* 0=dumb 1=tmux (for now) */ 79 static uint8_t x_term_mode; 80 81 static void x_adjust(void); 82 static int x_getc(void); 83 static void x_putcf(int); 84 static void x_modified(void); 85 static void x_mode(bool); 86 static int x_do_comment(char *, ssize_t, ssize_t *); 87 static void x_print_expansions(int, char * const *, bool); 88 static int x_cf_glob(int *, const char *, int, int, int *, int *, char ***); 89 static size_t x_longest_prefix(int, char * const *); 90 static void x_glob_hlp_add_qchar(char *); 91 static char *x_glob_hlp_tilde_and_rem_qchar(char *, bool); 92 static size_t x_basename(const char *, const char *); 93 static void x_free_words(int, char **); 94 static int x_escape(const char *, size_t, int (*)(const char *, size_t)); 95 static int x_emacs(char *); 96 static void x_init_prompt(bool); 97 #if !MKSH_S_NOVI 98 static int x_vi(char *); 99 #endif 100 static void x_intr(int, int) MKSH_A_NORETURN; 101 102 #define x_flush() shf_flush(shl_out) 103 #if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST) 104 #define x_putc(c) x_putcf(c) 105 #else 106 #define x_putc(c) shf_putc((c), shl_out) 107 #endif 108 109 static int path_order_cmp(const void *, const void *); 110 static void glob_table(const char *, XPtrV *, struct table *); 111 static void glob_path(int, const char *, XPtrV *, const char *); 112 static int x_file_glob(int *, char *, char ***); 113 static int x_command_glob(int, char *, char ***); 114 static int x_locate_word(const char *, int, int, int *, bool *); 115 116 static int x_e_getmbc(char *); 117 118 /* +++ generic editing functions +++ */ 119 120 /* 121 * read an edited command line 122 */ 123 int 124 x_read(char *buf) 125 { 126 int i; 127 128 x_mode(true); 129 modified = 1; 130 if (Flag(FEMACS) || Flag(FGMACS)) 131 i = x_emacs(buf); 132 #if !MKSH_S_NOVI 133 else if (Flag(FVI)) 134 i = x_vi(buf); 135 #endif 136 else 137 /* internal error */ 138 i = -1; 139 editmode = 0; 140 x_mode(false); 141 return (i); 142 } 143 144 /* tty I/O */ 145 146 static int 147 x_getc(void) 148 { 149 #ifdef __OS2__ 150 return (_read_kbd(0, 1, 0)); 151 #else 152 char c; 153 ssize_t n; 154 155 while ((n = blocking_read(STDIN_FILENO, &c, 1)) < 0 && errno == EINTR) 156 if (trap) { 157 x_mode(false); 158 runtraps(0); 159 #ifdef SIGWINCH 160 if (got_winch) { 161 change_winsz(); 162 if (x_cols != xx_cols && editmode == 1) { 163 /* redraw line in Emacs mode */ 164 xx_cols = x_cols; 165 x_init_prompt(false); 166 x_adjust(); 167 } 168 } 169 #endif 170 x_mode(true); 171 } 172 return ((n == 1) ? (int)(unsigned char)c : -1); 173 #endif 174 } 175 176 static void 177 x_putcf(int c) 178 { 179 shf_putc(c, shl_out); 180 } 181 182 /********************************* 183 * Misc common code for vi/emacs * 184 *********************************/ 185 186 /*- 187 * Handle the commenting/uncommenting of a line. 188 * Returns: 189 * 1 if a carriage return is indicated (comment added) 190 * 0 if no return (comment removed) 191 * -1 if there is an error (not enough room for comment chars) 192 * If successful, *lenp contains the new length. Note: cursor should be 193 * moved to the start of the line after (un)commenting. 194 */ 195 static int 196 x_do_comment(char *buf, ssize_t bsize, ssize_t *lenp) 197 { 198 ssize_t i, j, len = *lenp; 199 200 if (len == 0) 201 /* somewhat arbitrary - it's what AT&T ksh does */ 202 return (1); 203 204 /* Already commented? */ 205 if (buf[0] == '#') { 206 bool saw_nl = false; 207 208 for (j = 0, i = 1; i < len; i++) { 209 if (!saw_nl || buf[i] != '#') 210 buf[j++] = buf[i]; 211 saw_nl = buf[i] == '\n'; 212 } 213 *lenp = j; 214 return (0); 215 } else { 216 int n = 1; 217 218 /* See if there's room for the #s - 1 per \n */ 219 for (i = 0; i < len; i++) 220 if (buf[i] == '\n') 221 n++; 222 if (len + n >= bsize) 223 return (-1); 224 /* Now add them... */ 225 for (i = len, j = len + n; --i >= 0; ) { 226 if (buf[i] == '\n') 227 buf[--j] = '#'; 228 buf[--j] = buf[i]; 229 } 230 buf[0] = '#'; 231 *lenp += n; 232 return (1); 233 } 234 } 235 236 /**************************************************** 237 * Common file/command completion code for vi/emacs * 238 ****************************************************/ 239 240 static void 241 x_print_expansions(int nwords, char * const *words, bool is_command) 242 { 243 bool use_copy = false; 244 size_t prefix_len; 245 XPtrV l = { NULL, 0, 0 }; 246 struct columnise_opts co; 247 248 /* 249 * Check if all matches are in the same directory (in this 250 * case, we want to omit the directory name) 251 */ 252 if (!is_command && 253 (prefix_len = x_longest_prefix(nwords, words)) > 0) { 254 int i; 255 256 /* Special case for 1 match (prefix is whole word) */ 257 if (nwords == 1) 258 prefix_len = x_basename(words[0], NULL); 259 /* Any (non-trailing) slashes in non-common word suffixes? */ 260 for (i = 0; i < nwords; i++) 261 if (x_basename(words[i] + prefix_len, NULL) > 262 prefix_len) 263 break; 264 /* All in same directory? */ 265 if (i == nwords) { 266 while (prefix_len > 0 && 267 !mksh_cdirsep(words[0][prefix_len - 1])) 268 prefix_len--; 269 use_copy = true; 270 XPinit(l, nwords + 1); 271 for (i = 0; i < nwords; i++) 272 XPput(l, words[i] + prefix_len); 273 XPput(l, NULL); 274 } 275 } 276 /* 277 * Enumerate expansions 278 */ 279 x_putc('\r'); 280 x_putc('\n'); 281 co.shf = shl_out; 282 co.linesep = '\n'; 283 co.do_last = true; 284 co.prefcol = false; 285 pr_list(&co, use_copy ? (char **)XPptrv(l) : words); 286 287 if (use_copy) 288 /* not x_free_words() */ 289 XPfree(l); 290 } 291 292 /* 293 * Convert backslash-escaped string to QCHAR-escaped 294 * string useful for globbing; loses QCHAR unless it 295 * can squeeze in, eg. by previous loss of backslash 296 */ 297 static void 298 x_glob_hlp_add_qchar(char *cp) 299 { 300 char ch, *dp = cp; 301 bool escaping = false; 302 303 while ((ch = *cp++)) { 304 if (ch == '\\' && !escaping) { 305 escaping = true; 306 continue; 307 } 308 if (escaping || (ch == QCHAR && (cp - dp) > 1)) { 309 /* 310 * empirically made list of chars to escape 311 * for globbing as well as QCHAR itself 312 */ 313 switch (ord(ch)) { 314 case QCHAR: 315 case ORD('$'): 316 case ORD('*'): 317 case ORD('?'): 318 case ORD('['): 319 case ORD('\\'): 320 case ORD('`'): 321 *dp++ = QCHAR; 322 break; 323 } 324 escaping = false; 325 } 326 *dp++ = ch; 327 } 328 *dp = '\0'; 329 } 330 331 /* 332 * Run tilde expansion on argument string, return the result 333 * after unescaping; if the flag is set, the original string 334 * is freed if changed and assumed backslash-escaped, if not 335 * it is assumed QCHAR-escaped 336 */ 337 static char * 338 x_glob_hlp_tilde_and_rem_qchar(char *s, bool magic_flag) 339 { 340 char ch, *cp, *dp; 341 342 /* 343 * On the string, check whether we have a tilde expansion, 344 * and if so, discern "~foo/bar" and "~/baz" from "~blah"; 345 * if we have a directory part (the former), try to expand 346 */ 347 if (*s == '~' && (cp = /* not sdirsep */ strchr(s, '/')) != NULL) { 348 /* ok, so split into "~foo"/"bar" or "~"/"baz" */ 349 *cp++ = 0; 350 /* try to expand the tilde */ 351 if (!(dp = do_tilde(s + 1))) { 352 /* nope, revert damage */ 353 *--cp = '/'; 354 } else { 355 /* ok, expand and replace */ 356 cp = shf_smprintf(Tf_sSs, dp, cp); 357 if (magic_flag) 358 afree(s, ATEMP); 359 s = cp; 360 } 361 } 362 363 /* ... convert it from backslash-escaped via QCHAR-escaped... */ 364 if (magic_flag) 365 x_glob_hlp_add_qchar(s); 366 /* ... to unescaped, for comparison with the matches */ 367 cp = dp = s; 368 369 while ((ch = *cp++)) { 370 if (ch == QCHAR && !(ch = *cp++)) 371 break; 372 *dp++ = ch; 373 } 374 *dp = '\0'; 375 376 return (s); 377 } 378 379 /** 380 * Do file globbing: 381 * - does expansion, checks for no match, etc. 382 * - sets *wordsp to array of matching strings 383 * - returns number of matching strings 384 */ 385 static int 386 x_file_glob(int *flagsp, char *toglob, char ***wordsp) 387 { 388 char **words, *cp; 389 int nwords; 390 XPtrV w; 391 struct source *s, *sold; 392 393 /* remove all escaping backward slashes */ 394 x_glob_hlp_add_qchar(toglob); 395 396 /* 397 * Convert "foo*" (toglob) to an array of strings (words) 398 */ 399 sold = source; 400 s = pushs(SWSTR, ATEMP); 401 s->start = s->str = toglob; 402 source = s; 403 if (yylex(ONEWORD | LQCHAR) != LWORD) { 404 source = sold; 405 internal_warningf(Tfg_badsubst); 406 return (0); 407 } 408 source = sold; 409 afree(s, ATEMP); 410 XPinit(w, 32); 411 cp = yylval.cp; 412 while (*cp == CHAR || *cp == QCHAR) 413 cp += 2; 414 nwords = DOGLOB | DOTILDE | DOMARKDIRS; 415 if (*cp != EOS) { 416 /* probably a $FOO expansion */ 417 *flagsp |= XCF_IS_NOSPACE; 418 /* this always results in at most one match */ 419 nwords = 0; 420 } 421 expand(yylval.cp, &w, nwords); 422 XPput(w, NULL); 423 words = (char **)XPclose(w); 424 425 for (nwords = 0; words[nwords]; nwords++) 426 ; 427 if (nwords == 1) { 428 struct stat statb; 429 430 /* Expand any tilde and drop all QCHAR for comparison */ 431 toglob = x_glob_hlp_tilde_and_rem_qchar(toglob, false); 432 433 /* 434 * Check if globbing failed (returned glob pattern), 435 * but be careful (e.g. toglob == "ab*" when the file 436 * "ab*" exists is not an error). 437 * Also, check for empty result - happens if we tried 438 * to glob something which evaluated to an empty 439 * string (e.g., "$FOO" when there is no FOO, etc). 440 */ 441 if ((strcmp(words[0], toglob) == 0 && 442 stat(words[0], &statb) < 0) || 443 words[0][0] == '\0') { 444 x_free_words(nwords, words); 445 words = NULL; 446 nwords = 0; 447 } 448 } 449 450 if ((*wordsp = nwords ? words : NULL) == NULL && words != NULL) 451 x_free_words(nwords, words); 452 453 return (nwords); 454 } 455 456 /* Data structure used in x_command_glob() */ 457 struct path_order_info { 458 char *word; 459 size_t base; 460 size_t path_order; 461 }; 462 463 /* Compare routine used in x_command_glob() */ 464 static int 465 path_order_cmp(const void *aa, const void *bb) 466 { 467 const struct path_order_info *a = (const struct path_order_info *)aa; 468 const struct path_order_info *b = (const struct path_order_info *)bb; 469 int t; 470 471 if ((t = ascstrcmp(a->word + a->base, b->word + b->base))) 472 return (t); 473 if (a->path_order > b->path_order) 474 return (1); 475 if (a->path_order < b->path_order) 476 return (-1); 477 return (0); 478 } 479 480 static int 481 x_command_glob(int flags, char *toglob, char ***wordsp) 482 { 483 char *pat, *fpath; 484 size_t nwords; 485 XPtrV w; 486 struct block *l; 487 488 /* Convert "foo*" (toglob) to a pattern for future use */ 489 pat = evalstr(toglob, DOPAT | DOTILDE); 490 491 XPinit(w, 32); 492 493 glob_table(pat, &w, &keywords); 494 glob_table(pat, &w, &aliases); 495 glob_table(pat, &w, &builtins); 496 for (l = e->loc; l; l = l->next) 497 glob_table(pat, &w, &l->funs); 498 499 glob_path(flags, pat, &w, path); 500 if ((fpath = str_val(global(TFPATH))) != null) 501 glob_path(flags, pat, &w, fpath); 502 503 nwords = XPsize(w); 504 505 if (!nwords) { 506 *wordsp = NULL; 507 XPfree(w); 508 return (0); 509 } 510 /* Sort entries */ 511 if (flags & XCF_FULLPATH) { 512 /* Sort by basename, then path order */ 513 struct path_order_info *info, *last_info = NULL; 514 char **words = (char **)XPptrv(w); 515 size_t i, path_order = 0; 516 517 info = (struct path_order_info *) 518 alloc2(nwords, sizeof(struct path_order_info), ATEMP); 519 for (i = 0; i < nwords; i++) { 520 info[i].word = words[i]; 521 info[i].base = x_basename(words[i], NULL); 522 if (!last_info || info[i].base != last_info->base || 523 strncmp(words[i], last_info->word, info[i].base) != 0) { 524 last_info = &info[i]; 525 path_order++; 526 } 527 info[i].path_order = path_order; 528 } 529 qsort(info, nwords, sizeof(struct path_order_info), 530 path_order_cmp); 531 for (i = 0; i < nwords; i++) 532 words[i] = info[i].word; 533 afree(info, ATEMP); 534 } else { 535 /* Sort and remove duplicate entries */ 536 char **words = (char **)XPptrv(w); 537 size_t i, j; 538 539 qsort(words, nwords, sizeof(void *), ascpstrcmp); 540 for (i = j = 0; i < nwords - 1; i++) { 541 if (strcmp(words[i], words[i + 1])) 542 words[j++] = words[i]; 543 else 544 afree(words[i], ATEMP); 545 } 546 words[j++] = words[i]; 547 w.len = nwords = j; 548 } 549 550 XPput(w, NULL); 551 *wordsp = (char **)XPclose(w); 552 553 return (nwords); 554 } 555 556 #define IS_WORDC(c) (!ctype(c, C_EDNWC)) 557 558 static int 559 x_locate_word(const char *buf, int buflen, int pos, int *startp, 560 bool *is_commandp) 561 { 562 int start, end; 563 564 /* Bad call? Probably should report error */ 565 if (pos < 0 || pos > buflen) { 566 *startp = pos; 567 *is_commandp = false; 568 return (0); 569 } 570 /* The case where pos == buflen happens to take care of itself... */ 571 572 start = pos; 573 /* 574 * Keep going backwards to start of word (has effect of allowing 575 * one blank after the end of a word) 576 */ 577 for (; (start > 0 && IS_WORDC(buf[start - 1])) || 578 (start > 1 && buf[start - 2] == '\\'); start--) 579 ; 580 /* Go forwards to end of word */ 581 for (end = start; end < buflen && IS_WORDC(buf[end]); end++) { 582 if (buf[end] == '\\' && (end + 1) < buflen) 583 end++; 584 } 585 586 if (is_commandp) { 587 bool iscmd; 588 int p = start - 1; 589 590 /* Figure out if this is a command */ 591 while (p >= 0 && ctype(buf[p], C_SPACE)) 592 p--; 593 iscmd = p < 0 || ctype(buf[p], C_EDCMD); 594 if (iscmd) { 595 /* 596 * If command has a /, path, etc. is not searched; 597 * only current directory is searched which is just 598 * like file globbing. 599 */ 600 for (p = start; p < end; p++) 601 if (mksh_cdirsep(buf[p])) 602 break; 603 iscmd = p == end; 604 } 605 *is_commandp = iscmd; 606 } 607 *startp = start; 608 609 return (end - start); 610 } 611 612 static int 613 x_cf_glob(int *flagsp, const char *buf, int buflen, int pos, int *startp, 614 int *endp, char ***wordsp) 615 { 616 int len, nwords = 0; 617 char **words = NULL; 618 bool is_command; 619 620 len = x_locate_word(buf, buflen, pos, startp, &is_command); 621 if (!((*flagsp) & XCF_COMMAND)) 622 is_command = false; 623 /* 624 * Don't do command globing on zero length strings - it takes too 625 * long and isn't very useful. File globs are more likely to be 626 * useful, so allow these. 627 */ 628 if (len == 0 && is_command) 629 return (0); 630 631 if (len >= 0) { 632 char *toglob, *s; 633 634 /* 635 * Given a string, copy it and possibly add a '*' to the end. 636 */ 637 638 strndupx(toglob, buf + *startp, len + /* the '*' */ 1, ATEMP); 639 toglob[len] = '\0'; 640 641 /* 642 * If the pathname contains a wildcard (an unquoted '*', 643 * '?', or '[') or an extglob, then it is globbed based 644 * on that value (i.e., without the appended '*'). Same 645 * for parameter substitutions (as in cat $HOME/.ss) 646 * without appending a trailing space (LP: #710539), as 647 * well as for ~foo (but not ~foo/). 648 */ 649 for (s = toglob; *s; s++) { 650 if (*s == '\\' && s[1]) 651 s++; 652 else if (ctype(*s, C_QUEST | C_DOLAR) || 653 ord(*s) == ORD('*') || ord(*s) == ORD('[') || 654 /* ?() *() +() @() !() but two already checked */ 655 (ord(s[1]) == ORD('(' /*)*/) && 656 (ord(*s) == ORD('+') || ord(*s) == ORD('@') || 657 ord(*s) == ORD('!')))) { 658 /* 659 * just expand based on the extglob 660 * or parameter 661 */ 662 goto dont_add_glob; 663 } 664 } 665 666 if (*toglob == '~' && /* not vdirsep */ !vstrchr(toglob, '/')) { 667 /* neither for '~foo' (but '~foo/bar') */ 668 *flagsp |= XCF_IS_NOSPACE; 669 goto dont_add_glob; 670 } 671 672 /* append a glob */ 673 toglob[len] = '*'; 674 toglob[len + 1] = '\0'; 675 dont_add_glob: 676 /* 677 * Expand (glob) it now. 678 */ 679 680 nwords = is_command ? 681 x_command_glob(*flagsp, toglob, &words) : 682 x_file_glob(flagsp, toglob, &words); 683 afree(toglob, ATEMP); 684 } 685 if (nwords == 0) { 686 *wordsp = NULL; 687 return (0); 688 } 689 if (is_command) 690 *flagsp |= XCF_IS_COMMAND; 691 *wordsp = words; 692 *endp = *startp + len; 693 694 return (nwords); 695 } 696 697 /* 698 * Find longest common prefix 699 */ 700 static size_t 701 x_longest_prefix(int nwords, char * const * words) 702 { 703 int i; 704 size_t j, prefix_len; 705 char *p; 706 707 if (nwords <= 0) 708 return (0); 709 710 prefix_len = strlen(words[0]); 711 for (i = 1; i < nwords; i++) 712 for (j = 0, p = words[i]; j < prefix_len; j++) 713 if (p[j] != words[0][j]) { 714 prefix_len = j; 715 break; 716 } 717 /* false for nwords==1 as 0 = words[0][prefix_len] then */ 718 if (UTFMODE && prefix_len && (rtt2asc(words[0][prefix_len]) & 0xC0) == 0x80) 719 while (prefix_len && (rtt2asc(words[0][prefix_len]) & 0xC0) != 0xC0) 720 --prefix_len; 721 return (prefix_len); 722 } 723 724 static void 725 x_free_words(int nwords, char **words) 726 { 727 while (nwords) 728 afree(words[--nwords], ATEMP); 729 afree(words, ATEMP); 730 } 731 732 /*- 733 * Return the offset of the basename of string s (which ends at se - need not 734 * be null terminated). Trailing slashes are ignored. If s is just a slash, 735 * then the offset is 0 (actually, length - 1). 736 * s Return 737 * /etc 1 738 * /etc/ 1 739 * /etc// 1 740 * /etc/fo 5 741 * foo 0 742 * /// 2 743 * 0 744 */ 745 static size_t 746 x_basename(const char *s, const char *se) 747 { 748 const char *p; 749 750 if (se == NULL) 751 se = strnul(s); 752 if (s == se) 753 return (0); 754 755 /* skip trailing directory separators */ 756 p = se - 1; 757 while (p > s && mksh_cdirsep(*p)) 758 --p; 759 /* drop last component */ 760 while (p > s && !mksh_cdirsep(*p)) 761 --p; 762 if (mksh_cdirsep(*p) && p + 1 < se) 763 ++p; 764 765 return (p - s); 766 } 767 768 /* 769 * Apply pattern matching to a table: all table entries that match a pattern 770 * are added to wp. 771 */ 772 static void 773 glob_table(const char *pat, XPtrV *wp, struct table *tp) 774 { 775 struct tstate ts; 776 struct tbl *te; 777 778 ktwalk(&ts, tp); 779 while ((te = ktnext(&ts))) 780 if (gmatchx(te->name, pat, false)) { 781 char *cp; 782 783 strdupx(cp, te->name, ATEMP); 784 XPput(*wp, cp); 785 } 786 } 787 788 static void 789 glob_path(int flags, const char *pat, XPtrV *wp, const char *lpath) 790 { 791 const char *sp = lpath, *p; 792 char *xp, **words; 793 size_t pathlen, patlen, oldsize, newsize, i, j; 794 XString xs; 795 796 patlen = strlen(pat); 797 checkoktoadd(patlen, 129 + X_EXTRA); 798 ++patlen; 799 Xinit(xs, xp, patlen + 128, ATEMP); 800 while (sp) { 801 xp = Xstring(xs, xp); 802 if (!(p = cstrchr(sp, MKSH_PATHSEPC))) 803 p = strnul(sp); 804 pathlen = p - sp; 805 if (pathlen) { 806 /* 807 * Copy sp into xp, stuffing any MAGIC characters 808 * on the way 809 */ 810 const char *s = sp; 811 812 XcheckN(xs, xp, pathlen * 2); 813 while (s < p) { 814 if (ISMAGIC(*s)) 815 *xp++ = MAGIC; 816 *xp++ = *s++; 817 } 818 *xp++ = '/'; 819 pathlen++; 820 } 821 sp = p; 822 XcheckN(xs, xp, patlen); 823 memcpy(xp, pat, patlen); 824 825 oldsize = XPsize(*wp); 826 /* mark dirs */ 827 glob_str(Xstring(xs, xp), wp, true); 828 newsize = XPsize(*wp); 829 830 /* Check that each match is executable... */ 831 words = (char **)XPptrv(*wp); 832 for (i = j = oldsize; i < newsize; i++) { 833 if (ksh_access(words[i], X_OK) == 0) { 834 words[j] = words[i]; 835 if (!(flags & XCF_FULLPATH)) 836 memmove(words[j], words[j] + pathlen, 837 strlen(words[j] + pathlen) + 1); 838 j++; 839 } else 840 afree(words[i], ATEMP); 841 } 842 wp->len = j; 843 844 if (!*sp++) 845 break; 846 } 847 Xfree(xs, xp); 848 } 849 850 /* 851 * if argument string contains any special characters, they will 852 * be escaped and the result will be put into edit buffer by 853 * keybinding-specific function 854 */ 855 static int 856 x_escape(const char *s, size_t len, int (*putbuf_func)(const char *, size_t)) 857 { 858 size_t add = 0, wlen = len; 859 int rval = 0; 860 861 while (wlen - add > 0) 862 if (ctype(s[add], C_IFS | C_EDQ)) { 863 if (putbuf_func(s, add) != 0) { 864 rval = -1; 865 break; 866 } 867 putbuf_func(s[add] == '\n' ? "'" : "\\", 1); 868 putbuf_func(&s[add], 1); 869 if (s[add] == '\n') 870 putbuf_func("'", 1); 871 872 add++; 873 wlen -= add; 874 s += add; 875 add = 0; 876 } else 877 ++add; 878 if (wlen > 0 && rval == 0) 879 rval = putbuf_func(s, wlen); 880 881 return (rval); 882 } 883 884 885 /* +++ emacs editing mode +++ */ 886 887 static Area aedit; 888 #define AEDIT &aedit /* area for kill ring and macro defns */ 889 890 /* values returned by keyboard functions */ 891 #define KSTD 0 892 #define KEOL 1 /* ^M, ^J */ 893 #define KINTR 2 /* ^G, ^C */ 894 895 struct x_ftab { 896 int (*xf_func)(int c); 897 const char *xf_name; 898 short xf_flags; 899 }; 900 901 struct x_defbindings { 902 unsigned char xdb_func; /* XFUNC_* */ 903 unsigned char xdb_tab; 904 unsigned char xdb_char; 905 }; 906 907 #define XF_ARG 1 /* command takes number prefix */ 908 #define XF_NOBIND 2 /* not allowed to bind to function */ 909 #define XF_PREFIX 4 /* function sets prefix */ 910 911 #define X_NTABS 4 /* normal, meta1, meta2, pc */ 912 #define X_TABSZ 256 /* size of keydef tables etc */ 913 914 /*- 915 * Arguments for do_complete() 916 * 0 = enumerate M-= complete as much as possible and then list 917 * 1 = complete M-Esc 918 * 2 = list M-? 919 */ 920 typedef enum { 921 CT_LIST, /* list the possible completions */ 922 CT_COMPLETE, /* complete to longest prefix */ 923 CT_COMPLIST /* complete and then list (if non-exact) */ 924 } Comp_type; 925 926 /* 927 * The following are used for my horizontal scrolling stuff 928 */ 929 static char *xbuf; /* beg input buffer */ 930 static char *xend; /* end input buffer */ 931 static char *xcp; /* current position */ 932 static char *xep; /* current end */ 933 static char *xbp; /* start of visible portion of input buffer */ 934 static char *xlp; /* last char visible on screen */ 935 static bool x_adj_ok; 936 /* 937 * we use x_adj_done so that functions can tell 938 * whether x_adjust() has been called while they are active. 939 */ 940 static int x_adj_done; /* is incremented by x_adjust() */ 941 942 static int x_displen; 943 static int x_arg; /* general purpose arg */ 944 static bool x_arg_defaulted; /* x_arg not explicitly set; defaulted to 1 */ 945 946 static bool xlp_valid; /* lastvis pointer was recalculated */ 947 948 static char **x_histp; /* history position */ 949 static int x_nextcmd; /* for newline-and-next */ 950 static char **x_histncp; /* saved x_histp for " */ 951 static char **x_histmcp; /* saved x_histp for " */ 952 static char *xmp; /* mark pointer */ 953 static unsigned char x_last_command; 954 static unsigned char (*x_tab)[X_TABSZ]; /* key definition */ 955 #ifndef MKSH_SMALL 956 static char *(*x_atab)[X_TABSZ]; /* macro definitions */ 957 #endif 958 static unsigned char x_bound[(X_TABSZ * X_NTABS + 7) / 8]; 959 #define KILLSIZE 20 960 static char *killstack[KILLSIZE]; 961 static int killsp, killtp; 962 static int x_curprefix; 963 #ifndef MKSH_SMALL 964 static char *macroptr; /* bind key macro active? */ 965 #endif 966 #if !MKSH_S_NOVI 967 static int winwidth; /* width of window */ 968 static char *wbuf[2]; /* window buffers */ 969 static int wbuf_len; /* length of window buffers (x_cols - 3) */ 970 static int win; /* window buffer in use */ 971 static char morec; /* more character at right of window */ 972 static int lastref; /* argument to last refresh() */ 973 static int holdlen; /* length of holdbuf */ 974 #endif 975 static int pwidth; /* width of prompt */ 976 static int prompt_trunc; /* how much of prompt to truncate or -1 */ 977 static int x_col; /* current column on line */ 978 979 static int x_ins(const char *); 980 static void x_delete(size_t, bool); 981 static size_t x_bword(void); 982 static size_t x_fword(bool); 983 static void x_goto(char *); 984 static char *x_bs0(char *, char *) MKSH_A_PURE; 985 static void x_bs3(char **); 986 static int x_size2(char *, char **); 987 static void x_zots(char *); 988 static void x_zotc3(char **); 989 static void x_vi_zotc(int); 990 static void x_load_hist(char **); 991 static int x_search(char *, int, int); 992 #ifndef MKSH_SMALL 993 static int x_search_dir(int); 994 #endif 995 static int x_match(char *, char *); 996 static void x_redraw(int); 997 static void x_push(size_t); 998 static char *x_mapin(const char *, Area *); 999 static char *x_mapout(int); 1000 static void x_mapout2(int, char **); 1001 static void x_print(int, int); 1002 static void x_e_ungetc(int); 1003 static int x_e_getc(void); 1004 static void x_e_putc2(int); 1005 static void x_e_putc3(const char **); 1006 static void x_e_puts(const char *); 1007 #ifndef MKSH_SMALL 1008 static int x_fold_case(int); 1009 #endif 1010 static char *x_lastcp(void); 1011 static void x_lastpos(void); 1012 static void do_complete(int, Comp_type); 1013 static size_t x_nb2nc(size_t) MKSH_A_PURE; 1014 1015 static int unget_char = -1; 1016 1017 static int x_do_ins(const char *, size_t); 1018 static void bind_if_not_bound(int, int, int); 1019 1020 enum emacs_funcs { 1021 #define EMACSFN_ENUMS 1022 #include "emacsfn.h" 1023 XFUNC_MAX 1024 }; 1025 1026 #define EMACSFN_DEFNS 1027 #include "emacsfn.h" 1028 1029 static const struct x_ftab x_ftab[] = { 1030 #define EMACSFN_ITEMS 1031 #include "emacsfn.h" 1032 }; 1033 1034 static struct x_defbindings const x_defbindings[] = { 1035 { XFUNC_del_back, 0, CTRL_QM }, 1036 { XFUNC_del_bword, 1, CTRL_QM }, 1037 { XFUNC_eot_del, 0, CTRL_D }, 1038 { XFUNC_del_back, 0, CTRL_H }, 1039 { XFUNC_del_bword, 1, CTRL_H }, 1040 { XFUNC_del_bword, 1, 'h' }, 1041 { XFUNC_mv_bword, 1, 'b' }, 1042 { XFUNC_mv_fword, 1, 'f' }, 1043 { XFUNC_del_fword, 1, 'd' }, 1044 { XFUNC_mv_back, 0, CTRL_B }, 1045 { XFUNC_mv_forw, 0, CTRL_F }, 1046 { XFUNC_search_char_forw, 0, CTRL_BC }, 1047 { XFUNC_search_char_back, 1, CTRL_BC }, 1048 { XFUNC_newline, 0, CTRL_M }, 1049 { XFUNC_newline, 0, CTRL_J }, 1050 { XFUNC_end_of_text, 0, CTRL_US }, 1051 { XFUNC_abort, 0, CTRL_G }, 1052 { XFUNC_prev_com, 0, CTRL_P }, 1053 { XFUNC_next_com, 0, CTRL_N }, 1054 { XFUNC_nl_next_com, 0, CTRL_O }, 1055 { XFUNC_search_hist, 0, CTRL_R }, 1056 { XFUNC_beg_hist, 1, '<' }, 1057 { XFUNC_end_hist, 1, '>' }, 1058 { XFUNC_goto_hist, 1, 'g' }, 1059 { XFUNC_mv_end, 0, CTRL_E }, 1060 { XFUNC_mv_beg, 0, CTRL_A }, 1061 { XFUNC_draw_line, 0, CTRL_L }, 1062 { XFUNC_cls, 1, CTRL_L }, 1063 { XFUNC_meta1, 0, CTRL_BO }, 1064 { XFUNC_meta2, 0, CTRL_X }, 1065 { XFUNC_kill, 0, CTRL_K }, 1066 { XFUNC_yank, 0, CTRL_Y }, 1067 { XFUNC_meta_yank, 1, 'y' }, 1068 { XFUNC_literal, 0, CTRL_CA }, 1069 { XFUNC_comment, 1, '#' }, 1070 { XFUNC_transpose, 0, CTRL_T }, 1071 { XFUNC_complete, 1, CTRL_BO }, 1072 { XFUNC_comp_list, 0, CTRL_I }, 1073 { XFUNC_comp_list, 1, '=' }, 1074 { XFUNC_enumerate, 1, '?' }, 1075 { XFUNC_expand, 1, '*' }, 1076 { XFUNC_comp_file, 1, CTRL_X }, 1077 { XFUNC_comp_comm, 2, CTRL_BO }, 1078 { XFUNC_list_comm, 2, '?' }, 1079 { XFUNC_list_file, 2, CTRL_Y }, 1080 { XFUNC_set_mark, 1, ' ' }, 1081 { XFUNC_kill_region, 0, CTRL_W }, 1082 { XFUNC_xchg_point_mark, 2, CTRL_X }, 1083 { XFUNC_literal, 0, CTRL_V }, 1084 { XFUNC_version, 1, CTRL_V }, 1085 { XFUNC_prev_histword, 1, '.' }, 1086 { XFUNC_prev_histword, 1, '_' }, 1087 { XFUNC_set_arg, 1, '0' }, 1088 { XFUNC_set_arg, 1, '1' }, 1089 { XFUNC_set_arg, 1, '2' }, 1090 { XFUNC_set_arg, 1, '3' }, 1091 { XFUNC_set_arg, 1, '4' }, 1092 { XFUNC_set_arg, 1, '5' }, 1093 { XFUNC_set_arg, 1, '6' }, 1094 { XFUNC_set_arg, 1, '7' }, 1095 { XFUNC_set_arg, 1, '8' }, 1096 { XFUNC_set_arg, 1, '9' }, 1097 #ifndef MKSH_SMALL 1098 { XFUNC_fold_upper, 1, 'U' }, 1099 { XFUNC_fold_upper, 1, 'u' }, 1100 { XFUNC_fold_lower, 1, 'L' }, 1101 { XFUNC_fold_lower, 1, 'l' }, 1102 { XFUNC_fold_capitalise, 1, 'C' }, 1103 { XFUNC_fold_capitalise, 1, 'c' }, 1104 #endif 1105 /* 1106 * These for ANSI arrow keys: arguablely shouldn't be here by 1107 * default, but its simpler/faster/smaller than using termcap 1108 * entries. 1109 */ 1110 { XFUNC_meta2, 1, '[' }, 1111 { XFUNC_meta2, 1, 'O' }, 1112 { XFUNC_prev_com, 2, 'A' }, 1113 { XFUNC_next_com, 2, 'B' }, 1114 { XFUNC_mv_forw, 2, 'C' }, 1115 { XFUNC_mv_back, 2, 'D' }, 1116 #ifndef MKSH_SMALL 1117 { XFUNC_vt_hack, 2, '1' }, 1118 { XFUNC_mv_beg | 0x80, 2, '7' }, 1119 { XFUNC_mv_beg, 2, 'H' }, 1120 { XFUNC_mv_end | 0x80, 2, '4' }, 1121 { XFUNC_mv_end | 0x80, 2, '8' }, 1122 { XFUNC_mv_end, 2, 'F' }, 1123 { XFUNC_del_char | 0x80, 2, '3' }, 1124 { XFUNC_del_char, 2, 'P' }, 1125 { XFUNC_search_hist_up | 0x80, 2, '5' }, 1126 { XFUNC_search_hist_dn | 0x80, 2, '6' }, 1127 #endif 1128 /* PC scancodes */ 1129 #if !defined(MKSH_SMALL) || defined(__OS2__) 1130 { XFUNC_meta3, 0, 0 }, 1131 { XFUNC_mv_beg, 3, 71 }, 1132 { XFUNC_prev_com, 3, 72 }, 1133 #ifndef MKSH_SMALL 1134 { XFUNC_search_hist_up, 3, 73 }, 1135 #endif 1136 { XFUNC_mv_back, 3, 75 }, 1137 { XFUNC_mv_forw, 3, 77 }, 1138 { XFUNC_mv_end, 3, 79 }, 1139 { XFUNC_next_com, 3, 80 }, 1140 #ifndef MKSH_SMALL 1141 { XFUNC_search_hist_dn, 3, 81 }, 1142 #endif 1143 { XFUNC_del_char, 3, 83 }, 1144 #endif 1145 #ifndef MKSH_SMALL 1146 /* more non-standard ones */ 1147 { XFUNC_eval_region, 1, CTRL_E }, 1148 { XFUNC_edit_line, 2, 'e' } 1149 #endif 1150 }; 1151 1152 static size_t 1153 x_nb2nc(size_t nb) 1154 { 1155 char *cp; 1156 size_t nc = 0; 1157 1158 for (cp = xcp; cp < (xcp + nb); ++nc) 1159 cp += utf_ptradj(cp); 1160 return (nc); 1161 } 1162 1163 static void 1164 x_modified(void) 1165 { 1166 if (!modified) { 1167 x_histmcp = x_histp; 1168 x_histp = histptr + 1; 1169 modified = 1; 1170 } 1171 } 1172 1173 #ifdef MKSH_SMALL 1174 #define XFUNC_VALUE(f) (f) 1175 #else 1176 #define XFUNC_VALUE(f) (f & 0x7F) 1177 #endif 1178 1179 static int 1180 x_e_getmbc(char *sbuf) 1181 { 1182 int c, pos = 0; 1183 unsigned char *buf = (unsigned char *)sbuf; 1184 1185 memset(buf, 0, 4); 1186 buf[pos++] = c = x_e_getc(); 1187 if (c == -1) 1188 return (-1); 1189 if (UTFMODE) { 1190 if ((rtt2asc(buf[0]) >= (unsigned char)0xC2) && 1191 (rtt2asc(buf[0]) < (unsigned char)0xF0)) { 1192 c = x_e_getc(); 1193 if (c == -1) 1194 return (-1); 1195 if ((rtt2asc(c) & 0xC0) != 0x80) { 1196 x_e_ungetc(c); 1197 return (1); 1198 } 1199 buf[pos++] = c; 1200 } 1201 if ((rtt2asc(buf[0]) >= (unsigned char)0xE0) && 1202 (rtt2asc(buf[0]) < (unsigned char)0xF0)) { 1203 /* XXX x_e_ungetc is one-octet only */ 1204 buf[pos++] = c = x_e_getc(); 1205 if (c == -1) 1206 return (-1); 1207 } 1208 } 1209 return (pos); 1210 } 1211 1212 /* 1213 * minimum required space to work with on a line - if the prompt 1214 * leaves less space than this on a line, the prompt is truncated 1215 */ 1216 #define MIN_EDIT_SPACE 7 1217 1218 static void 1219 x_init_prompt(bool doprint) 1220 { 1221 prompt_trunc = pprompt(prompt, doprint ? 0 : -1); 1222 pwidth = prompt_trunc % x_cols; 1223 prompt_trunc -= pwidth; 1224 if ((mksh_uari_t)pwidth > ((mksh_uari_t)x_cols - 3 - MIN_EDIT_SPACE)) { 1225 /* force newline after prompt */ 1226 prompt_trunc = -1; 1227 pwidth = 0; 1228 if (doprint) 1229 x_e_putc2('\n'); 1230 } 1231 } 1232 1233 static int 1234 x_emacs(char *buf) 1235 { 1236 int c, i; 1237 unsigned char f; 1238 1239 xbp = xbuf = buf; 1240 xend = buf + LINE; 1241 xlp = xcp = xep = buf; 1242 *xcp = 0; 1243 xlp_valid = true; 1244 xmp = NULL; 1245 x_curprefix = 0; 1246 x_histmcp = x_histp = histptr + 1; 1247 x_last_command = XFUNC_error; 1248 1249 x_init_prompt(true); 1250 x_displen = (xx_cols = x_cols) - 2 - (x_col = pwidth); 1251 x_adj_done = 0; 1252 x_adj_ok = true; 1253 1254 x_histncp = NULL; 1255 if (x_nextcmd >= 0) { 1256 int off = source->line - x_nextcmd; 1257 if (histptr - history >= off) { 1258 x_load_hist(histptr - off); 1259 x_histncp = x_histp; 1260 } 1261 x_nextcmd = -1; 1262 } 1263 editmode = 1; 1264 while (/* CONSTCOND */ 1) { 1265 x_flush(); 1266 if ((c = x_e_getc()) < 0) 1267 return (0); 1268 1269 f = x_curprefix == -1 ? XFUNC_insert : 1270 x_tab[x_curprefix][c]; 1271 #ifndef MKSH_SMALL 1272 if (f & 0x80) { 1273 f &= 0x7F; 1274 if ((i = x_e_getc()) != '~') 1275 x_e_ungetc(i); 1276 } 1277 1278 /* avoid bind key macro recursion */ 1279 if (macroptr && f == XFUNC_ins_string) 1280 f = XFUNC_insert; 1281 #endif 1282 1283 if (!(x_ftab[f].xf_flags & XF_PREFIX) && 1284 x_last_command != XFUNC_set_arg) { 1285 x_arg = 1; 1286 x_arg_defaulted = true; 1287 } 1288 i = c | (x_curprefix << 8); 1289 x_curprefix = 0; 1290 switch ((*x_ftab[f].xf_func)(i)) { 1291 case KSTD: 1292 if (!(x_ftab[f].xf_flags & XF_PREFIX)) 1293 x_last_command = f; 1294 break; 1295 case KEOL: 1296 i = xep - xbuf; 1297 return (i); 1298 case KINTR: 1299 /* special case for interrupt */ 1300 x_intr(SIGINT, c); 1301 } 1302 /* ad-hoc hack for fixing the cursor position */ 1303 x_goto(xcp); 1304 } 1305 } 1306 1307 static int 1308 x_insert(int c) 1309 { 1310 static int left, pos, save_arg; 1311 static char str[4]; 1312 1313 /* 1314 * Should allow tab and control chars. 1315 */ 1316 if (c == 0) { 1317 invmbs: 1318 left = 0; 1319 x_e_putc2(KSH_BEL); 1320 return (KSTD); 1321 } 1322 if (UTFMODE) { 1323 if (((rtt2asc(c) & 0xC0) == 0x80) && left) { 1324 str[pos++] = c; 1325 if (!--left) { 1326 str[pos] = '\0'; 1327 x_arg = save_arg; 1328 while (x_arg--) 1329 x_ins(str); 1330 } 1331 return (KSTD); 1332 } 1333 if (left) { 1334 if (x_curprefix == -1) { 1335 /* flush invalid multibyte */ 1336 str[pos] = '\0'; 1337 while (save_arg--) 1338 x_ins(str); 1339 } 1340 } 1341 if ((c >= 0xC2) && (c < 0xE0)) 1342 left = 1; 1343 else if ((c >= 0xE0) && (c < 0xF0)) 1344 left = 2; 1345 else if (c > 0x7F) 1346 goto invmbs; 1347 else 1348 left = 0; 1349 if (left) { 1350 save_arg = x_arg; 1351 pos = 1; 1352 str[0] = c; 1353 return (KSTD); 1354 } 1355 } 1356 left = 0; 1357 str[0] = c; 1358 str[1] = '\0'; 1359 while (x_arg--) 1360 x_ins(str); 1361 return (KSTD); 1362 } 1363 1364 #ifndef MKSH_SMALL 1365 static int 1366 x_ins_string(int c) 1367 { 1368 macroptr = x_atab[c >> 8][c & 255]; 1369 /* 1370 * we no longer need to bother checking if macroptr is 1371 * not NULL but first char is NUL; x_e_getc() does it 1372 */ 1373 return (KSTD); 1374 } 1375 #endif 1376 1377 static int 1378 x_do_ins(const char *cp, size_t len) 1379 { 1380 if (xep + len >= xend) { 1381 x_e_putc2(KSH_BEL); 1382 return (-1); 1383 } 1384 memmove(xcp + len, xcp, xep - xcp + 1); 1385 memmove(xcp, cp, len); 1386 xcp += len; 1387 xep += len; 1388 x_modified(); 1389 return (0); 1390 } 1391 1392 static int 1393 x_ins(const char *s) 1394 { 1395 char *cp = xcp; 1396 int adj = x_adj_done; 1397 1398 if (x_do_ins(s, strlen(s)) < 0) 1399 return (-1); 1400 /* 1401 * x_zots() may result in a call to x_adjust() 1402 * we want xcp to reflect the new position. 1403 */ 1404 xlp_valid = false; 1405 x_lastcp(); 1406 x_adj_ok = tobool(xcp >= xlp); 1407 x_zots(cp); 1408 if (adj == x_adj_done) 1409 /* x_adjust() has not been called */ 1410 x_lastpos(); 1411 x_adj_ok = true; 1412 return (0); 1413 } 1414 1415 static int 1416 x_del_back(int c MKSH_A_UNUSED) 1417 { 1418 ssize_t i = 0; 1419 1420 if (xcp == xbuf) { 1421 x_e_putc2(KSH_BEL); 1422 return (KSTD); 1423 } 1424 do { 1425 x_goto(xcp - 1); 1426 } while ((++i < x_arg) && (xcp != xbuf)); 1427 x_delete(i, false); 1428 return (KSTD); 1429 } 1430 1431 static int 1432 x_del_char(int c MKSH_A_UNUSED) 1433 { 1434 char *cp, *cp2; 1435 size_t i = 0; 1436 1437 cp = xcp; 1438 while (i < (size_t)x_arg) { 1439 utf_ptradjx(cp, cp2); 1440 if (cp2 > xep) 1441 break; 1442 cp = cp2; 1443 i++; 1444 } 1445 1446 if (!i) { 1447 x_e_putc2(KSH_BEL); 1448 return (KSTD); 1449 } 1450 x_delete(i, false); 1451 return (KSTD); 1452 } 1453 1454 /* Delete nc chars to the right of the cursor (including cursor position) */ 1455 static void 1456 x_delete(size_t nc, bool push) 1457 { 1458 size_t i, nb, nw; 1459 char *cp; 1460 1461 if (nc == 0) 1462 return; 1463 1464 nw = 0; 1465 cp = xcp; 1466 for (i = 0; i < nc; ++i) { 1467 char *cp2; 1468 int j; 1469 1470 j = x_size2(cp, &cp2); 1471 if (cp2 > xep) 1472 break; 1473 cp = cp2; 1474 nw += j; 1475 } 1476 nb = cp - xcp; 1477 /* nc = i; */ 1478 1479 if (xmp != NULL && xmp > xcp) { 1480 if (xcp + nb > xmp) 1481 xmp = xcp; 1482 else 1483 xmp -= nb; 1484 } 1485 /* 1486 * This lets us yank a word we have deleted. 1487 */ 1488 if (push) 1489 x_push(nb); 1490 1491 xep -= nb; 1492 /* Copies the NUL */ 1493 memmove(xcp, xcp + nb, xep - xcp + 1); 1494 /* don't redraw */ 1495 x_adj_ok = false; 1496 xlp_valid = false; 1497 x_zots(xcp); 1498 /* 1499 * if we are already filling the line, 1500 * there is no need to ' ', '\b'. 1501 * But if we must, make sure we do the minimum. 1502 */ 1503 if ((i = xx_cols - 2 - x_col) > 0 || xep - xlp == 0) { 1504 nw = i = (nw < i) ? nw : i; 1505 while (i--) 1506 x_e_putc2(' '); 1507 if (x_col == xx_cols - 2) { 1508 x_e_putc2((xep > xlp) ? '>' : (xbp > xbuf) ? '<' : ' '); 1509 ++nw; 1510 } 1511 while (nw--) 1512 x_e_putc2('\b'); 1513 } 1514 /*x_goto(xcp);*/ 1515 x_adj_ok = true; 1516 xlp_valid = false; 1517 x_lastpos(); 1518 x_modified(); 1519 return; 1520 } 1521 1522 static int 1523 x_del_bword(int c MKSH_A_UNUSED) 1524 { 1525 x_delete(x_bword(), true); 1526 return (KSTD); 1527 } 1528 1529 static int 1530 x_mv_bword(int c MKSH_A_UNUSED) 1531 { 1532 x_bword(); 1533 return (KSTD); 1534 } 1535 1536 static int 1537 x_mv_fword(int c MKSH_A_UNUSED) 1538 { 1539 x_fword(true); 1540 return (KSTD); 1541 } 1542 1543 static int 1544 x_del_fword(int c MKSH_A_UNUSED) 1545 { 1546 x_delete(x_fword(false), true); 1547 return (KSTD); 1548 } 1549 1550 static size_t 1551 x_bword(void) 1552 { 1553 size_t nb = 0; 1554 char *cp = xcp; 1555 1556 if (cp == xbuf) { 1557 x_e_putc2(KSH_BEL); 1558 return (0); 1559 } 1560 while (x_arg--) { 1561 while (cp != xbuf && ctype(cp[-1], C_MFS)) { 1562 cp--; 1563 nb++; 1564 } 1565 while (cp != xbuf && !ctype(cp[-1], C_MFS)) { 1566 cp--; 1567 nb++; 1568 } 1569 } 1570 x_goto(cp); 1571 return (x_nb2nc(nb)); 1572 } 1573 1574 static size_t 1575 x_fword(bool move) 1576 { 1577 size_t nc; 1578 char *cp = xcp; 1579 1580 if (cp == xep) { 1581 x_e_putc2(KSH_BEL); 1582 return (0); 1583 } 1584 while (x_arg--) { 1585 while (cp != xep && ctype(*cp, C_MFS)) 1586 cp++; 1587 while (cp != xep && !ctype(*cp, C_MFS)) 1588 cp++; 1589 } 1590 nc = x_nb2nc(cp - xcp); 1591 if (move) 1592 x_goto(cp); 1593 return (nc); 1594 } 1595 1596 static void 1597 x_goto(char *cp) 1598 { 1599 cp = cp >= xep ? xep : x_bs0(cp, xbuf); 1600 if (cp < xbp || cp >= utf_skipcols(xbp, x_displen, NULL)) { 1601 /* we are heading off screen */ 1602 xcp = cp; 1603 x_adjust(); 1604 } else if (cp < xcp) { 1605 /* move back */ 1606 while (cp < xcp) 1607 x_bs3(&xcp); 1608 } else if (cp > xcp) { 1609 /* move forward */ 1610 while (cp > xcp) 1611 x_zotc3(&xcp); 1612 } 1613 } 1614 1615 static char * 1616 x_bs0(char *cp, char *lower_bound) 1617 { 1618 if (UTFMODE) 1619 while ((!lower_bound || (cp > lower_bound)) && 1620 ((rtt2asc(*cp) & 0xC0) == 0x80)) 1621 --cp; 1622 return (cp); 1623 } 1624 1625 static void 1626 x_bs3(char **p) 1627 { 1628 int i; 1629 1630 *p = x_bs0((*p) - 1, NULL); 1631 i = x_size2(*p, NULL); 1632 while (i--) 1633 x_e_putc2('\b'); 1634 } 1635 1636 static int 1637 x_size2(char *cp, char **dcp) 1638 { 1639 uint8_t c = *(unsigned char *)cp; 1640 1641 if (UTFMODE && (rtt2asc(c) > 0x7F)) 1642 return (utf_widthadj(cp, (const char **)dcp)); 1643 if (dcp) 1644 *dcp = cp + 1; 1645 if (c == '\t') 1646 /* Kludge, tabs are always four spaces. */ 1647 return (4); 1648 if (ksh_isctrl(c)) 1649 /* control unsigned char */ 1650 return (2); 1651 return (1); 1652 } 1653 1654 static void 1655 x_zots(char *str) 1656 { 1657 int adj = x_adj_done; 1658 1659 x_lastcp(); 1660 while (*str && str < xlp && x_col < xx_cols && adj == x_adj_done) 1661 x_zotc3(&str); 1662 } 1663 1664 static void 1665 x_zotc3(char **cp) 1666 { 1667 unsigned char c = **(unsigned char **)cp; 1668 1669 if (c == '\t') { 1670 /* Kludge, tabs are always four spaces. */ 1671 x_e_puts(T4spaces); 1672 (*cp)++; 1673 } else if (ksh_isctrl(c)) { 1674 x_e_putc2('^'); 1675 x_e_putc2(ksh_unctrl(c)); 1676 (*cp)++; 1677 } else 1678 x_e_putc3((const char **)cp); 1679 } 1680 1681 static int 1682 x_mv_back(int c MKSH_A_UNUSED) 1683 { 1684 if (xcp == xbuf) { 1685 x_e_putc2(KSH_BEL); 1686 return (KSTD); 1687 } 1688 while (x_arg--) { 1689 x_goto(xcp - 1); 1690 if (xcp == xbuf) 1691 break; 1692 } 1693 return (KSTD); 1694 } 1695 1696 static int 1697 x_mv_forw(int c MKSH_A_UNUSED) 1698 { 1699 char *cp = xcp, *cp2; 1700 1701 if (xcp == xep) { 1702 x_e_putc2(KSH_BEL); 1703 return (KSTD); 1704 } 1705 while (x_arg--) { 1706 utf_ptradjx(cp, cp2); 1707 if (cp2 > xep) 1708 break; 1709 cp = cp2; 1710 } 1711 x_goto(cp); 1712 return (KSTD); 1713 } 1714 1715 static int 1716 x_search_char_forw(int c MKSH_A_UNUSED) 1717 { 1718 char *cp = xcp; 1719 char tmp[4]; 1720 1721 *xep = '\0'; 1722 if (x_e_getmbc(tmp) < 0) { 1723 x_e_putc2(KSH_BEL); 1724 return (KSTD); 1725 } 1726 while (x_arg--) { 1727 if ((cp = (cp == xep) ? NULL : strstr(cp + 1, tmp)) == NULL && 1728 (cp = strstr(xbuf, tmp)) == NULL) { 1729 x_e_putc2(KSH_BEL); 1730 return (KSTD); 1731 } 1732 } 1733 x_goto(cp); 1734 return (KSTD); 1735 } 1736 1737 static int 1738 x_search_char_back(int c MKSH_A_UNUSED) 1739 { 1740 char *cp = xcp, *p, tmp[4]; 1741 bool b; 1742 1743 if (x_e_getmbc(tmp) < 0) { 1744 x_e_putc2(KSH_BEL); 1745 return (KSTD); 1746 } 1747 for (; x_arg--; cp = p) 1748 for (p = cp; ; ) { 1749 if (p-- == xbuf) 1750 p = xep; 1751 if (p == cp) { 1752 x_e_putc2(KSH_BEL); 1753 return (KSTD); 1754 } 1755 if ((tmp[1] && ((p+1) > xep)) || 1756 (tmp[2] && ((p+2) > xep))) 1757 continue; 1758 b = true; 1759 if (*p != tmp[0]) 1760 b = false; 1761 if (b && tmp[1] && p[1] != tmp[1]) 1762 b = false; 1763 if (b && tmp[2] && p[2] != tmp[2]) 1764 b = false; 1765 if (b) 1766 break; 1767 } 1768 x_goto(cp); 1769 return (KSTD); 1770 } 1771 1772 static int 1773 x_newline(int c MKSH_A_UNUSED) 1774 { 1775 x_e_putc2('\r'); 1776 x_e_putc2('\n'); 1777 x_flush(); 1778 *xep++ = '\n'; 1779 return (KEOL); 1780 } 1781 1782 static int 1783 x_end_of_text(int c MKSH_A_UNUSED) 1784 { 1785 unsigned char tmp[1], *cp = tmp; 1786 1787 *tmp = isedchar(edchars.eof) ? (unsigned char)edchars.eof : 1788 (unsigned char)CTRL_D; 1789 x_zotc3((char **)&cp); 1790 x_putc('\r'); 1791 x_putc('\n'); 1792 x_flush(); 1793 return (KEOL); 1794 } 1795 1796 static int 1797 x_beg_hist(int c MKSH_A_UNUSED) 1798 { 1799 x_load_hist(history); 1800 return (KSTD); 1801 } 1802 1803 static int 1804 x_end_hist(int c MKSH_A_UNUSED) 1805 { 1806 x_load_hist(histptr); 1807 return (KSTD); 1808 } 1809 1810 static int 1811 x_prev_com(int c MKSH_A_UNUSED) 1812 { 1813 x_load_hist(x_histp - x_arg); 1814 return (KSTD); 1815 } 1816 1817 static int 1818 x_next_com(int c MKSH_A_UNUSED) 1819 { 1820 x_load_hist(x_histp + x_arg); 1821 return (KSTD); 1822 } 1823 1824 /* 1825 * Goto a particular history number obtained from argument. 1826 * If no argument is given history 1 is probably not what you 1827 * want so we'll simply go to the oldest one. 1828 */ 1829 static int 1830 x_goto_hist(int c MKSH_A_UNUSED) 1831 { 1832 if (x_arg_defaulted) 1833 x_load_hist(history); 1834 else 1835 x_load_hist(histptr + x_arg - source->line); 1836 return (KSTD); 1837 } 1838 1839 static void 1840 x_load_hist(char **hp) 1841 { 1842 char *sp = NULL; 1843 1844 if (hp == histptr + 1) { 1845 sp = holdbufp; 1846 modified = 0; 1847 } else if (hp < history || hp > histptr) { 1848 x_e_putc2(KSH_BEL); 1849 return; 1850 } 1851 if (sp == NULL) 1852 sp = *hp; 1853 x_histp = hp; 1854 if (modified) 1855 strlcpy(holdbufp, xbuf, LINE); 1856 strlcpy(xbuf, sp, xend - xbuf); 1857 xbp = xbuf; 1858 xep = xcp = strnul(xbuf); 1859 x_adjust(); 1860 modified = 0; 1861 } 1862 1863 static int 1864 x_nl_next_com(int c MKSH_A_UNUSED) 1865 { 1866 if (!modified) 1867 x_histmcp = x_histp; 1868 if (!x_histncp || (x_histmcp != x_histncp && x_histmcp != histptr + 1)) 1869 /* fresh start of ^O */ 1870 x_histncp = x_histmcp; 1871 x_nextcmd = source->line - (histptr - x_histncp) + 1; 1872 return (x_newline('\n')); 1873 } 1874 1875 static int 1876 x_eot_del(int c) 1877 { 1878 if (xep == xbuf && x_arg_defaulted) 1879 return (x_end_of_text(c)); 1880 else 1881 return (x_del_char(c)); 1882 } 1883 1884 /* reverse incremental history search */ 1885 static int 1886 x_search_hist(int c) 1887 { 1888 int offset = -1; /* offset of match in xbuf, else -1 */ 1889 char pat[80 + 1]; /* pattern buffer */ 1890 char *p = pat; 1891 unsigned char f; 1892 1893 *p = '\0'; 1894 while (/* CONSTCOND */ 1) { 1895 if (offset < 0) { 1896 x_e_puts("\nI-search: "); 1897 x_e_puts(pat); 1898 } 1899 x_flush(); 1900 if ((c = x_e_getc()) < 0) 1901 return (KSTD); 1902 f = x_tab[0][c]; 1903 if (c == CTRL_BO) { 1904 if ((f & 0x7F) == XFUNC_meta1) { 1905 if ((c = x_e_getc()) < 0) 1906 return (KSTD); 1907 f = x_tab[1][c] & 0x7F; 1908 if (f == XFUNC_meta1 || f == XFUNC_meta2) 1909 x_meta1(CTRL_BO); 1910 x_e_ungetc(c); 1911 } 1912 break; 1913 } 1914 #ifndef MKSH_SMALL 1915 if (f & 0x80) { 1916 f &= 0x7F; 1917 if ((c = x_e_getc()) != '~') 1918 x_e_ungetc(c); 1919 } 1920 #endif 1921 if (f == XFUNC_search_hist) 1922 offset = x_search(pat, 0, offset); 1923 else if (f == XFUNC_del_back) { 1924 if (p == pat) { 1925 offset = -1; 1926 break; 1927 } 1928 if (p > pat) { 1929 p = x_bs0(p - 1, pat); 1930 *p = '\0'; 1931 } 1932 if (p == pat) 1933 offset = -1; 1934 else 1935 offset = x_search(pat, 1, offset); 1936 continue; 1937 } else if (f == XFUNC_insert) { 1938 /* add char to pattern */ 1939 /* overflow check... */ 1940 if ((size_t)(p - pat) >= sizeof(pat) - 1) { 1941 x_e_putc2(KSH_BEL); 1942 continue; 1943 } 1944 *p++ = c, *p = '\0'; 1945 if (offset >= 0) { 1946 /* already have partial match */ 1947 offset = x_match(xbuf, pat); 1948 if (offset >= 0) { 1949 x_goto(xbuf + offset + (p - pat) - 1950 (*pat == '^')); 1951 continue; 1952 } 1953 } 1954 offset = x_search(pat, 0, offset); 1955 } else if (f == XFUNC_abort) { 1956 if (offset >= 0) 1957 x_load_hist(histptr + 1); 1958 break; 1959 } else { 1960 /* other command */ 1961 x_e_ungetc(c); 1962 break; 1963 } 1964 } 1965 if (offset < 0) 1966 x_redraw('\n'); 1967 return (KSTD); 1968 } 1969 1970 /* search backward from current line */ 1971 static int 1972 x_search(char *pat, int sameline, int offset) 1973 { 1974 char **hp; 1975 int i; 1976 1977 for (hp = x_histp - (sameline ? 0 : 1); hp >= history; --hp) { 1978 i = x_match(*hp, pat); 1979 if (i >= 0) { 1980 if (offset < 0) 1981 x_e_putc2('\n'); 1982 x_load_hist(hp); 1983 x_goto(xbuf + i + strlen(pat) - (*pat == '^')); 1984 return (i); 1985 } 1986 } 1987 x_e_putc2(KSH_BEL); 1988 x_histp = histptr; 1989 return (-1); 1990 } 1991 1992 #ifndef MKSH_SMALL 1993 /* anchored search up from current line */ 1994 static int 1995 x_search_hist_up(int c MKSH_A_UNUSED) 1996 { 1997 return (x_search_dir(-1)); 1998 } 1999 2000 /* anchored search down from current line */ 2001 static int 2002 x_search_hist_dn(int c MKSH_A_UNUSED) 2003 { 2004 return (x_search_dir(1)); 2005 } 2006 2007 /* anchored search in the indicated direction */ 2008 static int 2009 x_search_dir(int search_dir /* should've been bool */) 2010 { 2011 char **hp = x_histp + search_dir; 2012 size_t curs = xcp - xbuf; 2013 2014 while (histptr >= hp && hp >= history) { 2015 if (strncmp(xbuf, *hp, curs) == 0) { 2016 x_load_hist(hp); 2017 x_goto(xbuf + curs); 2018 break; 2019 } 2020 hp += search_dir; 2021 } 2022 return (KSTD); 2023 } 2024 #endif 2025 2026 /* return position of first match of pattern in string, else -1 */ 2027 static int 2028 x_match(char *str, char *pat) 2029 { 2030 if (*pat == '^') { 2031 return ((strncmp(str, pat + 1, strlen(pat + 1)) == 0) ? 0 : -1); 2032 } else { 2033 char *q = strstr(str, pat); 2034 return ((q == NULL) ? -1 : q - str); 2035 } 2036 } 2037 2038 static int 2039 x_del_line(int c MKSH_A_UNUSED) 2040 { 2041 *xep = 0; 2042 x_push(xep - (xcp = xbuf)); 2043 xlp = xbp = xep = xbuf; 2044 xlp_valid = true; 2045 *xcp = 0; 2046 xmp = NULL; 2047 x_redraw('\r'); 2048 x_modified(); 2049 return (KSTD); 2050 } 2051 2052 static int 2053 x_mv_end(int c MKSH_A_UNUSED) 2054 { 2055 x_goto(xep); 2056 return (KSTD); 2057 } 2058 2059 static int 2060 x_mv_beg(int c MKSH_A_UNUSED) 2061 { 2062 x_goto(xbuf); 2063 return (KSTD); 2064 } 2065 2066 static int 2067 x_draw_line(int c MKSH_A_UNUSED) 2068 { 2069 x_redraw('\n'); 2070 return (KSTD); 2071 } 2072 2073 static int 2074 x_cls(int c MKSH_A_UNUSED) 2075 { 2076 shf_puts(MKSH_CLS_STRING, shl_out); 2077 x_redraw(0); 2078 return (KSTD); 2079 } 2080 2081 /* 2082 * clear line from x_col (current cursor position) to xx_cols - 2, 2083 * then output lastch, then go back to x_col; if lastch is space, 2084 * clear with termcap instead of spaces, or not if line_was_cleared; 2085 * lastch MUST be an ASCII character with wcwidth(lastch) == 1 2086 */ 2087 static void 2088 x_clrtoeol(int lastch, bool line_was_cleared) 2089 { 2090 int col; 2091 2092 if (lastch == ' ' && !line_was_cleared && x_term_mode == 1) { 2093 shf_puts(KSH_ESC_STRING "[K", shl_out); 2094 line_was_cleared = true; 2095 } 2096 if (lastch == ' ' && line_was_cleared) 2097 return; 2098 2099 col = x_col; 2100 while (col < (xx_cols - 2)) { 2101 x_putc(' '); 2102 ++col; 2103 } 2104 x_putc(lastch); 2105 ++col; 2106 while (col > x_col) { 2107 x_putc('\b'); 2108 --col; 2109 } 2110 } 2111 2112 /* output the prompt, assuming a line has just been started */ 2113 static void 2114 x_pprompt(void) 2115 { 2116 if (prompt_trunc != -1) 2117 pprompt(prompt, prompt_trunc); 2118 x_col = pwidth; 2119 } 2120 2121 /* output CR, then redraw the line, clearing to EOL if needed (cr 0, LF) */ 2122 static void 2123 x_redraw(int cr) 2124 { 2125 int lch; 2126 2127 x_adj_ok = false; 2128 /* clear the line */ 2129 x_e_putc2(cr ? cr : '\r'); 2130 x_flush(); 2131 /* display the prompt */ 2132 if (xbp == xbuf) 2133 x_pprompt(); 2134 x_displen = xx_cols - 2 - x_col; 2135 /* display the line content */ 2136 xlp_valid = false; 2137 x_zots(xbp); 2138 /* check whether there is more off-screen */ 2139 lch = xep > xlp ? (xbp > xbuf ? '*' : '>') : (xbp > xbuf) ? '<' : ' '; 2140 /* clear the rest of the line */ 2141 x_clrtoeol(lch, !cr || cr == '\n'); 2142 /* go back to actual cursor position */ 2143 x_lastpos(); 2144 x_adj_ok = true; 2145 } 2146 2147 static int 2148 x_transpose(int c MKSH_A_UNUSED) 2149 { 2150 unsigned int tmpa, tmpb; 2151 2152 /*- 2153 * What transpose is meant to do seems to be up for debate. This 2154 * is a general summary of the options; the text is abcd with the 2155 * upper case character or underscore indicating the cursor position: 2156 * Who Before After Before After 2157 * AT&T ksh in emacs mode: abCd abdC abcd_ (bell) 2158 * AT&T ksh in gmacs mode: abCd baCd abcd_ abdc_ 2159 * gnu emacs: abCd acbD abcd_ abdc_ 2160 * Pdksh currently goes with GNU behavior since I believe this is the 2161 * most common version of emacs, unless in gmacs mode, in which case 2162 * it does the AT&T ksh gmacs mode. 2163 * This should really be broken up into 3 functions so users can bind 2164 * to the one they want. 2165 */ 2166 if (xcp == xbuf) { 2167 x_e_putc2(KSH_BEL); 2168 return (KSTD); 2169 } else if (xcp == xep || Flag(FGMACS)) { 2170 if (xcp - xbuf == 1) { 2171 x_e_putc2(KSH_BEL); 2172 return (KSTD); 2173 } 2174 /* 2175 * Gosling/Unipress emacs style: Swap two characters before 2176 * the cursor, do not change cursor position 2177 */ 2178 x_bs3(&xcp); 2179 if (utf_mbtowc(&tmpa, xcp) == (size_t)-1) { 2180 x_e_putc2(KSH_BEL); 2181 return (KSTD); 2182 } 2183 x_bs3(&xcp); 2184 if (utf_mbtowc(&tmpb, xcp) == (size_t)-1) { 2185 x_e_putc2(KSH_BEL); 2186 return (KSTD); 2187 } 2188 utf_wctomb(xcp, tmpa); 2189 x_zotc3(&xcp); 2190 utf_wctomb(xcp, tmpb); 2191 x_zotc3(&xcp); 2192 } else { 2193 /* 2194 * GNU emacs style: Swap the characters before and under the 2195 * cursor, move cursor position along one. 2196 */ 2197 if (utf_mbtowc(&tmpa, xcp) == (size_t)-1) { 2198 x_e_putc2(KSH_BEL); 2199 return (KSTD); 2200 } 2201 x_bs3(&xcp); 2202 if (utf_mbtowc(&tmpb, xcp) == (size_t)-1) { 2203 x_e_putc2(KSH_BEL); 2204 return (KSTD); 2205 } 2206 utf_wctomb(xcp, tmpa); 2207 x_zotc3(&xcp); 2208 utf_wctomb(xcp, tmpb); 2209 x_zotc3(&xcp); 2210 } 2211 x_modified(); 2212 return (KSTD); 2213 } 2214 2215 static int 2216 x_literal(int c MKSH_A_UNUSED) 2217 { 2218 x_curprefix = -1; 2219 return (KSTD); 2220 } 2221 2222 static int 2223 x_meta1(int c MKSH_A_UNUSED) 2224 { 2225 x_curprefix = 1; 2226 return (KSTD); 2227 } 2228 2229 static int 2230 x_meta2(int c MKSH_A_UNUSED) 2231 { 2232 x_curprefix = 2; 2233 return (KSTD); 2234 } 2235 2236 static int 2237 x_meta3(int c MKSH_A_UNUSED) 2238 { 2239 x_curprefix = 3; 2240 return (KSTD); 2241 } 2242 2243 static int 2244 x_kill(int c MKSH_A_UNUSED) 2245 { 2246 size_t col = xcp - xbuf; 2247 size_t lastcol = xep - xbuf; 2248 size_t ndel, narg; 2249 2250 if (x_arg_defaulted || (narg = x_arg) > lastcol) 2251 narg = lastcol; 2252 if (narg < col) { 2253 x_goto(xbuf + narg); 2254 ndel = col - narg; 2255 } else 2256 ndel = narg - col; 2257 x_delete(x_nb2nc(ndel), true); 2258 return (KSTD); 2259 } 2260 2261 static void 2262 x_push(size_t nchars) 2263 { 2264 afree(killstack[killsp], AEDIT); 2265 strndupx(killstack[killsp], xcp, nchars, AEDIT); 2266 killsp = (killsp + 1) % KILLSIZE; 2267 } 2268 2269 static int 2270 x_yank(int c MKSH_A_UNUSED) 2271 { 2272 if (killsp == 0) 2273 killtp = KILLSIZE; 2274 else 2275 killtp = killsp; 2276 killtp--; 2277 if (killstack[killtp] == 0) { 2278 x_e_puts("\nnothing to yank"); 2279 x_redraw('\n'); 2280 return (KSTD); 2281 } 2282 xmp = xcp; 2283 x_ins(killstack[killtp]); 2284 return (KSTD); 2285 } 2286 2287 static int 2288 x_meta_yank(int c MKSH_A_UNUSED) 2289 { 2290 size_t len; 2291 2292 if ((x_last_command != XFUNC_yank && x_last_command != XFUNC_meta_yank) || 2293 killstack[killtp] == 0) { 2294 killtp = killsp; 2295 x_e_puts("\nyank something first"); 2296 x_redraw('\n'); 2297 return (KSTD); 2298 } 2299 len = strlen(killstack[killtp]); 2300 x_goto(xcp - len); 2301 x_delete(x_nb2nc(len), false); 2302 do { 2303 if (killtp == 0) 2304 killtp = KILLSIZE - 1; 2305 else 2306 killtp--; 2307 } while (killstack[killtp] == 0); 2308 x_ins(killstack[killtp]); 2309 return (KSTD); 2310 } 2311 2312 /* fake receiving an interrupt */ 2313 static void 2314 x_intr(int signo, int c) 2315 { 2316 x_vi_zotc(c); 2317 *xep = '\0'; 2318 strip_nuls(xbuf, xep - xbuf); 2319 if (*xbuf) 2320 histsave(&source->line, xbuf, HIST_STORE, true); 2321 xlp = xep = xcp = xbp = xbuf; 2322 xlp_valid = true; 2323 *xcp = 0; 2324 x_modified(); 2325 x_flush(); 2326 trapsig(signo); 2327 x_mode(false); 2328 unwind(LSHELL); 2329 } 2330 2331 static int 2332 x_abort(int c MKSH_A_UNUSED) 2333 { 2334 return (KINTR); 2335 } 2336 2337 static int 2338 x_error(int c MKSH_A_UNUSED) 2339 { 2340 x_e_putc2(KSH_BEL); 2341 return (KSTD); 2342 } 2343 2344 #ifndef MKSH_SMALL 2345 /* special VT100 style key sequence hack */ 2346 static int 2347 x_vt_hack(int c) 2348 { 2349 /* we only support PF2-'1' for now */ 2350 if (c != (2 << 8 | '1')) 2351 return (x_error(c)); 2352 2353 /* what's the next character? */ 2354 switch ((c = x_e_getc())) { 2355 case '~': 2356 x_arg = 1; 2357 x_arg_defaulted = true; 2358 return (x_mv_beg(0)); 2359 case ';': 2360 /* "interesting" sequence detected */ 2361 break; 2362 default: 2363 goto unwind_err; 2364 } 2365 2366 /* XXX x_e_ungetc is one-octet only */ 2367 if ((c = x_e_getc()) != '5' && c != '3') 2368 goto unwind_err; 2369 2370 /*- 2371 * At this point, we have read the following octets so far: 2372 * - ESC+[ or ESC+O or Ctrl-X (Prefix 2) 2373 * - 1 (vt_hack) 2374 * - ; 2375 * - 5 (Ctrl key combiner) or 3 (Alt key combiner) 2376 * We can now accept one more octet designating the key. 2377 */ 2378 2379 switch ((c = x_e_getc())) { 2380 case 'C': 2381 return (x_mv_fword(c)); 2382 case 'D': 2383 return (x_mv_bword(c)); 2384 } 2385 2386 unwind_err: 2387 x_e_ungetc(c); 2388 return (x_error(c)); 2389 } 2390 #endif 2391 2392 static char * 2393 x_mapin(const char *cp, Area *ap) 2394 { 2395 char *news, *op; 2396 2397 strdupx(news, cp, ap); 2398 op = news; 2399 while (*cp) { 2400 switch (*cp) { 2401 case '^': 2402 cp++; 2403 *op++ = ksh_toctrl(*cp); 2404 break; 2405 case '\\': 2406 if (cp[1] == '\\' || cp[1] == '^') 2407 ++cp; 2408 /* FALLTHROUGH */ 2409 default: 2410 *op++ = *cp; 2411 } 2412 cp++; 2413 } 2414 *op = '\0'; 2415 2416 return (news); 2417 } 2418 2419 static void 2420 x_mapout2(int c, char **buf) 2421 { 2422 char *p = *buf; 2423 2424 if (ksh_isctrl(c)) { 2425 *p++ = '^'; 2426 *p++ = ksh_unctrl(c); 2427 } else 2428 *p++ = c; 2429 *p = 0; 2430 *buf = p; 2431 } 2432 2433 static char * 2434 x_mapout(int c) 2435 { 2436 static char buf[8]; 2437 char *bp = buf; 2438 2439 x_mapout2(c, &bp); 2440 return (buf); 2441 } 2442 2443 static void 2444 x_print(int prefix, int key) 2445 { 2446 int f = x_tab[prefix][key]; 2447 2448 if (prefix) 2449 /* prefix == 1 || prefix == 2 || prefix == 3 */ 2450 shf_puts(x_mapout(prefix == 1 ? CTRL_BO : 2451 prefix == 2 ? CTRL_X : 0), shl_stdout); 2452 #ifdef MKSH_SMALL 2453 shprintf("%s = ", x_mapout(key)); 2454 #else 2455 shprintf("%s%s = ", x_mapout(key), (f & 0x80) ? "~" : ""); 2456 if (XFUNC_VALUE(f) != XFUNC_ins_string) 2457 #endif 2458 shprintf(Tf_sN, x_ftab[XFUNC_VALUE(f)].xf_name); 2459 #ifndef MKSH_SMALL 2460 else 2461 shprintf("'%s'\n", x_atab[prefix][key]); 2462 #endif 2463 } 2464 2465 int 2466 x_bind(const char *a1, const char *a2, 2467 #ifndef MKSH_SMALL 2468 /* bind -m */ 2469 bool macro, 2470 #endif 2471 /* bind -l */ 2472 bool list) 2473 { 2474 unsigned char f; 2475 int prefix, key; 2476 char *m1, *m2; 2477 #ifndef MKSH_SMALL 2478 char *sp = NULL; 2479 bool hastilde; 2480 #endif 2481 2482 if (x_tab == NULL) { 2483 bi_errorf("can't bind, not a tty"); 2484 return (1); 2485 } 2486 /* List function names */ 2487 if (list) { 2488 for (f = 0; f < NELEM(x_ftab); f++) 2489 if (!(x_ftab[f].xf_flags & XF_NOBIND)) 2490 shprintf(Tf_sN, x_ftab[f].xf_name); 2491 return (0); 2492 } 2493 if (a1 == NULL) { 2494 for (prefix = 0; prefix < X_NTABS; prefix++) 2495 for (key = 0; key < X_TABSZ; key++) { 2496 f = XFUNC_VALUE(x_tab[prefix][key]); 2497 if (f == XFUNC_insert || f == XFUNC_error 2498 #ifndef MKSH_SMALL 2499 || (macro && f != XFUNC_ins_string) 2500 #endif 2501 ) 2502 continue; 2503 x_print(prefix, key); 2504 } 2505 return (0); 2506 } 2507 m2 = m1 = x_mapin(a1, ATEMP); 2508 prefix = 0; 2509 for (;; m1++) { 2510 key = (unsigned char)*m1; 2511 f = XFUNC_VALUE(x_tab[prefix][key]); 2512 if (f == XFUNC_meta1) 2513 prefix = 1; 2514 else if (f == XFUNC_meta2) 2515 prefix = 2; 2516 else if (f == XFUNC_meta3) 2517 prefix = 3; 2518 else 2519 break; 2520 } 2521 if (*++m1 2522 #ifndef MKSH_SMALL 2523 && ((*m1 != '~') || *(m1 + 1)) 2524 #endif 2525 ) { 2526 char msg[256]; 2527 const char *c = a1; 2528 m1 = msg; 2529 while (*c && (size_t)(m1 - msg) < sizeof(msg) - 3) 2530 x_mapout2(*c++, &m1); 2531 bi_errorf("too long key sequence: %s", msg); 2532 return (1); 2533 } 2534 #ifndef MKSH_SMALL 2535 hastilde = tobool(*m1); 2536 #endif 2537 afree(m2, ATEMP); 2538 2539 if (a2 == NULL) { 2540 x_print(prefix, key); 2541 return (0); 2542 } 2543 if (*a2 == 0) { 2544 f = XFUNC_insert; 2545 #ifndef MKSH_SMALL 2546 } else if (macro) { 2547 f = XFUNC_ins_string; 2548 sp = x_mapin(a2, AEDIT); 2549 #endif 2550 } else { 2551 for (f = 0; f < NELEM(x_ftab); f++) 2552 if (!strcmp(x_ftab[f].xf_name, a2)) 2553 break; 2554 if (f == NELEM(x_ftab) || x_ftab[f].xf_flags & XF_NOBIND) { 2555 bi_errorf("%s: no such function", a2); 2556 return (1); 2557 } 2558 } 2559 2560 #ifndef MKSH_SMALL 2561 if (XFUNC_VALUE(x_tab[prefix][key]) == XFUNC_ins_string && 2562 x_atab[prefix][key]) 2563 afree(x_atab[prefix][key], AEDIT); 2564 #endif 2565 x_tab[prefix][key] = f 2566 #ifndef MKSH_SMALL 2567 | (hastilde ? 0x80 : 0) 2568 #endif 2569 ; 2570 #ifndef MKSH_SMALL 2571 x_atab[prefix][key] = sp; 2572 #endif 2573 2574 /* Track what the user has bound so x_mode(true) won't toast things */ 2575 if (f == XFUNC_insert) 2576 x_bound[(prefix * X_TABSZ + key) / 8] &= 2577 ~(1 << ((prefix * X_TABSZ + key) % 8)); 2578 else 2579 x_bound[(prefix * X_TABSZ + key) / 8] |= 2580 (1 << ((prefix * X_TABSZ + key) % 8)); 2581 2582 return (0); 2583 } 2584 2585 static void 2586 bind_if_not_bound(int p, int k, int func) 2587 { 2588 int t; 2589 2590 /* 2591 * Has user already bound this key? 2592 * If so, do not override it. 2593 */ 2594 t = p * X_TABSZ + k; 2595 if (x_bound[t >> 3] & (1 << (t & 7))) 2596 return; 2597 2598 x_tab[p][k] = func; 2599 } 2600 2601 static int 2602 x_set_mark(int c MKSH_A_UNUSED) 2603 { 2604 xmp = xcp; 2605 return (KSTD); 2606 } 2607 2608 static int 2609 x_kill_region(int c MKSH_A_UNUSED) 2610 { 2611 size_t rsize; 2612 char *xr; 2613 2614 if (xmp == NULL) { 2615 x_e_putc2(KSH_BEL); 2616 return (KSTD); 2617 } 2618 if (xmp > xcp) { 2619 rsize = xmp - xcp; 2620 xr = xcp; 2621 } else { 2622 rsize = xcp - xmp; 2623 xr = xmp; 2624 } 2625 x_goto(xr); 2626 x_delete(x_nb2nc(rsize), true); 2627 xmp = xr; 2628 return (KSTD); 2629 } 2630 2631 static int 2632 x_xchg_point_mark(int c MKSH_A_UNUSED) 2633 { 2634 char *tmp; 2635 2636 if (xmp == NULL) { 2637 x_e_putc2(KSH_BEL); 2638 return (KSTD); 2639 } 2640 tmp = xmp; 2641 xmp = xcp; 2642 x_goto(tmp); 2643 return (KSTD); 2644 } 2645 2646 static int 2647 x_noop(int c MKSH_A_UNUSED) 2648 { 2649 return (KSTD); 2650 } 2651 2652 /* 2653 * File/command name completion routines 2654 */ 2655 static int 2656 x_comp_comm(int c MKSH_A_UNUSED) 2657 { 2658 do_complete(XCF_COMMAND, CT_COMPLETE); 2659 return (KSTD); 2660 } 2661 2662 static int 2663 x_list_comm(int c MKSH_A_UNUSED) 2664 { 2665 do_complete(XCF_COMMAND, CT_LIST); 2666 return (KSTD); 2667 } 2668 2669 static int 2670 x_complete(int c MKSH_A_UNUSED) 2671 { 2672 do_complete(XCF_COMMAND_FILE, CT_COMPLETE); 2673 return (KSTD); 2674 } 2675 2676 static int 2677 x_enumerate(int c MKSH_A_UNUSED) 2678 { 2679 do_complete(XCF_COMMAND_FILE, CT_LIST); 2680 return (KSTD); 2681 } 2682 2683 static int 2684 x_comp_file(int c MKSH_A_UNUSED) 2685 { 2686 do_complete(XCF_FILE, CT_COMPLETE); 2687 return (KSTD); 2688 } 2689 2690 static int 2691 x_list_file(int c MKSH_A_UNUSED) 2692 { 2693 do_complete(XCF_FILE, CT_LIST); 2694 return (KSTD); 2695 } 2696 2697 static int 2698 x_comp_list(int c MKSH_A_UNUSED) 2699 { 2700 do_complete(XCF_COMMAND_FILE, CT_COMPLIST); 2701 return (KSTD); 2702 } 2703 2704 static int 2705 x_expand(int c MKSH_A_UNUSED) 2706 { 2707 char **words; 2708 int start, end, nwords, i; 2709 2710 i = XCF_FILE; 2711 nwords = x_cf_glob(&i, xbuf, xep - xbuf, xcp - xbuf, 2712 &start, &end, &words); 2713 2714 if (nwords == 0) { 2715 x_e_putc2(KSH_BEL); 2716 return (KSTD); 2717 } 2718 x_goto(xbuf + start); 2719 x_delete(x_nb2nc(end - start), false); 2720 2721 i = 0; 2722 while (i < nwords) { 2723 if (x_escape(words[i], strlen(words[i]), x_do_ins) < 0 || 2724 (++i < nwords && x_ins(T1space) < 0)) { 2725 x_e_putc2(KSH_BEL); 2726 return (KSTD); 2727 } 2728 } 2729 x_adjust(); 2730 2731 return (KSTD); 2732 } 2733 2734 static void 2735 do_complete( 2736 /* XCF_{COMMAND,FILE,COMMAND_FILE} */ 2737 int flags, 2738 /* 0 for list, 1 for complete and 2 for complete-list */ 2739 Comp_type type) 2740 { 2741 char **words; 2742 int start, end, nlen, olen, nwords; 2743 bool completed; 2744 2745 nwords = x_cf_glob(&flags, xbuf, xep - xbuf, xcp - xbuf, 2746 &start, &end, &words); 2747 /* no match */ 2748 if (nwords == 0) { 2749 x_e_putc2(KSH_BEL); 2750 return; 2751 } 2752 if (type == CT_LIST) { 2753 x_print_expansions(nwords, words, 2754 tobool(flags & XCF_IS_COMMAND)); 2755 x_redraw(0); 2756 x_free_words(nwords, words); 2757 return; 2758 } 2759 olen = end - start; 2760 nlen = x_longest_prefix(nwords, words); 2761 if (nwords == 1) { 2762 /* 2763 * always complete single matches; 2764 * any expansion of parameter substitution 2765 * is always at most one result, too 2766 */ 2767 completed = true; 2768 } else { 2769 char *unescaped; 2770 2771 /* make a copy of the original string part */ 2772 strndupx(unescaped, xbuf + start, olen, ATEMP); 2773 2774 /* expand any tilde and unescape the string for comparison */ 2775 unescaped = x_glob_hlp_tilde_and_rem_qchar(unescaped, true); 2776 2777 /* 2778 * match iff entire original string is part of the 2779 * longest prefix, implying the latter is at least 2780 * the same size (after unescaping) 2781 */ 2782 completed = !strncmp(words[0], unescaped, strlen(unescaped)); 2783 2784 afree(unescaped, ATEMP); 2785 } 2786 if (type == CT_COMPLIST && nwords > 1) { 2787 /* 2788 * print expansions, since we didn't get back 2789 * just a single match 2790 */ 2791 x_print_expansions(nwords, words, 2792 tobool(flags & XCF_IS_COMMAND)); 2793 } 2794 if (completed) { 2795 /* expand on the command line */ 2796 xmp = NULL; 2797 xcp = xbuf + start; 2798 xep -= olen; 2799 memmove(xcp, xcp + olen, xep - xcp + 1); 2800 x_escape(words[0], nlen, x_do_ins); 2801 } 2802 x_adjust(); 2803 /* 2804 * append a space if this is a single non-directory match 2805 * and not a parameter or homedir substitution 2806 */ 2807 if (nwords == 1 && !mksh_cdirsep(words[0][nlen - 1]) && 2808 !(flags & XCF_IS_NOSPACE)) { 2809 x_ins(T1space); 2810 } 2811 2812 x_free_words(nwords, words); 2813 } 2814 2815 /*- 2816 * NAME: 2817 * x_adjust - redraw the line adjusting starting point etc. 2818 * 2819 * DESCRIPTION: 2820 * This function is called when we have exceeded the bounds 2821 * of the edit window. It increments x_adj_done so that 2822 * functions like x_ins and x_delete know that we have been 2823 * called and can skip the x_bs() stuff which has already 2824 * been done by x_redraw. 2825 * 2826 * RETURN VALUE: 2827 * None 2828 */ 2829 static void 2830 x_adjust(void) 2831 { 2832 int col_left, n; 2833 2834 /* flag the fact that we were called */ 2835 x_adj_done++; 2836 2837 /* 2838 * calculate the amount of columns we need to "go back" 2839 * from xcp to set xbp to (but never < xbuf) to 2/3 of 2840 * the display width; take care of pwidth though 2841 */ 2842 if ((col_left = xx_cols * 2 / 3) < MIN_EDIT_SPACE) { 2843 /* 2844 * cowardly refuse to do anything 2845 * if the available space is too small; 2846 * fall back to dumb pdksh code 2847 */ 2848 if ((xbp = xcp - (x_displen / 2)) < xbuf) 2849 xbp = xbuf; 2850 /* elide UTF-8 fixup as penalty */ 2851 goto x_adjust_out; 2852 } 2853 2854 /* fix up xbp to just past a character end first */ 2855 xbp = xcp >= xep ? xep : x_bs0(xcp, xbuf); 2856 /* walk backwards */ 2857 while (xbp > xbuf && col_left > 0) { 2858 xbp = x_bs0(xbp - 1, xbuf); 2859 col_left -= (n = x_size2(xbp, NULL)); 2860 } 2861 /* check if we hit the prompt */ 2862 if (xbp == xbuf && xcp != xbuf && col_left >= 0 && col_left < pwidth) { 2863 /* so we did; force scrolling occurs */ 2864 xbp += utf_ptradj(xbp); 2865 } 2866 2867 x_adjust_out: 2868 xlp_valid = false; 2869 x_redraw('\r'); 2870 x_flush(); 2871 } 2872 2873 static void 2874 x_e_ungetc(int c) 2875 { 2876 unget_char = c < 0 ? -1 : (c & 255); 2877 } 2878 2879 static int 2880 x_e_getc(void) 2881 { 2882 int c; 2883 2884 if (unget_char >= 0) { 2885 c = unget_char; 2886 unget_char = -1; 2887 return (c); 2888 } 2889 2890 #ifndef MKSH_SMALL 2891 if (macroptr) { 2892 if ((c = (unsigned char)*macroptr++)) 2893 return (c); 2894 macroptr = NULL; 2895 } 2896 #endif 2897 2898 return (x_getc()); 2899 } 2900 2901 static void 2902 x_e_putc2(int c) 2903 { 2904 int width = 1; 2905 2906 if (ctype(c, C_CR | C_LF)) 2907 x_col = 0; 2908 if (x_col < xx_cols) { 2909 #ifndef MKSH_EBCDIC 2910 if (UTFMODE && (c > 0x7F)) { 2911 char utf_tmp[3]; 2912 size_t x; 2913 2914 if (c < 0xA0) 2915 c = 0xFFFD; 2916 x = utf_wctomb(utf_tmp, c); 2917 x_putc(utf_tmp[0]); 2918 if (x > 1) 2919 x_putc(utf_tmp[1]); 2920 if (x > 2) 2921 x_putc(utf_tmp[2]); 2922 width = utf_wcwidth(c); 2923 } else 2924 #endif 2925 x_putc(c); 2926 switch (c) { 2927 case KSH_BEL: 2928 break; 2929 case '\r': 2930 case '\n': 2931 break; 2932 case '\b': 2933 x_col--; 2934 break; 2935 default: 2936 x_col += width; 2937 break; 2938 } 2939 } 2940 if (x_adj_ok && (x_col < 0 || x_col >= (xx_cols - 2))) 2941 x_adjust(); 2942 } 2943 2944 static void 2945 x_e_putc3(const char **cp) 2946 { 2947 int width = 1, c = **(const unsigned char **)cp; 2948 2949 if (ctype(c, C_CR | C_LF)) 2950 x_col = 0; 2951 if (x_col < xx_cols) { 2952 if (UTFMODE && (c > 0x7F)) { 2953 char *cp2; 2954 2955 width = utf_widthadj(*cp, (const char **)&cp2); 2956 if (cp2 == *cp + 1) { 2957 (*cp)++; 2958 #ifdef MKSH_EBCDIC 2959 x_putc(asc2rtt(0xEF)); 2960 x_putc(asc2rtt(0xBF)); 2961 x_putc(asc2rtt(0xBD)); 2962 #else 2963 shf_puts("\xEF\xBF\xBD", shl_out); 2964 #endif 2965 } else 2966 while (*cp < cp2) 2967 x_putcf(*(*cp)++); 2968 } else { 2969 (*cp)++; 2970 x_putc(c); 2971 } 2972 switch (c) { 2973 case KSH_BEL: 2974 break; 2975 case '\r': 2976 case '\n': 2977 break; 2978 case '\b': 2979 x_col--; 2980 break; 2981 default: 2982 x_col += width; 2983 break; 2984 } 2985 } 2986 if (x_adj_ok && (x_col < 0 || x_col >= (xx_cols - 2))) 2987 x_adjust(); 2988 } 2989 2990 static void 2991 x_e_puts(const char *s) 2992 { 2993 int adj = x_adj_done; 2994 2995 while (*s && adj == x_adj_done) 2996 x_e_putc3(&s); 2997 } 2998 2999 /*- 3000 * NAME: 3001 * x_set_arg - set an arg value for next function 3002 * 3003 * DESCRIPTION: 3004 * This is a simple implementation of M-[0-9]. 3005 * 3006 * RETURN VALUE: 3007 * KSTD 3008 */ 3009 static int 3010 x_set_arg(int c) 3011 { 3012 unsigned int n = 0; 3013 bool first = true; 3014 3015 /* strip command prefix */ 3016 c &= 255; 3017 while (c >= 0 && ctype(c, C_DIGIT)) { 3018 n = n * 10 + ksh_numdig(c); 3019 if (n > LINE) 3020 /* upper bound for repeat */ 3021 goto x_set_arg_too_big; 3022 c = x_e_getc(); 3023 first = false; 3024 } 3025 if (c < 0 || first) { 3026 x_set_arg_too_big: 3027 x_e_putc2(KSH_BEL); 3028 x_arg = 1; 3029 x_arg_defaulted = true; 3030 } else { 3031 x_e_ungetc(c); 3032 x_arg = n; 3033 x_arg_defaulted = false; 3034 } 3035 return (KSTD); 3036 } 3037 3038 /* Comment or uncomment the current line. */ 3039 static int 3040 x_comment(int c MKSH_A_UNUSED) 3041 { 3042 ssize_t len = xep - xbuf; 3043 int ret = x_do_comment(xbuf, xend - xbuf, &len); 3044 3045 if (ret < 0) 3046 x_e_putc2(KSH_BEL); 3047 else { 3048 x_modified(); 3049 xep = xbuf + len; 3050 *xep = '\0'; 3051 xcp = xbp = xbuf; 3052 x_redraw('\r'); 3053 if (ret > 0) 3054 return (x_newline('\n')); 3055 } 3056 return (KSTD); 3057 } 3058 3059 static int 3060 x_version(int c MKSH_A_UNUSED) 3061 { 3062 char *o_xbuf = xbuf, *o_xend = xend; 3063 char *o_xbp = xbp, *o_xep = xep, *o_xcp = xcp; 3064 char *v; 3065 3066 strdupx(v, KSH_VERSION, ATEMP); 3067 3068 xbuf = xbp = xcp = v; 3069 xend = xep = strnul(v); 3070 x_redraw('\r'); 3071 x_flush(); 3072 3073 c = x_e_getc(); 3074 xbuf = o_xbuf; 3075 xend = o_xend; 3076 xbp = o_xbp; 3077 xep = o_xep; 3078 xcp = o_xcp; 3079 x_redraw('\r'); 3080 3081 if (c < 0) 3082 return (KSTD); 3083 /* This is what AT&T ksh seems to do... Very bizarre */ 3084 if (c != ' ') 3085 x_e_ungetc(c); 3086 3087 afree(v, ATEMP); 3088 return (KSTD); 3089 } 3090 3091 #ifndef MKSH_SMALL 3092 static int 3093 x_edit_line(int c MKSH_A_UNUSED) 3094 { 3095 if (x_arg_defaulted) { 3096 if (xep == xbuf) { 3097 x_e_putc2(KSH_BEL); 3098 return (KSTD); 3099 } 3100 if (modified) { 3101 *xep = '\0'; 3102 histsave(&source->line, xbuf, HIST_STORE, true); 3103 x_arg = 0; 3104 } else 3105 x_arg = source->line - (histptr - x_histp); 3106 } 3107 if (x_arg) 3108 shf_snprintf(xbuf, xend - xbuf, Tf_sd, 3109 "fc -e ${VISUAL:-${EDITOR:-vi}} --", x_arg); 3110 else 3111 strlcpy(xbuf, "fc -e ${VISUAL:-${EDITOR:-vi}} --", xend - xbuf); 3112 xep = strnul(xbuf); 3113 return (x_newline('\n')); 3114 } 3115 #endif 3116 3117 /*- 3118 * NAME: 3119 * x_prev_histword - recover word from prev command 3120 * 3121 * DESCRIPTION: 3122 * This function recovers the last word from the previous 3123 * command and inserts it into the current edit line. If a 3124 * numeric arg is supplied then the n'th word from the 3125 * start of the previous command is used. 3126 * As a side effect, trashes the mark in order to achieve 3127 * being called in a repeatable fashion. 3128 * 3129 * Bound to M-. 3130 * 3131 * RETURN VALUE: 3132 * KSTD 3133 */ 3134 static int 3135 x_prev_histword(int c MKSH_A_UNUSED) 3136 { 3137 char *rcp, *cp; 3138 char **xhp; 3139 int m = 1; 3140 /* -1 = defaulted; 0+ = argument */ 3141 static int last_arg = -1; 3142 3143 if (x_last_command == XFUNC_prev_histword) { 3144 if (xmp && modified > 1) 3145 x_kill_region(0); 3146 if (modified) 3147 m = modified; 3148 } else 3149 last_arg = x_arg_defaulted ? -1 : x_arg; 3150 xhp = histptr - (m - 1); 3151 if ((xhp < history) || !(cp = *xhp)) { 3152 x_e_putc2(KSH_BEL); 3153 x_modified(); 3154 return (KSTD); 3155 } 3156 x_set_mark(0); 3157 if ((x_arg = last_arg) == -1) { 3158 /* x_arg_defaulted */ 3159 3160 rcp = &cp[strlen(cp) - 1]; 3161 /* 3162 * ignore white-space after the last word 3163 */ 3164 while (rcp > cp && ctype(*rcp, C_CFS)) 3165 rcp--; 3166 while (rcp > cp && !ctype(*rcp, C_CFS)) 3167 rcp--; 3168 if (ctype(*rcp, C_CFS)) 3169 rcp++; 3170 x_ins(rcp); 3171 } else { 3172 /* not x_arg_defaulted */ 3173 char ch; 3174 3175 rcp = cp; 3176 /* 3177 * ignore white-space at start of line 3178 */ 3179 while (*rcp && ctype(*rcp, C_CFS)) 3180 rcp++; 3181 while (x_arg-- > 0) { 3182 while (*rcp && !ctype(*rcp, C_CFS)) 3183 rcp++; 3184 while (*rcp && ctype(*rcp, C_CFS)) 3185 rcp++; 3186 } 3187 cp = rcp; 3188 while (*rcp && !ctype(*rcp, C_CFS)) 3189 rcp++; 3190 ch = *rcp; 3191 *rcp = '\0'; 3192 x_ins(cp); 3193 *rcp = ch; 3194 } 3195 if (!modified) 3196 x_histmcp = x_histp; 3197 modified = m + 1; 3198 return (KSTD); 3199 } 3200 3201 #ifndef MKSH_SMALL 3202 /* Uppercase N(1) words */ 3203 static int 3204 x_fold_upper(int c MKSH_A_UNUSED) 3205 { 3206 return (x_fold_case('U')); 3207 } 3208 3209 /* Lowercase N(1) words */ 3210 static int 3211 x_fold_lower(int c MKSH_A_UNUSED) 3212 { 3213 return (x_fold_case('L')); 3214 } 3215 3216 /* Titlecase N(1) words */ 3217 static int 3218 x_fold_capitalise(int c MKSH_A_UNUSED) 3219 { 3220 return (x_fold_case('C')); 3221 } 3222 3223 /*- 3224 * NAME: 3225 * x_fold_case - convert word to UPPER/lower/Capital case 3226 * 3227 * DESCRIPTION: 3228 * This function is used to implement M-U/M-u, M-L/M-l, M-C/M-c 3229 * to UPPER CASE, lower case or Capitalise Words. 3230 * 3231 * RETURN VALUE: 3232 * None 3233 */ 3234 static int 3235 x_fold_case(int c) 3236 { 3237 char *cp = xcp; 3238 3239 if (cp == xep) { 3240 x_e_putc2(KSH_BEL); 3241 return (KSTD); 3242 } 3243 while (x_arg--) { 3244 /* 3245 * first skip over any white-space 3246 */ 3247 while (cp != xep && ctype(*cp, C_MFS)) 3248 cp++; 3249 /* 3250 * do the first char on its own since it may be 3251 * a different action than for the rest. 3252 */ 3253 if (cp != xep) { 3254 if (c == 'L') 3255 /* lowercase */ 3256 *cp = ksh_tolower(*cp); 3257 else 3258 /* uppercase, capitalise */ 3259 *cp = ksh_toupper(*cp); 3260 cp++; 3261 } 3262 /* 3263 * now for the rest of the word 3264 */ 3265 while (cp != xep && !ctype(*cp, C_MFS)) { 3266 if (c == 'U') 3267 /* uppercase */ 3268 *cp = ksh_toupper(*cp); 3269 else 3270 /* lowercase, capitalise */ 3271 *cp = ksh_tolower(*cp); 3272 cp++; 3273 } 3274 } 3275 x_goto(cp); 3276 x_modified(); 3277 return (KSTD); 3278 } 3279 #endif 3280 3281 /*- 3282 * NAME: 3283 * x_lastcp - last visible char 3284 * 3285 * DESCRIPTION: 3286 * This function returns a pointer to that char in the 3287 * edit buffer that will be the last displayed on the 3288 * screen. 3289 */ 3290 static char * 3291 x_lastcp(void) 3292 { 3293 if (!xlp_valid) { 3294 int i = 0, j; 3295 char *xlp2; 3296 3297 xlp = xbp; 3298 while (xlp < xep) { 3299 j = x_size2(xlp, &xlp2); 3300 if ((i + j) > x_displen) 3301 break; 3302 i += j; 3303 xlp = xlp2; 3304 } 3305 } 3306 xlp_valid = true; 3307 return (xlp); 3308 } 3309 3310 /* correctly position the cursor on the screen from end of visible area */ 3311 static void 3312 x_lastpos(void) 3313 { 3314 char *cp = x_lastcp(); 3315 3316 while (cp > xcp) 3317 x_bs3(&cp); 3318 } 3319 3320 static void 3321 x_mode(bool onoff) 3322 { 3323 static bool x_cur_mode; 3324 3325 if (x_cur_mode == onoff) 3326 return; 3327 x_cur_mode = onoff; 3328 3329 if (onoff) { 3330 x_mkraw(tty_fd, NULL, false); 3331 3332 edchars.erase = toedchar(tty_state.c_cc[VERASE]); 3333 edchars.kill = toedchar(tty_state.c_cc[VKILL]); 3334 edchars.intr = toedchar(tty_state.c_cc[VINTR]); 3335 edchars.quit = toedchar(tty_state.c_cc[VQUIT]); 3336 edchars.eof = toedchar(tty_state.c_cc[VEOF]); 3337 #ifdef VWERASE 3338 edchars.werase = toedchar(tty_state.c_cc[VWERASE]); 3339 #else 3340 edchars.werase = 0; 3341 #endif 3342 3343 if (!edchars.erase) 3344 edchars.erase = CTRL_H; 3345 if (!edchars.kill) 3346 edchars.kill = CTRL_U; 3347 if (!edchars.intr) 3348 edchars.intr = CTRL_C; 3349 if (!edchars.quit) 3350 edchars.quit = CTRL_BK; 3351 if (!edchars.eof) 3352 edchars.eof = CTRL_D; 3353 if (!edchars.werase) 3354 edchars.werase = CTRL_W; 3355 3356 if (isedchar(edchars.erase)) { 3357 bind_if_not_bound(0, edchars.erase, XFUNC_del_back); 3358 bind_if_not_bound(1, edchars.erase, XFUNC_del_bword); 3359 } 3360 if (isedchar(edchars.kill)) 3361 bind_if_not_bound(0, edchars.kill, XFUNC_del_line); 3362 if (isedchar(edchars.werase)) 3363 bind_if_not_bound(0, edchars.werase, XFUNC_del_bword); 3364 if (isedchar(edchars.intr)) 3365 bind_if_not_bound(0, edchars.intr, XFUNC_abort); 3366 if (isedchar(edchars.quit)) 3367 bind_if_not_bound(0, edchars.quit, XFUNC_noop); 3368 } else 3369 mksh_tcset(tty_fd, &tty_state); 3370 } 3371 3372 #if !MKSH_S_NOVI 3373 /* +++ vi editing mode +++ */ 3374 3375 struct edstate { 3376 char *cbuf; 3377 ssize_t winleft; 3378 ssize_t cbufsize; 3379 ssize_t linelen; 3380 ssize_t cursor; 3381 }; 3382 3383 static int vi_hook(int); 3384 static int nextstate(int); 3385 static int vi_insert(int); 3386 static int vi_cmd(int, const char *); 3387 static int domove(int, const char *, int); 3388 static int domovebeg(void); 3389 static int redo_insert(int); 3390 static void yank_range(int, int); 3391 static int bracktype(int); 3392 static void save_cbuf(void); 3393 static void restore_cbuf(void); 3394 static int putbuf(const char *, ssize_t, bool); 3395 static void del_range(int, int); 3396 static int findch(int, int, bool, bool) MKSH_A_PURE; 3397 static int forwword(int); 3398 static int backword(int); 3399 static int endword(int); 3400 static int Forwword(int); 3401 static int Backword(int); 3402 static int Endword(int); 3403 static int grabhist(int, int); 3404 static int grabsearch(int, int, int, const char *); 3405 static void redraw_line(bool); 3406 static void refresh(int); 3407 static int outofwin(void); 3408 static void rewindow(void); 3409 static int newcol(unsigned char, int); 3410 static void display(char *, char *, int); 3411 static void ed_mov_opt(int, char *); 3412 static int expand_word(int); 3413 static int complete_word(int, int); 3414 static int print_expansions(struct edstate *, int); 3415 static void vi_error(void); 3416 static void vi_macro_reset(void); 3417 static int x_vi_putbuf(const char *, size_t); 3418 #define char_len(c) (ksh_isctrl(c) ? 2 : 1) 3419 3420 #define vC 0x01 /* a valid command that isn't a vM, vE, vU */ 3421 #define vM 0x02 /* movement command (h, l, etc.) */ 3422 #define vE 0x04 /* extended command (c, d, y) */ 3423 #define vX 0x08 /* long command (@, f, F, t, T, etc.) */ 3424 #define vU 0x10 /* an UN-undoable command (that isn't a vM) */ 3425 #define vB 0x20 /* bad command (^@) */ 3426 #define vZ 0x40 /* repeat count defaults to 0 (not 1) */ 3427 #define vS 0x80 /* search (/, ?) */ 3428 3429 #define is_bad(c) (classify[rtt2asc(c) & 0x7F] & vB) 3430 #define is_cmd(c) (classify[rtt2asc(c) & 0x7F] & (vM | vE | vC | vU)) 3431 #define is_move(c) (classify[rtt2asc(c) & 0x7F] & vM) 3432 #define is_extend(c) (classify[rtt2asc(c) & 0x7F] & vE) 3433 #define is_long(c) (classify[rtt2asc(c) & 0x7F] & vX) 3434 #define is_undoable(c) (!(classify[rtt2asc(c) & 0x7F] & vU)) 3435 #define is_srch(c) (classify[rtt2asc(c) & 0x7F] & vS) 3436 #define is_zerocount(c) (classify[rtt2asc(c) & 0x7F] & vZ) 3437 3438 static const unsigned char classify[128] = { 3439 /* 0 1 2 3 4 5 6 7 */ 3440 /* 0 ^@ ^A ^B ^C ^D ^E ^F ^G */ 3441 vB, 0, 0, 0, 0, vC|vU, vC|vZ, 0, 3442 /* 1 ^H ^I ^J ^K ^L ^M ^N ^O */ 3443 vM, vC|vZ, 0, 0, vC|vU, 0, vC, 0, 3444 /* 2 ^P ^Q ^R ^S ^T ^U ^V ^W */ 3445 vC, 0, vC|vU, 0, 0, 0, vC, 0, 3446 /* 3 ^X ^Y ^Z ^[ ^\ ^] ^^ ^_ */ 3447 vC, 0, 0, vC|vZ, 0, 0, 0, 0, 3448 /* 4 <space> ! " # $ % & ' */ 3449 vM, 0, 0, vC, vM, vM, 0, 0, 3450 /* 5 ( ) * + , - . / */ 3451 0, 0, vC, vC, vM, vC, 0, vC|vS, 3452 /* 6 0 1 2 3 4 5 6 7 */ 3453 vM, 0, 0, 0, 0, 0, 0, 0, 3454 /* 7 8 9 : ; < = > ? */ 3455 0, 0, 0, vM, 0, vC, 0, vC|vS, 3456 /* 8 @ A B C D E F G */ 3457 vC|vX, vC, vM, vC, vC, vM, vM|vX, vC|vU|vZ, 3458 /* 9 H I J K L M N O */ 3459 0, vC, 0, 0, 0, 0, vC|vU, vU, 3460 /* A P Q R S T U V W */ 3461 vC, 0, vC, vC, vM|vX, vC, 0, vM, 3462 /* B X Y Z [ \ ] ^ _ */ 3463 vC, vC|vU, 0, vU, vC|vZ, 0, vM, vC|vZ, 3464 /* C ` a b c d e f g */ 3465 0, vC, vM, vE, vE, vM, vM|vX, vC|vZ, 3466 /* D h i j k l m n o */ 3467 vM, vC, vC|vU, vC|vU, vM, 0, vC|vU, 0, 3468 /* E p q r s t u v w */ 3469 vC, 0, vX, vC, vM|vX, vC|vU, vC|vU|vZ, vM, 3470 /* F x y z { | } ~ ^? */ 3471 vC, vE|vU, 0, 0, vM|vZ, 0, vC, 0 3472 }; 3473 3474 #define MAXVICMD 3 3475 #define SRCHLEN 40 3476 3477 #define INSERT 1 3478 #define REPLACE 2 3479 3480 #define VNORMAL 0 /* command, insert or replace mode */ 3481 #define VARG1 1 /* digit prefix (first, eg, 5l) */ 3482 #define VEXTCMD 2 /* cmd + movement (eg, cl) */ 3483 #define VARG2 3 /* digit prefix (second, eg, 2c3l) */ 3484 #define VXCH 4 /* f, F, t, T, @ */ 3485 #define VFAIL 5 /* bad command */ 3486 #define VCMD 6 /* single char command (eg, X) */ 3487 #define VREDO 7 /* . */ 3488 #define VLIT 8 /* ^V */ 3489 #define VSEARCH 9 /* /, ? */ 3490 #define VVERSION 10 /* <ESC> ^V */ 3491 #define VPREFIX2 11 /* ^[[ and ^[O in insert mode */ 3492 3493 static struct edstate *save_edstate(struct edstate *old); 3494 static void restore_edstate(struct edstate *old, struct edstate *news); 3495 static void free_edstate(struct edstate *old); 3496 3497 static struct edstate ebuf; 3498 static struct edstate undobuf; 3499 3500 static struct edstate *vs; /* current Vi editing mode state */ 3501 static struct edstate *undo; 3502 3503 static char *ibuf; /* input buffer */ 3504 static bool first_insert; /* set when starting in insert mode */ 3505 static int saved_inslen; /* saved inslen for first insert */ 3506 static int inslen; /* length of input buffer */ 3507 static int srchlen; /* length of current search pattern */ 3508 static char *ybuf; /* yank buffer */ 3509 static int yanklen; /* length of yank buffer */ 3510 static int fsavecmd = ' '; /* last find command */ 3511 static int fsavech; /* character to find */ 3512 static char lastcmd[MAXVICMD]; /* last non-move command */ 3513 static int lastac; /* argcnt for lastcmd */ 3514 static int lastsearch = ' '; /* last search command */ 3515 static char srchpat[SRCHLEN]; /* last search pattern */ 3516 static int insert; /* <>0 in insert mode */ 3517 static int hnum; /* position in history */ 3518 static int ohnum; /* history line copied (after mod) */ 3519 static int hlast; /* 1 past last position in history */ 3520 static int state; 3521 3522 /* 3523 * Information for keeping track of macros that are being expanded. 3524 * The format of buf is the alias contents followed by a NUL byte followed 3525 * by the name (letter) of the alias. The end of the buffer is marked by 3526 * a double NUL. The name of the alias is stored so recursive macros can 3527 * be detected. 3528 */ 3529 struct macro_state { 3530 unsigned char *p; /* current position in buf */ 3531 unsigned char *buf; /* pointer to macro(s) being expanded */ 3532 size_t len; /* how much data in buffer */ 3533 }; 3534 static struct macro_state macro; 3535 3536 /* last input was expanded */ 3537 static enum expand_mode { 3538 NONE = 0, EXPAND, COMPLETE, PRINT 3539 } expanded; 3540 3541 static int 3542 x_vi(char *buf) 3543 { 3544 int c; 3545 3546 state = VNORMAL; 3547 ohnum = hnum = hlast = histnum(-1) + 1; 3548 insert = INSERT; 3549 saved_inslen = inslen; 3550 first_insert = true; 3551 inslen = 0; 3552 vi_macro_reset(); 3553 3554 ebuf.cbuf = buf; 3555 if (undobuf.cbuf == NULL) { 3556 ibuf = alloc(LINE, AEDIT); 3557 ybuf = alloc(LINE, AEDIT); 3558 undobuf.cbuf = alloc(LINE, AEDIT); 3559 } 3560 undobuf.cbufsize = ebuf.cbufsize = LINE; 3561 undobuf.linelen = ebuf.linelen = 0; 3562 undobuf.cursor = ebuf.cursor = 0; 3563 undobuf.winleft = ebuf.winleft = 0; 3564 vs = &ebuf; 3565 undo = &undobuf; 3566 3567 x_init_prompt(true); 3568 x_col = pwidth; 3569 3570 if (wbuf_len != x_cols - 3 && ((wbuf_len = x_cols - 3))) { 3571 wbuf[0] = aresize(wbuf[0], wbuf_len, AEDIT); 3572 wbuf[1] = aresize(wbuf[1], wbuf_len, AEDIT); 3573 } 3574 if (wbuf_len) { 3575 memset(wbuf[0], ' ', wbuf_len); 3576 memset(wbuf[1], ' ', wbuf_len); 3577 } 3578 winwidth = x_cols - pwidth - 3; 3579 win = 0; 3580 morec = ' '; 3581 lastref = 1; 3582 holdlen = 0; 3583 3584 editmode = 2; 3585 x_flush(); 3586 while (/* CONSTCOND */ 1) { 3587 if (macro.p) { 3588 c = (unsigned char)*macro.p++; 3589 /* end of current macro? */ 3590 if (!c) { 3591 /* more macros left to finish? */ 3592 if (*macro.p++) 3593 continue; 3594 /* must be the end of all the macros */ 3595 vi_macro_reset(); 3596 c = x_getc(); 3597 } 3598 } else 3599 c = x_getc(); 3600 3601 if (c == -1) 3602 break; 3603 if (state != VLIT) { 3604 if (isched(c, edchars.intr) || 3605 isched(c, edchars.quit)) { 3606 /* shove input buffer away */ 3607 xbuf = ebuf.cbuf; 3608 xep = xbuf; 3609 if (ebuf.linelen > 0) 3610 xep += ebuf.linelen; 3611 /* pretend we got an interrupt */ 3612 x_intr(isched(c, edchars.intr) ? 3613 SIGINT : SIGQUIT, c); 3614 } else if (isched(c, edchars.eof) && 3615 state != VVERSION) { 3616 if (vs->linelen == 0) { 3617 x_vi_zotc(c); 3618 c = -1; 3619 break; 3620 } 3621 continue; 3622 } 3623 } 3624 if (vi_hook(c)) 3625 break; 3626 x_flush(); 3627 } 3628 3629 x_putc('\r'); 3630 x_putc('\n'); 3631 x_flush(); 3632 3633 if (c == -1 || (ssize_t)LINE <= vs->linelen) 3634 return (-1); 3635 3636 if (vs->cbuf != buf) 3637 memcpy(buf, vs->cbuf, vs->linelen); 3638 3639 buf[vs->linelen++] = '\n'; 3640 3641 return (vs->linelen); 3642 } 3643 3644 static int 3645 vi_hook(int ch) 3646 { 3647 static char curcmd[MAXVICMD], locpat[SRCHLEN]; 3648 static int cmdlen, argc1, argc2; 3649 3650 switch (state) { 3651 3652 case VNORMAL: 3653 /* PC scancodes */ 3654 if (!ch) switch (cmdlen = 0, (ch = x_getc())) { 3655 case 71: ch = '0'; goto pseudo_vi_command; 3656 case 72: ch = 'k'; goto pseudo_vi_command; 3657 case 73: ch = 'A'; goto vi_xfunc_search_up; 3658 case 75: ch = 'h'; goto pseudo_vi_command; 3659 case 77: ch = 'l'; goto pseudo_vi_command; 3660 case 79: ch = '$'; goto pseudo_vi_command; 3661 case 80: ch = 'j'; goto pseudo_vi_command; 3662 case 83: ch = 'x'; goto pseudo_vi_command; 3663 default: ch = 0; goto vi_insert_failed; 3664 } 3665 if (insert != 0) { 3666 if (ch == CTRL_V) { 3667 state = VLIT; 3668 ch = '^'; 3669 } 3670 switch (vi_insert(ch)) { 3671 case -1: 3672 vi_insert_failed: 3673 vi_error(); 3674 state = VNORMAL; 3675 break; 3676 case 0: 3677 if (state == VLIT) { 3678 vs->cursor--; 3679 refresh(0); 3680 } else 3681 refresh(insert != 0); 3682 break; 3683 case 1: 3684 return (1); 3685 } 3686 } else { 3687 if (ctype(ch, C_CR | C_LF)) 3688 return (1); 3689 cmdlen = 0; 3690 argc1 = 0; 3691 if (ctype(ch, C_DIGIT) && ord(ch) != ORD('0')) { 3692 argc1 = ksh_numdig(ch); 3693 state = VARG1; 3694 } else { 3695 pseudo_vi_command: 3696 curcmd[cmdlen++] = ch; 3697 state = nextstate(ch); 3698 if (state == VSEARCH) { 3699 save_cbuf(); 3700 vs->cursor = 0; 3701 vs->linelen = 0; 3702 if (putbuf(ch == '/' ? "/" : "?", 1, 3703 false) != 0) 3704 return (-1); 3705 refresh(0); 3706 } 3707 if (state == VVERSION) { 3708 save_cbuf(); 3709 vs->cursor = 0; 3710 vs->linelen = 0; 3711 putbuf(KSH_VERSION, 3712 strlen(KSH_VERSION), false); 3713 refresh(0); 3714 } 3715 } 3716 } 3717 break; 3718 3719 case VLIT: 3720 if (is_bad(ch)) { 3721 del_range(vs->cursor, vs->cursor + 1); 3722 vi_error(); 3723 } else 3724 vs->cbuf[vs->cursor++] = ch; 3725 refresh(1); 3726 state = VNORMAL; 3727 break; 3728 3729 case VVERSION: 3730 restore_cbuf(); 3731 state = VNORMAL; 3732 refresh(0); 3733 break; 3734 3735 case VARG1: 3736 if (ctype(ch, C_DIGIT)) 3737 argc1 = argc1 * 10 + ksh_numdig(ch); 3738 else { 3739 curcmd[cmdlen++] = ch; 3740 state = nextstate(ch); 3741 } 3742 break; 3743 3744 case VEXTCMD: 3745 argc2 = 0; 3746 if (ctype(ch, C_DIGIT) && ord(ch) != ORD('0')) { 3747 argc2 = ksh_numdig(ch); 3748 state = VARG2; 3749 return (0); 3750 } else { 3751 curcmd[cmdlen++] = ch; 3752 if (ch == curcmd[0]) 3753 state = VCMD; 3754 else if (is_move(ch)) 3755 state = nextstate(ch); 3756 else 3757 state = VFAIL; 3758 } 3759 break; 3760 3761 case VARG2: 3762 if (ctype(ch, C_DIGIT)) 3763 argc2 = argc2 * 10 + ksh_numdig(ch); 3764 else { 3765 if (argc1 == 0) 3766 argc1 = argc2; 3767 else 3768 argc1 *= argc2; 3769 curcmd[cmdlen++] = ch; 3770 if (ch == curcmd[0]) 3771 state = VCMD; 3772 else if (is_move(ch)) 3773 state = nextstate(ch); 3774 else 3775 state = VFAIL; 3776 } 3777 break; 3778 3779 case VXCH: 3780 if (ch == CTRL_BO) 3781 state = VNORMAL; 3782 else { 3783 curcmd[cmdlen++] = ch; 3784 state = VCMD; 3785 } 3786 break; 3787 3788 case VSEARCH: 3789 if (ctype(ch, C_CR | C_LF) /* || ch == CTRL_BO */ ) { 3790 restore_cbuf(); 3791 /* Repeat last search? */ 3792 if (srchlen == 0) { 3793 if (!srchpat[0]) { 3794 vi_error(); 3795 state = VNORMAL; 3796 refresh(0); 3797 return (0); 3798 } 3799 } else { 3800 locpat[srchlen] = '\0'; 3801 memcpy(srchpat, locpat, srchlen + 1); 3802 } 3803 state = VCMD; 3804 } else if (isched(ch, edchars.erase) || ch == CTRL_H) { 3805 if (srchlen != 0) { 3806 srchlen--; 3807 vs->linelen -= char_len(locpat[srchlen]); 3808 vs->cursor = vs->linelen; 3809 refresh(0); 3810 return (0); 3811 } 3812 restore_cbuf(); 3813 state = VNORMAL; 3814 refresh(0); 3815 } else if (isched(ch, edchars.kill)) { 3816 srchlen = 0; 3817 vs->linelen = 1; 3818 vs->cursor = 1; 3819 refresh(0); 3820 return (0); 3821 } else if (isched(ch, edchars.werase)) { 3822 unsigned int i, n; 3823 struct edstate new_es, *save_es; 3824 3825 new_es.cursor = srchlen; 3826 new_es.cbuf = locpat; 3827 3828 save_es = vs; 3829 vs = &new_es; 3830 n = backword(1); 3831 vs = save_es; 3832 3833 i = (unsigned)srchlen; 3834 while (--i >= n) 3835 vs->linelen -= char_len(locpat[i]); 3836 srchlen = (int)n; 3837 vs->cursor = vs->linelen; 3838 refresh(0); 3839 return (0); 3840 } else { 3841 if (srchlen == SRCHLEN - 1) 3842 vi_error(); 3843 else { 3844 locpat[srchlen++] = ch; 3845 if (ksh_isctrl(ch)) { 3846 if ((size_t)vs->linelen + 2 > 3847 (size_t)vs->cbufsize) 3848 vi_error(); 3849 vs->cbuf[vs->linelen++] = '^'; 3850 vs->cbuf[vs->linelen++] = ksh_unctrl(ch); 3851 } else { 3852 if (vs->linelen >= vs->cbufsize) 3853 vi_error(); 3854 vs->cbuf[vs->linelen++] = ch; 3855 } 3856 vs->cursor = vs->linelen; 3857 refresh(0); 3858 } 3859 return (0); 3860 } 3861 break; 3862 3863 case VPREFIX2: 3864 vi_xfunc_search_up: 3865 state = VFAIL; 3866 switch (ch) { 3867 case 'A': 3868 /* the cursor may not be at the BOL */ 3869 if (!vs->cursor) 3870 break; 3871 /* nor further in the line than we can search for */ 3872 if ((size_t)vs->cursor >= sizeof(srchpat) - 1) 3873 vs->cursor = sizeof(srchpat) - 2; 3874 /* anchor the search pattern */ 3875 srchpat[0] = '^'; 3876 /* take the current line up to the cursor */ 3877 memmove(srchpat + 1, vs->cbuf, vs->cursor); 3878 srchpat[vs->cursor + 1] = '\0'; 3879 /* set a magic flag */ 3880 argc1 = 2 + (int)vs->cursor; 3881 /* and emulate a backwards history search */ 3882 lastsearch = '/'; 3883 *curcmd = 'n'; 3884 goto pseudo_VCMD; 3885 } 3886 break; 3887 } 3888 3889 switch (state) { 3890 case VCMD: 3891 pseudo_VCMD: 3892 state = VNORMAL; 3893 switch (vi_cmd(argc1, curcmd)) { 3894 case -1: 3895 vi_error(); 3896 refresh(0); 3897 break; 3898 case 0: 3899 if (insert != 0) 3900 inslen = 0; 3901 refresh(insert != 0); 3902 break; 3903 case 1: 3904 refresh(0); 3905 return (1); 3906 case 2: 3907 /* back from a 'v' command - don't redraw the screen */ 3908 return (1); 3909 } 3910 break; 3911 3912 case VREDO: 3913 state = VNORMAL; 3914 if (argc1 != 0) 3915 lastac = argc1; 3916 switch (vi_cmd(lastac, lastcmd)) { 3917 case -1: 3918 vi_error(); 3919 refresh(0); 3920 break; 3921 case 0: 3922 if (insert != 0) { 3923 if (lastcmd[0] == 's' || 3924 ksh_eq(lastcmd[0], 'C', 'c')) { 3925 if (redo_insert(1) != 0) 3926 vi_error(); 3927 } else { 3928 if (redo_insert(lastac) != 0) 3929 vi_error(); 3930 } 3931 } 3932 refresh(0); 3933 break; 3934 case 1: 3935 refresh(0); 3936 return (1); 3937 case 2: 3938 /* back from a 'v' command - can't happen */ 3939 break; 3940 } 3941 break; 3942 3943 case VFAIL: 3944 state = VNORMAL; 3945 vi_error(); 3946 break; 3947 } 3948 return (0); 3949 } 3950 3951 static int 3952 nextstate(int ch) 3953 { 3954 if (is_extend(ch)) 3955 return (VEXTCMD); 3956 else if (is_srch(ch)) 3957 return (VSEARCH); 3958 else if (is_long(ch)) 3959 return (VXCH); 3960 else if (ch == '.') 3961 return (VREDO); 3962 else if (ch == CTRL_V) 3963 return (VVERSION); 3964 else if (is_cmd(ch)) 3965 return (VCMD); 3966 else 3967 return (VFAIL); 3968 } 3969 3970 static int 3971 vi_insert(int ch) 3972 { 3973 int tcursor; 3974 3975 if (isched(ch, edchars.erase) || ch == CTRL_H) { 3976 if (insert == REPLACE) { 3977 if (vs->cursor == undo->cursor) { 3978 vi_error(); 3979 return (0); 3980 } 3981 if (inslen > 0) 3982 inslen--; 3983 vs->cursor--; 3984 if (vs->cursor >= undo->linelen) 3985 vs->linelen--; 3986 else 3987 vs->cbuf[vs->cursor] = undo->cbuf[vs->cursor]; 3988 } else { 3989 if (vs->cursor == 0) 3990 return (0); 3991 if (inslen > 0) 3992 inslen--; 3993 vs->cursor--; 3994 vs->linelen--; 3995 memmove(&vs->cbuf[vs->cursor], &vs->cbuf[vs->cursor + 1], 3996 vs->linelen - vs->cursor + 1); 3997 } 3998 expanded = NONE; 3999 return (0); 4000 } 4001 if (isched(ch, edchars.kill)) { 4002 if (vs->cursor != 0) { 4003 inslen = 0; 4004 memmove(vs->cbuf, &vs->cbuf[vs->cursor], 4005 vs->linelen - vs->cursor); 4006 vs->linelen -= vs->cursor; 4007 vs->cursor = 0; 4008 } 4009 expanded = NONE; 4010 return (0); 4011 } 4012 if (isched(ch, edchars.werase)) { 4013 if (vs->cursor != 0) { 4014 tcursor = backword(1); 4015 memmove(&vs->cbuf[tcursor], &vs->cbuf[vs->cursor], 4016 vs->linelen - vs->cursor); 4017 vs->linelen -= vs->cursor - tcursor; 4018 if (inslen < vs->cursor - tcursor) 4019 inslen = 0; 4020 else 4021 inslen -= vs->cursor - tcursor; 4022 vs->cursor = tcursor; 4023 } 4024 expanded = NONE; 4025 return (0); 4026 } 4027 /* 4028 * If any chars are entered before escape, trash the saved insert 4029 * buffer (if user inserts & deletes char, ibuf gets trashed and 4030 * we don't want to use it) 4031 */ 4032 if (first_insert && ch != CTRL_BO) 4033 saved_inslen = 0; 4034 switch (ch) { 4035 case '\0': 4036 return (-1); 4037 4038 case '\r': 4039 case '\n': 4040 return (1); 4041 4042 case CTRL_BO: 4043 expanded = NONE; 4044 if (first_insert) { 4045 first_insert = false; 4046 if (inslen == 0) { 4047 inslen = saved_inslen; 4048 return (redo_insert(0)); 4049 } 4050 lastcmd[0] = 'a'; 4051 lastac = 1; 4052 } 4053 if (lastcmd[0] == 's' || ksh_eq(lastcmd[0], 'C', 'c')) 4054 return (redo_insert(0)); 4055 else 4056 return (redo_insert(lastac - 1)); 4057 4058 /* { start nonstandard vi commands */ 4059 case CTRL_X: 4060 expand_word(0); 4061 break; 4062 4063 case CTRL_F: 4064 complete_word(0, 0); 4065 break; 4066 4067 case CTRL_E: 4068 print_expansions(vs, 0); 4069 break; 4070 4071 case CTRL_I: 4072 if (Flag(FVITABCOMPLETE)) { 4073 complete_word(0, 0); 4074 break; 4075 } 4076 /* FALLTHROUGH */ 4077 /* end nonstandard vi commands } */ 4078 4079 default: 4080 if (vs->linelen >= vs->cbufsize - 1) 4081 return (-1); 4082 ibuf[inslen++] = ch; 4083 if (insert == INSERT) { 4084 memmove(&vs->cbuf[vs->cursor + 1], &vs->cbuf[vs->cursor], 4085 vs->linelen - vs->cursor); 4086 vs->linelen++; 4087 } 4088 vs->cbuf[vs->cursor++] = ch; 4089 if (insert == REPLACE && vs->cursor > vs->linelen) 4090 vs->linelen++; 4091 expanded = NONE; 4092 } 4093 return (0); 4094 } 4095 4096 static int 4097 vi_cmd(int argcnt, const char *cmd) 4098 { 4099 int ncursor; 4100 int cur, c1, c2, c3 = 0; 4101 int any; 4102 struct edstate *t; 4103 4104 if (argcnt == 0 && !is_zerocount(*cmd)) 4105 argcnt = 1; 4106 4107 if (is_move(*cmd)) { 4108 if ((cur = domove(argcnt, cmd, 0)) >= 0) { 4109 if (cur == vs->linelen && cur != 0) 4110 cur--; 4111 vs->cursor = cur; 4112 } else 4113 return (-1); 4114 } else { 4115 /* Don't save state in middle of macro.. */ 4116 if (is_undoable(*cmd) && !macro.p) { 4117 undo->winleft = vs->winleft; 4118 memmove(undo->cbuf, vs->cbuf, vs->linelen); 4119 undo->linelen = vs->linelen; 4120 undo->cursor = vs->cursor; 4121 lastac = argcnt; 4122 memmove(lastcmd, cmd, MAXVICMD); 4123 } 4124 switch (ord(*cmd)) { 4125 4126 case CTRL_L: 4127 case CTRL_R: 4128 redraw_line(true); 4129 break; 4130 4131 case ORD('@'): 4132 { 4133 static char alias[] = "_\0"; 4134 struct tbl *ap; 4135 size_t olen, nlen; 4136 char *p, *nbuf; 4137 4138 /* lookup letter in alias list... */ 4139 alias[1] = cmd[1]; 4140 ap = ktsearch(&aliases, alias, hash(alias)); 4141 if (!cmd[1] || !ap || !(ap->flag & ISSET)) 4142 return (-1); 4143 /* check if this is a recursive call... */ 4144 if ((p = (char *)macro.p)) 4145 while ((p = strnul(p)) && p[1]) 4146 if (*++p == cmd[1]) 4147 return (-1); 4148 /* insert alias into macro buffer */ 4149 nlen = strlen(ap->val.s) + 1; 4150 olen = !macro.p ? 2 : 4151 macro.len - (macro.p - macro.buf); 4152 /* 4153 * at this point, it's fairly reasonable that 4154 * nlen + olen + 2 doesn't overflow 4155 */ 4156 nbuf = alloc(nlen + 1 + olen, AEDIT); 4157 memcpy(nbuf, ap->val.s, nlen); 4158 nbuf[nlen++] = cmd[1]; 4159 if (macro.p) { 4160 memcpy(nbuf + nlen, macro.p, olen); 4161 afree(macro.buf, AEDIT); 4162 nlen += olen; 4163 } else { 4164 nbuf[nlen++] = '\0'; 4165 nbuf[nlen++] = '\0'; 4166 } 4167 macro.p = macro.buf = (unsigned char *)nbuf; 4168 macro.len = nlen; 4169 } 4170 break; 4171 4172 case ORD('a'): 4173 modified = 1; 4174 hnum = hlast; 4175 if (vs->linelen != 0) 4176 vs->cursor++; 4177 insert = INSERT; 4178 break; 4179 4180 case ORD('A'): 4181 modified = 1; 4182 hnum = hlast; 4183 del_range(0, 0); 4184 vs->cursor = vs->linelen; 4185 insert = INSERT; 4186 break; 4187 4188 case ORD('S'): 4189 vs->cursor = domovebeg(); 4190 del_range(vs->cursor, vs->linelen); 4191 modified = 1; 4192 hnum = hlast; 4193 insert = INSERT; 4194 break; 4195 4196 case ORD('Y'): 4197 cmd = "y$"; 4198 /* ahhhhhh... */ 4199 4200 /* FALLTHROUGH */ 4201 case ORD('c'): 4202 case ORD('d'): 4203 case ORD('y'): 4204 if (*cmd == cmd[1]) { 4205 c1 = *cmd == 'c' ? domovebeg() : 0; 4206 c2 = vs->linelen; 4207 } else if (!is_move(cmd[1])) 4208 return (-1); 4209 else { 4210 if ((ncursor = domove(argcnt, &cmd[1], 1)) < 0) 4211 return (-1); 4212 if (*cmd == 'c' && ksh_eq(cmd[1], 'W', 'w') && 4213 !ctype(vs->cbuf[vs->cursor], C_SPACE)) { 4214 do { 4215 --ncursor; 4216 } while (ctype(vs->cbuf[ncursor], C_SPACE)); 4217 ncursor++; 4218 } 4219 if (ncursor > vs->cursor) { 4220 c1 = vs->cursor; 4221 c2 = ncursor; 4222 } else { 4223 c1 = ncursor; 4224 c2 = vs->cursor; 4225 if (cmd[1] == '%') 4226 c2++; 4227 } 4228 } 4229 if (*cmd != 'c' && c1 != c2) 4230 yank_range(c1, c2); 4231 if (*cmd != 'y') { 4232 del_range(c1, c2); 4233 vs->cursor = c1; 4234 } 4235 if (*cmd == 'c') { 4236 modified = 1; 4237 hnum = hlast; 4238 insert = INSERT; 4239 } 4240 break; 4241 4242 case ORD('p'): 4243 modified = 1; 4244 hnum = hlast; 4245 if (vs->linelen != 0) 4246 vs->cursor++; 4247 while (putbuf(ybuf, yanklen, false) == 0 && 4248 --argcnt > 0) 4249 ; 4250 if (vs->cursor != 0) 4251 vs->cursor--; 4252 if (argcnt != 0) 4253 return (-1); 4254 break; 4255 4256 case ORD('P'): 4257 modified = 1; 4258 hnum = hlast; 4259 any = 0; 4260 while (putbuf(ybuf, yanklen, false) == 0 && 4261 --argcnt > 0) 4262 any = 1; 4263 if (any && vs->cursor != 0) 4264 vs->cursor--; 4265 if (argcnt != 0) 4266 return (-1); 4267 break; 4268 4269 case ORD('C'): 4270 modified = 1; 4271 hnum = hlast; 4272 del_range(vs->cursor, vs->linelen); 4273 insert = INSERT; 4274 break; 4275 4276 case ORD('D'): 4277 yank_range(vs->cursor, vs->linelen); 4278 del_range(vs->cursor, vs->linelen); 4279 if (vs->cursor != 0) 4280 vs->cursor--; 4281 break; 4282 4283 case ORD('g'): 4284 if (!argcnt) 4285 argcnt = hlast; 4286 /* FALLTHROUGH */ 4287 case ORD('G'): 4288 if (!argcnt) 4289 argcnt = 1; 4290 else 4291 argcnt = hlast - (source->line - argcnt); 4292 if (grabhist(modified, argcnt - 1) < 0) 4293 return (-1); 4294 else { 4295 modified = 0; 4296 hnum = argcnt - 1; 4297 } 4298 break; 4299 4300 case ORD('i'): 4301 modified = 1; 4302 hnum = hlast; 4303 insert = INSERT; 4304 break; 4305 4306 case ORD('I'): 4307 modified = 1; 4308 hnum = hlast; 4309 vs->cursor = domovebeg(); 4310 insert = INSERT; 4311 break; 4312 4313 case ORD('j'): 4314 case ORD('+'): 4315 case CTRL_N: 4316 if (grabhist(modified, hnum + argcnt) < 0) 4317 return (-1); 4318 else { 4319 modified = 0; 4320 hnum += argcnt; 4321 } 4322 break; 4323 4324 case ORD('k'): 4325 case ORD('-'): 4326 case CTRL_P: 4327 if (grabhist(modified, hnum - argcnt) < 0) 4328 return (-1); 4329 else { 4330 modified = 0; 4331 hnum -= argcnt; 4332 } 4333 break; 4334 4335 case ORD('r'): 4336 if (vs->linelen == 0) 4337 return (-1); 4338 modified = 1; 4339 hnum = hlast; 4340 if (cmd[1] == 0) 4341 vi_error(); 4342 else { 4343 int n; 4344 4345 if (vs->cursor + argcnt > vs->linelen) 4346 return (-1); 4347 for (n = 0; n < argcnt; ++n) 4348 vs->cbuf[vs->cursor + n] = cmd[1]; 4349 vs->cursor += n - 1; 4350 } 4351 break; 4352 4353 case ORD('R'): 4354 modified = 1; 4355 hnum = hlast; 4356 insert = REPLACE; 4357 break; 4358 4359 case ORD('s'): 4360 if (vs->linelen == 0) 4361 return (-1); 4362 modified = 1; 4363 hnum = hlast; 4364 if (vs->cursor + argcnt > vs->linelen) 4365 argcnt = vs->linelen - vs->cursor; 4366 del_range(vs->cursor, vs->cursor + argcnt); 4367 insert = INSERT; 4368 break; 4369 4370 case ORD('v'): 4371 if (!argcnt) { 4372 if (vs->linelen == 0) 4373 return (-1); 4374 if (modified) { 4375 vs->cbuf[vs->linelen] = '\0'; 4376 histsave(&source->line, vs->cbuf, 4377 HIST_STORE, true); 4378 } else 4379 argcnt = source->line + 1 - 4380 (hlast - hnum); 4381 } 4382 if (argcnt) 4383 shf_snprintf(vs->cbuf, vs->cbufsize, Tf_sd, 4384 "fc -e ${VISUAL:-${EDITOR:-vi}} --", 4385 argcnt); 4386 else 4387 strlcpy(vs->cbuf, 4388 "fc -e ${VISUAL:-${EDITOR:-vi}} --", 4389 vs->cbufsize); 4390 vs->linelen = strlen(vs->cbuf); 4391 return (2); 4392 4393 case ORD('x'): 4394 if (vs->linelen == 0) 4395 return (-1); 4396 modified = 1; 4397 hnum = hlast; 4398 if (vs->cursor + argcnt > vs->linelen) 4399 argcnt = vs->linelen - vs->cursor; 4400 yank_range(vs->cursor, vs->cursor + argcnt); 4401 del_range(vs->cursor, vs->cursor + argcnt); 4402 break; 4403 4404 case ORD('X'): 4405 if (vs->cursor > 0) { 4406 modified = 1; 4407 hnum = hlast; 4408 if (vs->cursor < argcnt) 4409 argcnt = vs->cursor; 4410 yank_range(vs->cursor - argcnt, vs->cursor); 4411 del_range(vs->cursor - argcnt, vs->cursor); 4412 vs->cursor -= argcnt; 4413 } else 4414 return (-1); 4415 break; 4416 4417 case ORD('u'): 4418 t = vs; 4419 vs = undo; 4420 undo = t; 4421 break; 4422 4423 case ORD('U'): 4424 if (!modified) 4425 return (-1); 4426 if (grabhist(modified, ohnum) < 0) 4427 return (-1); 4428 modified = 0; 4429 hnum = ohnum; 4430 break; 4431 4432 case ORD('?'): 4433 if (hnum == hlast) 4434 hnum = -1; 4435 /* ahhh */ 4436 4437 /* FALLTHROUGH */ 4438 case ORD('/'): 4439 c3 = 1; 4440 srchlen = 0; 4441 lastsearch = *cmd; 4442 /* FALLTHROUGH */ 4443 case ORD('n'): 4444 case ORD('N'): 4445 if (lastsearch == ' ') 4446 return (-1); 4447 if (lastsearch == '?') 4448 c1 = 1; 4449 else 4450 c1 = 0; 4451 if (*cmd == 'N') 4452 c1 = !c1; 4453 if ((c2 = grabsearch(modified, hnum, 4454 c1, srchpat)) < 0) { 4455 if (c3) { 4456 restore_cbuf(); 4457 refresh(0); 4458 } 4459 return (-1); 4460 } else { 4461 modified = 0; 4462 hnum = c2; 4463 ohnum = hnum; 4464 } 4465 if (argcnt >= 2) { 4466 /* flag from cursor-up command */ 4467 vs->cursor = argcnt - 2; 4468 return (0); 4469 } 4470 break; 4471 case ORD('_'): 4472 { 4473 bool inspace; 4474 char *p, *sp; 4475 4476 if (histnum(-1) < 0) 4477 return (-1); 4478 p = *histpos(); 4479 if (argcnt) { 4480 while (ctype(*p, C_SPACE)) 4481 p++; 4482 while (*p && --argcnt) { 4483 while (*p && !ctype(*p, C_SPACE)) 4484 p++; 4485 while (ctype(*p, C_SPACE)) 4486 p++; 4487 } 4488 if (!*p) 4489 return (-1); 4490 sp = p; 4491 } else { 4492 sp = p; 4493 inspace = false; 4494 while (*p) { 4495 if (ctype(*p, C_SPACE)) 4496 inspace = true; 4497 else if (inspace) { 4498 inspace = false; 4499 sp = p; 4500 } 4501 p++; 4502 } 4503 p = sp; 4504 } 4505 modified = 1; 4506 hnum = hlast; 4507 if (vs->cursor != vs->linelen) 4508 vs->cursor++; 4509 while (*p && !ctype(*p, C_SPACE)) { 4510 argcnt++; 4511 p++; 4512 } 4513 if (putbuf(T1space, 1, false) != 0 || 4514 putbuf(sp, argcnt, false) != 0) { 4515 if (vs->cursor != 0) 4516 vs->cursor--; 4517 return (-1); 4518 } 4519 insert = INSERT; 4520 } 4521 break; 4522 4523 case ORD('~'): 4524 { 4525 char *p; 4526 int i; 4527 4528 if (vs->linelen == 0) 4529 return (-1); 4530 for (i = 0; i < argcnt; i++) { 4531 p = &vs->cbuf[vs->cursor]; 4532 if (ctype(*p, C_LOWER)) { 4533 modified = 1; 4534 hnum = hlast; 4535 *p = ksh_toupper(*p); 4536 } else if (ctype(*p, C_UPPER)) { 4537 modified = 1; 4538 hnum = hlast; 4539 *p = ksh_tolower(*p); 4540 } 4541 if (vs->cursor < vs->linelen - 1) 4542 vs->cursor++; 4543 } 4544 break; 4545 } 4546 4547 case ORD('#'): 4548 { 4549 int ret = x_do_comment(vs->cbuf, vs->cbufsize, 4550 &vs->linelen); 4551 if (ret >= 0) 4552 vs->cursor = 0; 4553 return (ret); 4554 } 4555 4556 /* AT&T ksh */ 4557 case ORD('='): 4558 /* Nonstandard vi/ksh */ 4559 case CTRL_E: 4560 print_expansions(vs, 1); 4561 break; 4562 4563 4564 /* Nonstandard vi/ksh */ 4565 case CTRL_I: 4566 if (!Flag(FVITABCOMPLETE)) 4567 return (-1); 4568 complete_word(1, argcnt); 4569 break; 4570 4571 /* some annoying AT&T kshs */ 4572 case CTRL_BO: 4573 if (!Flag(FVIESCCOMPLETE)) 4574 return (-1); 4575 /* FALLTHROUGH */ 4576 /* AT&T ksh */ 4577 case ORD('\\'): 4578 /* Nonstandard vi/ksh */ 4579 case CTRL_F: 4580 complete_word(1, argcnt); 4581 break; 4582 4583 4584 /* AT&T ksh */ 4585 case ORD('*'): 4586 /* Nonstandard vi/ksh */ 4587 case CTRL_X: 4588 expand_word(1); 4589 break; 4590 4591 4592 /* mksh: cursor movement */ 4593 case ORD('['): 4594 case ORD('O'): 4595 state = VPREFIX2; 4596 if (vs->linelen != 0) 4597 vs->cursor++; 4598 insert = INSERT; 4599 return (0); 4600 } 4601 if (insert == 0 && vs->cursor != 0 && vs->cursor >= vs->linelen) 4602 vs->cursor--; 4603 } 4604 return (0); 4605 } 4606 4607 static int 4608 domove(int argcnt, const char *cmd, int sub) 4609 { 4610 int ncursor = 0, i = 0, t; 4611 unsigned int bcount; 4612 4613 switch (ord(*cmd)) { 4614 case ORD('b'): 4615 if (!sub && vs->cursor == 0) 4616 return (-1); 4617 ncursor = backword(argcnt); 4618 break; 4619 4620 case ORD('B'): 4621 if (!sub && vs->cursor == 0) 4622 return (-1); 4623 ncursor = Backword(argcnt); 4624 break; 4625 4626 case ORD('e'): 4627 if (!sub && vs->cursor + 1 >= vs->linelen) 4628 return (-1); 4629 ncursor = endword(argcnt); 4630 if (sub && ncursor < vs->linelen) 4631 ncursor++; 4632 break; 4633 4634 case ORD('E'): 4635 if (!sub && vs->cursor + 1 >= vs->linelen) 4636 return (-1); 4637 ncursor = Endword(argcnt); 4638 if (sub && ncursor < vs->linelen) 4639 ncursor++; 4640 break; 4641 4642 case ORD('f'): 4643 case ORD('F'): 4644 case ORD('t'): 4645 case ORD('T'): 4646 fsavecmd = *cmd; 4647 fsavech = cmd[1]; 4648 /* FALLTHROUGH */ 4649 case ORD(','): 4650 case ORD(';'): 4651 if (fsavecmd == ' ') 4652 return (-1); 4653 i = ksh_eq(fsavecmd, 'F', 'f'); 4654 t = fsavecmd > 'a'; 4655 if (*cmd == ',') 4656 t = !t; 4657 if ((ncursor = findch(fsavech, argcnt, tobool(t), 4658 tobool(i))) < 0) 4659 return (-1); 4660 if (sub && t) 4661 ncursor++; 4662 break; 4663 4664 case ORD('h'): 4665 case CTRL_H: 4666 if (!sub && vs->cursor == 0) 4667 return (-1); 4668 ncursor = vs->cursor - argcnt; 4669 if (ncursor < 0) 4670 ncursor = 0; 4671 break; 4672 4673 case ORD(' '): 4674 case ORD('l'): 4675 if (!sub && vs->cursor + 1 >= vs->linelen) 4676 return (-1); 4677 if (vs->linelen != 0) { 4678 ncursor = vs->cursor + argcnt; 4679 if (ncursor > vs->linelen) 4680 ncursor = vs->linelen; 4681 } 4682 break; 4683 4684 case ORD('w'): 4685 if (!sub && vs->cursor + 1 >= vs->linelen) 4686 return (-1); 4687 ncursor = forwword(argcnt); 4688 break; 4689 4690 case ORD('W'): 4691 if (!sub && vs->cursor + 1 >= vs->linelen) 4692 return (-1); 4693 ncursor = Forwword(argcnt); 4694 break; 4695 4696 case ORD('0'): 4697 ncursor = 0; 4698 break; 4699 4700 case ORD('^'): 4701 ncursor = domovebeg(); 4702 break; 4703 4704 case ORD('|'): 4705 ncursor = argcnt; 4706 if (ncursor > vs->linelen) 4707 ncursor = vs->linelen; 4708 if (ncursor) 4709 ncursor--; 4710 break; 4711 4712 case ORD('$'): 4713 if (vs->linelen != 0) 4714 ncursor = vs->linelen; 4715 else 4716 ncursor = 0; 4717 break; 4718 4719 case ORD('%'): 4720 ncursor = vs->cursor; 4721 while (ncursor < vs->linelen && 4722 (i = bracktype(vs->cbuf[ncursor])) == 0) 4723 ncursor++; 4724 if (ncursor == vs->linelen) 4725 return (-1); 4726 bcount = 1; 4727 do { 4728 if (i > 0) { 4729 if (++ncursor >= vs->linelen) 4730 return (-1); 4731 } else { 4732 if (--ncursor < 0) 4733 return (-1); 4734 } 4735 t = bracktype(vs->cbuf[ncursor]); 4736 if (t == i) 4737 bcount++; 4738 else if (t == -i) 4739 bcount--; 4740 } while (bcount != 0); 4741 if (sub && i > 0) 4742 ncursor++; 4743 break; 4744 4745 default: 4746 return (-1); 4747 } 4748 return (ncursor); 4749 } 4750 4751 static int 4752 domovebeg(void) 4753 { 4754 int ncursor = 0; 4755 4756 while (ncursor < vs->linelen - 1 && 4757 ctype(vs->cbuf[ncursor], C_SPACE)) 4758 ncursor++; 4759 return (ncursor); 4760 } 4761 4762 static int 4763 redo_insert(int count) 4764 { 4765 while (count-- > 0) 4766 if (putbuf(ibuf, inslen, tobool(insert == REPLACE)) != 0) 4767 return (-1); 4768 if (vs->cursor > 0) 4769 vs->cursor--; 4770 insert = 0; 4771 return (0); 4772 } 4773 4774 static void 4775 yank_range(int a, int b) 4776 { 4777 yanklen = b - a; 4778 if (yanklen != 0) 4779 memmove(ybuf, &vs->cbuf[a], yanklen); 4780 } 4781 4782 static int 4783 bracktype(int ch) 4784 { 4785 switch (ord(ch)) { 4786 4787 case ORD('('): 4788 return (1); 4789 4790 case ORD('['): 4791 return (2); 4792 4793 case ORD('{'): 4794 return (3); 4795 4796 case ORD(')'): 4797 return (-1); 4798 4799 case ORD(']'): 4800 return (-2); 4801 4802 case ORD('}'): 4803 return (-3); 4804 4805 default: 4806 return (0); 4807 } 4808 } 4809 4810 /* 4811 * Non user interface editor routines below here 4812 */ 4813 4814 static void 4815 save_cbuf(void) 4816 { 4817 memmove(holdbufp, vs->cbuf, vs->linelen); 4818 holdlen = vs->linelen; 4819 holdbufp[holdlen] = '\0'; 4820 } 4821 4822 static void 4823 restore_cbuf(void) 4824 { 4825 vs->cursor = 0; 4826 vs->linelen = holdlen; 4827 memmove(vs->cbuf, holdbufp, holdlen); 4828 } 4829 4830 /* return a new edstate */ 4831 static struct edstate * 4832 save_edstate(struct edstate *old) 4833 { 4834 struct edstate *news; 4835 4836 news = alloc(sizeof(struct edstate), AEDIT); 4837 news->cbuf = alloc(old->cbufsize, AEDIT); 4838 memcpy(news->cbuf, old->cbuf, old->linelen); 4839 news->cbufsize = old->cbufsize; 4840 news->linelen = old->linelen; 4841 news->cursor = old->cursor; 4842 news->winleft = old->winleft; 4843 return (news); 4844 } 4845 4846 static void 4847 restore_edstate(struct edstate *news, struct edstate *old) 4848 { 4849 memcpy(news->cbuf, old->cbuf, old->linelen); 4850 news->linelen = old->linelen; 4851 news->cursor = old->cursor; 4852 news->winleft = old->winleft; 4853 free_edstate(old); 4854 } 4855 4856 static void 4857 free_edstate(struct edstate *old) 4858 { 4859 afree(old->cbuf, AEDIT); 4860 afree(old, AEDIT); 4861 } 4862 4863 /* 4864 * this is used for calling x_escape() in complete_word() 4865 */ 4866 static int 4867 x_vi_putbuf(const char *s, size_t len) 4868 { 4869 return (putbuf(s, len, false)); 4870 } 4871 4872 static int 4873 putbuf(const char *buf, ssize_t len, bool repl) 4874 { 4875 if (len == 0) 4876 return (0); 4877 if (repl) { 4878 if (vs->cursor + len >= vs->cbufsize) 4879 return (-1); 4880 if (vs->cursor + len > vs->linelen) 4881 vs->linelen = vs->cursor + len; 4882 } else { 4883 if (vs->linelen + len >= vs->cbufsize) 4884 return (-1); 4885 memmove(&vs->cbuf[vs->cursor + len], &vs->cbuf[vs->cursor], 4886 vs->linelen - vs->cursor); 4887 vs->linelen += len; 4888 } 4889 memmove(&vs->cbuf[vs->cursor], buf, len); 4890 vs->cursor += len; 4891 return (0); 4892 } 4893 4894 static void 4895 del_range(int a, int b) 4896 { 4897 if (vs->linelen != b) 4898 memmove(&vs->cbuf[a], &vs->cbuf[b], vs->linelen - b); 4899 vs->linelen -= b - a; 4900 } 4901 4902 static int 4903 findch(int ch, int cnt, bool forw, bool incl) 4904 { 4905 int ncursor; 4906 4907 if (vs->linelen == 0) 4908 return (-1); 4909 ncursor = vs->cursor; 4910 while (cnt--) { 4911 do { 4912 if (forw) { 4913 if (++ncursor == vs->linelen) 4914 return (-1); 4915 } else { 4916 if (--ncursor < 0) 4917 return (-1); 4918 } 4919 } while (vs->cbuf[ncursor] != ch); 4920 } 4921 if (!incl) { 4922 if (forw) 4923 ncursor--; 4924 else 4925 ncursor++; 4926 } 4927 return (ncursor); 4928 } 4929 4930 static int 4931 forwword(int argcnt) 4932 { 4933 int ncursor; 4934 4935 ncursor = vs->cursor; 4936 while (ncursor < vs->linelen && argcnt--) { 4937 if (ctype(vs->cbuf[ncursor], C_ALNUX)) 4938 while (ncursor < vs->linelen && 4939 ctype(vs->cbuf[ncursor], C_ALNUX)) 4940 ncursor++; 4941 else if (!ctype(vs->cbuf[ncursor], C_SPACE)) 4942 while (ncursor < vs->linelen && 4943 !ctype(vs->cbuf[ncursor], C_ALNUX | C_SPACE)) 4944 ncursor++; 4945 while (ncursor < vs->linelen && 4946 ctype(vs->cbuf[ncursor], C_SPACE)) 4947 ncursor++; 4948 } 4949 return (ncursor); 4950 } 4951 4952 static int 4953 backword(int argcnt) 4954 { 4955 int ncursor; 4956 4957 ncursor = vs->cursor; 4958 while (ncursor > 0 && argcnt--) { 4959 while (--ncursor > 0 && ctype(vs->cbuf[ncursor], C_SPACE)) 4960 ; 4961 if (ncursor > 0) { 4962 if (ctype(vs->cbuf[ncursor], C_ALNUX)) 4963 while (--ncursor >= 0 && 4964 ctype(vs->cbuf[ncursor], C_ALNUX)) 4965 ; 4966 else 4967 while (--ncursor >= 0 && 4968 !ctype(vs->cbuf[ncursor], C_ALNUX | C_SPACE)) 4969 ; 4970 ncursor++; 4971 } 4972 } 4973 return (ncursor); 4974 } 4975 4976 static int 4977 endword(int argcnt) 4978 { 4979 int ncursor; 4980 4981 ncursor = vs->cursor; 4982 while (ncursor < vs->linelen && argcnt--) { 4983 while (++ncursor < vs->linelen - 1 && 4984 ctype(vs->cbuf[ncursor], C_SPACE)) 4985 ; 4986 if (ncursor < vs->linelen - 1) { 4987 if (ctype(vs->cbuf[ncursor], C_ALNUX)) 4988 while (++ncursor < vs->linelen && 4989 ctype(vs->cbuf[ncursor], C_ALNUX)) 4990 ; 4991 else 4992 while (++ncursor < vs->linelen && 4993 !ctype(vs->cbuf[ncursor], C_ALNUX | C_SPACE)) 4994 ; 4995 ncursor--; 4996 } 4997 } 4998 return (ncursor); 4999 } 5000 5001 static int 5002 Forwword(int argcnt) 5003 { 5004 int ncursor; 5005 5006 ncursor = vs->cursor; 5007 while (ncursor < vs->linelen && argcnt--) { 5008 while (ncursor < vs->linelen && 5009 !ctype(vs->cbuf[ncursor], C_SPACE)) 5010 ncursor++; 5011 while (ncursor < vs->linelen && 5012 ctype(vs->cbuf[ncursor], C_SPACE)) 5013 ncursor++; 5014 } 5015 return (ncursor); 5016 } 5017 5018 static int 5019 Backword(int argcnt) 5020 { 5021 int ncursor; 5022 5023 ncursor = vs->cursor; 5024 while (ncursor > 0 && argcnt--) { 5025 while (--ncursor >= 0 && ctype(vs->cbuf[ncursor], C_SPACE)) 5026 ; 5027 while (ncursor >= 0 && !ctype(vs->cbuf[ncursor], C_SPACE)) 5028 ncursor--; 5029 ncursor++; 5030 } 5031 return (ncursor); 5032 } 5033 5034 static int 5035 Endword(int argcnt) 5036 { 5037 int ncursor; 5038 5039 ncursor = vs->cursor; 5040 while (ncursor < vs->linelen - 1 && argcnt--) { 5041 while (++ncursor < vs->linelen - 1 && 5042 ctype(vs->cbuf[ncursor], C_SPACE)) 5043 ; 5044 if (ncursor < vs->linelen - 1) { 5045 while (++ncursor < vs->linelen && 5046 !ctype(vs->cbuf[ncursor], C_SPACE)) 5047 ; 5048 ncursor--; 5049 } 5050 } 5051 return (ncursor); 5052 } 5053 5054 static int 5055 grabhist(int save, int n) 5056 { 5057 char *hptr; 5058 5059 if (n < 0 || n > hlast) 5060 return (-1); 5061 if (n == hlast) { 5062 restore_cbuf(); 5063 ohnum = n; 5064 return (0); 5065 } 5066 (void)histnum(n); 5067 if ((hptr = *histpos()) == NULL) { 5068 internal_warningf("grabhist: bad history array"); 5069 return (-1); 5070 } 5071 if (save) 5072 save_cbuf(); 5073 if ((vs->linelen = strlen(hptr)) >= vs->cbufsize) 5074 vs->linelen = vs->cbufsize - 1; 5075 memmove(vs->cbuf, hptr, vs->linelen); 5076 vs->cursor = 0; 5077 ohnum = n; 5078 return (0); 5079 } 5080 5081 static int 5082 grabsearch(int save, int start, int fwd, const char *pat) 5083 { 5084 char *hptr; 5085 int hist; 5086 bool anchored; 5087 5088 if ((start == 0 && fwd == 0) || (start >= hlast - 1 && fwd == 1)) 5089 return (-1); 5090 if (fwd) 5091 start++; 5092 else 5093 start--; 5094 anchored = *pat == '^' ? (++pat, true) : false; 5095 if ((hist = findhist(start, fwd, pat, anchored)) < 0) { 5096 /* (start != 0 && fwd && match(holdbufp, pat) >= 0) */ 5097 if (start != 0 && fwd && strcmp(holdbufp, pat) >= 0) { 5098 restore_cbuf(); 5099 return (0); 5100 } else 5101 return (-1); 5102 } 5103 if (save) 5104 save_cbuf(); 5105 histnum(hist); 5106 hptr = *histpos(); 5107 if ((vs->linelen = strlen(hptr)) >= vs->cbufsize) 5108 vs->linelen = vs->cbufsize - 1; 5109 memmove(vs->cbuf, hptr, vs->linelen); 5110 vs->cursor = 0; 5111 return (hist); 5112 } 5113 5114 static void 5115 redraw_line(bool newl) 5116 { 5117 if (wbuf_len) 5118 memset(wbuf[win], ' ', wbuf_len); 5119 if (newl) { 5120 x_putc('\r'); 5121 x_putc('\n'); 5122 } 5123 x_pprompt(); 5124 morec = ' '; 5125 } 5126 5127 static void 5128 refresh(int leftside) 5129 { 5130 if (leftside < 0) 5131 leftside = lastref; 5132 else 5133 lastref = leftside; 5134 if (outofwin()) 5135 rewindow(); 5136 display(wbuf[1 - win], wbuf[win], leftside); 5137 win = 1 - win; 5138 } 5139 5140 static int 5141 outofwin(void) 5142 { 5143 int cur, col; 5144 5145 if (vs->cursor < vs->winleft) 5146 return (1); 5147 col = 0; 5148 cur = vs->winleft; 5149 while (cur < vs->cursor) 5150 col = newcol((unsigned char)vs->cbuf[cur++], col); 5151 if (col >= winwidth) 5152 return (1); 5153 return (0); 5154 } 5155 5156 static void 5157 rewindow(void) 5158 { 5159 int tcur, tcol; 5160 int holdcur1, holdcol1; 5161 int holdcur2, holdcol2; 5162 5163 holdcur1 = holdcur2 = tcur = 0; 5164 holdcol1 = holdcol2 = tcol = 0; 5165 while (tcur < vs->cursor) { 5166 if (tcol - holdcol2 > winwidth / 2) { 5167 holdcur1 = holdcur2; 5168 holdcol1 = holdcol2; 5169 holdcur2 = tcur; 5170 holdcol2 = tcol; 5171 } 5172 tcol = newcol((unsigned char)vs->cbuf[tcur++], tcol); 5173 } 5174 while (tcol - holdcol1 > winwidth / 2) 5175 holdcol1 = newcol((unsigned char)vs->cbuf[holdcur1++], 5176 holdcol1); 5177 vs->winleft = holdcur1; 5178 } 5179 5180 static int 5181 newcol(unsigned char ch, int col) 5182 { 5183 if (ch == '\t') 5184 return ((col | 7) + 1); 5185 return (col + char_len(ch)); 5186 } 5187 5188 static void 5189 display(char *wb1, char *wb2, int leftside) 5190 { 5191 unsigned char ch; 5192 char *twb1, *twb2, mc; 5193 int cur, col, cnt; 5194 int ncol = 0; 5195 int moreright; 5196 5197 col = 0; 5198 cur = vs->winleft; 5199 moreright = 0; 5200 twb1 = wb1; 5201 while (col < winwidth && cur < vs->linelen) { 5202 if (cur == vs->cursor && leftside) 5203 ncol = col + pwidth; 5204 if ((ch = vs->cbuf[cur]) == '\t') 5205 do { 5206 *twb1++ = ' '; 5207 } while (++col < winwidth && (col & 7) != 0); 5208 else if (col < winwidth) { 5209 if (ksh_isctrl(ch)) { 5210 *twb1++ = '^'; 5211 if (++col < winwidth) { 5212 *twb1++ = ksh_unctrl(ch); 5213 col++; 5214 } 5215 } else { 5216 *twb1++ = ch; 5217 col++; 5218 } 5219 } 5220 if (cur == vs->cursor && !leftside) 5221 ncol = col + pwidth - 1; 5222 cur++; 5223 } 5224 if (cur == vs->cursor) 5225 ncol = col + pwidth; 5226 if (col < winwidth) { 5227 while (col < winwidth) { 5228 *twb1++ = ' '; 5229 col++; 5230 } 5231 } else 5232 moreright++; 5233 *twb1 = ' '; 5234 5235 col = pwidth; 5236 cnt = winwidth; 5237 twb1 = wb1; 5238 twb2 = wb2; 5239 while (cnt--) { 5240 if (*twb1 != *twb2) { 5241 if (x_col != col) 5242 ed_mov_opt(col, wb1); 5243 x_putc(*twb1); 5244 x_col++; 5245 } 5246 twb1++; 5247 twb2++; 5248 col++; 5249 } 5250 if (vs->winleft > 0 && moreright) 5251 /* 5252 * POSIX says to use * for this but that is a globbing 5253 * character and may confuse people; + is more innocuous 5254 */ 5255 mc = '+'; 5256 else if (vs->winleft > 0) 5257 mc = '<'; 5258 else if (moreright) 5259 mc = '>'; 5260 else 5261 mc = ' '; 5262 if (mc != morec) { 5263 ed_mov_opt(pwidth + winwidth + 1, wb1); 5264 x_putc(mc); 5265 x_col++; 5266 morec = mc; 5267 } 5268 if (x_col != ncol) 5269 ed_mov_opt(ncol, wb1); 5270 } 5271 5272 static void 5273 ed_mov_opt(int col, char *wb) 5274 { 5275 if (col < x_col) { 5276 if (col + 1 < x_col - col) { 5277 x_putc('\r'); 5278 x_pprompt(); 5279 while (x_col++ < col) 5280 x_putcf(*wb++); 5281 } else { 5282 while (x_col-- > col) 5283 x_putc('\b'); 5284 } 5285 } else { 5286 wb = &wb[x_col - pwidth]; 5287 while (x_col++ < col) 5288 x_putcf(*wb++); 5289 } 5290 x_col = col; 5291 } 5292 5293 5294 /* replace word with all expansions (ie, expand word*) */ 5295 static int 5296 expand_word(int cmd) 5297 { 5298 static struct edstate *buf; 5299 int rval = 0, nwords, start, end, i; 5300 char **words; 5301 5302 /* Undo previous expansion */ 5303 if (cmd == 0 && expanded == EXPAND && buf) { 5304 restore_edstate(vs, buf); 5305 buf = 0; 5306 expanded = NONE; 5307 return (0); 5308 } 5309 if (buf) { 5310 free_edstate(buf); 5311 buf = 0; 5312 } 5313 5314 i = XCF_COMMAND_FILE | XCF_FULLPATH; 5315 nwords = x_cf_glob(&i, vs->cbuf, vs->linelen, vs->cursor, 5316 &start, &end, &words); 5317 if (nwords == 0) { 5318 vi_error(); 5319 return (-1); 5320 } 5321 5322 buf = save_edstate(vs); 5323 expanded = EXPAND; 5324 del_range(start, end); 5325 vs->cursor = start; 5326 i = 0; 5327 while (i < nwords) { 5328 if (x_escape(words[i], strlen(words[i]), x_vi_putbuf) != 0) { 5329 rval = -1; 5330 break; 5331 } 5332 if (++i < nwords && putbuf(T1space, 1, false) != 0) { 5333 rval = -1; 5334 break; 5335 } 5336 } 5337 i = buf->cursor - end; 5338 if (rval == 0 && i > 0) 5339 vs->cursor += i; 5340 modified = 1; 5341 hnum = hlast; 5342 insert = INSERT; 5343 lastac = 0; 5344 refresh(0); 5345 return (rval); 5346 } 5347 5348 static int 5349 complete_word(int cmd, int count) 5350 { 5351 static struct edstate *buf; 5352 int rval, nwords, start, end, flags; 5353 size_t match_len; 5354 char **words; 5355 char *match; 5356 bool is_unique; 5357 5358 /* Undo previous completion */ 5359 if (cmd == 0 && expanded == COMPLETE && buf) { 5360 print_expansions(buf, 0); 5361 expanded = PRINT; 5362 return (0); 5363 } 5364 if (cmd == 0 && expanded == PRINT && buf) { 5365 restore_edstate(vs, buf); 5366 buf = 0; 5367 expanded = NONE; 5368 return (0); 5369 } 5370 if (buf) { 5371 free_edstate(buf); 5372 buf = 0; 5373 } 5374 5375 /* 5376 * XCF_FULLPATH for count 'cause the menu printed by 5377 * print_expansions() was done this way. 5378 */ 5379 flags = XCF_COMMAND_FILE; 5380 if (count) 5381 flags |= XCF_FULLPATH; 5382 nwords = x_cf_glob(&flags, vs->cbuf, vs->linelen, vs->cursor, 5383 &start, &end, &words); 5384 if (nwords == 0) { 5385 vi_error(); 5386 return (-1); 5387 } 5388 if (count) { 5389 int i; 5390 5391 count--; 5392 if (count >= nwords) { 5393 vi_error(); 5394 x_print_expansions(nwords, words, 5395 tobool(flags & XCF_IS_COMMAND)); 5396 x_free_words(nwords, words); 5397 redraw_line(false); 5398 return (-1); 5399 } 5400 /* 5401 * Expand the count'th word to its basename 5402 */ 5403 if (flags & XCF_IS_COMMAND) { 5404 match = words[count] + 5405 x_basename(words[count], NULL); 5406 /* If more than one possible match, use full path */ 5407 for (i = 0; i < nwords; i++) 5408 if (i != count && 5409 strcmp(words[i] + x_basename(words[i], 5410 NULL), match) == 0) { 5411 match = words[count]; 5412 break; 5413 } 5414 } else 5415 match = words[count]; 5416 match_len = strlen(match); 5417 is_unique = true; 5418 /* expanded = PRINT; next call undo */ 5419 } else { 5420 match = words[0]; 5421 match_len = x_longest_prefix(nwords, words); 5422 /* next call will list completions */ 5423 expanded = COMPLETE; 5424 is_unique = nwords == 1; 5425 } 5426 5427 buf = save_edstate(vs); 5428 del_range(start, end); 5429 vs->cursor = start; 5430 5431 /* 5432 * escape all shell-sensitive characters and put the result into 5433 * command buffer 5434 */ 5435 rval = x_escape(match, match_len, x_vi_putbuf); 5436 5437 if (rval == 0 && is_unique) { 5438 /* 5439 * If exact match, don't undo. Allows directory completions 5440 * to be used (ie, complete the next portion of the path). 5441 */ 5442 expanded = NONE; 5443 5444 /* 5445 * append a space if this is a non-directory match 5446 * and not a parameter or homedir substitution 5447 */ 5448 if (match_len > 0 && !mksh_cdirsep(match[match_len - 1]) && 5449 !(flags & XCF_IS_NOSPACE)) 5450 rval = putbuf(T1space, 1, false); 5451 } 5452 x_free_words(nwords, words); 5453 5454 modified = 1; 5455 hnum = hlast; 5456 insert = INSERT; 5457 /* prevent this from being redone... */ 5458 lastac = 0; 5459 refresh(0); 5460 5461 return (rval); 5462 } 5463 5464 static int 5465 print_expansions(struct edstate *est, int cmd MKSH_A_UNUSED) 5466 { 5467 int start, end, nwords, i; 5468 char **words; 5469 5470 i = XCF_COMMAND_FILE | XCF_FULLPATH; 5471 nwords = x_cf_glob(&i, est->cbuf, est->linelen, est->cursor, 5472 &start, &end, &words); 5473 if (nwords == 0) { 5474 vi_error(); 5475 return (-1); 5476 } 5477 x_print_expansions(nwords, words, tobool(i & XCF_IS_COMMAND)); 5478 x_free_words(nwords, words); 5479 redraw_line(false); 5480 return (0); 5481 } 5482 #endif /* !MKSH_S_NOVI */ 5483 5484 /* Similar to x_zotc(emacs.c), but no tab weirdness */ 5485 static void 5486 x_vi_zotc(int c) 5487 { 5488 if (ksh_isctrl(c)) { 5489 x_putc('^'); 5490 c = ksh_unctrl(c); 5491 } 5492 x_putc(c); 5493 } 5494 5495 #if !MKSH_S_NOVI 5496 static void 5497 vi_error(void) 5498 { 5499 /* Beem out of any macros as soon as an error occurs */ 5500 vi_macro_reset(); 5501 x_putc(KSH_BEL); 5502 x_flush(); 5503 } 5504 5505 static void 5506 vi_macro_reset(void) 5507 { 5508 if (macro.p) { 5509 afree(macro.buf, AEDIT); 5510 memset((char *)¯o, 0, sizeof(macro)); 5511 } 5512 } 5513 #endif /* !MKSH_S_NOVI */ 5514 5515 /* called from main.c */ 5516 void 5517 x_init(void) 5518 { 5519 int i, j; 5520 5521 /* 5522 * set edchars to force initial binding, except we need 5523 * default values for ^W for some deficient systems 5524 */ 5525 edchars.erase = edchars.kill = edchars.intr = edchars.quit = 5526 edchars.eof = EDCHAR_INITIAL; 5527 edchars.werase = 027; 5528 5529 /* command line editing specific memory allocation */ 5530 ainit(AEDIT); 5531 holdbufp = alloc(LINE, AEDIT); 5532 5533 /* initialise Emacs command line editing mode */ 5534 x_nextcmd = -1; 5535 5536 x_tab = alloc2(X_NTABS, sizeof(*x_tab), AEDIT); 5537 for (j = 0; j < X_TABSZ; j++) 5538 x_tab[0][j] = XFUNC_insert; 5539 for (i = 1; i < X_NTABS; i++) 5540 for (j = 0; j < X_TABSZ; j++) 5541 x_tab[i][j] = XFUNC_error; 5542 for (i = 0; i < (int)NELEM(x_defbindings); i++) 5543 x_tab[x_defbindings[i].xdb_tab][x_defbindings[i].xdb_char] 5544 = x_defbindings[i].xdb_func; 5545 5546 #ifndef MKSH_SMALL 5547 x_atab = alloc2(X_NTABS, sizeof(*x_atab), AEDIT); 5548 for (i = 1; i < X_NTABS; i++) 5549 for (j = 0; j < X_TABSZ; j++) 5550 x_atab[i][j] = NULL; 5551 #endif 5552 } 5553 5554 #ifdef DEBUG_LEAKS 5555 void 5556 x_done(void) 5557 { 5558 if (x_tab != NULL) 5559 afreeall(AEDIT); 5560 } 5561 #endif 5562 5563 void 5564 x_initterm(const char *termtype) 5565 { 5566 /* default must be 0 (bss) */ 5567 x_term_mode = 0; 5568 /* this is what tmux uses, don't ask me about it */ 5569 if (!strcmp(termtype, "screen") || !strncmp(termtype, "screen-", 7)) 5570 x_term_mode = 1; 5571 } 5572 5573 #ifndef MKSH_SMALL 5574 static char * 5575 x_eval_region_helper(const char *cmd, size_t len) 5576 { 5577 char * volatile cp; 5578 newenv(E_ERRH); 5579 5580 if (!kshsetjmp(e->jbuf)) { 5581 char *wds = alloc(len + 3, ATEMP); 5582 5583 wds[0] = FUNASUB; 5584 memcpy(wds + 1, cmd, len); 5585 wds[len + 1] = '\0'; 5586 wds[len + 2] = EOS; 5587 5588 cp = evalstr(wds, DOSCALAR); 5589 afree(wds, ATEMP); 5590 strdupx(cp, cp, AEDIT); 5591 } else 5592 cp = NULL; 5593 quitenv(NULL); 5594 return (cp); 5595 } 5596 5597 static int 5598 x_eval_region(int c MKSH_A_UNUSED) 5599 { 5600 char *evbeg, *evend, *cp; 5601 size_t newlen; 5602 /* only for LINE overflow checking */ 5603 size_t restlen; 5604 5605 if (xmp == NULL) { 5606 evbeg = xbuf; 5607 evend = xep; 5608 } else if (xmp < xcp) { 5609 evbeg = xmp; 5610 evend = xcp; 5611 } else { 5612 evbeg = xcp; 5613 evend = xmp; 5614 } 5615 5616 x_e_putc2('\r'); 5617 x_clrtoeol(' ', false); 5618 x_flush(); 5619 x_mode(false); 5620 cp = x_eval_region_helper(evbeg, evend - evbeg); 5621 x_mode(true); 5622 5623 if (cp == NULL) { 5624 /* command cannot be parsed */ 5625 x_eval_region_err: 5626 x_e_putc2(KSH_BEL); 5627 x_redraw('\r'); 5628 return (KSTD); 5629 } 5630 5631 newlen = strlen(cp); 5632 restlen = xep - evend; 5633 /* check for LINE overflow, until this is dynamically allocated */ 5634 if (evbeg + newlen + restlen >= xend) 5635 goto x_eval_region_err; 5636 5637 xmp = evbeg; 5638 xcp = evbeg + newlen; 5639 xep = xcp + restlen; 5640 memmove(xcp, evend, restlen + /* NUL */ 1); 5641 memcpy(xmp, cp, newlen); 5642 afree(cp, AEDIT); 5643 x_adjust(); 5644 x_modified(); 5645 return (KSTD); 5646 } 5647 #endif /* !MKSH_SMALL */ 5648 #endif /* !MKSH_NO_CMDLINE_EDITING */ 5649