xref: /openbsd/gnu/lib/libreadline/examples/rlfe.c (revision 15b117ea)
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
100 extern int errno;
101 #endif
102 
103 extern int optind;
104 extern char *optarg;
105 
106 static char *progname;
107 static char *progversion;
108 
109 static int in_from_inferior_fd;
110 static 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
123 char echo_suppress_buffer[ECHO_SUPPRESS_MAX];
124 int echo_suppress_start = 0;
125 int echo_suppress_limit = 0;
126 
127 /* #define DEBUG */
128 
129 static FILE *logfile = NULL;
130 
131 #ifdef DEBUG
132 FILE *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 
142 struct termios orig_term;
143 
144 static int rlfe_directory_completion_hook __P((char **));
145 static int rlfe_directory_rewrite_hook __P((char **));
146 static char *rlfe_filename_completion_function __P((const char *, int));
147 
148 /* Pid of child process. */
149 static pid_t child = -1;
150 
151 static void
sig_child(int signo)152 sig_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 
161 volatile int propagate_sigwinch = 0;
162 
163 /* sigwinch_handler
164  * propagate window size changes from input file descriptor to
165  * master side of pty.
166  */
sigwinch_handler(int signal)167 void 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 
get_master_pty(char ** name)179 int 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 
get_slave_pty(char * name)230 int 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 
268 static char special_chars[20];
269 static int special_chars_count;
270 
271 static void
add_special_char(int ch)272 add_special_char(int ch)
273 {
274   if (ch != 0)
275     special_chars[special_chars_count++] = ch;
276 }
277 
278 static int eof_char;
279 
280 static int
is_special_char(int ch)281 is_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 
294 static 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. */
297 static int buf_count = 0;
298 
299 int num_keys = 0;
300 
301 static void
null_prep_terminal(int meta)302 null_prep_terminal (int meta)
303 {
304 }
305 
306 static void
null_deprep_terminal()307 null_deprep_terminal ()
308 {
309 }
310 
311 char pending_special_char;
312 
313 static void
line_handler(char * line)314 line_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 
381 int
my_rl_getc(FILE * dummy)382 my_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 
393 static void
usage()394 usage()
395 {
396   fprintf (stderr, "%s: usage: %s [-l filename] [-a] [-n appname] [-hv] [command [arguments...]]\n",
397 		   progname, progname);
398 }
399 
400 int
main(int argc,char ** argv)401 main(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 
834 static int
get_inferior_cwd(path,psize)835 get_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 
853 static int
rlfe_directory_rewrite_hook(dirnamep)854 rlfe_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. */
904 static int
rlfe_directory_completion_hook(dirnamep)905 rlfe_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 
943 static char *
rlfe_filename_completion_function(text,state)944 rlfe_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