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 } 1043