1 /* $OpenBSD: emacs.c,v 1.66 2016/08/09 11:04:46 schwarze 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 <locale.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 26 #include "sh.h" 27 #include "edit.h" 28 29 static Area aedit; 30 #define AEDIT &aedit /* area for kill ring and macro defns */ 31 32 #define CTRL(x) ((x) == '?' ? 0x7F : (x) & 0x1F) /* ASCII */ 33 #define UNCTRL(x) ((x) == 0x7F ? '?' : (x) | 0x40) /* ASCII */ 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 struct x_ftab { 41 int (*xf_func)(int c); 42 const char *xf_name; 43 short xf_flags; 44 }; 45 46 #define XF_ARG 1 /* command takes number prefix */ 47 #define XF_NOBIND 2 /* not allowed to bind to function */ 48 #define XF_PREFIX 4 /* function sets prefix */ 49 50 /* Separator for completion */ 51 #define is_cfs(c) (c == ' ' || c == '\t' || c == '"' || c == '\'') 52 53 /* Separator for motion */ 54 #define is_mfs(c) (!(isalnum((unsigned char)c) || \ 55 c == '_' || c == '$' || c & 0x80)) 56 57 /* Arguments for do_complete() 58 * 0 = enumerate M-= complete as much as possible and then list 59 * 1 = complete M-Esc 60 * 2 = list M-? 61 */ 62 typedef enum { 63 CT_LIST, /* list the possible completions */ 64 CT_COMPLETE, /* complete to longest prefix */ 65 CT_COMPLIST /* complete and then list (if non-exact) */ 66 } Comp_type; 67 68 /* keybindings */ 69 struct kb_entry { 70 TAILQ_ENTRY(kb_entry) entry; 71 unsigned char *seq; 72 int len; 73 struct x_ftab *ftab; 74 void *args; 75 }; 76 TAILQ_HEAD(kb_list, kb_entry); 77 struct kb_list kblist = TAILQ_HEAD_INITIALIZER(kblist); 78 79 /* { from 4.9 edit.h */ 80 /* 81 * The following are used for my horizontal scrolling stuff 82 */ 83 static char *xbuf; /* beg input buffer */ 84 static char *xend; /* end input buffer */ 85 static char *xcp; /* current position */ 86 static char *xep; /* current end */ 87 static char *xbp; /* start of visible portion of input buffer */ 88 static char *xlp; /* last byte visible on screen */ 89 static int x_adj_ok; 90 /* 91 * we use x_adj_done so that functions can tell 92 * whether x_adjust() has been called while they are active. 93 */ 94 static int x_adj_done; 95 96 static int xx_cols; 97 static int x_col; 98 static int x_displen; 99 static int x_arg; /* general purpose arg */ 100 static int x_arg_defaulted;/* x_arg not explicitly set; defaulted to 1 */ 101 102 static int xlp_valid; 103 /* end from 4.9 edit.h } */ 104 static int x_tty; /* are we on a tty? */ 105 static int x_bind_quiet; /* be quiet when binding keys */ 106 static int (*x_last_command)(int); 107 108 static char **x_histp; /* history position */ 109 static int x_nextcmd; /* for newline-and-next */ 110 static char *xmp; /* mark pointer */ 111 #define KILLSIZE 20 112 static char *killstack[KILLSIZE]; 113 static int killsp, killtp; 114 static int x_literal_set; 115 static int x_arg_set; 116 static char *macro_args; 117 static int prompt_skip; 118 static int prompt_redraw; 119 120 static int x_ins(char *); 121 static void x_delete(int, int); 122 static int x_bword(void); 123 static int x_fword(void); 124 static void x_goto(char *); 125 static void x_bs(int); 126 static int x_size_str(char *); 127 static int x_size(int); 128 static void x_zots(char *); 129 static void x_zotc(int); 130 static void x_load_hist(char **); 131 static int x_search(char *, int, int); 132 static int x_match(char *, char *); 133 static void x_redraw(int); 134 static void x_push(int); 135 static void x_adjust(void); 136 static void x_e_ungetc(int); 137 static int x_e_getc(void); 138 static void x_e_putc(int); 139 static void x_e_puts(const char *); 140 static int x_comment(int); 141 static int x_fold_case(int); 142 static char *x_lastcp(void); 143 static void do_complete(int, Comp_type); 144 static int isu8cont(unsigned char); 145 146 /* proto's for keybindings */ 147 static int x_abort(int); 148 static int x_beg_hist(int); 149 static int x_comp_comm(int); 150 static int x_comp_file(int); 151 static int x_complete(int); 152 static int x_del_back(int); 153 static int x_del_bword(int); 154 static int x_del_char(int); 155 static int x_del_fword(int); 156 static int x_del_line(int); 157 static int x_draw_line(int); 158 static int x_end_hist(int); 159 static int x_end_of_text(int); 160 static int x_enumerate(int); 161 static int x_eot_del(int); 162 static int x_error(int); 163 static int x_goto_hist(int); 164 static int x_ins_string(int); 165 static int x_insert(int); 166 static int x_kill(int); 167 static int x_kill_region(int); 168 static int x_list_comm(int); 169 static int x_list_file(int); 170 static int x_literal(int); 171 static int x_meta_yank(int); 172 static int x_mv_back(int); 173 static int x_mv_begin(int); 174 static int x_mv_bword(int); 175 static int x_mv_end(int); 176 static int x_mv_forw(int); 177 static int x_mv_fword(int); 178 static int x_newline(int); 179 static int x_next_com(int); 180 static int x_nl_next_com(int); 181 static int x_noop(int); 182 static int x_prev_com(int); 183 static int x_prev_histword(int); 184 static int x_search_char_forw(int); 185 static int x_search_char_back(int); 186 static int x_search_hist(int); 187 static int x_set_mark(int); 188 static int x_stuff(int); 189 static int x_stuffreset(int); 190 static int x_transpose(int); 191 static int x_version(int); 192 static int x_xchg_point_mark(int); 193 static int x_yank(int); 194 static int x_comp_list(int); 195 static int x_expand(int); 196 static int x_fold_capitalize(int); 197 static int x_fold_lower(int); 198 static int x_fold_upper(int); 199 static int x_set_arg(int); 200 static int x_comment(int); 201 #ifdef DEBUG 202 static int x_debug_info(int); 203 #endif 204 205 static const struct x_ftab x_ftab[] = { 206 { x_abort, "abort", 0 }, 207 { x_beg_hist, "beginning-of-history", 0 }, 208 { x_comp_comm, "complete-command", 0 }, 209 { x_comp_file, "complete-file", 0 }, 210 { x_complete, "complete", 0 }, 211 { x_del_back, "delete-char-backward", XF_ARG }, 212 { x_del_bword, "delete-word-backward", XF_ARG }, 213 { x_del_char, "delete-char-forward", XF_ARG }, 214 { x_del_fword, "delete-word-forward", XF_ARG }, 215 { x_del_line, "kill-line", 0 }, 216 { x_draw_line, "redraw", 0 }, 217 { x_end_hist, "end-of-history", 0 }, 218 { x_end_of_text, "eot", 0 }, 219 { x_enumerate, "list", 0 }, 220 { x_eot_del, "eot-or-delete", XF_ARG }, 221 { x_error, "error", 0 }, 222 { x_goto_hist, "goto-history", XF_ARG }, 223 { x_ins_string, "macro-string", XF_NOBIND }, 224 { x_insert, "auto-insert", XF_ARG }, 225 { x_kill, "kill-to-eol", XF_ARG }, 226 { x_kill_region, "kill-region", 0 }, 227 { x_list_comm, "list-command", 0 }, 228 { x_list_file, "list-file", 0 }, 229 { x_literal, "quote", 0 }, 230 { x_meta_yank, "yank-pop", 0 }, 231 { x_mv_back, "backward-char", XF_ARG }, 232 { x_mv_begin, "beginning-of-line", 0 }, 233 { x_mv_bword, "backward-word", XF_ARG }, 234 { x_mv_end, "end-of-line", 0 }, 235 { x_mv_forw, "forward-char", XF_ARG }, 236 { x_mv_fword, "forward-word", XF_ARG }, 237 { x_newline, "newline", 0 }, 238 { x_next_com, "down-history", XF_ARG }, 239 { x_nl_next_com, "newline-and-next", 0 }, 240 { x_noop, "no-op", 0 }, 241 { x_prev_com, "up-history", XF_ARG }, 242 { x_prev_histword, "prev-hist-word", XF_ARG }, 243 { x_search_char_forw, "search-character-forward", XF_ARG }, 244 { x_search_char_back, "search-character-backward", XF_ARG }, 245 { x_search_hist, "search-history", 0 }, 246 { x_set_mark, "set-mark-command", 0 }, 247 { x_stuff, "stuff", 0 }, 248 { x_stuffreset, "stuff-reset", 0 }, 249 { x_transpose, "transpose-chars", 0 }, 250 { x_version, "version", 0 }, 251 { x_xchg_point_mark, "exchange-point-and-mark", 0 }, 252 { x_yank, "yank", 0 }, 253 { x_comp_list, "complete-list", 0 }, 254 { x_expand, "expand-file", 0 }, 255 { x_fold_capitalize, "capitalize-word", XF_ARG }, 256 { x_fold_lower, "downcase-word", XF_ARG }, 257 { x_fold_upper, "upcase-word", XF_ARG }, 258 { x_set_arg, "set-arg", XF_NOBIND }, 259 { x_comment, "comment", 0 }, 260 { 0, 0, 0 }, 261 #ifdef DEBUG 262 { x_debug_info, "debug-info", 0 }, 263 #else 264 { 0, 0, 0 }, 265 #endif 266 { 0, 0, 0 }, 267 }; 268 269 int 270 isu8cont(unsigned char c) 271 { 272 return (c & (0x80 | 0x40)) == 0x80; 273 } 274 275 int 276 x_emacs(char *buf, size_t len) 277 { 278 struct kb_entry *k, *kmatch = NULL; 279 char line[LINE + 1]; 280 int at = 0, submatch, ret, c; 281 const char *p; 282 283 xbp = xbuf = buf; xend = buf + len; 284 xlp = xcp = xep = buf; 285 *xcp = 0; 286 xlp_valid = true; 287 xmp = NULL; 288 x_histp = histptr + 1; 289 290 xx_cols = x_cols; 291 x_col = promptlen(prompt, &p); 292 prompt_skip = p - prompt; 293 x_adj_ok = 1; 294 prompt_redraw = 1; 295 if (x_col > xx_cols) 296 x_col = x_col - (x_col / xx_cols) * xx_cols; 297 x_displen = xx_cols - 2 - x_col; 298 x_adj_done = 0; 299 300 pprompt(prompt, 0); 301 if (x_displen < 1) { 302 x_col = 0; 303 x_displen = xx_cols - 2; 304 x_e_putc('\n'); 305 prompt_redraw = 0; 306 } 307 308 if (x_nextcmd >= 0) { 309 int off = source->line - x_nextcmd; 310 if (histptr - history >= off) 311 x_load_hist(histptr - off); 312 x_nextcmd = -1; 313 } 314 315 line[0] = '\0'; 316 x_literal_set = 0; 317 x_arg = -1; 318 x_last_command = NULL; 319 while (1) { 320 x_flush(); 321 if ((c = x_e_getc()) < 0) 322 return 0; 323 324 line[at++] = c; 325 line[at] = '\0'; 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(c); 364 } else { 365 if (submatch) 366 continue; 367 if (at == 1) 368 ret = x_insert(c); 369 else 370 ret = x_error(c); /* not matched meta sequence */ 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 = 0; 396 line[0] = '\0'; 397 if (x_arg_set) 398 x_arg_set = 0; /* reset args next time around */ 399 else 400 x_arg = -1; 401 } 402 } 403 404 static int 405 x_insert(int c) 406 { 407 char str[2]; 408 409 /* 410 * Should allow tab and control chars. 411 */ 412 if (c == 0) { 413 x_e_putc(BEL); 414 return KSTD; 415 } 416 str[0] = c; 417 str[1] = '\0'; 418 while (x_arg--) 419 x_ins(str); 420 return KSTD; 421 } 422 423 static int 424 x_ins_string(int c) 425 { 426 return x_insert(c); 427 } 428 429 static int 430 x_do_ins(const char *cp, size_t len) 431 { 432 if (xep+len >= xend) { 433 x_e_putc(BEL); 434 return -1; 435 } 436 437 memmove(xcp+len, xcp, xep - xcp + 1); 438 memmove(xcp, cp, len); 439 xcp += len; 440 xep += len; 441 return 0; 442 } 443 444 static int 445 x_ins(char *s) 446 { 447 char *cp = xcp; 448 int adj = x_adj_done; 449 450 if (x_do_ins(s, strlen(s)) < 0) 451 return -1; 452 /* 453 * x_zots() may result in a call to x_adjust() 454 * we want xcp to reflect the new position. 455 */ 456 xlp_valid = false; 457 x_lastcp(); 458 x_adj_ok = (xcp >= xlp); 459 x_zots(cp); 460 if (adj == x_adj_done) { /* has x_adjust() been called? */ 461 /* no */ 462 for (cp = xlp; cp > xcp; ) 463 x_bs(*--cp); 464 } 465 466 x_adj_ok = 1; 467 return 0; 468 } 469 470 static int 471 x_del_back(int c) 472 { 473 int col = xcp - xbuf; 474 475 if (col == 0) { 476 x_e_putc(BEL); 477 return KSTD; 478 } 479 if (x_arg > col) 480 x_arg = col; 481 while (x_arg < col && isu8cont(xcp[-x_arg])) 482 x_arg++; 483 x_goto(xcp - x_arg); 484 x_delete(x_arg, false); 485 return KSTD; 486 } 487 488 static int 489 x_del_char(int c) 490 { 491 int nleft = xep - xcp; 492 493 if (!nleft) { 494 x_e_putc(BEL); 495 return KSTD; 496 } 497 if (x_arg > nleft) 498 x_arg = nleft; 499 while (x_arg < nleft && isu8cont(xcp[x_arg])) 500 x_arg++; 501 x_delete(x_arg, false); 502 return KSTD; 503 } 504 505 /* Delete nc bytes to the right of the cursor (including cursor position) */ 506 static void 507 x_delete(int nc, int push) 508 { 509 int i,j; 510 char *cp; 511 512 if (nc == 0) 513 return; 514 if (xmp != NULL && xmp > xcp) { 515 if (xcp + nc > xmp) 516 xmp = xcp; 517 else 518 xmp -= nc; 519 } 520 521 /* 522 * This lets us yank a word we have deleted. 523 */ 524 if (push) 525 x_push(nc); 526 527 xep -= nc; 528 cp = xcp; 529 j = 0; 530 i = nc; 531 while (i--) { 532 j += x_size((unsigned char)*cp++); 533 } 534 memmove(xcp, xcp+nc, xep - xcp + 1); /* Copies the null */ 535 x_adj_ok = 0; /* don't redraw */ 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 void * 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('[')) { 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 1015 /* Redraw (part of) the line. If limit is < 0, the everything is redrawn 1016 * on a NEW line, otherwise limit is the screen column up to which needs 1017 * redrawing. 1018 */ 1019 static void 1020 x_redraw(int limit) 1021 { 1022 int i, j, truncate = 0; 1023 char *cp; 1024 1025 x_adj_ok = 0; 1026 if (limit == -1) 1027 x_e_putc('\n'); 1028 else 1029 x_e_putc('\r'); 1030 x_flush(); 1031 if (xbp == xbuf) { 1032 x_col = promptlen(prompt, NULL); 1033 if (x_col > xx_cols) 1034 truncate = (x_col / xx_cols) * xx_cols; 1035 if (prompt_redraw) 1036 pprompt(prompt + prompt_skip, truncate); 1037 } 1038 if (x_col > xx_cols) 1039 x_col = x_col - (x_col / xx_cols) * xx_cols; 1040 x_displen = xx_cols - 2 - x_col; 1041 if (x_displen < 1) { 1042 x_col = 0; 1043 x_displen = xx_cols - 2; 1044 } 1045 xlp_valid = false; 1046 cp = x_lastcp(); 1047 x_zots(xbp); 1048 if (xbp != xbuf || xep > xlp) 1049 limit = xx_cols; 1050 if (limit >= 0) { 1051 if (xep > xlp) 1052 i = 0; /* we fill the line */ 1053 else 1054 i = limit - (xlp - xbp); 1055 1056 for (j = 0; j < i && x_col < (xx_cols - 2); j++) 1057 x_e_putc(' '); 1058 i = ' '; 1059 if (xep > xlp) { /* more off screen */ 1060 if (xbp > xbuf) 1061 i = '*'; 1062 else 1063 i = '>'; 1064 } else if (xbp > xbuf) 1065 i = '<'; 1066 x_e_putc(i); 1067 j++; 1068 while (j--) 1069 x_e_putc('\b'); 1070 } 1071 for (cp = xlp; cp > xcp; ) 1072 x_bs(*--cp); 1073 x_adj_ok = 1; 1074 #ifdef DEBUG 1075 x_flush(); 1076 #endif 1077 return; 1078 } 1079 1080 static int 1081 x_transpose(int c) 1082 { 1083 char tmp; 1084 1085 /* What transpose is meant to do seems to be up for debate. This 1086 * is a general summary of the options; the text is abcd with the 1087 * upper case character or underscore indicating the cursor position: 1088 * Who Before After Before After 1089 * at&t ksh in emacs mode: abCd abdC abcd_ (bell) 1090 * at&t ksh in gmacs mode: abCd baCd abcd_ abdc_ 1091 * gnu emacs: abCd acbD abcd_ abdc_ 1092 * Pdksh currently goes with GNU behavior since I believe this is the 1093 * most common version of emacs, unless in gmacs mode, in which case 1094 * it does the at&t ksh gmacs mode. 1095 * This should really be broken up into 3 functions so users can bind 1096 * to the one they want. 1097 */ 1098 if (xcp == xbuf) { 1099 x_e_putc(BEL); 1100 return KSTD; 1101 } else if (xcp == xep || Flag(FGMACS)) { 1102 if (xcp - xbuf == 1) { 1103 x_e_putc(BEL); 1104 return KSTD; 1105 } 1106 /* Gosling/Unipress emacs style: Swap two characters before the 1107 * cursor, do not change cursor position 1108 */ 1109 x_bs(xcp[-1]); 1110 x_bs(xcp[-2]); 1111 x_zotc(xcp[-1]); 1112 x_zotc(xcp[-2]); 1113 tmp = xcp[-1]; 1114 xcp[-1] = xcp[-2]; 1115 xcp[-2] = tmp; 1116 } else { 1117 /* GNU emacs style: Swap the characters before and under the 1118 * cursor, move cursor position along one. 1119 */ 1120 x_bs(xcp[-1]); 1121 x_zotc(xcp[0]); 1122 x_zotc(xcp[-1]); 1123 tmp = xcp[-1]; 1124 xcp[-1] = xcp[0]; 1125 xcp[0] = tmp; 1126 x_bs(xcp[0]); 1127 x_goto(xcp + 1); 1128 } 1129 return KSTD; 1130 } 1131 1132 static int 1133 x_literal(int c) 1134 { 1135 x_literal_set = 1; 1136 return KSTD; 1137 } 1138 1139 static int 1140 x_kill(int c) 1141 { 1142 int col = xcp - xbuf; 1143 int lastcol = xep - xbuf; 1144 int ndel; 1145 1146 if (x_arg_defaulted) 1147 x_arg = lastcol; 1148 else if (x_arg > lastcol) 1149 x_arg = lastcol; 1150 while (x_arg < lastcol && isu8cont(xbuf[x_arg])) 1151 x_arg++; 1152 ndel = x_arg - col; 1153 if (ndel < 0) { 1154 x_goto(xbuf + x_arg); 1155 ndel = -ndel; 1156 } 1157 x_delete(ndel, true); 1158 return KSTD; 1159 } 1160 1161 static void 1162 x_push(int nchars) 1163 { 1164 char *cp = str_nsave(xcp, nchars, AEDIT); 1165 afree(killstack[killsp], AEDIT); 1166 killstack[killsp] = cp; 1167 killsp = (killsp + 1) % KILLSIZE; 1168 } 1169 1170 static int 1171 x_yank(int c) 1172 { 1173 if (killsp == 0) 1174 killtp = KILLSIZE; 1175 else 1176 killtp = killsp; 1177 killtp --; 1178 if (killstack[killtp] == 0) { 1179 x_e_puts("\nnothing to yank"); 1180 x_redraw(-1); 1181 return KSTD; 1182 } 1183 xmp = xcp; 1184 x_ins(killstack[killtp]); 1185 return KSTD; 1186 } 1187 1188 static int 1189 x_meta_yank(int c) 1190 { 1191 int len; 1192 if ((x_last_command != x_yank && x_last_command != x_meta_yank) || 1193 killstack[killtp] == 0) { 1194 killtp = killsp; 1195 x_e_puts("\nyank something first"); 1196 x_redraw(-1); 1197 return KSTD; 1198 } 1199 len = strlen(killstack[killtp]); 1200 x_goto(xcp - len); 1201 x_delete(len, false); 1202 do { 1203 if (killtp == 0) 1204 killtp = KILLSIZE - 1; 1205 else 1206 killtp--; 1207 } while (killstack[killtp] == 0); 1208 x_ins(killstack[killtp]); 1209 return KSTD; 1210 } 1211 1212 static int 1213 x_abort(int c) 1214 { 1215 /* x_zotc(c); */ 1216 xlp = xep = xcp = xbp = xbuf; 1217 xlp_valid = true; 1218 *xcp = 0; 1219 return KINTR; 1220 } 1221 1222 static int 1223 x_error(int c) 1224 { 1225 x_e_putc(BEL); 1226 return KSTD; 1227 } 1228 1229 static int 1230 x_stuffreset(int c) 1231 { 1232 #ifdef TIOCSTI 1233 (void)x_stuff(c); 1234 return KINTR; 1235 #else 1236 x_zotc(c); 1237 xlp = xcp = xep = xbp = xbuf; 1238 xlp_valid = true; 1239 *xcp = 0; 1240 x_redraw(-1); 1241 return KSTD; 1242 #endif 1243 } 1244 1245 static int 1246 x_stuff(int c) 1247 { 1248 #ifdef TIOCSTI 1249 char ch = c; 1250 bool savmode = x_mode(false); 1251 1252 (void)ioctl(TTY, TIOCSTI, &ch); 1253 (void)x_mode(savmode); 1254 x_redraw(-1); 1255 #endif 1256 return KSTD; 1257 } 1258 1259 static char * 1260 kb_encode(const char *s) 1261 { 1262 static char l[LINE + 1]; 1263 int at = 0; 1264 1265 l[at] = '\0'; 1266 while (*s) { 1267 if (*s == '^') { 1268 s++; 1269 if (*s >= '?') 1270 l[at++] = CTRL(*s); 1271 else { 1272 l[at++] = '^'; 1273 s--; 1274 } 1275 } else 1276 l[at++] = *s; 1277 l[at] = '\0'; 1278 s++; 1279 } 1280 return (l); 1281 } 1282 1283 static char * 1284 kb_decode(const char *s) 1285 { 1286 static char l[LINE + 1]; 1287 int i, at = 0; 1288 1289 l[0] = '\0'; 1290 for (i = 0; i < strlen(s); i++) { 1291 if (iscntrl((unsigned char)s[i])) { 1292 l[at++] = '^'; 1293 l[at++] = UNCTRL(s[i]); 1294 } else 1295 l[at++] = s[i]; 1296 l[at] = '\0'; 1297 } 1298 1299 return (l); 1300 } 1301 1302 static int 1303 kb_match(char *s) 1304 { 1305 int len = strlen(s); 1306 struct kb_entry *k; 1307 1308 TAILQ_FOREACH(k, &kblist, entry) { 1309 if (len > k->len) 1310 continue; 1311 1312 if (memcmp(k->seq, s, len) == 0) 1313 return (1); 1314 } 1315 1316 return (0); 1317 } 1318 1319 static void 1320 kb_del(struct kb_entry *k) 1321 { 1322 TAILQ_REMOVE(&kblist, k, entry); 1323 free(k->args); 1324 afree(k, AEDIT); 1325 } 1326 1327 static struct kb_entry * 1328 kb_add_string(void *func, void *args, char *str) 1329 { 1330 int i, count; 1331 struct kb_entry *k; 1332 struct x_ftab *xf = NULL; 1333 1334 for (i = 0; i < NELEM(x_ftab); i++) 1335 if (x_ftab[i].xf_func == func) { 1336 xf = (struct x_ftab *)&x_ftab[i]; 1337 break; 1338 } 1339 if (xf == NULL) 1340 return (NULL); 1341 1342 if (kb_match(str)) { 1343 if (x_bind_quiet == 0) 1344 bi_errorf("duplicate binding for %s", kb_decode(str)); 1345 return (NULL); 1346 } 1347 count = strlen(str); 1348 1349 k = alloc(sizeof *k + count + 1, AEDIT); 1350 k->seq = (unsigned char *)(k + 1); 1351 k->len = count; 1352 k->ftab = xf; 1353 k->args = args ? strdup(args) : NULL; 1354 1355 strlcpy(k->seq, str, count + 1); 1356 1357 TAILQ_INSERT_TAIL(&kblist, k, entry); 1358 1359 return (k); 1360 } 1361 1362 static struct kb_entry * 1363 kb_add(void *func, void *args, ...) 1364 { 1365 va_list ap; 1366 int i, count; 1367 char l[LINE + 1]; 1368 1369 va_start(ap, args); 1370 count = 0; 1371 while (va_arg(ap, unsigned int) != 0) 1372 count++; 1373 va_end(ap); 1374 1375 va_start(ap, args); 1376 for (i = 0; i <= count /* <= is correct */; i++) 1377 l[i] = (unsigned char)va_arg(ap, unsigned int); 1378 va_end(ap); 1379 1380 return (kb_add_string(func, args, l)); 1381 } 1382 1383 static void 1384 kb_print(struct kb_entry *k) 1385 { 1386 if (!(k->ftab->xf_flags & XF_NOBIND)) 1387 shprintf("%s = %s\n", 1388 kb_decode(k->seq), k->ftab->xf_name); 1389 else if (k->args) { 1390 shprintf("%s = ", kb_decode(k->seq)); 1391 shprintf("'%s'\n", kb_decode(k->args)); 1392 } 1393 } 1394 1395 int 1396 x_bind(const char *a1, const char *a2, 1397 int macro, /* bind -m */ 1398 int list) /* bind -l */ 1399 { 1400 int i; 1401 struct kb_entry *k, *kb; 1402 char in[LINE + 1]; 1403 1404 if (x_tty == 0) { 1405 bi_errorf("cannot bind, not a tty"); 1406 return (1); 1407 } 1408 1409 if (list) { 1410 /* show all function names */ 1411 for (i = 0; i < NELEM(x_ftab); i++) { 1412 if (x_ftab[i].xf_name == NULL) 1413 continue; 1414 if (x_ftab[i].xf_name && 1415 !(x_ftab[i].xf_flags & XF_NOBIND)) 1416 shprintf("%s\n", x_ftab[i].xf_name); 1417 } 1418 return (0); 1419 } 1420 1421 if (a1 == NULL) { 1422 /* show all bindings */ 1423 TAILQ_FOREACH(k, &kblist, entry) 1424 kb_print(k); 1425 return (0); 1426 } 1427 1428 snprintf(in, sizeof in, "%s", kb_encode(a1)); 1429 if (a2 == NULL) { 1430 /* print binding */ 1431 TAILQ_FOREACH(k, &kblist, entry) 1432 if (!strcmp(k->seq, in)) { 1433 kb_print(k); 1434 return (0); 1435 } 1436 shprintf("%s = %s\n", kb_decode(a1), "auto-insert"); 1437 return (0); 1438 } 1439 1440 if (strlen(a2) == 0) { 1441 /* clear binding */ 1442 TAILQ_FOREACH_SAFE(k, &kblist, entry, kb) 1443 if (!strcmp(k->seq, in)) { 1444 kb_del(k); 1445 break; 1446 } 1447 return (0); 1448 } 1449 1450 /* set binding */ 1451 if (macro) { 1452 /* delete old mapping */ 1453 TAILQ_FOREACH_SAFE(k, &kblist, entry, kb) 1454 if (!strcmp(k->seq, in)) { 1455 kb_del(k); 1456 break; 1457 } 1458 kb_add_string(x_ins_string, kb_encode(a2), in); 1459 return (0); 1460 } 1461 1462 /* set non macro binding */ 1463 for (i = 0; i < NELEM(x_ftab); i++) { 1464 if (x_ftab[i].xf_name == NULL) 1465 continue; 1466 if (!strcmp(x_ftab[i].xf_name, a2)) { 1467 /* delete old mapping */ 1468 TAILQ_FOREACH_SAFE(k, &kblist, entry, kb) 1469 if (!strcmp(k->seq, in)) { 1470 kb_del(k); 1471 break; 1472 } 1473 kb_add_string(x_ftab[i].xf_func, NULL, in); 1474 return (0); 1475 } 1476 } 1477 bi_errorf("%s: no such function", a2); 1478 return (1); 1479 } 1480 1481 void 1482 x_init_emacs(void) 1483 { 1484 char *locale; 1485 1486 x_tty = 1; 1487 ainit(AEDIT); 1488 x_nextcmd = -1; 1489 1490 /* Determine if we can translate meta key or use 8-bit AscII 1491 * XXX - It would be nice if there was a locale attribute to 1492 * determine if the locale is 7-bit or not. 1493 */ 1494 locale = setlocale(LC_CTYPE, NULL); 1495 if (locale == NULL || !strcmp(locale, "C") || !strcmp(locale, "POSIX")) 1496 Flag(FEMACSUSEMETA) = 1; 1497 1498 /* new keybinding stuff */ 1499 TAILQ_INIT(&kblist); 1500 1501 /* man page order */ 1502 kb_add(x_abort, NULL, CTRL('G'), 0); 1503 kb_add(x_mv_back, NULL, CTRL('B'), 0); 1504 kb_add(x_mv_back, NULL, CTRL('X'), CTRL('D'), 0); 1505 kb_add(x_mv_bword, NULL, CTRL('['), 'b', 0); 1506 kb_add(x_beg_hist, NULL, CTRL('['), '<', 0); 1507 kb_add(x_mv_begin, NULL, CTRL('A'), 0); 1508 kb_add(x_fold_capitalize, NULL, CTRL('['), 'C', 0); 1509 kb_add(x_fold_capitalize, NULL, CTRL('['), 'c', 0); 1510 kb_add(x_comment, NULL, CTRL('['), '#', 0); 1511 kb_add(x_complete, NULL, CTRL('['), CTRL('['), 0); 1512 kb_add(x_comp_comm, NULL, CTRL('X'), CTRL('['), 0); 1513 kb_add(x_comp_file, NULL, CTRL('['), CTRL('X'), 0); 1514 kb_add(x_comp_list, NULL, CTRL('I'), 0); 1515 kb_add(x_comp_list, NULL, CTRL('['), '=', 0); 1516 kb_add(x_del_back, NULL, CTRL('?'), 0); 1517 kb_add(x_del_back, NULL, CTRL('H'), 0); 1518 kb_add(x_del_char, NULL, CTRL('['), '[', '3', '~', 0); /* delete */ 1519 kb_add(x_del_bword, NULL, CTRL('['), CTRL('?'), 0); 1520 kb_add(x_del_bword, NULL, CTRL('['), CTRL('H'), 0); 1521 kb_add(x_del_bword, NULL, CTRL('['), 'h', 0); 1522 kb_add(x_del_fword, NULL, CTRL('['), 'd', 0); 1523 kb_add(x_next_com, NULL, CTRL('N'), 0); 1524 kb_add(x_next_com, NULL, CTRL('X'), 'B', 0); 1525 kb_add(x_fold_lower, NULL, CTRL('['), 'L', 0); 1526 kb_add(x_fold_lower, NULL, CTRL('['), 'l', 0); 1527 kb_add(x_end_hist, NULL, CTRL('['), '>', 0); 1528 kb_add(x_mv_end, NULL, CTRL('E'), 0); 1529 /* how to handle: eot: ^_, underneath copied from original keybindings */ 1530 kb_add(x_end_of_text, NULL, CTRL('_'), 0); 1531 kb_add(x_eot_del, NULL, CTRL('D'), 0); 1532 /* error */ 1533 kb_add(x_xchg_point_mark, NULL, CTRL('X'), CTRL('X'), 0); 1534 kb_add(x_expand, NULL, CTRL('['), '*', 0); 1535 kb_add(x_mv_forw, NULL, CTRL('F'), 0); 1536 kb_add(x_mv_forw, NULL, CTRL('X'), 'C', 0); 1537 kb_add(x_mv_fword, NULL, CTRL('['), 'f', 0); 1538 kb_add(x_goto_hist, NULL, CTRL('['), 'g', 0); 1539 /* kill-line */ 1540 kb_add(x_del_bword, NULL, CTRL('W'), 0); /* not what man says */ 1541 kb_add(x_kill, NULL, CTRL('K'), 0); 1542 kb_add(x_enumerate, NULL, CTRL('['), '?', 0); 1543 kb_add(x_list_comm, NULL, CTRL('X'), '?', 0); 1544 kb_add(x_list_file, NULL, CTRL('X'), CTRL('Y'), 0); 1545 kb_add(x_newline, NULL, CTRL('J'), 0); 1546 kb_add(x_newline, NULL, CTRL('M'), 0); 1547 kb_add(x_nl_next_com, NULL, CTRL('O'), 0); 1548 /* no-op */ 1549 kb_add(x_prev_histword, NULL, CTRL('['), '.', 0); 1550 kb_add(x_prev_histword, NULL, CTRL('['), '_', 0); 1551 /* how to handle: quote: ^^ */ 1552 kb_add(x_draw_line, NULL, CTRL('L'), 0); 1553 kb_add(x_search_char_back, NULL, CTRL('['), CTRL(']'), 0); 1554 kb_add(x_search_char_forw, NULL, CTRL(']'), 0); 1555 kb_add(x_search_hist, NULL, CTRL('R'), 0); 1556 kb_add(x_set_mark, NULL, CTRL('['), ' ', 0); 1557 #if defined(TIOCSTI) 1558 kb_add(x_stuff, NULL, CTRL('T'), 0); 1559 /* stuff-reset */ 1560 #else 1561 kb_add(x_transpose, NULL, CTRL('T'), 0); 1562 #endif 1563 kb_add(x_prev_com, NULL, CTRL('P'), 0); 1564 kb_add(x_prev_com, NULL, CTRL('X'), 'A', 0); 1565 kb_add(x_fold_upper, NULL, CTRL('['), 'U', 0); 1566 kb_add(x_fold_upper, NULL, CTRL('['), 'u', 0); 1567 kb_add(x_literal, NULL, CTRL('V'), 0); 1568 kb_add(x_literal, NULL, CTRL('^'), 0); 1569 kb_add(x_yank, NULL, CTRL('Y'), 0); 1570 kb_add(x_meta_yank, NULL, CTRL('['), 'y', 0); 1571 /* man page ends here */ 1572 1573 /* arrow keys */ 1574 kb_add(x_prev_com, NULL, CTRL('['), '[', 'A', 0); /* up */ 1575 kb_add(x_next_com, NULL, CTRL('['), '[', 'B', 0); /* down */ 1576 kb_add(x_mv_forw, NULL, CTRL('['), '[', 'C', 0); /* right */ 1577 kb_add(x_mv_back, NULL, CTRL('['), '[', 'D', 0); /* left */ 1578 kb_add(x_prev_com, NULL, CTRL('['), 'O', 'A', 0); /* up */ 1579 kb_add(x_next_com, NULL, CTRL('['), 'O', 'B', 0); /* down */ 1580 kb_add(x_mv_forw, NULL, CTRL('['), 'O', 'C', 0); /* right */ 1581 kb_add(x_mv_back, NULL, CTRL('['), 'O', 'D', 0); /* left */ 1582 1583 /* more navigation keys */ 1584 kb_add(x_mv_begin, NULL, CTRL('['), '[', 'H', 0); /* home */ 1585 kb_add(x_mv_end, NULL, CTRL('['), '[', 'F', 0); /* end */ 1586 kb_add(x_mv_begin, NULL, CTRL('['), 'O', 'H', 0); /* home */ 1587 kb_add(x_mv_end, NULL, CTRL('['), 'O', 'F', 0); /* end */ 1588 kb_add(x_mv_begin, NULL, CTRL('['), '[', '1', '~', 0); /* home */ 1589 kb_add(x_mv_end, NULL, CTRL('['), '[', '4', '~', 0); /* end */ 1590 1591 /* can't be bound */ 1592 kb_add(x_set_arg, NULL, CTRL('['), '0', 0); 1593 kb_add(x_set_arg, NULL, CTRL('['), '1', 0); 1594 kb_add(x_set_arg, NULL, CTRL('['), '2', 0); 1595 kb_add(x_set_arg, NULL, CTRL('['), '3', 0); 1596 kb_add(x_set_arg, NULL, CTRL('['), '4', 0); 1597 kb_add(x_set_arg, NULL, CTRL('['), '5', 0); 1598 kb_add(x_set_arg, NULL, CTRL('['), '6', 0); 1599 kb_add(x_set_arg, NULL, CTRL('['), '7', 0); 1600 kb_add(x_set_arg, NULL, CTRL('['), '8', 0); 1601 kb_add(x_set_arg, NULL, CTRL('['), '9', 0); 1602 1603 /* ctrl arrow keys */ 1604 kb_add(x_mv_end, NULL, CTRL('['), '[', '1', ';', '5', 'A', 0); /* ctrl up */ 1605 kb_add(x_mv_begin, NULL, CTRL('['), '[', '1', ';', '5', 'B', 0); /* ctrl down */ 1606 kb_add(x_mv_fword, NULL, CTRL('['), '[', '1', ';', '5', 'C', 0); /* ctrl right */ 1607 kb_add(x_mv_bword, NULL, CTRL('['), '[', '1', ';', '5', 'D', 0); /* ctrl left */ 1608 } 1609 1610 void 1611 x_emacs_keys(X_chars *ec) 1612 { 1613 x_bind_quiet = 1; 1614 if (ec->erase >= 0) { 1615 kb_add(x_del_back, NULL, ec->erase, 0); 1616 kb_add(x_del_bword, NULL, CTRL('['), ec->erase, 0); 1617 } 1618 if (ec->kill >= 0) 1619 kb_add(x_del_line, NULL, ec->kill, 0); 1620 if (ec->werase >= 0) 1621 kb_add(x_del_bword, NULL, ec->werase, 0); 1622 if (ec->intr >= 0) 1623 kb_add(x_abort, NULL, ec->intr, 0); 1624 if (ec->quit >= 0) 1625 kb_add(x_noop, NULL, ec->quit, 0); 1626 x_bind_quiet = 0; 1627 } 1628 1629 static int 1630 x_set_mark(int c) 1631 { 1632 xmp = xcp; 1633 return KSTD; 1634 } 1635 1636 static int 1637 x_kill_region(int c) 1638 { 1639 int rsize; 1640 char *xr; 1641 1642 if (xmp == NULL) { 1643 x_e_putc(BEL); 1644 return KSTD; 1645 } 1646 if (xmp > xcp) { 1647 rsize = xmp - xcp; 1648 xr = xcp; 1649 } else { 1650 rsize = xcp - xmp; 1651 xr = xmp; 1652 } 1653 x_goto(xr); 1654 x_delete(rsize, true); 1655 xmp = xr; 1656 return KSTD; 1657 } 1658 1659 static int 1660 x_xchg_point_mark(int c) 1661 { 1662 char *tmp; 1663 1664 if (xmp == NULL) { 1665 x_e_putc(BEL); 1666 return KSTD; 1667 } 1668 tmp = xmp; 1669 xmp = xcp; 1670 x_goto( tmp ); 1671 return KSTD; 1672 } 1673 1674 static int 1675 x_version(int c) 1676 { 1677 char *o_xbuf = xbuf, *o_xend = xend; 1678 char *o_xbp = xbp, *o_xep = xep, *o_xcp = xcp; 1679 int lim = x_lastcp() - xbp; 1680 1681 xbuf = xbp = xcp = (char *) ksh_version + 4; 1682 xend = xep = (char *) ksh_version + 4 + strlen(ksh_version + 4); 1683 x_redraw(lim); 1684 x_flush(); 1685 1686 c = x_e_getc(); 1687 xbuf = o_xbuf; 1688 xend = o_xend; 1689 xbp = o_xbp; 1690 xep = o_xep; 1691 xcp = o_xcp; 1692 x_redraw(strlen(ksh_version)); 1693 1694 if (c < 0) 1695 return KSTD; 1696 /* This is what at&t ksh seems to do... Very bizarre */ 1697 if (c != ' ') 1698 x_e_ungetc(c); 1699 1700 return KSTD; 1701 } 1702 1703 static int 1704 x_noop(int c) 1705 { 1706 return KSTD; 1707 } 1708 1709 /* 1710 * File/command name completion routines 1711 */ 1712 1713 static int 1714 x_comp_comm(int c) 1715 { 1716 do_complete(XCF_COMMAND, CT_COMPLETE); 1717 return KSTD; 1718 } 1719 static int 1720 x_list_comm(int c) 1721 { 1722 do_complete(XCF_COMMAND, CT_LIST); 1723 return KSTD; 1724 } 1725 static int 1726 x_complete(int c) 1727 { 1728 do_complete(XCF_COMMAND_FILE, CT_COMPLETE); 1729 return KSTD; 1730 } 1731 static int 1732 x_enumerate(int c) 1733 { 1734 do_complete(XCF_COMMAND_FILE, CT_LIST); 1735 return KSTD; 1736 } 1737 static int 1738 x_comp_file(int c) 1739 { 1740 do_complete(XCF_FILE, CT_COMPLETE); 1741 return KSTD; 1742 } 1743 static int 1744 x_list_file(int c) 1745 { 1746 do_complete(XCF_FILE, CT_LIST); 1747 return KSTD; 1748 } 1749 static int 1750 x_comp_list(int c) 1751 { 1752 do_complete(XCF_COMMAND_FILE, CT_COMPLIST); 1753 return KSTD; 1754 } 1755 static int 1756 x_expand(int c) 1757 { 1758 char **words; 1759 int nwords = 0; 1760 int start, end; 1761 int is_command; 1762 int i; 1763 1764 nwords = x_cf_glob(XCF_FILE, xbuf, xep - xbuf, xcp - xbuf, 1765 &start, &end, &words, &is_command); 1766 1767 if (nwords == 0) { 1768 x_e_putc(BEL); 1769 return KSTD; 1770 } 1771 1772 x_goto(xbuf + start); 1773 x_delete(end - start, false); 1774 for (i = 0; i < nwords;) { 1775 if (x_escape(words[i], strlen(words[i]), x_do_ins) < 0 || 1776 (++i < nwords && x_ins(" ") < 0)) { 1777 x_e_putc(BEL); 1778 return KSTD; 1779 } 1780 } 1781 x_adjust(); 1782 1783 return KSTD; 1784 } 1785 1786 /* type == 0 for list, 1 for complete and 2 for complete-list */ 1787 static void 1788 do_complete(int flags, /* XCF_{COMMAND,FILE,COMMAND_FILE} */ 1789 Comp_type type) 1790 { 1791 char **words; 1792 int nwords; 1793 int start, end, nlen, olen; 1794 int is_command; 1795 int completed = 0; 1796 1797 nwords = x_cf_glob(flags, xbuf, xep - xbuf, xcp - xbuf, 1798 &start, &end, &words, &is_command); 1799 /* no match */ 1800 if (nwords == 0) { 1801 x_e_putc(BEL); 1802 return; 1803 } 1804 1805 if (type == CT_LIST) { 1806 x_print_expansions(nwords, words, is_command); 1807 x_redraw(0); 1808 x_free_words(nwords, words); 1809 return; 1810 } 1811 1812 olen = end - start; 1813 nlen = x_longest_prefix(nwords, words); 1814 /* complete */ 1815 if (nwords == 1 || nlen > olen) { 1816 x_goto(xbuf + start); 1817 x_delete(olen, false); 1818 x_escape(words[0], nlen, x_do_ins); 1819 x_adjust(); 1820 completed = 1; 1821 } 1822 /* add space if single non-dir match */ 1823 if (nwords == 1 && words[0][nlen - 1] != '/') { 1824 x_ins(" "); 1825 completed = 1; 1826 } 1827 1828 if (type == CT_COMPLIST && !completed) { 1829 x_print_expansions(nwords, words, is_command); 1830 completed = 1; 1831 } 1832 1833 if (completed) 1834 x_redraw(0); 1835 1836 x_free_words(nwords, words); 1837 } 1838 1839 /* NAME: 1840 * x_adjust - redraw the line adjusting starting point etc. 1841 * 1842 * DESCRIPTION: 1843 * This function is called when we have exceeded the bounds 1844 * of the edit window. It increments x_adj_done so that 1845 * functions like x_ins and x_delete know that we have been 1846 * called and can skip the x_bs() stuff which has already 1847 * been done by x_redraw. 1848 * 1849 * RETURN VALUE: 1850 * None 1851 */ 1852 1853 static void 1854 x_adjust(void) 1855 { 1856 x_adj_done++; /* flag the fact that we were called. */ 1857 /* 1858 * we had a problem if the prompt length > xx_cols / 2 1859 */ 1860 if ((xbp = xcp - (x_displen / 2)) < xbuf) 1861 xbp = xbuf; 1862 xlp_valid = false; 1863 x_redraw(xx_cols); 1864 x_flush(); 1865 } 1866 1867 static int unget_char = -1; 1868 1869 static void 1870 x_e_ungetc(int c) 1871 { 1872 unget_char = c; 1873 } 1874 1875 static int 1876 x_e_getc(void) 1877 { 1878 int c; 1879 1880 if (unget_char >= 0) { 1881 c = unget_char; 1882 unget_char = -1; 1883 } else if (macro_args) { 1884 c = *macro_args++; 1885 if (!c) { 1886 macro_args = NULL; 1887 c = x_getc(); 1888 } 1889 } else 1890 c = x_getc(); 1891 1892 return c; 1893 } 1894 1895 static void 1896 x_e_putc(int c) 1897 { 1898 if (c == '\r' || c == '\n') 1899 x_col = 0; 1900 if (x_col < xx_cols) { 1901 x_putc(c); 1902 switch (c) { 1903 case BEL: 1904 break; 1905 case '\r': 1906 case '\n': 1907 break; 1908 case '\b': 1909 x_col--; 1910 break; 1911 default: 1912 x_col++; 1913 break; 1914 } 1915 } 1916 if (x_adj_ok && (x_col < 0 || x_col >= (xx_cols - 2))) 1917 x_adjust(); 1918 } 1919 1920 #ifdef DEBUG 1921 static int 1922 x_debug_info(int c) 1923 { 1924 x_flush(); 1925 shellf("\nksh debug:\n"); 1926 shellf("\tx_col == %d,\t\tx_cols == %d,\tx_displen == %d\n", 1927 x_col, xx_cols, x_displen); 1928 shellf("\txcp == 0x%lx,\txep == 0x%lx\n", (long) xcp, (long) xep); 1929 shellf("\txbp == 0x%lx,\txbuf == 0x%lx\n", (long) xbp, (long) xbuf); 1930 shellf("\txlp == 0x%lx\n", (long) xlp); 1931 shellf("\txlp == 0x%lx\n", (long) x_lastcp()); 1932 shellf("\n"); 1933 x_redraw(-1); 1934 return 0; 1935 } 1936 #endif 1937 1938 static void 1939 x_e_puts(const char *s) 1940 { 1941 int adj = x_adj_done; 1942 1943 while (*s && adj == x_adj_done) 1944 x_e_putc(*s++); 1945 } 1946 1947 /* NAME: 1948 * x_set_arg - set an arg value for next function 1949 * 1950 * DESCRIPTION: 1951 * This is a simple implementation of M-[0-9]. 1952 * 1953 * RETURN VALUE: 1954 * KSTD 1955 */ 1956 1957 static int 1958 x_set_arg(int c) 1959 { 1960 int n = 0; 1961 int first = 1; 1962 1963 for (; c >= 0 && isdigit(c); c = x_e_getc(), first = 0) 1964 n = n * 10 + (c - '0'); 1965 if (c < 0 || first) { 1966 x_e_putc(BEL); 1967 x_arg = 1; 1968 x_arg_defaulted = 1; 1969 } else { 1970 x_e_ungetc(c); 1971 x_arg = n; 1972 x_arg_defaulted = 0; 1973 x_arg_set = 1; 1974 } 1975 return KSTD; 1976 } 1977 1978 1979 /* Comment or uncomment the current line. */ 1980 static int 1981 x_comment(int c) 1982 { 1983 int oldsize = x_size_str(xbuf); 1984 int len = xep - xbuf; 1985 int ret = x_do_comment(xbuf, xend - xbuf, &len); 1986 1987 if (ret < 0) 1988 x_e_putc(BEL); 1989 else { 1990 xep = xbuf + len; 1991 *xep = '\0'; 1992 xcp = xbp = xbuf; 1993 x_redraw(oldsize); 1994 if (ret > 0) 1995 return x_newline('\n'); 1996 } 1997 return KSTD; 1998 } 1999 2000 2001 /* NAME: 2002 * x_prev_histword - recover word from prev command 2003 * 2004 * DESCRIPTION: 2005 * This function recovers the last word from the previous 2006 * command and inserts it into the current edit line. If a 2007 * numeric arg is supplied then the n'th word from the 2008 * start of the previous command is used. 2009 * 2010 * Bound to M-. 2011 * 2012 * RETURN VALUE: 2013 * KSTD 2014 */ 2015 2016 static int 2017 x_prev_histword(int c) 2018 { 2019 char *rcp; 2020 char *cp; 2021 2022 cp = *histptr; 2023 if (!cp) 2024 x_e_putc(BEL); 2025 else if (x_arg_defaulted) { 2026 rcp = &cp[strlen(cp) - 1]; 2027 /* 2028 * ignore white-space after the last word 2029 */ 2030 while (rcp > cp && is_cfs(*rcp)) 2031 rcp--; 2032 while (rcp > cp && !is_cfs(*rcp)) 2033 rcp--; 2034 if (is_cfs(*rcp)) 2035 rcp++; 2036 x_ins(rcp); 2037 } else { 2038 int c; 2039 2040 rcp = cp; 2041 /* 2042 * ignore white-space at start of line 2043 */ 2044 while (*rcp && is_cfs(*rcp)) 2045 rcp++; 2046 while (x_arg-- > 1) { 2047 while (*rcp && !is_cfs(*rcp)) 2048 rcp++; 2049 while (*rcp && is_cfs(*rcp)) 2050 rcp++; 2051 } 2052 cp = rcp; 2053 while (*rcp && !is_cfs(*rcp)) 2054 rcp++; 2055 c = *rcp; 2056 *rcp = '\0'; 2057 x_ins(cp); 2058 *rcp = c; 2059 } 2060 return KSTD; 2061 } 2062 2063 /* Uppercase N(1) words */ 2064 static int 2065 x_fold_upper(int c) 2066 { 2067 return x_fold_case('U'); 2068 } 2069 2070 /* Lowercase N(1) words */ 2071 static int 2072 x_fold_lower(int c) 2073 { 2074 return x_fold_case('L'); 2075 } 2076 2077 /* Lowercase N(1) words */ 2078 static int 2079 x_fold_capitalize(int c) 2080 { 2081 return x_fold_case('C'); 2082 } 2083 2084 /* NAME: 2085 * x_fold_case - convert word to UPPER/lower/Capital case 2086 * 2087 * DESCRIPTION: 2088 * This function is used to implement M-U,M-u,M-L,M-l,M-C and M-c 2089 * to UPPER case, lower case or Capitalize words. 2090 * 2091 * RETURN VALUE: 2092 * None 2093 */ 2094 2095 static int 2096 x_fold_case(int c) 2097 { 2098 char *cp = xcp; 2099 2100 if (cp == xep) { 2101 x_e_putc(BEL); 2102 return KSTD; 2103 } 2104 while (x_arg--) { 2105 /* 2106 * first skip over any white-space 2107 */ 2108 while (cp != xep && is_mfs(*cp)) 2109 cp++; 2110 /* 2111 * do the first char on its own since it may be 2112 * a different action than for the rest. 2113 */ 2114 if (cp != xep) { 2115 if (c == 'L') { /* lowercase */ 2116 if (isupper((unsigned char)*cp)) 2117 *cp = tolower((unsigned char)*cp); 2118 } else { /* uppercase, capitalize */ 2119 if (islower((unsigned char)*cp)) 2120 *cp = toupper((unsigned char)*cp); 2121 } 2122 cp++; 2123 } 2124 /* 2125 * now for the rest of the word 2126 */ 2127 while (cp != xep && !is_mfs(*cp)) { 2128 if (c == 'U') { /* uppercase */ 2129 if (islower((unsigned char)*cp)) 2130 *cp = toupper((unsigned char)*cp); 2131 } else { /* lowercase, capitalize */ 2132 if (isupper((unsigned char)*cp)) 2133 *cp = tolower((unsigned char)*cp); 2134 } 2135 cp++; 2136 } 2137 } 2138 x_goto(cp); 2139 return KSTD; 2140 } 2141 2142 /* NAME: 2143 * x_lastcp - last visible byte 2144 * 2145 * SYNOPSIS: 2146 * x_lastcp() 2147 * 2148 * DESCRIPTION: 2149 * This function returns a pointer to that byte in the 2150 * edit buffer that will be the last displayed on the 2151 * screen. The sequence: 2152 * 2153 * for (cp = x_lastcp(); cp > xcp; cp) 2154 * x_bs(*--cp); 2155 * 2156 * Will position the cursor correctly on the screen. 2157 * 2158 * RETURN VALUE: 2159 * cp or NULL 2160 */ 2161 2162 static char * 2163 x_lastcp(void) 2164 { 2165 char *rcp; 2166 int i; 2167 2168 if (!xlp_valid) { 2169 for (i = 0, rcp = xbp; rcp < xep && i < x_displen; rcp++) 2170 i += x_size((unsigned char)*rcp); 2171 xlp = rcp; 2172 } 2173 xlp_valid = true; 2174 return (xlp); 2175 } 2176 2177 #endif /* EDIT */ 2178