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