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