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