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