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