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