Annotation of embedaddon/readline/examples/rlfe/rlfe.c, revision 1.1.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>