xref: /openbsd-src/gnu/lib/libreadline/examples/rlfe.c (revision 15b117eae8ea0caffec2fbd5cbd366a5ccee20ab)
11acd27e7Smillert /* A front-end using readline to "cook" input lines for Kawa.
21acd27e7Smillert  *
31acd27e7Smillert  * Copyright (C) 1999  Per Bothner
41acd27e7Smillert  *
51acd27e7Smillert  * This front-end program is free software; you can redistribute it and/or
61acd27e7Smillert  * modify it under the terms of the GNU General Public License as published
71acd27e7Smillert  * by the Free Software Foundation; either version 2, or (at your option)
81acd27e7Smillert  * any later version.
91acd27e7Smillert  *
101acd27e7Smillert  * Some code from Johnson & Troan: "Linux Application Development"
111acd27e7Smillert  * (Addison-Wesley, 1998) was used directly or for inspiration.
121acd27e7Smillert  */
131acd27e7Smillert 
141acd27e7Smillert /* PROBLEMS/TODO:
151acd27e7Smillert  *
161acd27e7Smillert  * Only tested under Linux;  needs to be ported.
171acd27e7Smillert  *
181acd27e7Smillert  * When running mc -c under the Linux console, mc does not recognize
191acd27e7Smillert  * mouse clicks, which mc does when not running under fep.
201acd27e7Smillert  *
211acd27e7Smillert  * Pasting selected text containing tabs is like hitting the tab character,
221acd27e7Smillert  * which invokes readline completion.  We don't want this.  I don't know
231acd27e7Smillert  * if this is fixable without integrating fep into a terminal emulator.
241acd27e7Smillert  *
251acd27e7Smillert  * Echo suppression is a kludge, but can only be avoided with better kernel
261acd27e7Smillert  * support: We need a tty mode to disable "real" echoing, while still
271acd27e7Smillert  * letting the inferior think its tty driver to doing echoing.
281acd27e7Smillert  * Stevens's book claims SCR$ and BSD4.3+ have TIOCREMOTE.
291acd27e7Smillert  *
301acd27e7Smillert  * The latest readline may have some hooks we can use to avoid having
311acd27e7Smillert  * to back up the prompt.
321acd27e7Smillert  *
331acd27e7Smillert  * Desirable readline feature:  When in cooked no-echo mode (e.g. password),
341acd27e7Smillert  * echo characters are they are types with '*', but remove them when done.
351acd27e7Smillert  *
361acd27e7Smillert  * A synchronous output while we're editing an input line should be
371acd27e7Smillert  * inserted in the output view *before* the input line, so that the
381acd27e7Smillert  * lines being edited (with the prompt) float at the end of the input.
391acd27e7Smillert  *
401acd27e7Smillert  * A "page mode" option to emulate more/less behavior:  At each page of
411acd27e7Smillert  * output, pause for a user command.  This required parsing the output
421acd27e7Smillert  * to keep track of line lengths.  It also requires remembering the
431acd27e7Smillert  * output, if we want an option to scroll back, which suggests that
441acd27e7Smillert  * this should be integrated with a terminal emulator like xterm.
451acd27e7Smillert  */
461acd27e7Smillert 
471acd27e7Smillert #ifdef HAVE_CONFIG_H
481acd27e7Smillert #  include <config.h>
491acd27e7Smillert #endif
501acd27e7Smillert 
511acd27e7Smillert #include <stdio.h>
521acd27e7Smillert #include <fcntl.h>
531acd27e7Smillert #include <sys/types.h>
541acd27e7Smillert #include <sys/socket.h>
551acd27e7Smillert #include <netinet/in.h>
561acd27e7Smillert #include <arpa/inet.h>
571acd27e7Smillert #include <signal.h>
581acd27e7Smillert #include <netdb.h>
591acd27e7Smillert #include <stdlib.h>
601acd27e7Smillert #include <errno.h>
611acd27e7Smillert #include <grp.h>
621acd27e7Smillert #include <string.h>
631acd27e7Smillert #include <sys/stat.h>
641acd27e7Smillert #include <unistd.h>
651acd27e7Smillert #include <sys/ioctl.h>
661acd27e7Smillert #include <termios.h>
67*15b117eaSkettenis #include <limits.h>
68*15b117eaSkettenis #include <dirent.h>
691acd27e7Smillert 
701acd27e7Smillert #ifdef READLINE_LIBRARY
711acd27e7Smillert #  include "readline.h"
721acd27e7Smillert #  include "history.h"
731acd27e7Smillert #else
741acd27e7Smillert #  include <readline/readline.h>
751acd27e7Smillert #  include <readline/history.h>
761acd27e7Smillert #endif
771acd27e7Smillert 
781acd27e7Smillert #ifndef COMMAND
791acd27e7Smillert #define COMMAND "/bin/sh"
801acd27e7Smillert #endif
811acd27e7Smillert #ifndef COMMAND_ARGS
821acd27e7Smillert #define COMMAND_ARGS COMMAND
831acd27e7Smillert #endif
841acd27e7Smillert 
851acd27e7Smillert #ifndef HAVE_MEMMOVE
86*15b117eaSkettenis #ifndef memmove
871acd27e7Smillert #  if __GNUC__ > 1
881acd27e7Smillert #    define memmove(d, s, n)	__builtin_memcpy(d, s, n)
891acd27e7Smillert #  else
901acd27e7Smillert #    define memmove(d, s, n)	memcpy(d, s, n)
911acd27e7Smillert #  endif
921acd27e7Smillert #else
931acd27e7Smillert #  define memmove(d, s, n)	memcpy(d, s, n)
941acd27e7Smillert #endif
95*15b117eaSkettenis #endif
961acd27e7Smillert 
97*15b117eaSkettenis #define APPLICATION_NAME "Rlfe"
98*15b117eaSkettenis 
99*15b117eaSkettenis #ifndef errno
100*15b117eaSkettenis extern int errno;
101*15b117eaSkettenis #endif
102*15b117eaSkettenis 
103*15b117eaSkettenis extern int optind;
104*15b117eaSkettenis extern char *optarg;
105*15b117eaSkettenis 
106*15b117eaSkettenis static char *progname;
107*15b117eaSkettenis static char *progversion;
1081acd27e7Smillert 
1091acd27e7Smillert static int in_from_inferior_fd;
1101acd27e7Smillert static int out_to_inferior_fd;
1111acd27e7Smillert 
1121acd27e7Smillert /* Unfortunately, we cannot safely display echo from the inferior process.
1131acd27e7Smillert    The reason is that the echo bit in the pty is "owned" by the inferior,
1141acd27e7Smillert    and if we try to turn it off, we could confuse the inferior.
1151acd27e7Smillert    Thus, when echoing, we get echo twice:  First readline echoes while
1161acd27e7Smillert    we're actually editing. Then we send the line to the inferior, and the
1171acd27e7Smillert    terminal driver send back an extra echo.
1181acd27e7Smillert    The work-around is to remember the input lines, and when we see that
1191acd27e7Smillert    line come back, we supress the output.
1201acd27e7Smillert    A better solution (supposedly available on SVR4) would be a smarter
1211acd27e7Smillert    terminal driver, with more flags ... */
1221acd27e7Smillert #define ECHO_SUPPRESS_MAX 1024
1231acd27e7Smillert char echo_suppress_buffer[ECHO_SUPPRESS_MAX];
1241acd27e7Smillert int echo_suppress_start = 0;
1251acd27e7Smillert int echo_suppress_limit = 0;
1261acd27e7Smillert 
127*15b117eaSkettenis /* #define DEBUG */
128*15b117eaSkettenis 
129*15b117eaSkettenis static FILE *logfile = NULL;
1301acd27e7Smillert 
1311acd27e7Smillert #ifdef DEBUG
132*15b117eaSkettenis FILE *debugfile = NULL;
133*15b117eaSkettenis #define DPRINT0(FMT) (fprintf(debugfile, FMT), fflush(debugfile))
134*15b117eaSkettenis #define DPRINT1(FMT, V1) (fprintf(debugfile, FMT, V1), fflush(debugfile))
135*15b117eaSkettenis #define DPRINT2(FMT, V1, V2) (fprintf(debugfile, FMT, V1, V2), fflush(debugfile))
1361acd27e7Smillert #else
1371acd27e7Smillert #define DPRINT0(FMT) /* Do nothing */
1381acd27e7Smillert #define DPRINT1(FMT, V1) /* Do nothing */
1391acd27e7Smillert #define DPRINT2(FMT, V1, V2) /* Do nothing */
1401acd27e7Smillert #endif
1411acd27e7Smillert 
1421acd27e7Smillert struct termios orig_term;
1431acd27e7Smillert 
144*15b117eaSkettenis static int rlfe_directory_completion_hook __P((char **));
145*15b117eaSkettenis static int rlfe_directory_rewrite_hook __P((char **));
146*15b117eaSkettenis static char *rlfe_filename_completion_function __P((const char *, int));
147*15b117eaSkettenis 
1481acd27e7Smillert /* Pid of child process. */
1491acd27e7Smillert static pid_t child = -1;
1501acd27e7Smillert 
1511acd27e7Smillert static void
sig_child(int signo)1521acd27e7Smillert sig_child (int signo)
1531acd27e7Smillert {
1541acd27e7Smillert   int status;
1551acd27e7Smillert   wait (&status);
1561acd27e7Smillert   DPRINT0 ("(Child process died.)\n");
1571acd27e7Smillert   tcsetattr(STDIN_FILENO, TCSANOW, &orig_term);
1581acd27e7Smillert   exit (0);
1591acd27e7Smillert }
1601acd27e7Smillert 
1611acd27e7Smillert volatile int propagate_sigwinch = 0;
1621acd27e7Smillert 
1631acd27e7Smillert /* sigwinch_handler
1641acd27e7Smillert  * propagate window size changes from input file descriptor to
1651acd27e7Smillert  * master side of pty.
1661acd27e7Smillert  */
sigwinch_handler(int signal)1671acd27e7Smillert void sigwinch_handler(int signal) {
1681acd27e7Smillert    propagate_sigwinch = 1;
1691acd27e7Smillert }
1701acd27e7Smillert 
1711acd27e7Smillert /* get_master_pty() takes a double-indirect character pointer in which
1721acd27e7Smillert  * to put a slave name, and returns an integer file descriptor.
1731acd27e7Smillert  * If it returns < 0, an error has occurred.
1741acd27e7Smillert  * Otherwise, it has returned the master pty file descriptor, and fills
1751acd27e7Smillert  * in *name with the name of the corresponding slave pty.
1761acd27e7Smillert  * Once the slave pty has been opened, you are responsible to free *name.
1771acd27e7Smillert  */
1781acd27e7Smillert 
get_master_pty(char ** name)1791acd27e7Smillert int get_master_pty(char **name) {
1801acd27e7Smillert    int i, j;
1811acd27e7Smillert    /* default to returning error */
1821acd27e7Smillert    int master = -1;
1831acd27e7Smillert 
1841acd27e7Smillert    /* create a dummy name to fill in */
1851acd27e7Smillert    *name = strdup("/dev/ptyXX");
1861acd27e7Smillert 
1871acd27e7Smillert    /* search for an unused pty */
1881acd27e7Smillert    for (i=0; i<16 && master <= 0; i++) {
1891acd27e7Smillert       for (j=0; j<16 && master <= 0; j++) {
1901acd27e7Smillert          (*name)[5] = 'p';
1911acd27e7Smillert          (*name)[8] = "pqrstuvwxyzPQRST"[i];
1921acd27e7Smillert          (*name)[9] = "0123456789abcdef"[j];
1931acd27e7Smillert          /* open the master pty */
1941acd27e7Smillert          if ((master = open(*name, O_RDWR)) < 0) {
1951acd27e7Smillert             if (errno == ENOENT) {
1961acd27e7Smillert                /* we are out of pty devices */
1971acd27e7Smillert                free (*name);
1981acd27e7Smillert                return (master);
1991acd27e7Smillert             }
2001acd27e7Smillert          }
2011acd27e7Smillert          else {
2021acd27e7Smillert            /* By substituting a letter, we change the master pty
2031acd27e7Smillert             * name into the slave pty name.
2041acd27e7Smillert             */
2051acd27e7Smillert            (*name)[5] = 't';
2061acd27e7Smillert            if (access(*name, R_OK|W_OK) != 0)
2071acd27e7Smillert              {
2081acd27e7Smillert                close(master);
2091acd27e7Smillert                master = -1;
2101acd27e7Smillert              }
2111acd27e7Smillert          }
2121acd27e7Smillert       }
2131acd27e7Smillert    }
2141acd27e7Smillert    if ((master < 0) && (i == 16) && (j == 16)) {
2151acd27e7Smillert       /* must have tried every pty unsuccessfully */
2161acd27e7Smillert       free (*name);
2171acd27e7Smillert       return (master);
2181acd27e7Smillert    }
2191acd27e7Smillert 
2201acd27e7Smillert    (*name)[5] = 't';
2211acd27e7Smillert 
2221acd27e7Smillert    return (master);
2231acd27e7Smillert }
2241acd27e7Smillert 
2251acd27e7Smillert /* get_slave_pty() returns an integer file descriptor.
2261acd27e7Smillert  * If it returns < 0, an error has occurred.
2271acd27e7Smillert  * Otherwise, it has returned the slave file descriptor.
2281acd27e7Smillert  */
2291acd27e7Smillert 
get_slave_pty(char * name)2301acd27e7Smillert int get_slave_pty(char *name) {
2311acd27e7Smillert    struct group *gptr;
2321acd27e7Smillert    gid_t gid;
2331acd27e7Smillert    int slave = -1;
2341acd27e7Smillert 
2351acd27e7Smillert    /* chown/chmod the corresponding pty, if possible.
2361acd27e7Smillert     * This will only work if the process has root permissions.
2371acd27e7Smillert     * Alternatively, write and exec a small setuid program that
2381acd27e7Smillert     * does just this.
2391acd27e7Smillert     */
2401acd27e7Smillert    if ((gptr = getgrnam("tty")) != 0) {
2411acd27e7Smillert       gid = gptr->gr_gid;
2421acd27e7Smillert    } else {
2431acd27e7Smillert       /* if the tty group does not exist, don't change the
2441acd27e7Smillert        * group on the slave pty, only the owner
2451acd27e7Smillert        */
2461acd27e7Smillert       gid = -1;
2471acd27e7Smillert    }
2481acd27e7Smillert 
2491acd27e7Smillert    /* Note that we do not check for errors here.  If this is code
2501acd27e7Smillert     * where these actions are critical, check for errors!
2511acd27e7Smillert     */
2521acd27e7Smillert    chown(name, getuid(), gid);
2531acd27e7Smillert    /* This code only makes the slave read/writeable for the user.
2541acd27e7Smillert     * If this is for an interactive shell that will want to
2551acd27e7Smillert     * receive "write" and "wall" messages, OR S_IWGRP into the
2561acd27e7Smillert     * second argument below.
2571acd27e7Smillert     */
2581acd27e7Smillert    chmod(name, S_IRUSR|S_IWUSR);
2591acd27e7Smillert 
2601acd27e7Smillert    /* open the corresponding slave pty */
2611acd27e7Smillert    slave = open(name, O_RDWR);
2621acd27e7Smillert    return (slave);
2631acd27e7Smillert }
2641acd27e7Smillert 
2651acd27e7Smillert /* Certain special characters, such as ctrl/C, we want to pass directly
2661acd27e7Smillert    to the inferior, rather than letting readline handle them. */
2671acd27e7Smillert 
2681acd27e7Smillert static char special_chars[20];
2691acd27e7Smillert static int special_chars_count;
2701acd27e7Smillert 
2711acd27e7Smillert static void
add_special_char(int ch)2721acd27e7Smillert add_special_char(int ch)
2731acd27e7Smillert {
2741acd27e7Smillert   if (ch != 0)
2751acd27e7Smillert     special_chars[special_chars_count++] = ch;
2761acd27e7Smillert }
2771acd27e7Smillert 
2781acd27e7Smillert static int eof_char;
2791acd27e7Smillert 
2801acd27e7Smillert static int
is_special_char(int ch)2811acd27e7Smillert is_special_char(int ch)
2821acd27e7Smillert {
2831acd27e7Smillert   int i;
2841acd27e7Smillert #if 0
2851acd27e7Smillert   if (ch == eof_char && rl_point == rl_end)
2861acd27e7Smillert     return 1;
2871acd27e7Smillert #endif
2881acd27e7Smillert   for (i = special_chars_count;  --i >= 0; )
2891acd27e7Smillert     if (special_chars[i] == ch)
2901acd27e7Smillert       return 1;
2911acd27e7Smillert   return 0;
2921acd27e7Smillert }
2931acd27e7Smillert 
2941acd27e7Smillert static char buf[1024];
2951acd27e7Smillert /* buf[0 .. buf_count-1] is the what has been emitted on the current line.
2961acd27e7Smillert    It is used as the readline prompt. */
2971acd27e7Smillert static int buf_count = 0;
2981acd27e7Smillert 
2991acd27e7Smillert int num_keys = 0;
3001acd27e7Smillert 
3011acd27e7Smillert static void
null_prep_terminal(int meta)3021acd27e7Smillert null_prep_terminal (int meta)
3031acd27e7Smillert {
3041acd27e7Smillert }
3051acd27e7Smillert 
3061acd27e7Smillert static void
null_deprep_terminal()3071acd27e7Smillert null_deprep_terminal ()
3081acd27e7Smillert {
3091acd27e7Smillert }
3101acd27e7Smillert 
3111acd27e7Smillert char pending_special_char;
3121acd27e7Smillert 
3131acd27e7Smillert static void
line_handler(char * line)3141acd27e7Smillert line_handler (char *line)
3151acd27e7Smillert {
3161acd27e7Smillert   if (line == NULL)
3171acd27e7Smillert     {
3181acd27e7Smillert       char buf[1];
3191acd27e7Smillert       DPRINT0("saw eof!\n");
3201acd27e7Smillert       buf[0] = '\004'; /* ctrl/d */
3211acd27e7Smillert       write (out_to_inferior_fd, buf, 1);
3221acd27e7Smillert     }
3231acd27e7Smillert   else
3241acd27e7Smillert     {
3251acd27e7Smillert       static char enter[] = "\r";
3261acd27e7Smillert       /*  Send line to inferior: */
3271acd27e7Smillert       int length = strlen (line);
3281acd27e7Smillert       if (length > ECHO_SUPPRESS_MAX-2)
3291acd27e7Smillert 	{
3301acd27e7Smillert 	  echo_suppress_start = 0;
3311acd27e7Smillert 	  echo_suppress_limit = 0;
3321acd27e7Smillert 	}
3331acd27e7Smillert       else
3341acd27e7Smillert 	{
3351acd27e7Smillert 	  if (echo_suppress_limit + length > ECHO_SUPPRESS_MAX - 2)
3361acd27e7Smillert 	    {
3371acd27e7Smillert 	      if (echo_suppress_limit - echo_suppress_start + length
3381acd27e7Smillert 		  <= ECHO_SUPPRESS_MAX - 2)
3391acd27e7Smillert 		{
3401acd27e7Smillert 		  memmove (echo_suppress_buffer,
3411acd27e7Smillert 			   echo_suppress_buffer + echo_suppress_start,
3421acd27e7Smillert 			   echo_suppress_limit - echo_suppress_start);
3431acd27e7Smillert 		  echo_suppress_limit -= echo_suppress_start;
3441acd27e7Smillert 		  echo_suppress_start = 0;
3451acd27e7Smillert 		}
3461acd27e7Smillert 	      else
3471acd27e7Smillert 		{
3481acd27e7Smillert 		  echo_suppress_limit = 0;
3491acd27e7Smillert 		}
3501acd27e7Smillert 	      echo_suppress_start = 0;
3511acd27e7Smillert 	    }
3521acd27e7Smillert 	  memcpy (echo_suppress_buffer + echo_suppress_limit,
3531acd27e7Smillert 		  line, length);
3541acd27e7Smillert 	  echo_suppress_limit += length;
3551acd27e7Smillert 	  echo_suppress_buffer[echo_suppress_limit++] = '\r';
3561acd27e7Smillert 	  echo_suppress_buffer[echo_suppress_limit++] = '\n';
3571acd27e7Smillert 	}
3581acd27e7Smillert       write (out_to_inferior_fd, line, length);
3591acd27e7Smillert       if (pending_special_char == 0)
3601acd27e7Smillert         {
3611acd27e7Smillert           write (out_to_inferior_fd, enter, sizeof(enter)-1);
3621acd27e7Smillert           if (*line)
3631acd27e7Smillert             add_history (line);
3641acd27e7Smillert         }
3651acd27e7Smillert       free (line);
3661acd27e7Smillert     }
3671acd27e7Smillert   rl_callback_handler_remove ();
3681acd27e7Smillert   buf_count = 0;
3691acd27e7Smillert   num_keys = 0;
3701acd27e7Smillert   if (pending_special_char != 0)
3711acd27e7Smillert     {
3721acd27e7Smillert       write (out_to_inferior_fd, &pending_special_char, 1);
3731acd27e7Smillert       pending_special_char = 0;
3741acd27e7Smillert     }
3751acd27e7Smillert }
3761acd27e7Smillert 
3771acd27e7Smillert /* Value of rl_getc_function.
3781acd27e7Smillert    Use this because readline should read from stdin, not rl_instream,
3791acd27e7Smillert    points to the pty (so readline has monitor its terminal modes). */
3801acd27e7Smillert 
3811acd27e7Smillert int
my_rl_getc(FILE * dummy)3821acd27e7Smillert my_rl_getc (FILE *dummy)
3831acd27e7Smillert {
3841acd27e7Smillert   int ch = rl_getc (stdin);
3851acd27e7Smillert   if (is_special_char (ch))
3861acd27e7Smillert     {
3871acd27e7Smillert       pending_special_char = ch;
3881acd27e7Smillert       return '\r';
3891acd27e7Smillert     }
3901acd27e7Smillert   return ch;
3911acd27e7Smillert }
3921acd27e7Smillert 
393*15b117eaSkettenis static void
usage()394*15b117eaSkettenis usage()
395*15b117eaSkettenis {
396*15b117eaSkettenis   fprintf (stderr, "%s: usage: %s [-l filename] [-a] [-n appname] [-hv] [command [arguments...]]\n",
397*15b117eaSkettenis 		   progname, progname);
398*15b117eaSkettenis }
399*15b117eaSkettenis 
4001acd27e7Smillert int
main(int argc,char ** argv)4011acd27e7Smillert main(int argc, char** argv)
4021acd27e7Smillert {
4031acd27e7Smillert   char *path;
404*15b117eaSkettenis   int i, append;
4051acd27e7Smillert   int master;
406*15b117eaSkettenis   char *name, *logfname, *appname;
4071acd27e7Smillert   int in_from_tty_fd;
4081acd27e7Smillert   struct sigaction act;
4091acd27e7Smillert   struct winsize ws;
4101acd27e7Smillert   struct termios t;
4111acd27e7Smillert   int maxfd;
4121acd27e7Smillert   fd_set in_set;
4131acd27e7Smillert   static char empty_string[1] = "";
4141acd27e7Smillert   char *prompt = empty_string;
4151acd27e7Smillert   int ioctl_err = 0;
4161acd27e7Smillert 
417*15b117eaSkettenis   if ((progname = strrchr (argv[0], '/')) == 0)
418*15b117eaSkettenis     progname = argv[0];
419*15b117eaSkettenis   else
420*15b117eaSkettenis     progname++;
421*15b117eaSkettenis   progversion = RL_LIBRARY_VERSION;
4221acd27e7Smillert 
423*15b117eaSkettenis   append = 0;
424*15b117eaSkettenis   appname = APPLICATION_NAME;
425*15b117eaSkettenis   logfname = (char *)NULL;
426*15b117eaSkettenis 
427*15b117eaSkettenis   while ((i = getopt (argc, argv, "ahl:n:v")) != EOF)
428*15b117eaSkettenis     {
429*15b117eaSkettenis       switch (i)
430*15b117eaSkettenis 	{
431*15b117eaSkettenis 	case 'l':
432*15b117eaSkettenis 	  logfname = optarg;
433*15b117eaSkettenis 	  break;
434*15b117eaSkettenis 	case 'n':
435*15b117eaSkettenis 	  appname = optarg;
436*15b117eaSkettenis 	  break;
437*15b117eaSkettenis 	case 'a':
438*15b117eaSkettenis 	  append = 1;
439*15b117eaSkettenis 	  break;
440*15b117eaSkettenis 	case 'h':
441*15b117eaSkettenis 	  usage ();
442*15b117eaSkettenis 	  exit (0);
443*15b117eaSkettenis 	case 'v':
444*15b117eaSkettenis 	  fprintf (stderr, "%s version %s\n", progname, progversion);
445*15b117eaSkettenis 	  exit (0);
446*15b117eaSkettenis 	default:
447*15b117eaSkettenis 	  usage ();
448*15b117eaSkettenis 	  exit (2);
449*15b117eaSkettenis 	}
450*15b117eaSkettenis     }
451*15b117eaSkettenis 
452*15b117eaSkettenis   argc -= optind;
453*15b117eaSkettenis   argv += optind;
454*15b117eaSkettenis 
455*15b117eaSkettenis   if (logfname)
456*15b117eaSkettenis     {
457*15b117eaSkettenis       logfile = fopen (logfname, append ? "a" : "w");
458*15b117eaSkettenis       if (logfile == 0)
459*15b117eaSkettenis 	fprintf (stderr, "%s: warning: could not open log file %s: %s\n",
460*15b117eaSkettenis 			 progname, logfname, strerror (errno));
461*15b117eaSkettenis     }
462*15b117eaSkettenis 
463*15b117eaSkettenis   rl_readline_name = appname;
464*15b117eaSkettenis 
465*15b117eaSkettenis #ifdef DEBUG
466*15b117eaSkettenis   debugfile = fopen("LOG", "w");
467*15b117eaSkettenis #endif
4681acd27e7Smillert 
4691acd27e7Smillert   if ((master = get_master_pty(&name)) < 0)
4701acd27e7Smillert     {
4711acd27e7Smillert       perror("ptypair: could not open master pty");
4721acd27e7Smillert       exit(1);
4731acd27e7Smillert     }
4741acd27e7Smillert 
4751acd27e7Smillert   DPRINT1("pty name: '%s'\n", name);
4761acd27e7Smillert 
4771acd27e7Smillert   /* set up SIGWINCH handler */
4781acd27e7Smillert   act.sa_handler = sigwinch_handler;
4791acd27e7Smillert   sigemptyset(&(act.sa_mask));
4801acd27e7Smillert   act.sa_flags = 0;
4811acd27e7Smillert   if (sigaction(SIGWINCH, &act, NULL) < 0)
4821acd27e7Smillert     {
4831acd27e7Smillert       perror("ptypair: could not handle SIGWINCH ");
4841acd27e7Smillert       exit(1);
4851acd27e7Smillert     }
4861acd27e7Smillert 
4871acd27e7Smillert   if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0)
4881acd27e7Smillert     {
4891acd27e7Smillert       perror("ptypair: could not get window size");
4901acd27e7Smillert       exit(1);
4911acd27e7Smillert     }
4921acd27e7Smillert 
4931acd27e7Smillert   if ((child = fork()) < 0)
4941acd27e7Smillert     {
4951acd27e7Smillert       perror("cannot fork");
4961acd27e7Smillert       exit(1);
4971acd27e7Smillert     }
4981acd27e7Smillert 
4991acd27e7Smillert   if (child == 0)
5001acd27e7Smillert     {
5011acd27e7Smillert       int slave;  /* file descriptor for slave pty */
5021acd27e7Smillert 
5031acd27e7Smillert       /* We are in the child process */
5041acd27e7Smillert       close(master);
5051acd27e7Smillert 
5061acd27e7Smillert #ifdef TIOCSCTTY
5071acd27e7Smillert       if ((slave = get_slave_pty(name)) < 0)
5081acd27e7Smillert 	{
5091acd27e7Smillert 	  perror("ptypair: could not open slave pty");
5101acd27e7Smillert 	  exit(1);
5111acd27e7Smillert 	}
5121acd27e7Smillert       free(name);
5131acd27e7Smillert #endif
5141acd27e7Smillert 
5151acd27e7Smillert       /* We need to make this process a session group leader, because
5161acd27e7Smillert        * it is on a new PTY, and things like job control simply will
5171acd27e7Smillert        * not work correctly unless there is a session group leader
5181acd27e7Smillert        * and process group leader (which a session group leader
5191acd27e7Smillert        * automatically is). This also disassociates us from our old
5201acd27e7Smillert        * controlling tty.
5211acd27e7Smillert        */
5221acd27e7Smillert       if (setsid() < 0)
5231acd27e7Smillert 	{
5241acd27e7Smillert 	  perror("could not set session leader");
5251acd27e7Smillert 	}
5261acd27e7Smillert 
5271acd27e7Smillert       /* Tie us to our new controlling tty. */
5281acd27e7Smillert #ifdef TIOCSCTTY
5291acd27e7Smillert       if (ioctl(slave, TIOCSCTTY, NULL))
5301acd27e7Smillert 	{
5311acd27e7Smillert 	  perror("could not set new controlling tty");
5321acd27e7Smillert 	}
5331acd27e7Smillert #else
5341acd27e7Smillert       if ((slave = get_slave_pty(name)) < 0)
5351acd27e7Smillert 	{
5361acd27e7Smillert 	  perror("ptypair: could not open slave pty");
5371acd27e7Smillert 	  exit(1);
5381acd27e7Smillert 	}
5391acd27e7Smillert       free(name);
5401acd27e7Smillert #endif
5411acd27e7Smillert 
5421acd27e7Smillert       /* make slave pty be standard in, out, and error */
5431acd27e7Smillert       dup2(slave, STDIN_FILENO);
5441acd27e7Smillert       dup2(slave, STDOUT_FILENO);
5451acd27e7Smillert       dup2(slave, STDERR_FILENO);
5461acd27e7Smillert 
5471acd27e7Smillert       /* at this point the slave pty should be standard input */
5481acd27e7Smillert       if (slave > 2)
5491acd27e7Smillert 	{
5501acd27e7Smillert 	  close(slave);
5511acd27e7Smillert 	}
5521acd27e7Smillert 
5531acd27e7Smillert       /* Try to restore window size; failure isn't critical */
5541acd27e7Smillert       if (ioctl(STDOUT_FILENO, TIOCSWINSZ, &ws) < 0)
5551acd27e7Smillert 	{
5561acd27e7Smillert 	  perror("could not restore window size");
5571acd27e7Smillert 	}
5581acd27e7Smillert 
5591acd27e7Smillert       /* now start the shell */
5601acd27e7Smillert       {
5611acd27e7Smillert 	static char* command_args[] = { COMMAND_ARGS, NULL };
562*15b117eaSkettenis 	if (argc < 1)
5631acd27e7Smillert 	  execvp(COMMAND, command_args);
5641acd27e7Smillert 	else
565*15b117eaSkettenis 	  execvp(argv[0], &argv[0]);
5661acd27e7Smillert       }
5671acd27e7Smillert 
5681acd27e7Smillert       /* should never be reached */
5691acd27e7Smillert       exit(1);
5701acd27e7Smillert     }
5711acd27e7Smillert 
5721acd27e7Smillert   /* parent */
5731acd27e7Smillert   signal (SIGCHLD, sig_child);
5741acd27e7Smillert   free(name);
5751acd27e7Smillert 
5761acd27e7Smillert   /* Note that we only set termios settings for standard input;
5771acd27e7Smillert    * the master side of a pty is NOT a tty.
5781acd27e7Smillert    */
5791acd27e7Smillert   tcgetattr(STDIN_FILENO, &orig_term);
5801acd27e7Smillert 
5811acd27e7Smillert   t = orig_term;
5821acd27e7Smillert   eof_char = t.c_cc[VEOF];
5831acd27e7Smillert   /*  add_special_char(t.c_cc[VEOF]);*/
5841acd27e7Smillert   add_special_char(t.c_cc[VINTR]);
5851acd27e7Smillert   add_special_char(t.c_cc[VQUIT]);
5861acd27e7Smillert   add_special_char(t.c_cc[VSUSP]);
5871acd27e7Smillert #if defined (VDISCARD)
5881acd27e7Smillert   add_special_char(t.c_cc[VDISCARD]);
5891acd27e7Smillert #endif
5901acd27e7Smillert 
5911acd27e7Smillert #if 0
5921acd27e7Smillert   t.c_lflag |= (ICANON | ISIG | ECHO | ECHOCTL | ECHOE | \
5931acd27e7Smillert 		ECHOK | ECHOKE | ECHONL | ECHOPRT );
5941acd27e7Smillert #else
5951acd27e7Smillert   t.c_lflag &= ~(ICANON | ISIG | ECHO | ECHOCTL | ECHOE | \
5961acd27e7Smillert 		 ECHOK | ECHOKE | ECHONL | ECHOPRT );
5971acd27e7Smillert #endif
5981acd27e7Smillert   t.c_iflag |= IGNBRK;
5991acd27e7Smillert   t.c_cc[VMIN] = 1;
6001acd27e7Smillert   t.c_cc[VTIME] = 0;
6011acd27e7Smillert   tcsetattr(STDIN_FILENO, TCSANOW, &t);
6021acd27e7Smillert   in_from_inferior_fd = master;
6031acd27e7Smillert   out_to_inferior_fd = master;
6041acd27e7Smillert   rl_instream = fdopen (master, "r");
6051acd27e7Smillert   rl_getc_function = my_rl_getc;
6061acd27e7Smillert 
6071acd27e7Smillert   rl_prep_term_function = null_prep_terminal;
6081acd27e7Smillert   rl_deprep_term_function = null_deprep_terminal;
6091acd27e7Smillert   rl_callback_handler_install (prompt, line_handler);
6101acd27e7Smillert 
611*15b117eaSkettenis #if 1
612*15b117eaSkettenis   rl_directory_completion_hook = rlfe_directory_completion_hook;
613*15b117eaSkettenis   rl_completion_entry_function = rlfe_filename_completion_function;
614*15b117eaSkettenis #else
615*15b117eaSkettenis   rl_directory_rewrite_hook = rlfe_directory_rewrite_hook;
616*15b117eaSkettenis #endif
617*15b117eaSkettenis 
6181acd27e7Smillert   in_from_tty_fd = STDIN_FILENO;
6191acd27e7Smillert   FD_ZERO (&in_set);
6201acd27e7Smillert   maxfd = in_from_inferior_fd > in_from_tty_fd ? in_from_inferior_fd
6211acd27e7Smillert     : in_from_tty_fd;
6221acd27e7Smillert   for (;;)
6231acd27e7Smillert     {
6241acd27e7Smillert       int num;
6251acd27e7Smillert       FD_SET (in_from_inferior_fd, &in_set);
6261acd27e7Smillert       FD_SET (in_from_tty_fd, &in_set);
6271acd27e7Smillert 
6281acd27e7Smillert       num = select(maxfd+1, &in_set, NULL, NULL, NULL);
6291acd27e7Smillert 
6301acd27e7Smillert       if (propagate_sigwinch)
6311acd27e7Smillert 	{
6321acd27e7Smillert 	  struct winsize ws;
6331acd27e7Smillert 	  if (ioctl (STDIN_FILENO, TIOCGWINSZ, &ws) >= 0)
6341acd27e7Smillert 	    {
6351acd27e7Smillert 	      ioctl (master, TIOCSWINSZ, &ws);
6361acd27e7Smillert 	    }
6371acd27e7Smillert 	  propagate_sigwinch = 0;
6381acd27e7Smillert 	  continue;
6391acd27e7Smillert 	}
6401acd27e7Smillert 
6411acd27e7Smillert       if (num <= 0)
6421acd27e7Smillert 	{
6431acd27e7Smillert 	  perror ("select");
6441acd27e7Smillert 	  exit (-1);
6451acd27e7Smillert 	}
6461acd27e7Smillert       if (FD_ISSET (in_from_tty_fd, &in_set))
6471acd27e7Smillert 	{
6481acd27e7Smillert 	  extern int readline_echoing_p;
6491acd27e7Smillert 	  struct termios term_master;
6501acd27e7Smillert 	  int do_canon = 1;
6511acd27e7Smillert 	  int ioctl_ret;
6521acd27e7Smillert 
6531acd27e7Smillert 	  DPRINT1("[tty avail num_keys:%d]\n", num_keys);
6541acd27e7Smillert 
6551acd27e7Smillert 	  /* If we can't get tty modes for the master side of the pty, we
6561acd27e7Smillert 	     can't handle non-canonical-mode programs.  Always assume the
6571acd27e7Smillert 	     master is in canonical echo mode if we can't tell. */
6581acd27e7Smillert 	  ioctl_ret = tcgetattr(master, &term_master);
6591acd27e7Smillert 
6601acd27e7Smillert 	  if (ioctl_ret >= 0)
6611acd27e7Smillert 	    {
6621acd27e7Smillert 	      DPRINT2 ("echo:%d, canon:%d\n",
6631acd27e7Smillert 			(term_master.c_lflag & ECHO) != 0,
6641acd27e7Smillert 			(term_master.c_lflag & ICANON) != 0);
6651acd27e7Smillert 	      do_canon = (term_master.c_lflag & ICANON) != 0;
6661acd27e7Smillert 	      readline_echoing_p = (term_master.c_lflag & ECHO) != 0;
6671acd27e7Smillert 	    }
6681acd27e7Smillert 	  else
6691acd27e7Smillert 	    {
6701acd27e7Smillert 	      if (ioctl_err == 0)
6711acd27e7Smillert 		DPRINT1("tcgetattr on master fd failed: errno = %d\n", errno);
6721acd27e7Smillert 	      ioctl_err = 1;
6731acd27e7Smillert 	    }
6741acd27e7Smillert 
6751acd27e7Smillert 	  if (do_canon == 0 && num_keys == 0)
6761acd27e7Smillert 	    {
6771acd27e7Smillert 	      char ch[10];
6781acd27e7Smillert 	      int count = read (STDIN_FILENO, ch, sizeof(ch));
6791acd27e7Smillert 	      write (out_to_inferior_fd, ch, count);
6801acd27e7Smillert 	    }
6811acd27e7Smillert 	  else
6821acd27e7Smillert 	    {
6831acd27e7Smillert 	      if (num_keys == 0)
6841acd27e7Smillert 		{
6851acd27e7Smillert 		  int i;
6861acd27e7Smillert 		  /* Re-install callback handler for new prompt. */
6871acd27e7Smillert 		  if (prompt != empty_string)
6881acd27e7Smillert 		    free (prompt);
6891acd27e7Smillert 		  prompt = malloc (buf_count + 1);
6901acd27e7Smillert 		  if (prompt == NULL)
6911acd27e7Smillert 		    prompt = empty_string;
6921acd27e7Smillert 		  else
6931acd27e7Smillert 		    {
6941acd27e7Smillert 		      memcpy (prompt, buf, buf_count);
6951acd27e7Smillert 		      prompt[buf_count] = '\0';
6961acd27e7Smillert 		      DPRINT1("New prompt '%s'\n", prompt);
6971acd27e7Smillert #if 0 /* ifdef HAVE_RL_ALREADY_PROMPTED -- doesn't work */
6981acd27e7Smillert 		      rl_already_prompted = buf_count > 0;
6991acd27e7Smillert #else
7001acd27e7Smillert 		      if (buf_count > 0)
7011acd27e7Smillert 			write (1, "\r", 1);
7021acd27e7Smillert #endif
7031acd27e7Smillert 		    }
7041acd27e7Smillert 		  rl_callback_handler_install (prompt, line_handler);
7051acd27e7Smillert 		}
7061acd27e7Smillert 	      num_keys++;
7071acd27e7Smillert 	      rl_callback_read_char ();
7081acd27e7Smillert 	    }
7091acd27e7Smillert 	}
7101acd27e7Smillert       else /* input from inferior. */
7111acd27e7Smillert 	{
7121acd27e7Smillert 	  int i;
7131acd27e7Smillert 	  int count;
7141acd27e7Smillert 	  int old_count;
7151acd27e7Smillert 	  if (buf_count > (sizeof(buf) >> 2))
7161acd27e7Smillert 	    buf_count = 0;
7171acd27e7Smillert 	  count = read (in_from_inferior_fd, buf+buf_count,
7181acd27e7Smillert 			sizeof(buf) - buf_count);
7191acd27e7Smillert 	  if (count <= 0)
7201acd27e7Smillert 	    {
7211acd27e7Smillert 	      DPRINT0 ("(Connection closed by foreign host.)\n");
7221acd27e7Smillert 	      tcsetattr(STDIN_FILENO, TCSANOW, &orig_term);
7231acd27e7Smillert 	      exit (0);
7241acd27e7Smillert 	    }
7251acd27e7Smillert 	  old_count = buf_count;
7261acd27e7Smillert 
727*15b117eaSkettenis 	  /* Do some minimal carriage return translation and backspace
728*15b117eaSkettenis 	     processing before logging the input line. */
729*15b117eaSkettenis 	  if (logfile)
730*15b117eaSkettenis 	    {
731*15b117eaSkettenis #ifndef __GNUC__
732*15b117eaSkettenis 	      char *b;
733*15b117eaSkettenis #else
734*15b117eaSkettenis 	      char b[count + 1];
735*15b117eaSkettenis #endif
736*15b117eaSkettenis 	      int i, j;
737*15b117eaSkettenis 
738*15b117eaSkettenis #ifndef __GNUC__
739*15b117eaSkettenis 	      b = malloc (count + 1);
740*15b117eaSkettenis 	      if (b) {
741*15b117eaSkettenis #endif
742*15b117eaSkettenis 	      for (i = 0; i < count; i++)
743*15b117eaSkettenis 	        b[i] = buf[buf_count + i];
744*15b117eaSkettenis 	      b[i] = '\0';
745*15b117eaSkettenis 	      for (i = j = 0; i <= count; i++)
746*15b117eaSkettenis 		{
747*15b117eaSkettenis 		  if (b[i] == '\r')
748*15b117eaSkettenis 		    {
749*15b117eaSkettenis 		      if (b[i+1] != '\n')
750*15b117eaSkettenis 		        b[j++] = '\n';
751*15b117eaSkettenis 		    }
752*15b117eaSkettenis 		  else if (b[i] == '\b')
753*15b117eaSkettenis 		    {
754*15b117eaSkettenis 		      if (i)
755*15b117eaSkettenis 			j--;
756*15b117eaSkettenis 		    }
757*15b117eaSkettenis 		  else
758*15b117eaSkettenis 		    b[j++] = b[i];
759*15b117eaSkettenis 		}
760*15b117eaSkettenis 	      fprintf (logfile, "%s", b);
761*15b117eaSkettenis 
762*15b117eaSkettenis #ifndef __GNUC__
763*15b117eaSkettenis 	      free (b);
764*15b117eaSkettenis 	      }
765*15b117eaSkettenis #endif
766*15b117eaSkettenis 	    }
767*15b117eaSkettenis 
7681acd27e7Smillert           /* Look for any pending echo that we need to suppress. */
7691acd27e7Smillert 	  while (echo_suppress_start < echo_suppress_limit
7701acd27e7Smillert 		 && count > 0
7711acd27e7Smillert 		 && buf[buf_count] == echo_suppress_buffer[echo_suppress_start])
7721acd27e7Smillert 	    {
7731acd27e7Smillert 	      count--;
7741acd27e7Smillert 	      buf_count++;
7751acd27e7Smillert 	      echo_suppress_start++;
7761acd27e7Smillert 	    }
7771acd27e7Smillert 
7781acd27e7Smillert           /* Write to the terminal anything that was not suppressed. */
7791acd27e7Smillert           if (count > 0)
7801acd27e7Smillert             write (1, buf + buf_count, count);
7811acd27e7Smillert 
7821acd27e7Smillert           /* Finally, look for a prompt candidate.
7831acd27e7Smillert            * When we get around to going input (from the keyboard),
7841acd27e7Smillert            * we will consider the prompt to be anything since the last
7851acd27e7Smillert            * line terminator.  So we need to save that text in the
7861acd27e7Smillert            * initial part of buf.  However, anything before the
7871acd27e7Smillert            * most recent end-of-line is not interesting. */
7881acd27e7Smillert 	  buf_count += count;
7891acd27e7Smillert #if 1
7901acd27e7Smillert 	  for (i = buf_count;  --i >= old_count; )
7911acd27e7Smillert #else
7921acd27e7Smillert 	  for (i = buf_count - 1;  i-- >= buf_count - count; )
7931acd27e7Smillert #endif
7941acd27e7Smillert 	    {
7951acd27e7Smillert 	      if (buf[i] == '\n' || buf[i] == '\r')
7961acd27e7Smillert 		{
7971acd27e7Smillert 		  i++;
7981acd27e7Smillert 		  memmove (buf, buf+i, buf_count - i);
7991acd27e7Smillert 		  buf_count -= i;
8001acd27e7Smillert 		  break;
8011acd27e7Smillert 		}
8021acd27e7Smillert 	    }
8031acd27e7Smillert 	  DPRINT2("-> i: %d, buf_count: %d\n", i, buf_count);
8041acd27e7Smillert 	}
8051acd27e7Smillert     }
8061acd27e7Smillert }
807*15b117eaSkettenis 
808*15b117eaSkettenis /*
809*15b117eaSkettenis  *
810*15b117eaSkettenis  * FILENAME COMPLETION FOR RLFE
811*15b117eaSkettenis  *
812*15b117eaSkettenis  */
813*15b117eaSkettenis 
814*15b117eaSkettenis #ifndef PATH_MAX
815*15b117eaSkettenis #  define PATH_MAX 1024
816*15b117eaSkettenis #endif
817*15b117eaSkettenis 
818*15b117eaSkettenis #define DIRSEP		'/'
819*15b117eaSkettenis #define ISDIRSEP(x)	((x) == '/')
820*15b117eaSkettenis #define PATHSEP(x)	(ISDIRSEP(x) || (x) == 0)
821*15b117eaSkettenis 
822*15b117eaSkettenis #define DOT_OR_DOTDOT(x) \
823*15b117eaSkettenis 	((x)[0] == '.' && (PATHSEP((x)[1]) || \
824*15b117eaSkettenis 			  ((x)[1] == '.' && PATHSEP((x)[2]))))
825*15b117eaSkettenis 
826*15b117eaSkettenis #define FREE(x)		if (x) free(x)
827*15b117eaSkettenis 
828*15b117eaSkettenis #define STRDUP(s, x)	do { \
829*15b117eaSkettenis 			  s = strdup (x);\
830*15b117eaSkettenis 			  if (s == 0) \
831*15b117eaSkettenis 			    return ((char *)NULL); \
832*15b117eaSkettenis 			} while (0)
833*15b117eaSkettenis 
834*15b117eaSkettenis static int
get_inferior_cwd(path,psize)835*15b117eaSkettenis get_inferior_cwd (path, psize)
836*15b117eaSkettenis      char *path;
837*15b117eaSkettenis      size_t psize;
838*15b117eaSkettenis {
839*15b117eaSkettenis   int n;
840*15b117eaSkettenis   static char procfsbuf[PATH_MAX] = { '\0' };
841*15b117eaSkettenis 
842*15b117eaSkettenis   if (procfsbuf[0] == '\0')
843*15b117eaSkettenis     sprintf (procfsbuf, "/proc/%d/cwd", (int)child);
844*15b117eaSkettenis   n = readlink (procfsbuf, path, psize);
845*15b117eaSkettenis   if (n < 0)
846*15b117eaSkettenis     return n;
847*15b117eaSkettenis   if (n > psize)
848*15b117eaSkettenis     return -1;
849*15b117eaSkettenis   path[n] = '\0';
850*15b117eaSkettenis   return n;
851*15b117eaSkettenis }
852*15b117eaSkettenis 
853*15b117eaSkettenis static int
rlfe_directory_rewrite_hook(dirnamep)854*15b117eaSkettenis rlfe_directory_rewrite_hook (dirnamep)
855*15b117eaSkettenis      char **dirnamep;
856*15b117eaSkettenis {
857*15b117eaSkettenis   char *ldirname, cwd[PATH_MAX], *retdir, *ld;
858*15b117eaSkettenis   int n, ldlen;
859*15b117eaSkettenis 
860*15b117eaSkettenis   ldirname = *dirnamep;
861*15b117eaSkettenis 
862*15b117eaSkettenis   if (*ldirname == '/')
863*15b117eaSkettenis     return 0;
864*15b117eaSkettenis 
865*15b117eaSkettenis   n = get_inferior_cwd (cwd, sizeof(cwd) - 1);
866*15b117eaSkettenis   if (n < 0)
867*15b117eaSkettenis     return 0;
868*15b117eaSkettenis   if (n == 0)	/* current directory */
869*15b117eaSkettenis     {
870*15b117eaSkettenis       cwd[0] = '.';
871*15b117eaSkettenis       cwd[1] = '\0';
872*15b117eaSkettenis       n = 1;
873*15b117eaSkettenis     }
874*15b117eaSkettenis 
875*15b117eaSkettenis   /* Minimally canonicalize ldirname by removing leading `./' */
876*15b117eaSkettenis   for (ld = ldirname; *ld; )
877*15b117eaSkettenis     {
878*15b117eaSkettenis       if (ISDIRSEP (ld[0]))
879*15b117eaSkettenis 	ld++;
880*15b117eaSkettenis       else if (ld[0] == '.' && PATHSEP(ld[1]))
881*15b117eaSkettenis 	ld++;
882*15b117eaSkettenis       else
883*15b117eaSkettenis 	break;
884*15b117eaSkettenis     }
885*15b117eaSkettenis   ldlen = (ld && *ld) ? strlen (ld) : 0;
886*15b117eaSkettenis 
887*15b117eaSkettenis   retdir = (char *)malloc (n + ldlen + 3);
888*15b117eaSkettenis   if (retdir == 0)
889*15b117eaSkettenis     return 0;
890*15b117eaSkettenis   if (ldlen)
891*15b117eaSkettenis     sprintf (retdir, "%s/%s", cwd, ld);
892*15b117eaSkettenis   else
893*15b117eaSkettenis     strcpy (retdir, cwd);
894*15b117eaSkettenis   free (ldirname);
895*15b117eaSkettenis 
896*15b117eaSkettenis   *dirnamep = retdir;
897*15b117eaSkettenis 
898*15b117eaSkettenis   DPRINT1("rl_directory_rewrite_hook returns %s\n", retdir);
899*15b117eaSkettenis   return 1;
900*15b117eaSkettenis }
901*15b117eaSkettenis 
902*15b117eaSkettenis /* Translate *DIRNAMEP to be relative to the inferior's CWD.  Leave a trailing
903*15b117eaSkettenis    slash on the result. */
904*15b117eaSkettenis static int
rlfe_directory_completion_hook(dirnamep)905*15b117eaSkettenis rlfe_directory_completion_hook (dirnamep)
906*15b117eaSkettenis      char **dirnamep;
907*15b117eaSkettenis {
908*15b117eaSkettenis   char *ldirname, *retdir;
909*15b117eaSkettenis   int n, ldlen;
910*15b117eaSkettenis 
911*15b117eaSkettenis   ldirname = *dirnamep;
912*15b117eaSkettenis 
913*15b117eaSkettenis   if (*ldirname == '/')
914*15b117eaSkettenis     return 0;
915*15b117eaSkettenis 
916*15b117eaSkettenis   n = rlfe_directory_rewrite_hook (dirnamep);
917*15b117eaSkettenis   if (n == 0)
918*15b117eaSkettenis     return 0;
919*15b117eaSkettenis 
920*15b117eaSkettenis   ldirname = *dirnamep;
921*15b117eaSkettenis   ldlen = (ldirname && *ldirname) ? strlen (ldirname) : 0;
922*15b117eaSkettenis 
923*15b117eaSkettenis   if (ldlen == 0 || ldirname[ldlen - 1] != '/')
924*15b117eaSkettenis     {
925*15b117eaSkettenis       retdir = (char *)malloc (ldlen + 3);
926*15b117eaSkettenis       if (retdir == 0)
927*15b117eaSkettenis 	return 0;
928*15b117eaSkettenis       if (ldlen)
929*15b117eaSkettenis 	strcpy (retdir, ldirname);
930*15b117eaSkettenis       else
931*15b117eaSkettenis 	retdir[ldlen++] = '.';
932*15b117eaSkettenis       retdir[ldlen] = '/';
933*15b117eaSkettenis       retdir[ldlen+1] = '\0';
934*15b117eaSkettenis       free (ldirname);
935*15b117eaSkettenis 
936*15b117eaSkettenis       *dirnamep = retdir;
937*15b117eaSkettenis     }
938*15b117eaSkettenis 
939*15b117eaSkettenis   DPRINT1("rl_directory_completion_hook returns %s\n", retdir);
940*15b117eaSkettenis   return 1;
941*15b117eaSkettenis }
942*15b117eaSkettenis 
943*15b117eaSkettenis static char *
rlfe_filename_completion_function(text,state)944*15b117eaSkettenis rlfe_filename_completion_function (text, state)
945*15b117eaSkettenis      const char *text;
946*15b117eaSkettenis      int state;
947*15b117eaSkettenis {
948*15b117eaSkettenis   static DIR *directory;
949*15b117eaSkettenis   static char *filename = (char *)NULL;
950*15b117eaSkettenis   static char *dirname = (char *)NULL, *ud = (char *)NULL;
951*15b117eaSkettenis   static int flen, udlen;
952*15b117eaSkettenis   char *temp;
953*15b117eaSkettenis   struct dirent *dentry;
954*15b117eaSkettenis 
955*15b117eaSkettenis   if (state == 0)
956*15b117eaSkettenis     {
957*15b117eaSkettenis       if (directory)
958*15b117eaSkettenis 	{
959*15b117eaSkettenis 	  closedir (directory);
960*15b117eaSkettenis 	  directory = 0;
961*15b117eaSkettenis 	}
962*15b117eaSkettenis       FREE (dirname);
963*15b117eaSkettenis       FREE (filename);
964*15b117eaSkettenis       FREE (ud);
965*15b117eaSkettenis 
966*15b117eaSkettenis       if (text && *text)
967*15b117eaSkettenis         STRDUP (filename, text);
968*15b117eaSkettenis       else
969*15b117eaSkettenis 	{
970*15b117eaSkettenis 	  filename = malloc(1);
971*15b117eaSkettenis 	  if (filename == 0)
972*15b117eaSkettenis 	    return ((char *)NULL);
973*15b117eaSkettenis 	  filename[0] = '\0';
974*15b117eaSkettenis 	}
975*15b117eaSkettenis       dirname = (text && *text) ? strdup (text) : strdup (".");
976*15b117eaSkettenis       if (dirname == 0)
977*15b117eaSkettenis         return ((char *)NULL);
978*15b117eaSkettenis 
979*15b117eaSkettenis       temp = strrchr (dirname, '/');
980*15b117eaSkettenis       if (temp)
981*15b117eaSkettenis 	{
982*15b117eaSkettenis 	  strcpy (filename, ++temp);
983*15b117eaSkettenis 	  *temp = '\0';
984*15b117eaSkettenis 	}
985*15b117eaSkettenis       else
986*15b117eaSkettenis 	{
987*15b117eaSkettenis 	  dirname[0] = '.';
988*15b117eaSkettenis 	  dirname[1] = '\0';
989*15b117eaSkettenis 	}
990*15b117eaSkettenis 
991*15b117eaSkettenis       STRDUP (ud, dirname);
992*15b117eaSkettenis       udlen = strlen (ud);
993*15b117eaSkettenis 
994*15b117eaSkettenis       rlfe_directory_completion_hook (&dirname);
995*15b117eaSkettenis 
996*15b117eaSkettenis       directory = opendir (dirname);
997*15b117eaSkettenis       flen = strlen (filename);
998*15b117eaSkettenis 
999*15b117eaSkettenis       rl_filename_completion_desired = 1;
1000*15b117eaSkettenis     }
1001*15b117eaSkettenis 
1002*15b117eaSkettenis   dentry = 0;
1003*15b117eaSkettenis   while (directory && (dentry = readdir (directory)))
1004*15b117eaSkettenis     {
1005*15b117eaSkettenis       if (flen == 0)
1006*15b117eaSkettenis 	{
1007*15b117eaSkettenis           if (DOT_OR_DOTDOT(dentry->d_name) == 0)
1008*15b117eaSkettenis             break;
1009*15b117eaSkettenis 	}
1010*15b117eaSkettenis       else
1011*15b117eaSkettenis 	{
1012*15b117eaSkettenis 	  if ((dentry->d_name[0] == filename[0]) &&
1013*15b117eaSkettenis 	      (strlen (dentry->d_name) >= flen) &&
1014*15b117eaSkettenis 	      (strncmp (filename, dentry->d_name, flen) == 0))
1015*15b117eaSkettenis 	    break;
1016*15b117eaSkettenis 	}
1017*15b117eaSkettenis     }
1018*15b117eaSkettenis 
1019*15b117eaSkettenis   if (dentry == 0)
1020*15b117eaSkettenis     {
1021*15b117eaSkettenis       if (directory)
1022*15b117eaSkettenis 	{
1023*15b117eaSkettenis 	  closedir (directory);
1024*15b117eaSkettenis 	  directory = 0;
1025*15b117eaSkettenis 	}
1026*15b117eaSkettenis       FREE (dirname);
1027*15b117eaSkettenis       FREE (filename);
1028*15b117eaSkettenis       FREE (ud);
1029*15b117eaSkettenis       dirname = filename = ud = 0;
1030*15b117eaSkettenis       return ((char *)NULL);
1031*15b117eaSkettenis     }
1032*15b117eaSkettenis 
1033*15b117eaSkettenis   if (ud == 0 || (ud[0] == '.' && ud[1] == '\0'))
1034*15b117eaSkettenis     temp = strdup (dentry->d_name);
1035*15b117eaSkettenis   else
1036*15b117eaSkettenis     {
1037*15b117eaSkettenis       temp = malloc (1 + udlen + strlen (dentry->d_name));
1038*15b117eaSkettenis       strcpy (temp, ud);
1039*15b117eaSkettenis       strcpy (temp + udlen, dentry->d_name);
1040*15b117eaSkettenis     }
1041*15b117eaSkettenis   return (temp);
1042*15b117eaSkettenis }
1043