1 /* $OpenBSD: emacs.c,v 1.90 2023/06/21 22:22:08 millert Exp $ */ 2 3 /* 4 * Emacs-like command line editing and history 5 * 6 * created by Ron Natalie at BRL 7 * modified by Doug Kingston, Doug Gwyn, and Lou Salkind 8 * adapted to PD ksh by Eric Gisin 9 * 10 * partial rewrite by Marco Peereboom <marco@openbsd.org> 11 * under the same license 12 */ 13 14 #include "config.h" 15 #ifdef EMACS 16 17 #include <sys/queue.h> 18 #include <sys/stat.h> 19 20 #include <ctype.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #ifndef SMALL 25 # include <term.h> 26 # include <curses.h> 27 #endif 28 29 #include "sh.h" 30 #include "edit.h" 31 32 static Area aedit; 33 #define AEDIT &aedit /* area for kill ring and macro defns */ 34 35 /* values returned by keyboard functions */ 36 #define KSTD 0 37 #define KEOL 1 /* ^M, ^J */ 38 #define KINTR 2 /* ^G, ^C */ 39 40 typedef int (*kb_func)(int); 41 42 struct x_ftab { 43 kb_func xf_func; 44 const char *xf_name; 45 short xf_flags; 46 }; 47 48 #define XF_ARG 1 /* command takes number prefix */ 49 #define XF_NOBIND 2 /* not allowed to bind to function */ 50 #define XF_PREFIX 4 /* function sets prefix */ 51 52 /* Separator for completion */ 53 #define is_cfs(c) (c == ' ' || c == '\t' || c == '"' || c == '\'') 54 55 /* Separator for motion */ 56 #define is_mfs(c) (!(isalnum((unsigned char)c) || \ 57 c == '_' || c == '$' || c & 0x80)) 58 59 /* Arguments for do_complete() 60 * 0 = enumerate M-= complete as much as possible and then list 61 * 1 = complete M-Esc 62 * 2 = list M-? 63 */ 64 typedef enum { 65 CT_LIST, /* list the possible completions */ 66 CT_COMPLETE, /* complete to longest prefix */ 67 CT_COMPLIST /* complete and then list (if non-exact) */ 68 } Comp_type; 69 70 /* keybindings */ 71 struct kb_entry { 72 TAILQ_ENTRY(kb_entry) entry; 73 unsigned char *seq; 74 int len; 75 struct x_ftab *ftab; 76 void *args; 77 }; 78 TAILQ_HEAD(kb_list, kb_entry); 79 struct kb_list kblist = TAILQ_HEAD_INITIALIZER(kblist); 80 81 /* { from 4.9 edit.h */ 82 /* 83 * The following are used for my horizontal scrolling stuff 84 */ 85 static char *xbuf; /* beg input buffer */ 86 static char *xend; /* end input buffer */ 87 static char *xcp; /* current position */ 88 static char *xep; /* current end */ 89 static char *xbp; /* start of visible portion of input buffer */ 90 static char *xlp; /* last byte visible on screen */ 91 static int x_adj_ok; 92 /* 93 * we use x_adj_done so that functions can tell 94 * whether x_adjust() has been called while they are active. 95 */ 96 static int x_adj_done; 97 98 static int xx_cols; 99 static int x_col; 100 static int x_displen; 101 static int x_arg; /* general purpose arg */ 102 static int x_arg_defaulted;/* x_arg not explicitly set; defaulted to 1 */ 103 104 static int xlp_valid; 105 /* end from 4.9 edit.h } */ 106 static int x_tty; /* are we on a tty? */ 107 static int x_bind_quiet; /* be quiet when binding keys */ 108 static int (*x_last_command)(int); 109 110 static char **x_histp; /* history position */ 111 static int x_nextcmd; /* for newline-and-next */ 112 static char *xmp; /* mark pointer */ 113 #define KILLSIZE 20 114 static char *killstack[KILLSIZE]; 115 static int killsp, killtp; 116 static int x_literal_set; 117 static int x_arg_set; 118 static char *macro_args; 119 static int prompt_skip; 120 static int prompt_redraw; 121 122 static int x_ins(char *); 123 static void x_delete(int, int); 124 static int x_bword(void); 125 static int x_fword(void); 126 static void x_goto(char *); 127 static void x_bs(int); 128 static int x_size_str(char *); 129 static int x_size(int); 130 static void x_zots(char *); 131 static void x_zotc(int); 132 static void x_load_hist(char **); 133 static int x_search(char *, int, int); 134 static int x_match(char *, char *); 135 static void x_redraw(int); 136 static void x_push(int); 137 static void x_adjust(void); 138 static void x_e_ungetc(int); 139 static int x_e_getc(void); 140 static int x_e_getu8(char *, int); 141 static void x_e_putc(int); 142 static void x_e_puts(const char *); 143 static int x_comment(int); 144 static int x_fold_case(int); 145 static char *x_lastcp(void); 146 static void do_complete(int, Comp_type); 147 static int isu8cont(unsigned char); 148 149 /* proto's for keybindings */ 150 static int x_abort(int); 151 static int x_beg_hist(int); 152 static int x_clear_screen(int); 153 static int x_comp_comm(int); 154 static int x_comp_file(int); 155 static int x_complete(int); 156 static int x_del_back(int); 157 static int x_del_bword(int); 158 static int x_del_char(int); 159 static int x_del_fword(int); 160 static int x_del_line(int); 161 static int x_draw_line(int); 162 static int x_end_hist(int); 163 static int x_end_of_text(int); 164 static int x_enumerate(int); 165 static int x_eot_del(int); 166 static int x_error(int); 167 static int x_goto_hist(int); 168 static int x_ins_string(int); 169 static int x_insert(int); 170 static int x_kill(int); 171 static int x_kill_region(int); 172 static int x_list_comm(int); 173 static int x_list_file(int); 174 static int x_literal(int); 175 static int x_meta_yank(int); 176 static int x_mv_back(int); 177 static int x_mv_begin(int); 178 static int x_mv_bword(int); 179 static int x_mv_end(int); 180 static int x_mv_forw(int); 181 static int x_mv_fword(int); 182 static int x_newline(int); 183 static int x_next_com(int); 184 static int x_nl_next_com(int); 185 static int x_noop(int); 186 static int x_prev_com(int); 187 static int x_prev_histword(int); 188 static int x_search_char_forw(int); 189 static int x_search_char_back(int); 190 static int x_search_hist(int); 191 static int x_set_mark(int); 192 static int x_transpose(int); 193 static int x_xchg_point_mark(int); 194 static int x_yank(int); 195 static int x_comp_list(int); 196 static int x_expand(int); 197 static int x_fold_capitalize(int); 198 static int x_fold_lower(int); 199 static int x_fold_upper(int); 200 static int x_set_arg(int); 201 static int x_comment(int); 202 #ifdef DEBUG 203 static int x_debug_info(int); 204 #endif 205 206 static const struct x_ftab x_ftab[] = { 207 { x_abort, "abort", 0 }, 208 { x_beg_hist, "beginning-of-history", 0 }, 209 { x_clear_screen, "clear-screen", 0 }, 210 { x_comp_comm, "complete-command", 0 }, 211 { x_comp_file, "complete-file", 0 }, 212 { x_complete, "complete", 0 }, 213 { x_del_back, "delete-char-backward", XF_ARG }, 214 { x_del_bword, "delete-word-backward", XF_ARG }, 215 { x_del_char, "delete-char-forward", XF_ARG }, 216 { x_del_fword, "delete-word-forward", XF_ARG }, 217 { x_del_line, "kill-line", 0 }, 218 { x_draw_line, "redraw", 0 }, 219 { x_end_hist, "end-of-history", 0 }, 220 { x_end_of_text, "eot", 0 }, 221 { x_enumerate, "list", 0 }, 222 { x_eot_del, "eot-or-delete", XF_ARG }, 223 { x_error, "error", 0 }, 224 { x_goto_hist, "goto-history", XF_ARG }, 225 { x_ins_string, "macro-string", XF_NOBIND }, 226 { x_insert, "auto-insert", XF_ARG }, 227 { x_kill, "kill-to-eol", XF_ARG }, 228 { x_kill_region, "kill-region", 0 }, 229 { x_list_comm, "list-command", 0 }, 230 { x_list_file, "list-file", 0 }, 231 { x_literal, "quote", 0 }, 232 { x_meta_yank, "yank-pop", 0 }, 233 { x_mv_back, "backward-char", XF_ARG }, 234 { x_mv_begin, "beginning-of-line", 0 }, 235 { x_mv_bword, "backward-word", XF_ARG }, 236 { x_mv_end, "end-of-line", 0 }, 237 { x_mv_forw, "forward-char", XF_ARG }, 238 { x_mv_fword, "forward-word", XF_ARG }, 239 { x_newline, "newline", 0 }, 240 { x_next_com, "down-history", XF_ARG }, 241 { x_nl_next_com, "newline-and-next", 0 }, 242 { x_noop, "no-op", 0 }, 243 { x_prev_com, "up-history", XF_ARG }, 244 { x_prev_histword, "prev-hist-word", XF_ARG }, 245 { x_search_char_forw, "search-character-forward", XF_ARG }, 246 { x_search_char_back, "search-character-backward", XF_ARG }, 247 { x_search_hist, "search-history", 0 }, 248 { x_set_mark, "set-mark-command", 0 }, 249 { x_transpose, "transpose-chars", 0 }, 250 { x_xchg_point_mark, "exchange-point-and-mark", 0 }, 251 { x_yank, "yank", 0 }, 252 { x_comp_list, "complete-list", 0 }, 253 { x_expand, "expand-file", 0 }, 254 { x_fold_capitalize, "capitalize-word", XF_ARG }, 255 { x_fold_lower, "downcase-word", XF_ARG }, 256 { x_fold_upper, "upcase-word", XF_ARG }, 257 { x_set_arg, "set-arg", XF_NOBIND }, 258 { x_comment, "comment", 0 }, 259 { 0, 0, 0 }, 260 #ifdef DEBUG 261 { x_debug_info, "debug-info", 0 }, 262 #else 263 { 0, 0, 0 }, 264 #endif 265 { 0, 0, 0 }, 266 }; 267 268 int 269 isu8cont(unsigned char c) 270 { 271 return (c & (0x80 | 0x40)) == 0x80; 272 } 273 274 int 275 x_emacs(char *buf, size_t len) 276 { 277 struct kb_entry *k, *kmatch = NULL; 278 char line[LINE + 1]; 279 int at = 0, ntries = 0, submatch, ret; 280 const char *p; 281 282 xbp = xbuf = buf; xend = buf + len; 283 xlp = xcp = xep = buf; 284 *xcp = 0; 285 xlp_valid = true; 286 xmp = NULL; 287 x_histp = histptr + 1; 288 289 xx_cols = x_cols; 290 x_col = promptlen(prompt, &p); 291 prompt_skip = p - prompt; 292 x_adj_ok = 1; 293 prompt_redraw = 1; 294 if (x_col > xx_cols) 295 x_col = x_col - (x_col / xx_cols) * xx_cols; 296 x_displen = xx_cols - 2 - x_col; 297 x_adj_done = 0; 298 299 pprompt(prompt, 0); 300 if (x_displen < 1) { 301 x_col = 0; 302 x_displen = xx_cols - 2; 303 x_e_putc('\n'); 304 prompt_redraw = 0; 305 } 306 307 if (x_nextcmd >= 0) { 308 int off = source->line - x_nextcmd; 309 if (histptr - history >= off) 310 x_load_hist(histptr - off); 311 x_nextcmd = -1; 312 } 313 314 x_literal_set = 0; 315 x_arg = -1; 316 x_last_command = NULL; 317 while (1) { 318 x_flush(); 319 if ((at = x_e_getu8(line, at)) < 0) 320 return 0; 321 ntries++; 322 323 if (x_arg == -1) { 324 x_arg = 1; 325 x_arg_defaulted = 1; 326 } 327 328 if (x_literal_set) { 329 /* literal, so insert it */ 330 x_literal_set = 0; 331 submatch = 0; 332 } else { 333 submatch = 0; 334 kmatch = NULL; 335 TAILQ_FOREACH(k, &kblist, entry) { 336 if (at > k->len) 337 continue; 338 339 if (memcmp(k->seq, line, at) == 0) { 340 /* sub match */ 341 submatch++; 342 if (k->len == at) 343 kmatch = k; 344 } 345 346 /* see if we can abort search early */ 347 if (submatch > 1) 348 break; 349 } 350 } 351 352 if (submatch == 1 && kmatch) { 353 if (kmatch->ftab->xf_func == x_ins_string && 354 kmatch->args && !macro_args) { 355 /* treat macro string as input */ 356 macro_args = kmatch->args; 357 ret = KSTD; 358 } else 359 ret = kmatch->ftab->xf_func(line[at - 1]); 360 } else { 361 if (submatch) 362 continue; 363 if (ntries > 1) { 364 ret = x_error(0); /* unmatched meta sequence */ 365 } else if (at > 1) { 366 x_ins(line); 367 ret = KSTD; 368 } else { 369 ret = x_insert(line[0]); 370 } 371 } 372 373 switch (ret) { 374 case KSTD: 375 if (kmatch) 376 x_last_command = kmatch->ftab->xf_func; 377 else 378 x_last_command = NULL; 379 break; 380 case KEOL: 381 ret = xep - xbuf; 382 return (ret); 383 break; 384 case KINTR: 385 trapsig(SIGINT); 386 x_mode(false); 387 unwind(LSHELL); 388 x_arg = -1; 389 break; 390 default: 391 bi_errorf("invalid return code"); /* can't happen */ 392 } 393 394 /* reset meta sequence */ 395 at = ntries = 0; 396 if (x_arg_set) 397 x_arg_set = 0; /* reset args next time around */ 398 else 399 x_arg = -1; 400 } 401 } 402 403 static int 404 x_insert(int c) 405 { 406 char str[2]; 407 408 /* 409 * Should allow tab and control chars. 410 */ 411 if (c == 0) { 412 x_e_putc(BEL); 413 return KSTD; 414 } 415 str[0] = c; 416 str[1] = '\0'; 417 while (x_arg--) 418 x_ins(str); 419 return KSTD; 420 } 421 422 static int 423 x_ins_string(int c) 424 { 425 return x_insert(c); 426 } 427 428 static int 429 x_do_ins(const char *cp, size_t len) 430 { 431 if (xep+len >= xend) { 432 x_e_putc(BEL); 433 return -1; 434 } 435 436 memmove(xcp+len, xcp, xep - xcp + 1); 437 memmove(xcp, cp, len); 438 xcp += len; 439 xep += len; 440 return 0; 441 } 442 443 static int 444 x_ins(char *s) 445 { 446 char *cp = xcp; 447 int adj = x_adj_done; 448 449 if (x_do_ins(s, strlen(s)) < 0) 450 return -1; 451 /* 452 * x_zots() may result in a call to x_adjust() 453 * we want xcp to reflect the new position. 454 */ 455 xlp_valid = false; 456 x_lastcp(); 457 x_adj_ok = (xcp >= xlp); 458 x_zots(cp); 459 if (adj == x_adj_done) { /* has x_adjust() been called? */ 460 /* no */ 461 for (cp = xlp; cp > xcp; ) 462 x_bs(*--cp); 463 } 464 465 x_adj_ok = 1; 466 return 0; 467 } 468 469 static int 470 x_del_back(int c) 471 { 472 int col = xcp - xbuf; 473 474 if (col == 0) { 475 x_e_putc(BEL); 476 return KSTD; 477 } 478 if (x_arg > col) 479 x_arg = col; 480 while (x_arg < col && isu8cont(xcp[-x_arg])) 481 x_arg++; 482 x_goto(xcp - x_arg); 483 x_delete(x_arg, false); 484 return KSTD; 485 } 486 487 static int 488 x_del_char(int c) 489 { 490 int nleft = xep - xcp; 491 492 if (!nleft) { 493 x_e_putc(BEL); 494 return KSTD; 495 } 496 if (x_arg > nleft) 497 x_arg = nleft; 498 while (x_arg < nleft && isu8cont(xcp[x_arg])) 499 x_arg++; 500 x_delete(x_arg, false); 501 return KSTD; 502 } 503 504 /* Delete nc bytes to the right of the cursor (including cursor position) */ 505 static void 506 x_delete(int nc, int push) 507 { 508 int i,j; 509 char *cp; 510 511 if (nc == 0) 512 return; 513 if (xmp != NULL && xmp > xcp) { 514 if (xcp + nc > xmp) 515 xmp = xcp; 516 else 517 xmp -= nc; 518 } 519 520 /* 521 * This lets us yank a word we have deleted. 522 */ 523 if (push) 524 x_push(nc); 525 526 xep -= nc; 527 cp = xcp; 528 j = 0; 529 i = nc; 530 while (i--) { 531 j += x_size((unsigned char)*cp++); 532 } 533 memmove(xcp, xcp+nc, xep - xcp + 1); /* Copies the null */ 534 x_adj_ok = 0; /* don't redraw */ 535 xlp_valid = false; 536 x_zots(xcp); 537 /* 538 * if we are already filling the line, 539 * there is no need to ' ','\b'. 540 * But if we must, make sure we do the minimum. 541 */ 542 if ((i = xx_cols - 2 - x_col) > 0) { 543 j = (j < i) ? j : i; 544 i = j; 545 while (i--) 546 x_e_putc(' '); 547 i = j; 548 while (i--) 549 x_e_putc('\b'); 550 } 551 /*x_goto(xcp);*/ 552 x_adj_ok = 1; 553 xlp_valid = false; 554 for (cp = x_lastcp(); cp > xcp; ) 555 x_bs(*--cp); 556 557 return; 558 } 559 560 static int 561 x_del_bword(int c) 562 { 563 x_delete(x_bword(), true); 564 return KSTD; 565 } 566 567 static int 568 x_mv_bword(int c) 569 { 570 (void)x_bword(); 571 return KSTD; 572 } 573 574 static int 575 x_mv_fword(int c) 576 { 577 x_goto(xcp + x_fword()); 578 return KSTD; 579 } 580 581 static int 582 x_del_fword(int c) 583 { 584 x_delete(x_fword(), true); 585 return KSTD; 586 } 587 588 static int 589 x_bword(void) 590 { 591 int nc = 0; 592 char *cp = xcp; 593 594 if (cp == xbuf) { 595 x_e_putc(BEL); 596 return 0; 597 } 598 while (x_arg--) { 599 while (cp != xbuf && is_mfs(cp[-1])) { 600 cp--; 601 nc++; 602 } 603 while (cp != xbuf && !is_mfs(cp[-1])) { 604 cp--; 605 nc++; 606 } 607 } 608 x_goto(cp); 609 return nc; 610 } 611 612 static int 613 x_fword(void) 614 { 615 int nc = 0; 616 char *cp = xcp; 617 618 if (cp == xep) { 619 x_e_putc(BEL); 620 return 0; 621 } 622 while (x_arg--) { 623 while (cp != xep && is_mfs(*cp)) { 624 cp++; 625 nc++; 626 } 627 while (cp != xep && !is_mfs(*cp)) { 628 cp++; 629 nc++; 630 } 631 } 632 return nc; 633 } 634 635 static void 636 x_goto(char *cp) 637 { 638 if (cp < xbp || cp >= (xbp + x_displen)) { 639 /* we are heading off screen */ 640 xcp = cp; 641 x_adjust(); 642 } else if (cp < xcp) { /* move back */ 643 while (cp < xcp) 644 x_bs((unsigned char)*--xcp); 645 } else if (cp > xcp) { /* move forward */ 646 while (cp > xcp) 647 x_zotc((unsigned char)*xcp++); 648 } 649 } 650 651 static void 652 x_bs(int c) 653 { 654 int i; 655 656 i = x_size(c); 657 while (i--) 658 x_e_putc('\b'); 659 } 660 661 static int 662 x_size_str(char *cp) 663 { 664 int size = 0; 665 while (*cp) 666 size += x_size(*cp++); 667 return size; 668 } 669 670 static int 671 x_size(int c) 672 { 673 if (c=='\t') 674 return 4; /* Kludge, tabs are always four spaces. */ 675 if (iscntrl(c)) /* control char */ 676 return 2; 677 if (isu8cont(c)) 678 return 0; 679 return 1; 680 } 681 682 static void 683 x_zots(char *str) 684 { 685 int adj = x_adj_done; 686 687 if (str > xbuf && isu8cont(*str)) { 688 while (str > xbuf && isu8cont(*str)) 689 str--; 690 x_e_putc('\b'); 691 } 692 x_lastcp(); 693 while (*str && str < xlp && adj == x_adj_done) 694 x_zotc(*str++); 695 } 696 697 static void 698 x_zotc(int c) 699 { 700 if (c == '\t') { 701 /* Kludge, tabs are always four spaces. */ 702 x_e_puts(" "); 703 } else if (iscntrl(c)) { 704 x_e_putc('^'); 705 x_e_putc(UNCTRL(c)); 706 } else 707 x_e_putc(c); 708 } 709 710 static int 711 x_mv_back(int c) 712 { 713 int col = xcp - xbuf; 714 715 if (col == 0) { 716 x_e_putc(BEL); 717 return KSTD; 718 } 719 if (x_arg > col) 720 x_arg = col; 721 while (x_arg < col && isu8cont(xcp[-x_arg])) 722 x_arg++; 723 x_goto(xcp - x_arg); 724 return KSTD; 725 } 726 727 static int 728 x_mv_forw(int c) 729 { 730 int nleft = xep - xcp; 731 732 if (!nleft) { 733 x_e_putc(BEL); 734 return KSTD; 735 } 736 if (x_arg > nleft) 737 x_arg = nleft; 738 while (x_arg < nleft && isu8cont(xcp[x_arg])) 739 x_arg++; 740 x_goto(xcp + x_arg); 741 return KSTD; 742 } 743 744 static int 745 x_search_char_forw(int c) 746 { 747 char *cp = xcp; 748 749 *xep = '\0'; 750 c = x_e_getc(); 751 while (x_arg--) { 752 if (c < 0 || 753 ((cp = (cp == xep) ? NULL : strchr(cp + 1, c)) == NULL && 754 (cp = strchr(xbuf, c)) == NULL)) { 755 x_e_putc(BEL); 756 return KSTD; 757 } 758 } 759 x_goto(cp); 760 return KSTD; 761 } 762 763 static int 764 x_search_char_back(int c) 765 { 766 char *cp = xcp, *p; 767 768 c = x_e_getc(); 769 for (; x_arg--; cp = p) 770 for (p = cp; ; ) { 771 if (p-- == xbuf) 772 p = xep; 773 if (c < 0 || p == cp) { 774 x_e_putc(BEL); 775 return KSTD; 776 } 777 if (*p == c) 778 break; 779 } 780 x_goto(cp); 781 return KSTD; 782 } 783 784 static int 785 x_newline(int c) 786 { 787 x_e_putc('\r'); 788 x_e_putc('\n'); 789 x_flush(); 790 *xep++ = '\n'; 791 return KEOL; 792 } 793 794 static int 795 x_end_of_text(int c) 796 { 797 x_zotc(edchars.eof); 798 x_putc('\r'); 799 x_putc('\n'); 800 x_flush(); 801 return KEOL; 802 } 803 804 static int x_beg_hist(int c) { x_load_hist(history); return KSTD;} 805 806 static int x_end_hist(int c) { x_load_hist(histptr); return KSTD;} 807 808 static int x_prev_com(int c) { x_load_hist(x_histp - x_arg); return KSTD;} 809 810 static int x_next_com(int c) { x_load_hist(x_histp + x_arg); return KSTD;} 811 812 /* Goto a particular history number obtained from argument. 813 * If no argument is given history 1 is probably not what you 814 * want so we'll simply go to the oldest one. 815 */ 816 static int 817 x_goto_hist(int c) 818 { 819 if (x_arg_defaulted) 820 x_load_hist(history); 821 else 822 x_load_hist(histptr + x_arg - source->line); 823 return KSTD; 824 } 825 826 static void 827 x_load_hist(char **hp) 828 { 829 int oldsize; 830 831 if (hp < history || hp > histptr) { 832 x_e_putc(BEL); 833 return; 834 } 835 x_histp = hp; 836 oldsize = x_size_str(xbuf); 837 strlcpy(xbuf, *hp, xend - xbuf); 838 xbp = xbuf; 839 xep = xcp = xbuf + strlen(xbuf); 840 xlp_valid = false; 841 if (xep <= x_lastcp()) 842 x_redraw(oldsize); 843 x_goto(xep); 844 } 845 846 static int 847 x_nl_next_com(int c) 848 { 849 x_nextcmd = source->line - (histptr - x_histp) + 1; 850 return (x_newline(c)); 851 } 852 853 static int 854 x_eot_del(int c) 855 { 856 if (xep == xbuf && x_arg_defaulted) 857 return (x_end_of_text(c)); 858 else 859 return (x_del_char(c)); 860 } 861 862 static kb_func 863 kb_find_hist_func(char c) 864 { 865 struct kb_entry *k; 866 char line[LINE + 1]; 867 868 line[0] = c; 869 line[1] = '\0'; 870 TAILQ_FOREACH(k, &kblist, entry) 871 if (!strcmp(k->seq, line)) 872 return (k->ftab->xf_func); 873 874 return (x_insert); 875 } 876 877 /* reverse incremental history search */ 878 static int 879 x_search_hist(int c) 880 { 881 int offset = -1; /* offset of match in xbuf, else -1 */ 882 char pat [256+1]; /* pattern buffer */ 883 char *p = pat; 884 int (*f)(int); 885 886 *p = '\0'; 887 while (1) { 888 if (offset < 0) { 889 x_e_puts("\nI-search: "); 890 x_e_puts(pat); 891 } 892 x_flush(); 893 if ((c = x_e_getc()) < 0) 894 return KSTD; 895 f = kb_find_hist_func(c); 896 if (c == CTRL('[') || c == CTRL('@')) { 897 x_e_ungetc(c); 898 break; 899 } else if (f == x_search_hist) 900 offset = x_search(pat, 0, offset); 901 else if (f == x_del_back) { 902 if (p == pat) { 903 offset = -1; 904 break; 905 } 906 if (p > pat) 907 *--p = '\0'; 908 if (p == pat) 909 offset = -1; 910 else 911 offset = x_search(pat, 1, offset); 912 continue; 913 } else if (f == x_insert) { 914 /* add char to pattern */ 915 /* overflow check... */ 916 if (p >= &pat[sizeof(pat) - 1]) { 917 x_e_putc(BEL); 918 continue; 919 } 920 *p++ = c, *p = '\0'; 921 if (offset >= 0) { 922 /* already have partial match */ 923 offset = x_match(xbuf, pat); 924 if (offset >= 0) { 925 x_goto(xbuf + offset + (p - pat) - 926 (*pat == '^')); 927 continue; 928 } 929 } 930 offset = x_search(pat, 0, offset); 931 } else { /* other command */ 932 x_e_ungetc(c); 933 break; 934 } 935 } 936 if (offset < 0) 937 x_redraw(-1); 938 return KSTD; 939 } 940 941 /* search backward from current line */ 942 static int 943 x_search(char *pat, int sameline, int offset) 944 { 945 char **hp; 946 int i; 947 948 for (hp = x_histp - (sameline ? 0 : 1) ; hp >= history; --hp) { 949 i = x_match(*hp, pat); 950 if (i >= 0) { 951 if (offset < 0) 952 x_e_putc('\n'); 953 x_load_hist(hp); 954 x_goto(xbuf + i + strlen(pat) - (*pat == '^')); 955 return i; 956 } 957 } 958 x_e_putc(BEL); 959 x_histp = histptr; 960 return -1; 961 } 962 963 /* return position of first match of pattern in string, else -1 */ 964 static int 965 x_match(char *str, char *pat) 966 { 967 if (*pat == '^') { 968 return (strncmp(str, pat+1, strlen(pat+1)) == 0) ? 0 : -1; 969 } else { 970 char *q = strstr(str, pat); 971 return (q == NULL) ? -1 : q - str; 972 } 973 } 974 975 static int 976 x_del_line(int c) 977 { 978 int i, j; 979 980 *xep = 0; 981 i = xep - xbuf; 982 j = x_size_str(xbuf); 983 xcp = xbuf; 984 x_push(i); 985 xlp = xbp = xep = xbuf; 986 xlp_valid = true; 987 *xcp = 0; 988 xmp = NULL; 989 x_redraw(j); 990 return KSTD; 991 } 992 993 static int 994 x_mv_end(int c) 995 { 996 x_goto(xep); 997 return KSTD; 998 } 999 1000 static int 1001 x_mv_begin(int c) 1002 { 1003 x_goto(xbuf); 1004 return KSTD; 1005 } 1006 1007 static int 1008 x_draw_line(int c) 1009 { 1010 x_redraw(-1); 1011 return KSTD; 1012 } 1013 1014 static int 1015 x_clear_screen(int c) 1016 { 1017 x_redraw(-2); 1018 return KSTD; 1019 } 1020 1021 /* Redraw (part of) the line. 1022 * A non-negative limit is the screen column up to which needs 1023 * redrawing. A limit of -1 redraws on a new line, while a limit 1024 * of -2 (attempts to) clear the screen. 1025 */ 1026 static void 1027 x_redraw(int limit) 1028 { 1029 int i, j, truncate = 0; 1030 char *cp; 1031 1032 x_adj_ok = 0; 1033 if (limit == -2) { 1034 int cleared = 0; 1035 #ifndef SMALL 1036 if (cur_term != NULL && clear_screen != NULL) { 1037 if (tputs(clear_screen, 1, x_putc) != ERR) 1038 cleared = 1; 1039 } 1040 #endif 1041 if (!cleared) 1042 x_e_putc('\n'); 1043 } 1044 else if (limit == -1) 1045 x_e_putc('\n'); 1046 else if (limit >= 0) 1047 x_e_putc('\r'); 1048 x_flush(); 1049 if (xbp == xbuf) { 1050 x_col = promptlen(prompt, NULL); 1051 if (x_col > xx_cols) 1052 truncate = (x_col / xx_cols) * xx_cols; 1053 if (prompt_redraw) 1054 pprompt(prompt + prompt_skip, truncate); 1055 } 1056 if (x_col > xx_cols) 1057 x_col = x_col - (x_col / xx_cols) * xx_cols; 1058 x_displen = xx_cols - 2 - x_col; 1059 if (x_displen < 1) { 1060 x_col = 0; 1061 x_displen = xx_cols - 2; 1062 } 1063 xlp_valid = false; 1064 x_lastcp(); 1065 x_zots(xbp); 1066 if (xbp != xbuf || xep > xlp) 1067 limit = xx_cols; 1068 if (limit >= 0) { 1069 if (xep > xlp) 1070 i = 0; /* we fill the line */ 1071 else 1072 i = limit - (xlp - xbp); 1073 1074 for (j = 0; j < i && x_col < (xx_cols - 2); j++) 1075 x_e_putc(' '); 1076 i = ' '; 1077 if (xep > xlp) { /* more off screen */ 1078 if (xbp > xbuf) 1079 i = '*'; 1080 else 1081 i = '>'; 1082 } else if (xbp > xbuf) 1083 i = '<'; 1084 x_e_putc(i); 1085 j++; 1086 while (j--) 1087 x_e_putc('\b'); 1088 } 1089 for (cp = xlp; cp > xcp; ) 1090 x_bs(*--cp); 1091 x_adj_ok = 1; 1092 #ifdef DEBUG 1093 x_flush(); 1094 #endif 1095 return; 1096 } 1097 1098 static int 1099 x_transpose(int c) 1100 { 1101 char tmp; 1102 1103 /* What transpose is meant to do seems to be up for debate. This 1104 * is a general summary of the options; the text is abcd with the 1105 * upper case character or underscore indicating the cursor position: 1106 * Who Before After Before After 1107 * at&t ksh in emacs mode: abCd abdC abcd_ (bell) 1108 * at&t ksh in gmacs mode: abCd baCd abcd_ abdc_ 1109 * gnu emacs: abCd acbD abcd_ abdc_ 1110 * Pdksh currently goes with GNU behavior since I believe this is the 1111 * most common version of emacs, unless in gmacs mode, in which case 1112 * it does the at&t ksh gmacs mode. 1113 * This should really be broken up into 3 functions so users can bind 1114 * to the one they want. 1115 */ 1116 if (xcp == xbuf) { 1117 x_e_putc(BEL); 1118 return KSTD; 1119 } else if (xcp == xep || Flag(FGMACS)) { 1120 if (xcp - xbuf == 1) { 1121 x_e_putc(BEL); 1122 return KSTD; 1123 } 1124 /* Gosling/Unipress emacs style: Swap two characters before the 1125 * cursor, do not change cursor position 1126 */ 1127 x_bs(xcp[-1]); 1128 x_bs(xcp[-2]); 1129 x_zotc(xcp[-1]); 1130 x_zotc(xcp[-2]); 1131 tmp = xcp[-1]; 1132 xcp[-1] = xcp[-2]; 1133 xcp[-2] = tmp; 1134 } else { 1135 /* GNU emacs style: Swap the characters before and under the 1136 * cursor, move cursor position along one. 1137 */ 1138 x_bs(xcp[-1]); 1139 x_zotc(xcp[0]); 1140 x_zotc(xcp[-1]); 1141 tmp = xcp[-1]; 1142 xcp[-1] = xcp[0]; 1143 xcp[0] = tmp; 1144 x_bs(xcp[0]); 1145 x_goto(xcp + 1); 1146 } 1147 return KSTD; 1148 } 1149 1150 static int 1151 x_literal(int c) 1152 { 1153 x_literal_set = 1; 1154 return KSTD; 1155 } 1156 1157 static int 1158 x_kill(int c) 1159 { 1160 int col = xcp - xbuf; 1161 int lastcol = xep - xbuf; 1162 int ndel; 1163 1164 if (x_arg_defaulted) 1165 x_arg = lastcol; 1166 else if (x_arg > lastcol) 1167 x_arg = lastcol; 1168 while (x_arg < lastcol && isu8cont(xbuf[x_arg])) 1169 x_arg++; 1170 ndel = x_arg - col; 1171 if (ndel < 0) { 1172 x_goto(xbuf + x_arg); 1173 ndel = -ndel; 1174 } 1175 x_delete(ndel, true); 1176 return KSTD; 1177 } 1178 1179 static void 1180 x_push(int nchars) 1181 { 1182 char *cp = str_nsave(xcp, nchars, AEDIT); 1183 afree(killstack[killsp], AEDIT); 1184 killstack[killsp] = cp; 1185 killsp = (killsp + 1) % KILLSIZE; 1186 } 1187 1188 static int 1189 x_yank(int c) 1190 { 1191 if (killsp == 0) 1192 killtp = KILLSIZE; 1193 else 1194 killtp = killsp; 1195 killtp --; 1196 if (killstack[killtp] == 0) { 1197 x_e_puts("\nnothing to yank"); 1198 x_redraw(-1); 1199 return KSTD; 1200 } 1201 xmp = xcp; 1202 x_ins(killstack[killtp]); 1203 return KSTD; 1204 } 1205 1206 static int 1207 x_meta_yank(int c) 1208 { 1209 int len; 1210 if ((x_last_command != x_yank && x_last_command != x_meta_yank) || 1211 killstack[killtp] == 0) { 1212 killtp = killsp; 1213 x_e_puts("\nyank something first"); 1214 x_redraw(-1); 1215 return KSTD; 1216 } 1217 len = strlen(killstack[killtp]); 1218 x_goto(xcp - len); 1219 x_delete(len, false); 1220 do { 1221 if (killtp == 0) 1222 killtp = KILLSIZE - 1; 1223 else 1224 killtp--; 1225 } while (killstack[killtp] == 0); 1226 x_ins(killstack[killtp]); 1227 return KSTD; 1228 } 1229 1230 static int 1231 x_abort(int c) 1232 { 1233 /* x_zotc(c); */ 1234 xlp = xep = xcp = xbp = xbuf; 1235 xlp_valid = true; 1236 *xcp = 0; 1237 return KINTR; 1238 } 1239 1240 static int 1241 x_error(int c) 1242 { 1243 x_e_putc(BEL); 1244 return KSTD; 1245 } 1246 1247 static char * 1248 kb_encode(const char *s) 1249 { 1250 static char l[LINE + 1]; 1251 int at = 0; 1252 1253 l[at] = '\0'; 1254 while (*s) { 1255 if (*s == '^') { 1256 s++; 1257 if (*s >= '?') 1258 l[at++] = CTRL(*s); 1259 else { 1260 l[at++] = '^'; 1261 s--; 1262 } 1263 } else 1264 l[at++] = *s; 1265 l[at] = '\0'; 1266 s++; 1267 } 1268 return (l); 1269 } 1270 1271 static char * 1272 kb_decode(const char *s) 1273 { 1274 static char l[LINE + 1]; 1275 unsigned int i, at = 0; 1276 1277 l[0] = '\0'; 1278 for (i = 0; i < strlen(s); i++) { 1279 if (iscntrl((unsigned char)s[i])) { 1280 l[at++] = '^'; 1281 l[at++] = UNCTRL(s[i]); 1282 } else 1283 l[at++] = s[i]; 1284 l[at] = '\0'; 1285 } 1286 1287 return (l); 1288 } 1289 1290 static int 1291 kb_match(char *s) 1292 { 1293 int len = strlen(s); 1294 struct kb_entry *k; 1295 1296 TAILQ_FOREACH(k, &kblist, entry) { 1297 if (len > k->len) 1298 continue; 1299 1300 if (memcmp(k->seq, s, len) == 0) 1301 return (1); 1302 } 1303 1304 return (0); 1305 } 1306 1307 static void 1308 kb_del(struct kb_entry *k) 1309 { 1310 TAILQ_REMOVE(&kblist, k, entry); 1311 free(k->args); 1312 afree(k, AEDIT); 1313 } 1314 1315 static struct kb_entry * 1316 kb_add_string(kb_func func, void *args, char *str) 1317 { 1318 unsigned int ele, count; 1319 struct kb_entry *k; 1320 struct x_ftab *xf = NULL; 1321 1322 for (ele = 0; ele < NELEM(x_ftab); ele++) 1323 if (x_ftab[ele].xf_func == func) { 1324 xf = (struct x_ftab *)&x_ftab[ele]; 1325 break; 1326 } 1327 if (xf == NULL) 1328 return (NULL); 1329 1330 if (kb_match(str)) { 1331 if (x_bind_quiet == 0) 1332 bi_errorf("duplicate binding for %s", kb_decode(str)); 1333 return (NULL); 1334 } 1335 count = strlen(str); 1336 1337 k = alloc(sizeof *k + count + 1, AEDIT); 1338 k->seq = (unsigned char *)(k + 1); 1339 k->len = count; 1340 k->ftab = xf; 1341 k->args = args ? strdup(args) : NULL; 1342 1343 strlcpy(k->seq, str, count + 1); 1344 1345 TAILQ_INSERT_TAIL(&kblist, k, entry); 1346 1347 return (k); 1348 } 1349 1350 static struct kb_entry * 1351 kb_add(kb_func func, ...) 1352 { 1353 va_list ap; 1354 unsigned char ch; 1355 unsigned int i; 1356 char line[LINE + 1]; 1357 1358 va_start(ap, func); 1359 for (i = 0; i < sizeof(line) - 1; i++) { 1360 ch = va_arg(ap, unsigned int); 1361 if (ch == 0) 1362 break; 1363 line[i] = ch; 1364 } 1365 va_end(ap); 1366 line[i] = '\0'; 1367 1368 return (kb_add_string(func, NULL, line)); 1369 } 1370 1371 static void 1372 kb_print(struct kb_entry *k) 1373 { 1374 if (!(k->ftab->xf_flags & XF_NOBIND)) 1375 shprintf("%s = %s\n", 1376 kb_decode(k->seq), k->ftab->xf_name); 1377 else if (k->args) { 1378 shprintf("%s = ", kb_decode(k->seq)); 1379 shprintf("'%s'\n", kb_decode(k->args)); 1380 } 1381 } 1382 1383 int 1384 x_bind(const char *a1, const char *a2, 1385 int macro, /* bind -m */ 1386 int list) /* bind -l */ 1387 { 1388 unsigned int i; 1389 struct kb_entry *k, *kb; 1390 char in[LINE + 1]; 1391 1392 if (x_tty == 0) { 1393 bi_errorf("cannot bind, not a tty"); 1394 return (1); 1395 } 1396 1397 if (list) { 1398 /* show all function names */ 1399 for (i = 0; i < NELEM(x_ftab); i++) { 1400 if (x_ftab[i].xf_name == NULL) 1401 continue; 1402 if (x_ftab[i].xf_name && 1403 !(x_ftab[i].xf_flags & XF_NOBIND)) 1404 shprintf("%s\n", x_ftab[i].xf_name); 1405 } 1406 return (0); 1407 } 1408 1409 if (a1 == NULL) { 1410 /* show all bindings */ 1411 TAILQ_FOREACH(k, &kblist, entry) 1412 kb_print(k); 1413 return (0); 1414 } 1415 1416 snprintf(in, sizeof in, "%s", kb_encode(a1)); 1417 if (a2 == NULL) { 1418 /* print binding */ 1419 TAILQ_FOREACH(k, &kblist, entry) 1420 if (!strcmp(k->seq, in)) { 1421 kb_print(k); 1422 return (0); 1423 } 1424 shprintf("%s = %s\n", kb_decode(a1), "auto-insert"); 1425 return (0); 1426 } 1427 1428 if (strlen(a2) == 0) { 1429 /* clear binding */ 1430 TAILQ_FOREACH_SAFE(k, &kblist, entry, kb) 1431 if (!strcmp(k->seq, in)) { 1432 kb_del(k); 1433 break; 1434 } 1435 return (0); 1436 } 1437 1438 /* set binding */ 1439 if (macro) { 1440 /* delete old mapping */ 1441 TAILQ_FOREACH_SAFE(k, &kblist, entry, kb) 1442 if (!strcmp(k->seq, in)) { 1443 kb_del(k); 1444 break; 1445 } 1446 kb_add_string(x_ins_string, kb_encode(a2), in); 1447 return (0); 1448 } 1449 1450 /* set non macro binding */ 1451 for (i = 0; i < NELEM(x_ftab); i++) { 1452 if (x_ftab[i].xf_name == NULL) 1453 continue; 1454 if (!strcmp(x_ftab[i].xf_name, a2)) { 1455 /* delete old mapping */ 1456 TAILQ_FOREACH_SAFE(k, &kblist, entry, kb) 1457 if (!strcmp(k->seq, in)) { 1458 kb_del(k); 1459 break; 1460 } 1461 kb_add_string(x_ftab[i].xf_func, NULL, in); 1462 return (0); 1463 } 1464 } 1465 bi_errorf("%s: no such function", a2); 1466 return (1); 1467 } 1468 1469 void 1470 x_init_emacs(void) 1471 { 1472 x_tty = 1; 1473 ainit(AEDIT); 1474 x_nextcmd = -1; 1475 1476 TAILQ_INIT(&kblist); 1477 1478 /* man page order */ 1479 kb_add(x_abort, CTRL('G'), 0); 1480 kb_add(x_mv_back, CTRL('B'), 0); 1481 kb_add(x_mv_back, CTRL('X'), CTRL('D'), 0); 1482 kb_add(x_mv_bword, CTRL('['), 'b', 0); 1483 kb_add(x_beg_hist, CTRL('['), '<', 0); 1484 kb_add(x_mv_begin, CTRL('A'), 0); 1485 kb_add(x_fold_capitalize, CTRL('['), 'C', 0); 1486 kb_add(x_fold_capitalize, CTRL('['), 'c', 0); 1487 kb_add(x_comment, CTRL('['), '#', 0); 1488 kb_add(x_complete, CTRL('['), CTRL('['), 0); 1489 kb_add(x_comp_comm, CTRL('X'), CTRL('['), 0); 1490 kb_add(x_comp_file, CTRL('['), CTRL('X'), 0); 1491 kb_add(x_comp_list, CTRL('I'), 0); 1492 kb_add(x_comp_list, CTRL('['), '=', 0); 1493 kb_add(x_del_back, CTRL('?'), 0); 1494 kb_add(x_del_back, CTRL('H'), 0); 1495 kb_add(x_del_char, CTRL('['), '[', '3', '~', 0); /* delete */ 1496 kb_add(x_del_bword, CTRL('W'), 0); 1497 kb_add(x_del_bword, CTRL('['), CTRL('?'), 0); 1498 kb_add(x_del_bword, CTRL('['), CTRL('H'), 0); 1499 kb_add(x_del_bword, CTRL('['), 'h', 0); 1500 kb_add(x_del_fword, CTRL('['), 'd', 0); 1501 kb_add(x_next_com, CTRL('N'), 0); 1502 kb_add(x_next_com, CTRL('X'), 'B', 0); 1503 kb_add(x_fold_lower, CTRL('['), 'L', 0); 1504 kb_add(x_fold_lower, CTRL('['), 'l', 0); 1505 kb_add(x_end_hist, CTRL('['), '>', 0); 1506 kb_add(x_mv_end, CTRL('E'), 0); 1507 /* how to handle: eot: ^_, underneath copied from original keybindings */ 1508 kb_add(x_end_of_text, CTRL('_'), 0); 1509 kb_add(x_eot_del, CTRL('D'), 0); 1510 /* error */ 1511 kb_add(x_xchg_point_mark, CTRL('X'), CTRL('X'), 0); 1512 kb_add(x_expand, CTRL('['), '*', 0); 1513 kb_add(x_mv_forw, CTRL('F'), 0); 1514 kb_add(x_mv_forw, CTRL('X'), 'C', 0); 1515 kb_add(x_mv_fword, CTRL('['), 'f', 0); 1516 kb_add(x_goto_hist, CTRL('['), 'g', 0); 1517 /* kill-line */ 1518 kb_add(x_kill, CTRL('K'), 0); 1519 kb_add(x_enumerate, CTRL('['), '?', 0); 1520 kb_add(x_list_comm, CTRL('X'), '?', 0); 1521 kb_add(x_list_file, CTRL('X'), CTRL('Y'), 0); 1522 kb_add(x_newline, CTRL('J'), 0); 1523 kb_add(x_newline, CTRL('M'), 0); 1524 kb_add(x_nl_next_com, CTRL('O'), 0); 1525 /* no-op */ 1526 kb_add(x_prev_histword, CTRL('['), '.', 0); 1527 kb_add(x_prev_histword, CTRL('['), '_', 0); 1528 /* how to handle: quote: ^^ */ 1529 kb_add(x_literal, CTRL('^'), 0); 1530 kb_add(x_clear_screen, CTRL('L'), 0); 1531 kb_add(x_search_char_back, CTRL('['), CTRL(']'), 0); 1532 kb_add(x_search_char_forw, CTRL(']'), 0); 1533 kb_add(x_search_hist, CTRL('R'), 0); 1534 kb_add(x_set_mark, CTRL('['), ' ', 0); 1535 kb_add(x_transpose, CTRL('T'), 0); 1536 kb_add(x_prev_com, CTRL('P'), 0); 1537 kb_add(x_prev_com, CTRL('X'), 'A', 0); 1538 kb_add(x_fold_upper, CTRL('['), 'U', 0); 1539 kb_add(x_fold_upper, CTRL('['), 'u', 0); 1540 kb_add(x_literal, CTRL('V'), 0); 1541 kb_add(x_yank, CTRL('Y'), 0); 1542 kb_add(x_meta_yank, CTRL('['), 'y', 0); 1543 /* man page ends here */ 1544 1545 /* arrow keys */ 1546 kb_add(x_prev_com, CTRL('['), '[', 'A', 0); /* up */ 1547 kb_add(x_next_com, CTRL('['), '[', 'B', 0); /* down */ 1548 kb_add(x_mv_forw, CTRL('['), '[', 'C', 0); /* right */ 1549 kb_add(x_mv_back, CTRL('['), '[', 'D', 0); /* left */ 1550 kb_add(x_prev_com, CTRL('['), 'O', 'A', 0); /* up */ 1551 kb_add(x_next_com, CTRL('['), 'O', 'B', 0); /* down */ 1552 kb_add(x_mv_forw, CTRL('['), 'O', 'C', 0); /* right */ 1553 kb_add(x_mv_back, CTRL('['), 'O', 'D', 0); /* left */ 1554 1555 /* more navigation keys */ 1556 kb_add(x_mv_begin, CTRL('['), '[', 'H', 0); /* home */ 1557 kb_add(x_mv_end, CTRL('['), '[', 'F', 0); /* end */ 1558 kb_add(x_mv_begin, CTRL('['), 'O', 'H', 0); /* home */ 1559 kb_add(x_mv_end, CTRL('['), 'O', 'F', 0); /* end */ 1560 kb_add(x_mv_begin, CTRL('['), '[', '1', '~', 0); /* home */ 1561 kb_add(x_mv_end, CTRL('['), '[', '4', '~', 0); /* end */ 1562 kb_add(x_mv_begin, CTRL('['), '[', '7', '~', 0); /* home */ 1563 kb_add(x_mv_end, CTRL('['), '[', '8', '~', 0); /* end */ 1564 1565 /* can't be bound */ 1566 kb_add(x_set_arg, CTRL('['), '0', 0); 1567 kb_add(x_set_arg, CTRL('['), '1', 0); 1568 kb_add(x_set_arg, CTRL('['), '2', 0); 1569 kb_add(x_set_arg, CTRL('['), '3', 0); 1570 kb_add(x_set_arg, CTRL('['), '4', 0); 1571 kb_add(x_set_arg, CTRL('['), '5', 0); 1572 kb_add(x_set_arg, CTRL('['), '6', 0); 1573 kb_add(x_set_arg, CTRL('['), '7', 0); 1574 kb_add(x_set_arg, CTRL('['), '8', 0); 1575 kb_add(x_set_arg, CTRL('['), '9', 0); 1576 1577 /* ctrl arrow keys */ 1578 kb_add(x_mv_end, CTRL('['), '[', '1', ';', '5', 'A', 0); /* ctrl up */ 1579 kb_add(x_mv_begin, CTRL('['), '[', '1', ';', '5', 'B', 0); /* ctrl down */ 1580 kb_add(x_mv_fword, CTRL('['), '[', '1', ';', '5', 'C', 0); /* ctrl right */ 1581 kb_add(x_mv_bword, CTRL('['), '[', '1', ';', '5', 'D', 0); /* ctrl left */ 1582 } 1583 1584 void 1585 x_emacs_keys(X_chars *ec) 1586 { 1587 x_bind_quiet = 1; 1588 if (ec->erase >= 0) { 1589 kb_add(x_del_back, ec->erase, 0); 1590 kb_add(x_del_bword, CTRL('['), ec->erase, 0); 1591 } 1592 if (ec->kill >= 0) 1593 kb_add(x_del_line, ec->kill, 0); 1594 if (ec->werase >= 0) 1595 kb_add(x_del_bword, ec->werase, 0); 1596 if (ec->intr >= 0) 1597 kb_add(x_abort, ec->intr, 0); 1598 if (ec->quit >= 0) 1599 kb_add(x_noop, ec->quit, 0); 1600 x_bind_quiet = 0; 1601 } 1602 1603 static int 1604 x_set_mark(int c) 1605 { 1606 xmp = xcp; 1607 return KSTD; 1608 } 1609 1610 static int 1611 x_kill_region(int c) 1612 { 1613 int rsize; 1614 char *xr; 1615 1616 if (xmp == NULL) { 1617 x_e_putc(BEL); 1618 return KSTD; 1619 } 1620 if (xmp > xcp) { 1621 rsize = xmp - xcp; 1622 xr = xcp; 1623 } else { 1624 rsize = xcp - xmp; 1625 xr = xmp; 1626 } 1627 x_goto(xr); 1628 x_delete(rsize, true); 1629 xmp = xr; 1630 return KSTD; 1631 } 1632 1633 static int 1634 x_xchg_point_mark(int c) 1635 { 1636 char *tmp; 1637 1638 if (xmp == NULL) { 1639 x_e_putc(BEL); 1640 return KSTD; 1641 } 1642 tmp = xmp; 1643 xmp = xcp; 1644 x_goto( tmp ); 1645 return KSTD; 1646 } 1647 1648 static int 1649 x_noop(int c) 1650 { 1651 return KSTD; 1652 } 1653 1654 /* 1655 * File/command name completion routines 1656 */ 1657 1658 static int 1659 x_comp_comm(int c) 1660 { 1661 do_complete(XCF_COMMAND, CT_COMPLETE); 1662 return KSTD; 1663 } 1664 static int 1665 x_list_comm(int c) 1666 { 1667 do_complete(XCF_COMMAND, CT_LIST); 1668 return KSTD; 1669 } 1670 static int 1671 x_complete(int c) 1672 { 1673 do_complete(XCF_COMMAND_FILE, CT_COMPLETE); 1674 return KSTD; 1675 } 1676 static int 1677 x_enumerate(int c) 1678 { 1679 do_complete(XCF_COMMAND_FILE, CT_LIST); 1680 return KSTD; 1681 } 1682 static int 1683 x_comp_file(int c) 1684 { 1685 do_complete(XCF_FILE, CT_COMPLETE); 1686 return KSTD; 1687 } 1688 static int 1689 x_list_file(int c) 1690 { 1691 do_complete(XCF_FILE, CT_LIST); 1692 return KSTD; 1693 } 1694 static int 1695 x_comp_list(int c) 1696 { 1697 do_complete(XCF_COMMAND_FILE, CT_COMPLIST); 1698 return KSTD; 1699 } 1700 static int 1701 x_expand(int c) 1702 { 1703 char **words; 1704 int nwords = 0; 1705 int start, end; 1706 int is_command; 1707 int i; 1708 1709 nwords = x_cf_glob(XCF_FILE, xbuf, xep - xbuf, xcp - xbuf, 1710 &start, &end, &words, &is_command); 1711 1712 if (nwords == 0) { 1713 x_e_putc(BEL); 1714 return KSTD; 1715 } 1716 1717 x_goto(xbuf + start); 1718 x_delete(end - start, false); 1719 for (i = 0; i < nwords;) { 1720 if (x_escape(words[i], strlen(words[i]), x_do_ins) < 0 || 1721 (++i < nwords && x_ins(" ") < 0)) { 1722 x_e_putc(BEL); 1723 return KSTD; 1724 } 1725 } 1726 x_adjust(); 1727 1728 return KSTD; 1729 } 1730 1731 /* type == 0 for list, 1 for complete and 2 for complete-list */ 1732 static void 1733 do_complete(int flags, /* XCF_{COMMAND,FILE,COMMAND_FILE} */ 1734 Comp_type type) 1735 { 1736 char **words; 1737 int nwords; 1738 int start, end, nlen, olen; 1739 int is_command; 1740 int completed = 0; 1741 1742 nwords = x_cf_glob(flags, xbuf, xep - xbuf, xcp - xbuf, 1743 &start, &end, &words, &is_command); 1744 /* no match */ 1745 if (nwords == 0) { 1746 x_e_putc(BEL); 1747 return; 1748 } 1749 1750 if (type == CT_LIST) { 1751 x_print_expansions(nwords, words, is_command); 1752 x_redraw(0); 1753 x_free_words(nwords, words); 1754 return; 1755 } 1756 1757 olen = end - start; 1758 nlen = x_longest_prefix(nwords, words); 1759 /* complete */ 1760 if (nwords == 1 || nlen > olen) { 1761 x_goto(xbuf + start); 1762 x_delete(olen, false); 1763 x_escape(words[0], nlen, x_do_ins); 1764 x_adjust(); 1765 completed = 1; 1766 } 1767 /* add space if single non-dir match */ 1768 if (nwords == 1 && words[0][nlen - 1] != '/') { 1769 x_ins(" "); 1770 completed = 1; 1771 } 1772 1773 if (type == CT_COMPLIST && !completed) { 1774 x_print_expansions(nwords, words, is_command); 1775 completed = 1; 1776 } 1777 1778 if (completed) 1779 x_redraw(0); 1780 1781 x_free_words(nwords, words); 1782 } 1783 1784 /* NAME: 1785 * x_adjust - redraw the line adjusting starting point etc. 1786 * 1787 * DESCRIPTION: 1788 * This function is called when we have exceeded the bounds 1789 * of the edit window. It increments x_adj_done so that 1790 * functions like x_ins and x_delete know that we have been 1791 * called and can skip the x_bs() stuff which has already 1792 * been done by x_redraw. 1793 * 1794 * RETURN VALUE: 1795 * None 1796 */ 1797 1798 static void 1799 x_adjust(void) 1800 { 1801 x_adj_done++; /* flag the fact that we were called. */ 1802 /* 1803 * we had a problem if the prompt length > xx_cols / 2 1804 */ 1805 if ((xbp = xcp - (x_displen / 2)) < xbuf) 1806 xbp = xbuf; 1807 xlp_valid = false; 1808 x_redraw(xx_cols); 1809 x_flush(); 1810 } 1811 1812 static int unget_char = -1; 1813 1814 static void 1815 x_e_ungetc(int c) 1816 { 1817 unget_char = c; 1818 } 1819 1820 static int 1821 x_e_getc(void) 1822 { 1823 int c; 1824 1825 if (unget_char >= 0) { 1826 c = unget_char; 1827 unget_char = -1; 1828 } else if (macro_args) { 1829 c = *macro_args++; 1830 if (!c) { 1831 macro_args = NULL; 1832 c = x_getc(); 1833 } 1834 } else 1835 c = x_getc(); 1836 1837 return c; 1838 } 1839 1840 static int 1841 x_e_getu8(char *buf, int off) 1842 { 1843 int c, cc, len; 1844 1845 c = x_e_getc(); 1846 if (c == -1) 1847 return -1; 1848 buf[off++] = c; 1849 1850 /* 1851 * In the following, comments refer to violations of 1852 * the inequality tests at the ends of the lines. 1853 * See the utf8(7) manual page for details. 1854 */ 1855 1856 if ((c & 0xf8) == 0xf0 && c < 0xf5) /* beyond Unicode */ 1857 len = 4; 1858 else if ((c & 0xf0) == 0xe0) 1859 len = 3; 1860 else if ((c & 0xe0) == 0xc0 && c > 0xc1) /* use single byte */ 1861 len = 2; 1862 else 1863 len = 1; 1864 1865 for (; len > 1; len--) { 1866 cc = x_e_getc(); 1867 if (cc == -1) 1868 break; 1869 if (isu8cont(cc) == 0 || 1870 (c == 0xe0 && len == 3 && cc < 0xa0) || /* use 2 bytes */ 1871 (c == 0xed && len == 3 && cc > 0x9f) || /* surrogates */ 1872 (c == 0xf0 && len == 4 && cc < 0x90) || /* use 3 bytes */ 1873 (c == 0xf4 && len == 4 && cc > 0x8f)) { /* beyond Uni. */ 1874 x_e_ungetc(cc); 1875 break; 1876 } 1877 buf[off++] = cc; 1878 } 1879 buf[off] = '\0'; 1880 1881 return off; 1882 } 1883 1884 static void 1885 x_e_putc(int c) 1886 { 1887 if (c == '\r' || c == '\n') 1888 x_col = 0; 1889 if (x_col < xx_cols) { 1890 x_putc(c); 1891 switch (c) { 1892 case BEL: 1893 break; 1894 case '\r': 1895 case '\n': 1896 break; 1897 case '\b': 1898 x_col--; 1899 break; 1900 default: 1901 if (!isu8cont(c)) 1902 x_col++; 1903 break; 1904 } 1905 } 1906 if (x_adj_ok && (x_col < 0 || x_col >= (xx_cols - 2))) 1907 x_adjust(); 1908 } 1909 1910 #ifdef DEBUG 1911 static int 1912 x_debug_info(int c) 1913 { 1914 x_flush(); 1915 shellf("\nksh debug:\n"); 1916 shellf("\tx_col == %d,\t\tx_cols == %d,\tx_displen == %d\n", 1917 x_col, xx_cols, x_displen); 1918 shellf("\txcp == 0x%lx,\txep == 0x%lx\n", (long) xcp, (long) xep); 1919 shellf("\txbp == 0x%lx,\txbuf == 0x%lx\n", (long) xbp, (long) xbuf); 1920 shellf("\txlp == 0x%lx\n", (long) xlp); 1921 shellf("\txlp == 0x%lx\n", (long) x_lastcp()); 1922 shellf("\n"); 1923 x_redraw(-1); 1924 return 0; 1925 } 1926 #endif 1927 1928 static void 1929 x_e_puts(const char *s) 1930 { 1931 int adj = x_adj_done; 1932 1933 while (*s && adj == x_adj_done) 1934 x_e_putc(*s++); 1935 } 1936 1937 /* NAME: 1938 * x_set_arg - set an arg value for next function 1939 * 1940 * DESCRIPTION: 1941 * This is a simple implementation of M-[0-9]. 1942 * 1943 * RETURN VALUE: 1944 * KSTD 1945 */ 1946 1947 static int 1948 x_set_arg(int c) 1949 { 1950 int n = 0; 1951 int first = 1; 1952 1953 for (; c >= 0 && isdigit(c); c = x_e_getc(), first = 0) 1954 n = n * 10 + (c - '0'); 1955 if (c < 0 || first) { 1956 x_e_putc(BEL); 1957 x_arg = 1; 1958 x_arg_defaulted = 1; 1959 } else { 1960 x_e_ungetc(c); 1961 x_arg = n; 1962 x_arg_defaulted = 0; 1963 x_arg_set = 1; 1964 } 1965 return KSTD; 1966 } 1967 1968 1969 /* Comment or uncomment the current line. */ 1970 static int 1971 x_comment(int c) 1972 { 1973 int oldsize = x_size_str(xbuf); 1974 int len = xep - xbuf; 1975 int ret = x_do_comment(xbuf, xend - xbuf, &len); 1976 1977 if (ret < 0) 1978 x_e_putc(BEL); 1979 else { 1980 xep = xbuf + len; 1981 *xep = '\0'; 1982 xcp = xbp = xbuf; 1983 x_redraw(oldsize); 1984 if (ret > 0) 1985 return x_newline('\n'); 1986 } 1987 return KSTD; 1988 } 1989 1990 1991 /* NAME: 1992 * x_prev_histword - recover word from prev command 1993 * 1994 * DESCRIPTION: 1995 * This function recovers the last word from the previous 1996 * command and inserts it into the current edit line. If a 1997 * numeric arg is supplied then the n'th word from the 1998 * start of the previous command is used. 1999 * 2000 * Bound to M-. 2001 * 2002 * RETURN VALUE: 2003 * KSTD 2004 */ 2005 2006 static int 2007 x_prev_histword(int c) 2008 { 2009 char *rcp; 2010 char *cp; 2011 2012 cp = *histptr; 2013 if (!cp) 2014 x_e_putc(BEL); 2015 else if (x_arg_defaulted) { 2016 rcp = &cp[strlen(cp) - 1]; 2017 /* 2018 * ignore white-space after the last word 2019 */ 2020 while (rcp > cp && is_cfs(*rcp)) 2021 rcp--; 2022 while (rcp > cp && !is_cfs(*rcp)) 2023 rcp--; 2024 if (is_cfs(*rcp)) 2025 rcp++; 2026 x_ins(rcp); 2027 } else { 2028 rcp = cp; 2029 /* 2030 * ignore white-space at start of line 2031 */ 2032 while (*rcp && is_cfs(*rcp)) 2033 rcp++; 2034 while (x_arg-- > 1) { 2035 while (*rcp && !is_cfs(*rcp)) 2036 rcp++; 2037 while (*rcp && is_cfs(*rcp)) 2038 rcp++; 2039 } 2040 cp = rcp; 2041 while (*rcp && !is_cfs(*rcp)) 2042 rcp++; 2043 c = *rcp; 2044 *rcp = '\0'; 2045 x_ins(cp); 2046 *rcp = c; 2047 } 2048 return KSTD; 2049 } 2050 2051 /* Uppercase N(1) words */ 2052 static int 2053 x_fold_upper(int c) 2054 { 2055 return x_fold_case('U'); 2056 } 2057 2058 /* Lowercase N(1) words */ 2059 static int 2060 x_fold_lower(int c) 2061 { 2062 return x_fold_case('L'); 2063 } 2064 2065 /* Lowercase N(1) words */ 2066 static int 2067 x_fold_capitalize(int c) 2068 { 2069 return x_fold_case('C'); 2070 } 2071 2072 /* NAME: 2073 * x_fold_case - convert word to UPPER/lower/Capital case 2074 * 2075 * DESCRIPTION: 2076 * This function is used to implement M-U,M-u,M-L,M-l,M-C and M-c 2077 * to UPPER case, lower case or Capitalize words. 2078 * 2079 * RETURN VALUE: 2080 * None 2081 */ 2082 2083 static int 2084 x_fold_case(int c) 2085 { 2086 char *cp = xcp; 2087 2088 if (cp == xep) { 2089 x_e_putc(BEL); 2090 return KSTD; 2091 } 2092 while (x_arg--) { 2093 /* 2094 * first skip over any white-space 2095 */ 2096 while (cp != xep && is_mfs(*cp)) 2097 cp++; 2098 /* 2099 * do the first char on its own since it may be 2100 * a different action than for the rest. 2101 */ 2102 if (cp != xep) { 2103 if (c == 'L') { /* lowercase */ 2104 if (isupper((unsigned char)*cp)) 2105 *cp = tolower((unsigned char)*cp); 2106 } else { /* uppercase, capitalize */ 2107 if (islower((unsigned char)*cp)) 2108 *cp = toupper((unsigned char)*cp); 2109 } 2110 cp++; 2111 } 2112 /* 2113 * now for the rest of the word 2114 */ 2115 while (cp != xep && !is_mfs(*cp)) { 2116 if (c == 'U') { /* uppercase */ 2117 if (islower((unsigned char)*cp)) 2118 *cp = toupper((unsigned char)*cp); 2119 } else { /* lowercase, capitalize */ 2120 if (isupper((unsigned char)*cp)) 2121 *cp = tolower((unsigned char)*cp); 2122 } 2123 cp++; 2124 } 2125 } 2126 x_goto(cp); 2127 return KSTD; 2128 } 2129 2130 /* NAME: 2131 * x_lastcp - last visible byte 2132 * 2133 * SYNOPSIS: 2134 * x_lastcp() 2135 * 2136 * DESCRIPTION: 2137 * This function returns a pointer to that byte in the 2138 * edit buffer that will be the last displayed on the 2139 * screen. The sequence: 2140 * 2141 * for (cp = x_lastcp(); cp > xcp; cp) 2142 * x_bs(*--cp); 2143 * 2144 * Will position the cursor correctly on the screen. 2145 * 2146 * RETURN VALUE: 2147 * cp or NULL 2148 */ 2149 2150 static char * 2151 x_lastcp(void) 2152 { 2153 char *rcp; 2154 int i; 2155 2156 if (!xlp_valid) { 2157 for (i = 0, rcp = xbp; rcp < xep && i < x_displen; rcp++) 2158 i += x_size((unsigned char)*rcp); 2159 xlp = rcp; 2160 } 2161 xlp_valid = true; 2162 return (xlp); 2163 } 2164 2165 #endif /* EMACS */ 2166