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