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