Return to rlfe.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / readline / examples / rlfe |
1.1 ! misho 1: /* A front-end using readline to "cook" input lines. ! 2: * ! 3: * Copyright (C) 2004, 1999 Per Bothner ! 4: * ! 5: * This front-end program is free software; you can redistribute it and/or ! 6: * modify it under the terms of the GNU General Public License as published ! 7: * by the Free Software Foundation; either version 2, or (at your option) ! 8: * any later version. ! 9: * ! 10: * Some code from Johnson & Troan: "Linux Application Development" ! 11: * (Addison-Wesley, 1998) was used directly or for inspiration. ! 12: * ! 13: * 2003-11-07 Wolfgang Taeuber <wolfgang_taeuber@agilent.com> ! 14: * Specify a history file and the size of the history file with command ! 15: * line options; use EDITOR/VISUAL to set vi/emacs preference. ! 16: */ ! 17: ! 18: /* PROBLEMS/TODO: ! 19: * ! 20: * Only tested under GNU/Linux and Mac OS 10.x; needs to be ported. ! 21: * ! 22: * Switching between line-editing-mode vs raw-char-mode depending on ! 23: * what tcgetattr returns is inherently not robust, plus it doesn't ! 24: * work when ssh/telnetting in. A better solution is possible if the ! 25: * tty system can send in-line escape sequences indicating the current ! 26: * mode, echo'd input, etc. That would also allow a user preference ! 27: * to set different colors for prompt, input, stdout, and stderr. ! 28: * ! 29: * When running mc -c under the Linux console, mc does not recognize ! 30: * mouse clicks, which mc does when not running under rlfe. ! 31: * ! 32: * Pasting selected text containing tabs is like hitting the tab character, ! 33: * which invokes readline completion. We don't want this. I don't know ! 34: * if this is fixable without integrating rlfe into a terminal emulator. ! 35: * ! 36: * Echo suppression is a kludge, but can only be avoided with better kernel ! 37: * support: We need a tty mode to disable "real" echoing, while still ! 38: * letting the inferior think its tty driver to doing echoing. ! 39: * Stevens's book claims SCR$ and BSD4.3+ have TIOCREMOTE. ! 40: * ! 41: * The latest readline may have some hooks we can use to avoid having ! 42: * to back up the prompt. (See HAVE_ALREADY_PROMPTED.) ! 43: * ! 44: * Desirable readline feature: When in cooked no-echo mode (e.g. password), ! 45: * echo characters are they are types with '*', but remove them when done. ! 46: * ! 47: * Asynchronous output while we're editing an input line should be ! 48: * inserted in the output view *before* the input line, so that the ! 49: * lines being edited (with the prompt) float at the end of the input. ! 50: * ! 51: * A "page mode" option to emulate more/less behavior: At each page of ! 52: * output, pause for a user command. This required parsing the output ! 53: * to keep track of line lengths. It also requires remembering the ! 54: * output, if we want an option to scroll back, which suggests that ! 55: * this should be integrated with a terminal emulator like xterm. ! 56: */ ! 57: ! 58: #include <stdio.h> ! 59: #include <fcntl.h> ! 60: #include <sys/types.h> ! 61: #include <sys/socket.h> ! 62: #include <netinet/in.h> ! 63: #include <arpa/inet.h> ! 64: #include <signal.h> ! 65: #include <netdb.h> ! 66: #include <stdlib.h> ! 67: #include <errno.h> ! 68: #include <grp.h> ! 69: #include <string.h> ! 70: #include <sys/stat.h> ! 71: #include <unistd.h> ! 72: #include <sys/ioctl.h> ! 73: #include <termios.h> ! 74: ! 75: #include "config.h" ! 76: #include "extern.h" ! 77: ! 78: #if defined (HAVE_SYS_WAIT_H) ! 79: # include <sys/wait.h> ! 80: #endif ! 81: ! 82: #ifdef READLINE_LIBRARY ! 83: # include "readline.h" ! 84: # include "history.h" ! 85: #else ! 86: # include <readline/readline.h> ! 87: # include <readline/history.h> ! 88: #endif ! 89: ! 90: #ifndef COMMAND ! 91: #define COMMAND "/bin/bash" ! 92: #endif ! 93: #ifndef COMMAND_ARGS ! 94: #define COMMAND_ARGS COMMAND ! 95: #endif ! 96: ! 97: #ifndef ALT_COMMAND ! 98: #define ALT_COMMAND "/bin/sh" ! 99: #endif ! 100: #ifndef ALT_COMMAND_ARGS ! 101: #define ALT_COMMAND_ARGS ALT_COMMAND ! 102: #endif ! 103: ! 104: #ifndef HAVE_MEMMOVE ! 105: # if __GNUC__ > 1 ! 106: # define memmove(d, s, n) __builtin_memcpy(d, s, n) ! 107: # else ! 108: # define memmove(d, s, n) memcpy(d, s, n) ! 109: # endif ! 110: #else ! 111: # define memmove(d, s, n) memcpy(d, s, n) ! 112: #endif ! 113: ! 114: #define APPLICATION_NAME "rlfe" ! 115: ! 116: static int in_from_inferior_fd; ! 117: static int out_to_inferior_fd; ! 118: static void set_edit_mode (); ! 119: static void usage_exit (); ! 120: static char *hist_file = 0; ! 121: static int hist_size = 0; ! 122: ! 123: /* Unfortunately, we cannot safely display echo from the inferior process. ! 124: The reason is that the echo bit in the pty is "owned" by the inferior, ! 125: and if we try to turn it off, we could confuse the inferior. ! 126: Thus, when echoing, we get echo twice: First readline echoes while ! 127: we're actually editing. Then we send the line to the inferior, and the ! 128: terminal driver send back an extra echo. ! 129: The work-around is to remember the input lines, and when we see that ! 130: line come back, we supress the output. ! 131: A better solution (supposedly available on SVR4) would be a smarter ! 132: terminal driver, with more flags ... */ ! 133: #define ECHO_SUPPRESS_MAX 1024 ! 134: char echo_suppress_buffer[ECHO_SUPPRESS_MAX]; ! 135: int echo_suppress_start = 0; ! 136: int echo_suppress_limit = 0; ! 137: ! 138: /*#define DEBUG*/ ! 139: ! 140: #ifdef DEBUG ! 141: FILE *logfile = NULL; ! 142: #define DPRINT0(FMT) (fprintf(logfile, FMT), fflush(logfile)) ! 143: #define DPRINT1(FMT, V1) (fprintf(logfile, FMT, V1), fflush(logfile)) ! 144: #define DPRINT2(FMT, V1, V2) (fprintf(logfile, FMT, V1, V2), fflush(logfile)) ! 145: #else ! 146: #define DPRINT0(FMT) ((void) 0) /* Do nothing */ ! 147: #define DPRINT1(FMT, V1) ((void) 0) /* Do nothing */ ! 148: #define DPRINT2(FMT, V1, V2) ((void) 0) /* Do nothing */ ! 149: #endif ! 150: ! 151: struct termios orig_term; ! 152: ! 153: /* Pid of child process. */ ! 154: static pid_t child = -1; ! 155: ! 156: static void ! 157: sig_child (int signo) ! 158: { ! 159: int status; ! 160: wait (&status); ! 161: if (hist_file != 0) ! 162: { ! 163: write_history (hist_file); ! 164: if (hist_size) ! 165: history_truncate_file (hist_file, hist_size); ! 166: } ! 167: DPRINT0 ("(Child process died.)\n"); ! 168: tcsetattr(STDIN_FILENO, TCSANOW, &orig_term); ! 169: exit (0); ! 170: } ! 171: ! 172: volatile int propagate_sigwinch = 0; ! 173: ! 174: /* sigwinch_handler ! 175: * propagate window size changes from input file descriptor to ! 176: * master side of pty. ! 177: */ ! 178: void sigwinch_handler(int signal) { ! 179: propagate_sigwinch = 1; ! 180: } ! 181: ! 182: ! 183: /* get_slave_pty() returns an integer file descriptor. ! 184: * If it returns < 0, an error has occurred. ! 185: * Otherwise, it has returned the slave file descriptor. ! 186: */ ! 187: ! 188: int get_slave_pty(char *name) { ! 189: struct group *gptr; ! 190: gid_t gid; ! 191: int slave = -1; ! 192: ! 193: /* chown/chmod the corresponding pty, if possible. ! 194: * This will only work if the process has root permissions. ! 195: * Alternatively, write and exec a small setuid program that ! 196: * does just this. ! 197: */ ! 198: if ((gptr = getgrnam("tty")) != 0) { ! 199: gid = gptr->gr_gid; ! 200: } else { ! 201: /* if the tty group does not exist, don't change the ! 202: * group on the slave pty, only the owner ! 203: */ ! 204: gid = -1; ! 205: } ! 206: ! 207: /* Note that we do not check for errors here. If this is code ! 208: * where these actions are critical, check for errors! ! 209: */ ! 210: chown(name, getuid(), gid); ! 211: /* This code only makes the slave read/writeable for the user. ! 212: * If this is for an interactive shell that will want to ! 213: * receive "write" and "wall" messages, OR S_IWGRP into the ! 214: * second argument below. ! 215: */ ! 216: chmod(name, S_IRUSR|S_IWUSR); ! 217: ! 218: /* open the corresponding slave pty */ ! 219: slave = open(name, O_RDWR); ! 220: return (slave); ! 221: } ! 222: ! 223: /* Certain special characters, such as ctrl/C, we want to pass directly ! 224: to the inferior, rather than letting readline handle them. */ ! 225: ! 226: static char special_chars[20]; ! 227: static int special_chars_count; ! 228: ! 229: static void ! 230: add_special_char(int ch) ! 231: { ! 232: if (ch != 0) ! 233: special_chars[special_chars_count++] = ch; ! 234: } ! 235: ! 236: static int eof_char; ! 237: ! 238: static int ! 239: is_special_char(int ch) ! 240: { ! 241: int i; ! 242: #if 0 ! 243: if (ch == eof_char && rl_point == rl_end) ! 244: return 1; ! 245: #endif ! 246: for (i = special_chars_count; --i >= 0; ) ! 247: if (special_chars[i] == ch) ! 248: return 1; ! 249: return 0; ! 250: } ! 251: ! 252: static char buf[1024]; ! 253: /* buf[0 .. buf_count-1] is the what has been emitted on the current line. ! 254: It is used as the readline prompt. */ ! 255: static int buf_count = 0; ! 256: ! 257: int do_emphasize_input = 1; ! 258: int current_emphasize_input; ! 259: ! 260: char *start_input_mode = "\033[1m"; ! 261: char *end_input_mode = "\033[0m"; ! 262: ! 263: int num_keys = 0; ! 264: ! 265: static void maybe_emphasize_input (int on) ! 266: { ! 267: if (on == current_emphasize_input ! 268: || (on && ! do_emphasize_input)) ! 269: return; ! 270: fprintf (rl_outstream, on ? start_input_mode : end_input_mode); ! 271: fflush (rl_outstream); ! 272: current_emphasize_input = on; ! 273: } ! 274: ! 275: static void ! 276: null_prep_terminal (int meta) ! 277: { ! 278: } ! 279: ! 280: static void ! 281: null_deprep_terminal () ! 282: { ! 283: maybe_emphasize_input (0); ! 284: } ! 285: ! 286: static int ! 287: pre_input_change_mode (void) ! 288: { ! 289: return 0; ! 290: } ! 291: ! 292: char pending_special_char; ! 293: ! 294: static void ! 295: line_handler (char *line) ! 296: { ! 297: if (line == NULL) ! 298: { ! 299: char buf[1]; ! 300: DPRINT0("saw eof!\n"); ! 301: buf[0] = '\004'; /* ctrl/d */ ! 302: write (out_to_inferior_fd, buf, 1); ! 303: } ! 304: else ! 305: { ! 306: static char enter[] = "\r"; ! 307: /* Send line to inferior: */ ! 308: int length = strlen (line); ! 309: if (length > ECHO_SUPPRESS_MAX-2) ! 310: { ! 311: echo_suppress_start = 0; ! 312: echo_suppress_limit = 0; ! 313: } ! 314: else ! 315: { ! 316: if (echo_suppress_limit + length > ECHO_SUPPRESS_MAX - 2) ! 317: { ! 318: if (echo_suppress_limit - echo_suppress_start + length ! 319: <= ECHO_SUPPRESS_MAX - 2) ! 320: { ! 321: memmove (echo_suppress_buffer, ! 322: echo_suppress_buffer + echo_suppress_start, ! 323: echo_suppress_limit - echo_suppress_start); ! 324: echo_suppress_limit -= echo_suppress_start; ! 325: echo_suppress_start = 0; ! 326: } ! 327: else ! 328: { ! 329: echo_suppress_limit = 0; ! 330: } ! 331: echo_suppress_start = 0; ! 332: } ! 333: memcpy (echo_suppress_buffer + echo_suppress_limit, ! 334: line, length); ! 335: echo_suppress_limit += length; ! 336: echo_suppress_buffer[echo_suppress_limit++] = '\r'; ! 337: echo_suppress_buffer[echo_suppress_limit++] = '\n'; ! 338: } ! 339: write (out_to_inferior_fd, line, length); ! 340: if (pending_special_char == 0) ! 341: { ! 342: write (out_to_inferior_fd, enter, sizeof(enter)-1); ! 343: if (*line) ! 344: add_history (line); ! 345: } ! 346: free (line); ! 347: } ! 348: rl_callback_handler_remove (); ! 349: buf_count = 0; ! 350: num_keys = 0; ! 351: if (pending_special_char != 0) ! 352: { ! 353: write (out_to_inferior_fd, &pending_special_char, 1); ! 354: pending_special_char = 0; ! 355: } ! 356: } ! 357: ! 358: /* Value of rl_getc_function. ! 359: Use this because readline should read from stdin, not rl_instream, ! 360: points to the pty (so readline has monitor its terminal modes). */ ! 361: ! 362: int ! 363: my_rl_getc (FILE *dummy) ! 364: { ! 365: int ch = rl_getc (stdin); ! 366: if (is_special_char (ch)) ! 367: { ! 368: pending_special_char = ch; ! 369: return '\r'; ! 370: } ! 371: return ch; ! 372: } ! 373: ! 374: int ! 375: main(int argc, char** argv) ! 376: { ! 377: char *path; ! 378: int i; ! 379: int master; ! 380: char *name; ! 381: int in_from_tty_fd; ! 382: struct sigaction act; ! 383: struct winsize ws; ! 384: struct termios t; ! 385: int maxfd; ! 386: fd_set in_set; ! 387: static char empty_string[1] = ""; ! 388: char *prompt = empty_string; ! 389: int ioctl_err = 0; ! 390: int arg_base = 1; ! 391: ! 392: #ifdef DEBUG ! 393: logfile = fopen("/tmp/rlfe.log", "w"); ! 394: #endif ! 395: ! 396: while (arg_base<argc) ! 397: { ! 398: if (argv[arg_base][0] != '-') ! 399: break; ! 400: if (arg_base+1 >= argc ) ! 401: usage_exit(); ! 402: switch(argv[arg_base][1]) ! 403: { ! 404: case 'h': ! 405: arg_base++; ! 406: hist_file = argv[arg_base]; ! 407: break; ! 408: case 's': ! 409: arg_base++; ! 410: hist_size = atoi(argv[arg_base]); ! 411: if (hist_size<0) ! 412: usage_exit(); ! 413: break; ! 414: default: ! 415: usage_exit(); ! 416: } ! 417: arg_base++; ! 418: } ! 419: if (hist_file) ! 420: read_history (hist_file); ! 421: ! 422: set_edit_mode (); ! 423: ! 424: rl_readline_name = APPLICATION_NAME; ! 425: ! 426: if ((master = OpenPTY (&name)) < 0) ! 427: { ! 428: perror("ptypair: could not open master pty"); ! 429: exit(1); ! 430: } ! 431: ! 432: DPRINT1("pty name: '%s'\n", name); ! 433: ! 434: /* set up SIGWINCH handler */ ! 435: act.sa_handler = sigwinch_handler; ! 436: sigemptyset(&(act.sa_mask)); ! 437: act.sa_flags = 0; ! 438: if (sigaction(SIGWINCH, &act, NULL) < 0) ! 439: { ! 440: perror("ptypair: could not handle SIGWINCH "); ! 441: exit(1); ! 442: } ! 443: ! 444: if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0) ! 445: { ! 446: perror("ptypair: could not get window size"); ! 447: exit(1); ! 448: } ! 449: ! 450: if ((child = fork()) < 0) ! 451: { ! 452: perror("cannot fork"); ! 453: exit(1); ! 454: } ! 455: ! 456: if (child == 0) ! 457: { ! 458: int slave; /* file descriptor for slave pty */ ! 459: ! 460: /* We are in the child process */ ! 461: close(master); ! 462: ! 463: #ifdef TIOCSCTTY ! 464: if ((slave = get_slave_pty(name)) < 0) ! 465: { ! 466: perror("ptypair: could not open slave pty"); ! 467: exit(1); ! 468: } ! 469: #endif ! 470: ! 471: /* We need to make this process a session group leader, because ! 472: * it is on a new PTY, and things like job control simply will ! 473: * not work correctly unless there is a session group leader ! 474: * and process group leader (which a session group leader ! 475: * automatically is). This also disassociates us from our old ! 476: * controlling tty. ! 477: */ ! 478: if (setsid() < 0) ! 479: { ! 480: perror("could not set session leader"); ! 481: } ! 482: ! 483: /* Tie us to our new controlling tty. */ ! 484: #ifdef TIOCSCTTY ! 485: if (ioctl(slave, TIOCSCTTY, NULL)) ! 486: { ! 487: perror("could not set new controlling tty"); ! 488: } ! 489: #else ! 490: if ((slave = get_slave_pty(name)) < 0) ! 491: { ! 492: perror("ptypair: could not open slave pty"); ! 493: exit(1); ! 494: } ! 495: #endif ! 496: ! 497: /* make slave pty be standard in, out, and error */ ! 498: dup2(slave, STDIN_FILENO); ! 499: dup2(slave, STDOUT_FILENO); ! 500: dup2(slave, STDERR_FILENO); ! 501: ! 502: /* at this point the slave pty should be standard input */ ! 503: if (slave > 2) ! 504: { ! 505: close(slave); ! 506: } ! 507: ! 508: /* Try to restore window size; failure isn't critical */ ! 509: if (ioctl(STDOUT_FILENO, TIOCSWINSZ, &ws) < 0) ! 510: { ! 511: perror("could not restore window size"); ! 512: } ! 513: ! 514: /* now start the shell */ ! 515: { ! 516: static char* command_args[] = { COMMAND_ARGS, NULL }; ! 517: static char* alt_command_args[] = { ALT_COMMAND_ARGS, NULL }; ! 518: if (argc <= 1) ! 519: { ! 520: execvp (COMMAND, command_args); ! 521: execvp (ALT_COMMAND, alt_command_args); ! 522: } ! 523: else ! 524: execvp (argv[arg_base], &argv[arg_base]); ! 525: } ! 526: ! 527: /* should never be reached */ ! 528: exit(1); ! 529: } ! 530: ! 531: /* parent */ ! 532: signal (SIGCHLD, sig_child); ! 533: ! 534: /* Note that we only set termios settings for standard input; ! 535: * the master side of a pty is NOT a tty. ! 536: */ ! 537: tcgetattr(STDIN_FILENO, &orig_term); ! 538: ! 539: t = orig_term; ! 540: eof_char = t.c_cc[VEOF]; ! 541: /* add_special_char(t.c_cc[VEOF]);*/ ! 542: add_special_char(t.c_cc[VINTR]); ! 543: add_special_char(t.c_cc[VQUIT]); ! 544: add_special_char(t.c_cc[VSUSP]); ! 545: #if defined (VDISCARD) ! 546: add_special_char(t.c_cc[VDISCARD]); ! 547: #endif ! 548: ! 549: t.c_lflag &= ~(ICANON | ISIG | ECHO | ECHOCTL | ECHOE | \ ! 550: ECHOK | ECHONL ! 551: #if defined (ECHOKE) ! 552: | ECHOKE ! 553: #endif ! 554: #if defined (ECHOPRT) ! 555: | ECHOPRT ! 556: #endif ! 557: ); ! 558: t.c_iflag &= ~ICRNL; ! 559: t.c_iflag |= IGNBRK; ! 560: t.c_cc[VMIN] = 1; ! 561: t.c_cc[VTIME] = 0; ! 562: tcsetattr(STDIN_FILENO, TCSANOW, &t); ! 563: in_from_inferior_fd = master; ! 564: out_to_inferior_fd = master; ! 565: rl_instream = fdopen (master, "r"); ! 566: rl_getc_function = my_rl_getc; ! 567: ! 568: rl_prep_term_function = null_prep_terminal; ! 569: rl_deprep_term_function = null_deprep_terminal; ! 570: rl_pre_input_hook = pre_input_change_mode; ! 571: rl_callback_handler_install (prompt, line_handler); ! 572: ! 573: in_from_tty_fd = STDIN_FILENO; ! 574: FD_ZERO (&in_set); ! 575: maxfd = in_from_inferior_fd > in_from_tty_fd ? in_from_inferior_fd ! 576: : in_from_tty_fd; ! 577: for (;;) ! 578: { ! 579: int num; ! 580: FD_SET (in_from_inferior_fd, &in_set); ! 581: FD_SET (in_from_tty_fd, &in_set); ! 582: ! 583: num = select(maxfd+1, &in_set, NULL, NULL, NULL); ! 584: ! 585: if (propagate_sigwinch) ! 586: { ! 587: struct winsize ws; ! 588: if (ioctl (STDIN_FILENO, TIOCGWINSZ, &ws) >= 0) ! 589: { ! 590: ioctl (master, TIOCSWINSZ, &ws); ! 591: } ! 592: propagate_sigwinch = 0; ! 593: continue; ! 594: } ! 595: ! 596: if (num <= 0) ! 597: { ! 598: perror ("select"); ! 599: exit (-1); ! 600: } ! 601: if (FD_ISSET (in_from_tty_fd, &in_set)) ! 602: { ! 603: extern int _rl_echoing_p; ! 604: struct termios term_master; ! 605: int do_canon = 1; ! 606: int do_icrnl = 1; ! 607: int ioctl_ret; ! 608: ! 609: DPRINT1("[tty avail num_keys:%d]\n", num_keys); ! 610: ! 611: /* If we can't get tty modes for the master side of the pty, we ! 612: can't handle non-canonical-mode programs. Always assume the ! 613: master is in canonical echo mode if we can't tell. */ ! 614: ioctl_ret = tcgetattr(master, &term_master); ! 615: ! 616: if (ioctl_ret >= 0) ! 617: { ! 618: do_canon = (term_master.c_lflag & ICANON) != 0; ! 619: do_icrnl = (term_master.c_lflag & ICRNL) != 0; ! 620: _rl_echoing_p = (term_master.c_lflag & ECHO) != 0; ! 621: DPRINT1 ("echo,canon,crnl:%03d\n", ! 622: 100 * _rl_echoing_p ! 623: + 10 * do_canon ! 624: + 1 * do_icrnl); ! 625: } ! 626: else ! 627: { ! 628: if (ioctl_err == 0) ! 629: DPRINT1("tcgetattr on master fd failed: errno = %d\n", errno); ! 630: ioctl_err = 1; ! 631: } ! 632: ! 633: if (do_canon == 0 && num_keys == 0) ! 634: { ! 635: char ch[10]; ! 636: int count = read (STDIN_FILENO, ch, sizeof(ch)); ! 637: DPRINT1("[read %d chars from stdin: ", count); ! 638: DPRINT2(" \"%.*s\"]\n", count, ch); ! 639: if (do_icrnl) ! 640: { ! 641: int i = count; ! 642: while (--i >= 0) ! 643: { ! 644: if (ch[i] == '\r') ! 645: ch[i] = '\n'; ! 646: } ! 647: } ! 648: maybe_emphasize_input (1); ! 649: write (out_to_inferior_fd, ch, count); ! 650: } ! 651: else ! 652: { ! 653: if (num_keys == 0) ! 654: { ! 655: int i; ! 656: /* Re-install callback handler for new prompt. */ ! 657: if (prompt != empty_string) ! 658: free (prompt); ! 659: if (prompt == NULL) ! 660: { ! 661: DPRINT0("New empty prompt\n"); ! 662: prompt = empty_string; ! 663: } ! 664: else ! 665: { ! 666: if (do_emphasize_input && buf_count > 0) ! 667: { ! 668: prompt = malloc (buf_count + strlen (end_input_mode) ! 669: + strlen (start_input_mode) + 5); ! 670: sprintf (prompt, "\001%s\002%.*s\001%s\002", ! 671: end_input_mode, ! 672: buf_count, buf, ! 673: start_input_mode); ! 674: } ! 675: else ! 676: { ! 677: prompt = malloc (buf_count + 1); ! 678: memcpy (prompt, buf, buf_count); ! 679: prompt[buf_count] = '\0'; ! 680: } ! 681: DPRINT1("New prompt '%s'\n", prompt); ! 682: #if 0 /* ifdef HAVE_RL_ALREADY_PROMPTED */ ! 683: /* Doesn't quite work when do_emphasize_input is 1. */ ! 684: rl_already_prompted = buf_count > 0; ! 685: #else ! 686: if (buf_count > 0) ! 687: write (1, "\r", 1); ! 688: #endif ! 689: } ! 690: ! 691: rl_callback_handler_install (prompt, line_handler); ! 692: } ! 693: num_keys++; ! 694: maybe_emphasize_input (1); ! 695: rl_callback_read_char (); ! 696: } ! 697: } ! 698: else /* output from inferior. */ ! 699: { ! 700: int i; ! 701: int count; ! 702: int old_count; ! 703: if (buf_count > (sizeof(buf) >> 2)) ! 704: buf_count = 0; ! 705: count = read (in_from_inferior_fd, buf+buf_count, ! 706: sizeof(buf) - buf_count); ! 707: DPRINT2("read %d from inferior, buf_count=%d", count, buf_count); ! 708: DPRINT2(": \"%.*s\"", count, buf+buf_count); ! 709: maybe_emphasize_input (0); ! 710: if (count <= 0) ! 711: { ! 712: DPRINT0 ("(Connection closed by foreign host.)\n"); ! 713: tcsetattr(STDIN_FILENO, TCSANOW, &orig_term); ! 714: exit (0); ! 715: } ! 716: old_count = buf_count; ! 717: ! 718: /* Look for any pending echo that we need to suppress. */ ! 719: while (echo_suppress_start < echo_suppress_limit ! 720: && count > 0 ! 721: && buf[buf_count] == echo_suppress_buffer[echo_suppress_start]) ! 722: { ! 723: count--; ! 724: buf_count++; ! 725: echo_suppress_start++; ! 726: } ! 727: DPRINT1("suppressed %d characters of echo.\n", buf_count-old_count); ! 728: ! 729: /* Write to the terminal anything that was not suppressed. */ ! 730: if (count > 0) ! 731: write (1, buf + buf_count, count); ! 732: ! 733: /* Finally, look for a prompt candidate. ! 734: * When we get around to going input (from the keyboard), ! 735: * we will consider the prompt to be anything since the last ! 736: * line terminator. So we need to save that text in the ! 737: * initial part of buf. However, anything before the ! 738: * most recent end-of-line is not interesting. */ ! 739: buf_count += count; ! 740: #if 1 ! 741: for (i = buf_count; --i >= old_count; ) ! 742: #else ! 743: for (i = buf_count - 1; i-- >= buf_count - count; ) ! 744: #endif ! 745: { ! 746: if (buf[i] == '\n' || buf[i] == '\r') ! 747: { ! 748: i++; ! 749: memmove (buf, buf+i, buf_count - i); ! 750: buf_count -= i; ! 751: break; ! 752: } ! 753: } ! 754: DPRINT2("-> i: %d, buf_count: %d\n", i, buf_count); ! 755: } ! 756: } ! 757: } ! 758: ! 759: static void set_edit_mode () ! 760: { ! 761: int vi = 0; ! 762: char *shellopts; ! 763: ! 764: shellopts = getenv ("SHELLOPTS"); ! 765: while (shellopts != 0) ! 766: { ! 767: if (strncmp ("vi", shellopts, 2) == 0) ! 768: { ! 769: vi = 1; ! 770: break; ! 771: } ! 772: shellopts = strchr (shellopts + 1, ':'); ! 773: } ! 774: ! 775: if (!vi) ! 776: { ! 777: if (getenv ("EDITOR") != 0) ! 778: vi |= strcmp (getenv ("EDITOR"), "vi") == 0; ! 779: } ! 780: ! 781: if (vi) ! 782: rl_variable_bind ("editing-mode", "vi"); ! 783: else ! 784: rl_variable_bind ("editing-mode", "emacs"); ! 785: } ! 786: ! 787: ! 788: static void usage_exit () ! 789: { ! 790: fprintf (stderr, "Usage: rlfe [-h histfile] [-s size] cmd [arg1] [arg2] ...\n\n"); ! 791: exit (1); ! 792: }