source: trunk/third/readline/examples/rlfe.c @ 17010

Revision 17010, 24.2 KB checked in by ghudson, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r17009, which included commits to RCS files with non-trunk default branches.
Line 
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}
Note: See TracBrowser for help on using the repository browser.