File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / readline / examples / rlfe / rlfe.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Wed Jul 30 08:16:46 2014 UTC (10 years, 8 months ago) by misho
Branches: readline, MAIN
CVS tags: v8_2p0, v8_1p0, v6_3p10_cross, v6_3p10, v6_3, p6, HEAD
readline 6.3

    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>