1/* A front-end using readline to "cook" input lines for Kawa.
2 *
3 * Copyright (C) 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
14/* PROBLEMS/TODO:
15 *
16 * Only tested under Linux;  needs to be ported.
17 *
18 * When running mc -c under the Linux console, mc does not recognize
19 * mouse clicks, which mc does when not running under fep.
20 *
21 * Pasting selected text containing tabs is like hitting the tab character,
22 * which invokes readline completion.  We don't want this.  I don't know
23 * if this is fixable without integrating fep into a terminal emulator.
24 *
25 * Echo suppression is a kludge, but can only be avoided with better kernel
26 * support: We need a tty mode to disable "real" echoing, while still
27 * letting the inferior think its tty driver to doing echoing.
28 * Stevens's book claims SCR$ and BSD4.3+ have TIOCREMOTE.
29 *
30 * The latest readline may have some hooks we can use to avoid having
31 * to back up the prompt.
32 *
33 * Desirable readline feature:  When in cooked no-echo mode (e.g. password),
34 * echo characters are they are types with '*', but remove them when done.
35 *
36 * A synchronous output while we're editing an input line should be
37 * inserted in the output view *before* the input line, so that the
38 * lines being edited (with the prompt) float at the end of the input.
39 *
40 * A "page mode" option to emulate more/less behavior:  At each page of
41 * output, pause for a user command.  This required parsing the output
42 * to keep track of line lengths.  It also requires remembering the
43 * output, if we want an option to scroll back, which suggests that
44 * this should be integrated with a terminal emulator like xterm.
45 */
46
47#ifdef HAVE_CONFIG_H
48#  include <config.h>
49#endif
50
51#include <stdio.h>
52#include <fcntl.h>
53#include <sys/types.h>
54#include <sys/socket.h>
55#include <netinet/in.h>
56#include <arpa/inet.h>
57#include <signal.h>
58#include <netdb.h>
59#include <stdlib.h>
60#include <errno.h>
61#include <grp.h>
62#include <string.h>
63#include <sys/stat.h>
64#include <unistd.h>
65#include <sys/ioctl.h>
66#include <termios.h>
67#include <limits.h>
68#include <dirent.h>
69
70#ifdef READLINE_LIBRARY
71#  include "readline.h"
72#  include "history.h"
73#else
74#  include <readline/readline.h>
75#  include <readline/history.h>
76#endif
77
78#ifndef COMMAND
79#define COMMAND "/bin/sh"
80#endif
81#ifndef COMMAND_ARGS
82#define COMMAND_ARGS COMMAND
83#endif
84
85#ifndef HAVE_MEMMOVE
86#ifndef memmove
87#  if __GNUC__ > 1
88#    define memmove(d, s, n)	__builtin_memcpy(d, s, n)
89#  else
90#    define memmove(d, s, n)	memcpy(d, s, n)
91#  endif
92#else
93#  define memmove(d, s, n)	memcpy(d, s, n)
94#endif
95#endif
96
97#define APPLICATION_NAME "Rlfe"
98
99#ifndef errno
100extern int errno;
101#endif
102
103extern int optind;
104extern char *optarg;
105
106static char *progname;
107static char *progversion;
108
109static int in_from_inferior_fd;
110static int out_to_inferior_fd;
111
112/* Unfortunately, we cannot safely display echo from the inferior process.
113   The reason is that the echo bit in the pty is "owned" by the inferior,
114   and if we try to turn it off, we could confuse the inferior.
115   Thus, when echoing, we get echo twice:  First readline echoes while
116   we're actually editing. Then we send the line to the inferior, and the
117   terminal driver send back an extra echo.
118   The work-around is to remember the input lines, and when we see that
119   line come back, we supress the output.
120   A better solution (supposedly available on SVR4) would be a smarter
121   terminal driver, with more flags ... */
122#define ECHO_SUPPRESS_MAX 1024
123char echo_suppress_buffer[ECHO_SUPPRESS_MAX];
124int echo_suppress_start = 0;
125int echo_suppress_limit = 0;
126
127/* #define DEBUG */
128
129static FILE *logfile = NULL;
130
131#ifdef DEBUG
132FILE *debugfile = NULL;
133#define DPRINT0(FMT) (fprintf(debugfile, FMT), fflush(debugfile))
134#define DPRINT1(FMT, V1) (fprintf(debugfile, FMT, V1), fflush(debugfile))
135#define DPRINT2(FMT, V1, V2) (fprintf(debugfile, FMT, V1, V2), fflush(debugfile))
136#else
137#define DPRINT0(FMT) /* Do nothing */
138#define DPRINT1(FMT, V1) /* Do nothing */
139#define DPRINT2(FMT, V1, V2) /* Do nothing */
140#endif
141
142struct termios orig_term;
143
144static int rlfe_directory_completion_hook __P((char **));
145static int rlfe_directory_rewrite_hook __P((char **));
146static char *rlfe_filename_completion_function __P((const char *, int));
147
148/* Pid of child process. */
149static pid_t child = -1;
150
151static void
152sig_child (int signo)
153{
154  int status;
155  wait (&status);
156  DPRINT0 ("(Child process died.)\n");
157  tcsetattr(STDIN_FILENO, TCSANOW, &orig_term);
158  exit (0);
159}
160
161volatile int propagate_sigwinch = 0;
162
163/* sigwinch_handler
164 * propagate window size changes from input file descriptor to
165 * master side of pty.
166 */
167void sigwinch_handler(int signal) {
168   propagate_sigwinch = 1;
169}
170
171/* get_master_pty() takes a double-indirect character pointer in which
172 * to put a slave name, and returns an integer file descriptor.
173 * If it returns < 0, an error has occurred.
174 * Otherwise, it has returned the master pty file descriptor, and fills
175 * in *name with the name of the corresponding slave pty.
176 * Once the slave pty has been opened, you are responsible to free *name.
177 */
178
179int get_master_pty(char **name) {
180   int i, j;
181   /* default to returning error */
182   int master = -1;
183
184   /* create a dummy name to fill in */
185   *name = strdup("/dev/ptyXX");
186
187   /* search for an unused pty */
188   for (i=0; i<16 && master <= 0; i++) {
189      for (j=0; j<16 && master <= 0; j++) {
190         (*name)[5] = 'p';
191         (*name)[8] = "pqrstuvwxyzPQRST"[i];
192         (*name)[9] = "0123456789abcdef"[j];
193         /* open the master pty */
194         if ((master = open(*name, O_RDWR)) < 0) {
195            if (errno == ENOENT) {
196               /* we are out of pty devices */
197               free (*name);
198               return (master);
199            }
200         }
201         else {
202           /* By substituting a letter, we change the master pty
203            * name into the slave pty name.
204            */
205           (*name)[5] = 't';
206           if (access(*name, R_OK|W_OK) != 0)
207             {
208               close(master);
209               master = -1;
210             }
211         }
212      }
213   }
214   if ((master < 0) && (i == 16) && (j == 16)) {
215      /* must have tried every pty unsuccessfully */
216      free (*name);
217      return (master);
218   }
219
220   (*name)[5] = 't';
221
222   return (master);
223}
224
225/* get_slave_pty() returns an integer file descriptor.
226 * If it returns < 0, an error has occurred.
227 * Otherwise, it has returned the slave file descriptor.
228 */
229
230int get_slave_pty(char *name) {
231   struct group *gptr;
232   gid_t gid;
233   int slave = -1;
234
235   /* chown/chmod the corresponding pty, if possible.
236    * This will only work if the process has root permissions.
237    * Alternatively, write and exec a small setuid program that
238    * does just this.
239    */
240   if ((gptr = getgrnam("tty")) != 0) {
241      gid = gptr->gr_gid;
242   } else {
243      /* if the tty group does not exist, don't change the
244       * group on the slave pty, only the owner
245       */
246      gid = -1;
247   }
248
249   /* Note that we do not check for errors here.  If this is code
250    * where these actions are critical, check for errors!
251    */
252   chown(name, getuid(), gid);
253   /* This code only makes the slave read/writeable for the user.
254    * If this is for an interactive shell that will want to
255    * receive "write" and "wall" messages, OR S_IWGRP into the
256    * second argument below.
257    */
258   chmod(name, S_IRUSR|S_IWUSR);
259
260   /* open the corresponding slave pty */
261   slave = open(name, O_RDWR);
262   return (slave);
263}
264
265/* Certain special characters, such as ctrl/C, we want to pass directly
266   to the inferior, rather than letting readline handle them. */
267
268static char special_chars[20];
269static int special_chars_count;
270
271static void
272add_special_char(int ch)
273{
274  if (ch != 0)
275    special_chars[special_chars_count++] = ch;
276}
277
278static int eof_char;
279
280static int
281is_special_char(int ch)
282{
283  int i;
284#if 0
285  if (ch == eof_char && rl_point == rl_end)
286    return 1;
287#endif
288  for (i = special_chars_count;  --i >= 0; )
289    if (special_chars[i] == ch)
290      return 1;
291  return 0;
292}
293
294static char buf[1024];
295/* buf[0 .. buf_count-1] is the what has been emitted on the current line.
296   It is used as the readline prompt. */
297static int buf_count = 0;
298
299int num_keys = 0;
300
301static void
302null_prep_terminal (int meta)
303{
304}
305
306static void
307null_deprep_terminal ()
308{
309}
310
311char pending_special_char;
312
313static void
314line_handler (char *line)
315{
316  if (line == NULL)
317    {
318      char buf[1];
319      DPRINT0("saw eof!\n");
320      buf[0] = '\004'; /* ctrl/d */
321      write (out_to_inferior_fd, buf, 1);
322    }
323  else
324    {
325      static char enter[] = "\r";
326      /*  Send line to inferior: */
327      int length = strlen (line);
328      if (length > ECHO_SUPPRESS_MAX-2)
329	{
330	  echo_suppress_start = 0;
331	  echo_suppress_limit = 0;
332	}
333      else
334	{
335	  if (echo_suppress_limit + length > ECHO_SUPPRESS_MAX - 2)
336	    {
337	      if (echo_suppress_limit - echo_suppress_start + length
338		  <= ECHO_SUPPRESS_MAX - 2)
339		{
340		  memmove (echo_suppress_buffer,
341			   echo_suppress_buffer + echo_suppress_start,
342			   echo_suppress_limit - echo_suppress_start);
343		  echo_suppress_limit -= echo_suppress_start;
344		  echo_suppress_start = 0;
345		}
346	      else
347		{
348		  echo_suppress_limit = 0;
349		}
350	      echo_suppress_start = 0;
351	    }
352	  memcpy (echo_suppress_buffer + echo_suppress_limit,
353		  line, length);
354	  echo_suppress_limit += length;
355	  echo_suppress_buffer[echo_suppress_limit++] = '\r';
356	  echo_suppress_buffer[echo_suppress_limit++] = '\n';
357	}
358      write (out_to_inferior_fd, line, length);
359      if (pending_special_char == 0)
360        {
361          write (out_to_inferior_fd, enter, sizeof(enter)-1);
362          if (*line)
363            add_history (line);
364        }
365      free (line);
366    }
367  rl_callback_handler_remove ();
368  buf_count = 0;
369  num_keys = 0;
370  if (pending_special_char != 0)
371    {
372      write (out_to_inferior_fd, &pending_special_char, 1);
373      pending_special_char = 0;
374    }
375}
376
377/* Value of rl_getc_function.
378   Use this because readline should read from stdin, not rl_instream,
379   points to the pty (so readline has monitor its terminal modes). */
380
381int
382my_rl_getc (FILE *dummy)
383{
384  int ch = rl_getc (stdin);
385  if (is_special_char (ch))
386    {
387      pending_special_char = ch;
388      return '\r';
389    }
390  return ch;
391}
392
393static void
394usage()
395{
396  fprintf (stderr, "%s: usage: %s [-l filename] [-a] [-n appname] [-hv] [command [arguments...]]\n",
397		   progname, progname);
398}
399
400int
401main(int argc, char** argv)
402{
403  char *path;
404  int i, append;
405  int master;
406  char *name, *logfname, *appname;
407  int in_from_tty_fd;
408  struct sigaction act;
409  struct winsize ws;
410  struct termios t;
411  int maxfd;
412  fd_set in_set;
413  static char empty_string[1] = "";
414  char *prompt = empty_string;
415  int ioctl_err = 0;
416
417  if ((progname = strrchr (argv[0], '/')) == 0)
418    progname = argv[0];
419  else
420    progname++;
421  progversion = RL_LIBRARY_VERSION;
422
423  append = 0;
424  appname = APPLICATION_NAME;
425  logfname = (char *)NULL;
426
427  while ((i = getopt (argc, argv, "ahl:n:v")) != EOF)
428    {
429      switch (i)
430	{
431	case 'l':
432	  logfname = optarg;
433	  break;
434	case 'n':
435	  appname = optarg;
436	  break;
437	case 'a':
438	  append = 1;
439	  break;
440	case 'h':
441	  usage ();
442	  exit (0);
443	case 'v':
444	  fprintf (stderr, "%s version %s\n", progname, progversion);
445	  exit (0);
446	default:
447	  usage ();
448	  exit (2);
449	}
450    }
451
452  argc -= optind;
453  argv += optind;
454
455  if (logfname)
456    {
457      logfile = fopen (logfname, append ? "a" : "w");
458      if (logfile == 0)
459	fprintf (stderr, "%s: warning: could not open log file %s: %s\n",
460			 progname, logfname, strerror (errno));
461    }
462
463  rl_readline_name = appname;
464
465#ifdef DEBUG
466  debugfile = fopen("LOG", "w");
467#endif
468
469  if ((master = get_master_pty(&name)) < 0)
470    {
471      perror("ptypair: could not open master pty");
472      exit(1);
473    }
474
475  DPRINT1("pty name: '%s'\n", name);
476
477  /* set up SIGWINCH handler */
478  act.sa_handler = sigwinch_handler;
479  sigemptyset(&(act.sa_mask));
480  act.sa_flags = 0;
481  if (sigaction(SIGWINCH, &act, NULL) < 0)
482    {
483      perror("ptypair: could not handle SIGWINCH ");
484      exit(1);
485    }
486
487  if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) < 0)
488    {
489      perror("ptypair: could not get window size");
490      exit(1);
491    }
492
493  if ((child = fork()) < 0)
494    {
495      perror("cannot fork");
496      exit(1);
497    }
498
499  if (child == 0)
500    {
501      int slave;  /* file descriptor for slave pty */
502
503      /* We are in the child process */
504      close(master);
505
506#ifdef TIOCSCTTY
507      if ((slave = get_slave_pty(name)) < 0)
508	{
509	  perror("ptypair: could not open slave pty");
510	  exit(1);
511	}
512      free(name);
513#endif
514
515      /* We need to make this process a session group leader, because
516       * it is on a new PTY, and things like job control simply will
517       * not work correctly unless there is a session group leader
518       * and process group leader (which a session group leader
519       * automatically is). This also disassociates us from our old
520       * controlling tty.
521       */
522      if (setsid() < 0)
523	{
524	  perror("could not set session leader");
525	}
526
527      /* Tie us to our new controlling tty. */
528#ifdef TIOCSCTTY
529      if (ioctl(slave, TIOCSCTTY, NULL))
530	{
531	  perror("could not set new controlling tty");
532	}
533#else
534      if ((slave = get_slave_pty(name)) < 0)
535	{
536	  perror("ptypair: could not open slave pty");
537	  exit(1);
538	}
539      free(name);
540#endif
541
542      /* make slave pty be standard in, out, and error */
543      dup2(slave, STDIN_FILENO);
544      dup2(slave, STDOUT_FILENO);
545      dup2(slave, STDERR_FILENO);
546
547      /* at this point the slave pty should be standard input */
548      if (slave > 2)
549	{
550	  close(slave);
551	}
552
553      /* Try to restore window size; failure isn't critical */
554      if (ioctl(STDOUT_FILENO, TIOCSWINSZ, &ws) < 0)
555	{
556	  perror("could not restore window size");
557	}
558
559      /* now start the shell */
560      {
561	static char* command_args[] = { COMMAND_ARGS, NULL };
562	if (argc < 1)
563	  execvp(COMMAND, command_args);
564	else
565	  execvp(argv[0], &argv[0]);
566      }
567
568      /* should never be reached */
569      exit(1);
570    }
571
572  /* parent */
573  signal (SIGCHLD, sig_child);
574  free(name);
575
576  /* Note that we only set termios settings for standard input;
577   * the master side of a pty is NOT a tty.
578   */
579  tcgetattr(STDIN_FILENO, &orig_term);
580
581  t = orig_term;
582  eof_char = t.c_cc[VEOF];
583  /*  add_special_char(t.c_cc[VEOF]);*/
584  add_special_char(t.c_cc[VINTR]);
585  add_special_char(t.c_cc[VQUIT]);
586  add_special_char(t.c_cc[VSUSP]);
587#if defined (VDISCARD)
588  add_special_char(t.c_cc[VDISCARD]);
589#endif
590
591#if 0
592  t.c_lflag |= (ICANON | ISIG | ECHO | ECHOCTL | ECHOE | \
593		ECHOK | ECHOKE | ECHONL | ECHOPRT );
594#else
595  t.c_lflag &= ~(ICANON | ISIG | ECHO | ECHOCTL | ECHOE | \
596		 ECHOK | ECHOKE | ECHONL | ECHOPRT );
597#endif
598  t.c_iflag |= IGNBRK;
599  t.c_cc[VMIN] = 1;
600  t.c_cc[VTIME] = 0;
601  tcsetattr(STDIN_FILENO, TCSANOW, &t);
602  in_from_inferior_fd = master;
603  out_to_inferior_fd = master;
604  rl_instream = fdopen (master, "r");
605  rl_getc_function = my_rl_getc;
606
607  rl_prep_term_function = null_prep_terminal;
608  rl_deprep_term_function = null_deprep_terminal;
609  rl_callback_handler_install (prompt, line_handler);
610
611#if 1
612  rl_directory_completion_hook = rlfe_directory_completion_hook;
613  rl_completion_entry_function = rlfe_filename_completion_function;
614#else
615  rl_directory_rewrite_hook = rlfe_directory_rewrite_hook;
616#endif
617
618  in_from_tty_fd = STDIN_FILENO;
619  FD_ZERO (&in_set);
620  maxfd = in_from_inferior_fd > in_from_tty_fd ? in_from_inferior_fd
621    : in_from_tty_fd;
622  for (;;)
623    {
624      int num;
625      FD_SET (in_from_inferior_fd, &in_set);
626      FD_SET (in_from_tty_fd, &in_set);
627
628      num = select(maxfd+1, &in_set, NULL, NULL, NULL);
629
630      if (propagate_sigwinch)
631	{
632	  struct winsize ws;
633	  if (ioctl (STDIN_FILENO, TIOCGWINSZ, &ws) >= 0)
634	    {
635	      ioctl (master, TIOCSWINSZ, &ws);
636	    }
637	  propagate_sigwinch = 0;
638	  continue;
639	}
640
641      if (num <= 0)
642	{
643	  perror ("select");
644	  exit (-1);
645	}
646      if (FD_ISSET (in_from_tty_fd, &in_set))
647	{
648	  extern int readline_echoing_p;
649	  struct termios term_master;
650	  int do_canon = 1;
651	  int ioctl_ret;
652
653	  DPRINT1("[tty avail num_keys:%d]\n", num_keys);
654
655	  /* If we can't get tty modes for the master side of the pty, we
656	     can't handle non-canonical-mode programs.  Always assume the
657	     master is in canonical echo mode if we can't tell. */
658	  ioctl_ret = tcgetattr(master, &term_master);
659
660	  if (ioctl_ret >= 0)
661	    {
662	      DPRINT2 ("echo:%d, canon:%d\n",
663			(term_master.c_lflag & ECHO) != 0,
664			(term_master.c_lflag & ICANON) != 0);
665	      do_canon = (term_master.c_lflag & ICANON) != 0;
666	      readline_echoing_p = (term_master.c_lflag & ECHO) != 0;
667	    }
668	  else
669	    {
670	      if (ioctl_err == 0)
671		DPRINT1("tcgetattr on master fd failed: errno = %d\n", errno);
672	      ioctl_err = 1;
673	    }
674
675	  if (do_canon == 0 && num_keys == 0)
676	    {
677	      char ch[10];
678	      int count = read (STDIN_FILENO, ch, sizeof(ch));
679	      write (out_to_inferior_fd, ch, count);
680	    }
681	  else
682	    {
683	      if (num_keys == 0)
684		{
685		  int i;
686		  /* Re-install callback handler for new prompt. */
687		  if (prompt != empty_string)
688		    free (prompt);
689		  prompt = malloc (buf_count + 1);
690		  if (prompt == NULL)
691		    prompt = empty_string;
692		  else
693		    {
694		      memcpy (prompt, buf, buf_count);
695		      prompt[buf_count] = '\0';
696		      DPRINT1("New prompt '%s'\n", prompt);
697#if 0 /* ifdef HAVE_RL_ALREADY_PROMPTED -- doesn't work */
698		      rl_already_prompted = buf_count > 0;
699#else
700		      if (buf_count > 0)
701			write (1, "\r", 1);
702#endif
703		    }
704		  rl_callback_handler_install (prompt, line_handler);
705		}
706	      num_keys++;
707	      rl_callback_read_char ();
708	    }
709	}
710      else /* input from inferior. */
711	{
712	  int i;
713	  int count;
714	  int old_count;
715	  if (buf_count > (sizeof(buf) >> 2))
716	    buf_count = 0;
717	  count = read (in_from_inferior_fd, buf+buf_count,
718			sizeof(buf) - buf_count);
719	  if (count <= 0)
720	    {
721	      DPRINT0 ("(Connection closed by foreign host.)\n");
722	      tcsetattr(STDIN_FILENO, TCSANOW, &orig_term);
723	      exit (0);
724	    }
725	  old_count = buf_count;
726
727	  /* Do some minimal carriage return translation and backspace
728	     processing before logging the input line. */
729	  if (logfile)
730	    {
731#ifndef __GNUC__
732	      char *b;
733#else
734	      char b[count + 1];
735#endif
736	      int i, j;
737
738#ifndef __GNUC__
739	      b = malloc (count + 1);
740	      if (b) {
741#endif
742	      for (i = 0; i < count; i++)
743	        b[i] = buf[buf_count + i];
744	      b[i] = '\0';
745	      for (i = j = 0; i <= count; i++)
746		{
747		  if (b[i] == '\r')
748		    {
749		      if (b[i+1] != '\n')
750		        b[j++] = '\n';
751		    }
752		  else if (b[i] == '\b')
753		    {
754		      if (i)
755			j--;
756		    }
757		  else
758		    b[j++] = b[i];
759		}
760	      fprintf (logfile, "%s", b);
761
762#ifndef __GNUC__
763	      free (b);
764	      }
765#endif
766	    }
767
768          /* Look for any pending echo that we need to suppress. */
769	  while (echo_suppress_start < echo_suppress_limit
770		 && count > 0
771		 && buf[buf_count] == echo_suppress_buffer[echo_suppress_start])
772	    {
773	      count--;
774	      buf_count++;
775	      echo_suppress_start++;
776	    }
777
778          /* Write to the terminal anything that was not suppressed. */
779          if (count > 0)
780            write (1, buf + buf_count, count);
781
782          /* Finally, look for a prompt candidate.
783           * When we get around to going input (from the keyboard),
784           * we will consider the prompt to be anything since the last
785           * line terminator.  So we need to save that text in the
786           * initial part of buf.  However, anything before the
787           * most recent end-of-line is not interesting. */
788	  buf_count += count;
789#if 1
790	  for (i = buf_count;  --i >= old_count; )
791#else
792	  for (i = buf_count - 1;  i-- >= buf_count - count; )
793#endif
794	    {
795	      if (buf[i] == '\n' || buf[i] == '\r')
796		{
797		  i++;
798		  memmove (buf, buf+i, buf_count - i);
799		  buf_count -= i;
800		  break;
801		}
802	    }
803	  DPRINT2("-> i: %d, buf_count: %d\n", i, buf_count);
804	}
805    }
806}
807
808/*
809 *
810 * FILENAME COMPLETION FOR RLFE
811 *
812 */
813
814#ifndef PATH_MAX
815#  define PATH_MAX 1024
816#endif
817
818#define DIRSEP		'/'
819#define ISDIRSEP(x)	((x) == '/')
820#define PATHSEP(x)	(ISDIRSEP(x) || (x) == 0)
821
822#define DOT_OR_DOTDOT(x) \
823	((x)[0] == '.' && (PATHSEP((x)[1]) || \
824			  ((x)[1] == '.' && PATHSEP((x)[2]))))
825
826#define FREE(x)		if (x) free(x)
827
828#define STRDUP(s, x)	do { \
829			  s = strdup (x);\
830			  if (s == 0) \
831			    return ((char *)NULL); \
832			} while (0)
833
834static int
835get_inferior_cwd (path, psize)
836     char *path;
837     size_t psize;
838{
839  int n;
840  static char procfsbuf[PATH_MAX] = { '\0' };
841
842  if (procfsbuf[0] == '\0')
843    sprintf (procfsbuf, "/proc/%d/cwd", (int)child);
844  n = readlink (procfsbuf, path, psize);
845  if (n < 0)
846    return n;
847  if (n > psize)
848    return -1;
849  path[n] = '\0';
850  return n;
851}
852
853static int
854rlfe_directory_rewrite_hook (dirnamep)
855     char **dirnamep;
856{
857  char *ldirname, cwd[PATH_MAX], *retdir, *ld;
858  int n, ldlen;
859
860  ldirname = *dirnamep;
861
862  if (*ldirname == '/')
863    return 0;
864
865  n = get_inferior_cwd (cwd, sizeof(cwd) - 1);
866  if (n < 0)
867    return 0;
868  if (n == 0)	/* current directory */
869    {
870      cwd[0] = '.';
871      cwd[1] = '\0';
872      n = 1;
873    }
874
875  /* Minimally canonicalize ldirname by removing leading `./' */
876  for (ld = ldirname; *ld; )
877    {
878      if (ISDIRSEP (ld[0]))
879	ld++;
880      else if (ld[0] == '.' && PATHSEP(ld[1]))
881	ld++;
882      else
883	break;
884    }
885  ldlen = (ld && *ld) ? strlen (ld) : 0;
886
887  retdir = (char *)malloc (n + ldlen + 3);
888  if (retdir == 0)
889    return 0;
890  if (ldlen)
891    sprintf (retdir, "%s/%s", cwd, ld);
892  else
893    strcpy (retdir, cwd);
894  free (ldirname);
895
896  *dirnamep = retdir;
897
898  DPRINT1("rl_directory_rewrite_hook returns %s\n", retdir);
899  return 1;
900}
901
902/* Translate *DIRNAMEP to be relative to the inferior's CWD.  Leave a trailing
903   slash on the result. */
904static int
905rlfe_directory_completion_hook (dirnamep)
906     char **dirnamep;
907{
908  char *ldirname, *retdir;
909  int n, ldlen;
910
911  ldirname = *dirnamep;
912
913  if (*ldirname == '/')
914    return 0;
915
916  n = rlfe_directory_rewrite_hook (dirnamep);
917  if (n == 0)
918    return 0;
919
920  ldirname = *dirnamep;
921  ldlen = (ldirname && *ldirname) ? strlen (ldirname) : 0;
922
923  if (ldlen == 0 || ldirname[ldlen - 1] != '/')
924    {
925      retdir = (char *)malloc (ldlen + 3);
926      if (retdir == 0)
927	return 0;
928      if (ldlen)
929	strcpy (retdir, ldirname);
930      else
931	retdir[ldlen++] = '.';
932      retdir[ldlen] = '/';
933      retdir[ldlen+1] = '\0';
934      free (ldirname);
935
936      *dirnamep = retdir;
937    }
938
939  DPRINT1("rl_directory_completion_hook returns %s\n", retdir);
940  return 1;
941}
942
943static char *
944rlfe_filename_completion_function (text, state)
945     const char *text;
946     int state;
947{
948  static DIR *directory;
949  static char *filename = (char *)NULL;
950  static char *dirname = (char *)NULL, *ud = (char *)NULL;
951  static int flen, udlen;
952  char *temp;
953  struct dirent *dentry;
954
955  if (state == 0)
956    {
957      if (directory)
958	{
959	  closedir (directory);
960	  directory = 0;
961	}
962      FREE (dirname);
963      FREE (filename);
964      FREE (ud);
965
966      if (text && *text)
967        STRDUP (filename, text);
968      else
969	{
970	  filename = malloc(1);
971	  if (filename == 0)
972	    return ((char *)NULL);
973	  filename[0] = '\0';
974	}
975      dirname = (text && *text) ? strdup (text) : strdup (".");
976      if (dirname == 0)
977        return ((char *)NULL);
978
979      temp = strrchr (dirname, '/');
980      if (temp)
981	{
982	  strcpy (filename, ++temp);
983	  *temp = '\0';
984	}
985      else
986	{
987	  dirname[0] = '.';
988	  dirname[1] = '\0';
989	}
990
991      STRDUP (ud, dirname);
992      udlen = strlen (ud);
993
994      rlfe_directory_completion_hook (&dirname);
995
996      directory = opendir (dirname);
997      flen = strlen (filename);
998
999      rl_filename_completion_desired = 1;
1000    }
1001
1002  dentry = 0;
1003  while (directory && (dentry = readdir (directory)))
1004    {
1005      if (flen == 0)
1006	{
1007          if (DOT_OR_DOTDOT(dentry->d_name) == 0)
1008            break;
1009	}
1010      else
1011	{
1012	  if ((dentry->d_name[0] == filename[0]) &&
1013	      (strlen (dentry->d_name) >= flen) &&
1014	      (strncmp (filename, dentry->d_name, flen) == 0))
1015	    break;
1016	}
1017    }
1018
1019  if (dentry == 0)
1020    {
1021      if (directory)
1022	{
1023	  closedir (directory);
1024	  directory = 0;
1025	}
1026      FREE (dirname);
1027      FREE (filename);
1028      FREE (ud);
1029      dirname = filename = ud = 0;
1030      return ((char *)NULL);
1031    }
1032
1033  if (ud == 0 || (ud[0] == '.' && ud[1] == '\0'))
1034    temp = strdup (dentry->d_name);
1035  else
1036    {
1037      temp = malloc (1 + udlen + strlen (dentry->d_name));
1038      strcpy (temp, ud);
1039      strcpy (temp + udlen, dentry->d_name);
1040    }
1041  return (temp);
1042}
1043