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