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