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