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