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