Annotation of embedaddon/readline/examples/rlfe/rlfe.c, revision 1.1
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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>