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