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