2006-05-05 H.J. Lu <hongjiu.lu@intel.com>
[deliverable/binutils-gdb.git] / readline / examples / rlfe.c
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
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 */
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
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
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
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
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
302 null_prep_terminal (int meta)
303 {
304 }
305
306 static void
307 null_deprep_terminal ()
308 {
309 }
310
311 char pending_special_char;
312
313 static void
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
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
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
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
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
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
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 *
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 }
This page took 0.072374 seconds and 4 git commands to generate.