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>