another typo: too late at light corrections...:-(
[midnight-commander.git] / src / subshell.c
blobbfab8aa713b3ba1c61db287ec8c33411417b9d60
1 /* {{{ Copyright notice */
3 /* Concurrent shell support for the Midnight Commander
4 Copyright (C) 1994, 1995 Dugan Porter
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of Version 2 of the GNU General Public
8 License, as published by the Free Software Foundation.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 /* }}} */
22 #include <config.h>
23 #ifdef HAVE_SUBSHELL_SUPPORT
25 /* {{{ Declarations */
27 #ifndef _GNU_SOURCE
28 # define _GNU_SOURCE 1
29 #endif
31 #include <stdio.h>
32 #include <stdlib.h> /* For errno, putenv, etc. */
33 #include <errno.h> /* For errno on SunOS systems */
34 #include <termios.h> /* tcgetattr(), struct termios, etc. */
35 #include <sys/types.h> /* Required by unistd.h below */
36 #ifdef HAVE_SYS_IOCTL_H
37 # include <sys/ioctl.h> /* For ioctl() (surprise, surprise) */
38 #endif
39 #include <string.h> /* strstr(), strcpy(), etc. */
40 #include <signal.h> /* sigaction(), sigprocmask(), etc. */
41 #include <sys/stat.h> /* Required by dir.h & panel.h below */
43 #ifdef HAVE_UNISTD_H
44 # include <unistd.h> /* For pipe, fork, setsid, access etc */
45 #endif
47 #ifdef HAVE_STROPTS_H
48 # include <stropts.h> /* For I_PUSH */
49 #endif /* HAVE_STROPTS_H */
51 #include "global.h" /* For home_dir */
52 #include "tty.h"
53 #include "dir.h" /* Required by panel.h below */
54 #include "util.h" /* Required by panel.h */
55 #include "panel.h" /* For WPanel and current_panel */
56 #include "dialog.h" /* For query_dialog() */
57 #include "main.h" /* For cpanel, quit & init_sigchld() */
58 #include "cons.saver.h" /* For handle_console(), etc. */
59 #include "key.h" /* XCTRL and ALT macros */
60 #include "subshell.h"
62 #ifndef WEXITSTATUS
63 # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
64 #endif
66 #ifndef WIFEXITED
67 # define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
68 #endif
70 /* tcsh closes all non-standard file descriptors, so we have to use a pipe */
71 static char tcsh_fifo[128];
73 /* Local functions */
74 static void init_raw_mode (void);
75 static int feed_subshell (int how, int fail_on_error);
76 static void synchronize (void);
77 static int pty_open_master (char *pty_name);
78 static int pty_open_slave (const char *pty_name);
79 static int resize_tty (int fd);
81 /* }}} */
82 /* {{{ Definitions */
84 #ifndef STDIN_FILENO
85 # define STDIN_FILENO 0
86 #endif
88 #ifndef STDOUT_FILENO
89 # define STDOUT_FILENO 1
90 #endif
92 #ifndef STDERR_FILENO
93 # define STDERR_FILENO 2
94 #endif
96 /* If using a subshell for evaluating commands this is true */
97 int use_subshell =
98 #ifdef SUBSHELL_OPTIONAL
99 FALSE;
100 #else
101 TRUE;
102 #endif
104 /* File descriptor of the pseudoterminal used by the subshell */
105 int subshell_pty = 0;
107 /* The key for switching back to MC from the subshell */
108 char subshell_switch_key = XCTRL('o');
110 /* State of the subshell:
111 * INACTIVE: the default state; awaiting a command
112 * ACTIVE: remain in the shell until the user hits `subshell_switch_key'
113 * RUNNING_COMMAND: return to MC when the current command finishes */
114 enum subshell_state_enum subshell_state;
116 /* Holds the latest prompt captured from the subshell */
117 char *subshell_prompt = NULL;
119 /* Initial length of the buffer for the subshell's prompt */
120 #define INITIAL_PROMPT_SIZE 10
122 /* Used by the child process to indicate failure to start the subshell */
123 #define FORK_FAILURE 69 /* Arbitrary */
125 /* Initial length of the buffer for all I/O with the subshell */
126 #define INITIAL_PTY_BUFFER_SIZE 100 /* Arbitrary; but keep it >= 80 */
128 /* For pipes */
129 enum {READ=0, WRITE=1};
132 /* Local variables */
134 static char *pty_buffer; /* For reading/writing on the subshell's pty */
135 static int pty_buffer_size; /* The buffer grows as needed */
136 static int subshell_pipe[2]; /* To pass CWD info from the subshell to MC */
137 static pid_t subshell_pid = 1; /* The subshell's process ID */
138 static char subshell_cwd[MC_MAXPATHLEN+1]; /* One extra char for final '\n' */
140 /* Subshell type (gleaned from the SHELL environment variable, if available) */
141 static enum {BASH, TCSH, ZSH} subshell_type;
143 /* Flag to indicate whether the subshell is ready for next command */
144 static int subshell_ready;
146 /* The following two flags can be changed by the SIGCHLD handler. This is */
147 /* OK, because the `int' type is updated atomically on all known machines */
148 static volatile int subshell_alive, subshell_stopped;
150 /* We store the terminal's initial mode here so that we can configure
151 the pty similarly, and also so we can restore the real terminal to
152 sanity if we have to exit abruptly */
153 static struct termios shell_mode;
155 /* This is a transparent mode for the terminal where MC is running on */
156 /* It is used when the shell is active, so that the control signals */
157 /* are delivered to the shell pty */
158 static struct termios raw_mode;
160 /* This counter indicates how many characters of prompt we have read */
161 /* FIXME: try to figure out why this had to become global */
162 static int prompt_pos;
164 /* }}} */
166 #ifdef HAVE_GRANTPT
167 # define SYNC_PTY_SIDES
168 #else
169 # define SYNC_PTY_SIDES
170 #endif
172 #undef SYNC_PTY_SIDES
174 #ifdef SYNC_PTY_SIDES
175 /* Handler for SIGUSR1 (used below), does nothing but accept the signal */
176 static void sigusr1_handler (int sig)
179 #endif
182 * Prepare child process to running the shell and run it.
184 * Modifies the global variables (in the child process only):
185 * shell_mode
187 * Returns: never.
189 static void init_subshell_child (const char *pty_name)
191 int pty_slave;
192 char *init_file = NULL;
194 setsid (); /* Get a fresh terminal session */
196 /* {{{ Open the slave side of the pty: again */
197 pty_slave = pty_open_slave (pty_name);
199 /* This must be done before closing the master side of the pty, */
200 /* or it will fail on certain idiotic systems, such as Solaris. */
202 /* Close master side of pty. This is important; apart from */
203 /* freeing up the descriptor for use in the subshell, it also */
204 /* means that when MC exits, the subshell will get a SIGHUP and */
205 /* exit too, because there will be no more descriptors pointing */
206 /* at the master side of the pty and so it will disappear. */
208 close (subshell_pty);
210 #ifdef SYNC_PTY_SIDES
211 /* Give our parent process (MC) the go-ahead */
212 kill (getppid (), SIGUSR1);
213 #endif
215 /* }}} */
216 /* {{{ Make sure that it has become our controlling terminal */
218 /* Redundant on Linux and probably most systems, but just in case: */
220 #ifdef TIOCSCTTY
221 ioctl (pty_slave, TIOCSCTTY, 0);
222 #endif
224 /* }}} */
225 /* {{{ Configure its terminal modes and window size */
227 /* Set up the pty with the same termios flags as our own tty, plus */
228 /* TOSTOP, which keeps background processes from writing to the pty */
230 shell_mode.c_lflag |= TOSTOP; /* So background writers get SIGTTOU */
231 if (tcsetattr (pty_slave, TCSANOW, &shell_mode))
233 perror (__FILE__": couldn't set pty terminal modes");
234 _exit (FORK_FAILURE);
237 /* Set the pty's size (80x25 by default on Linux) according to the */
238 /* size of the real terminal as calculated by ncurses, if possible */
239 resize_tty (pty_slave);
241 /* }}} */
242 /* {{{ Set up the subshell's environment and init file name */
244 /* It simplifies things to change to our home directory here, */
245 /* and the user's startup file may do a `cd' command anyway */
246 chdir (home_dir); /* FIXME? What about when we re-run the subshell? */
248 switch (subshell_type)
250 case BASH:
251 init_file = ".mc/bashrc";
252 if (access (init_file, R_OK) == -1)
253 init_file = ".bashrc";
255 /* Make MC's special commands not show up in bash's history */
256 putenv ("HISTCONTROL=ignorespace");
258 /* Allow alternative readline settings for MC */
259 if (access (".mc/inputrc", R_OK) == 0)
260 putenv ("INPUTRC=.mc/inputrc");
262 break;
264 case TCSH:
265 init_file = ".mc/tcshrc";
266 if (access (init_file, R_OK) == -1)
267 init_file += 3;
268 break;
270 case ZSH:
271 break;
273 default:
274 fprintf (stderr, __FILE__": unimplemented subshell type %d\n",
275 subshell_type);
276 _exit (FORK_FAILURE);
279 /* }}} */
280 /* {{{ Attach all our standard file descriptors to the pty */
282 /* This is done just before the fork, because stderr must still */
283 /* be connected to the real tty during the above error messages; */
284 /* otherwise the user will never see them. */
286 dup2 (pty_slave, STDIN_FILENO);
287 dup2 (pty_slave, STDOUT_FILENO);
288 dup2 (pty_slave, STDERR_FILENO);
290 /* }}} */
291 /* {{{ Execute the subshell at last */
293 close (subshell_pipe[READ]);
294 close (pty_slave); /* These may be FD_CLOEXEC, but just in case... */
296 switch (subshell_type)
298 case BASH:
299 execl (shell, "bash", "-rcfile", init_file, NULL);
300 break;
302 case TCSH:
303 execl (shell, "tcsh", NULL); /* What's the -rcfile equivalent? */
304 break;
306 case ZSH:
307 /* change from "+Z" to "-Z" by Michael Bramer
308 * (Debian-mc-maintainer) <grisu@debian.org> from a patch from
309 * Radovan Garabik <garabik@center.fmph.uniba.sk>
311 execl (shell, "zsh", "-Z", NULL);
313 break;
316 /* If we get this far, everything failed miserably */
317 _exit (FORK_FAILURE);
319 /* }}} */
322 /* {{{ init_subshell */
325 * Fork the subshell, and set up many, many things.
327 * Possibly modifies the global variables:
328 * subshell_type, subshell_alive, subshell_stopped, subshell_pid
329 * use_subshell - Is set to FALSE if we can't run the subshell
330 * quit - Can be set to SUBSHELL_EXIT by the SIGCHLD handler
333 void init_subshell (void)
335 /* {{{ Local variables */
337 /* This must be remembered across calls to init_subshell() */
338 static char pty_name[BUF_SMALL];
339 int pty_slave = -1;
342 #ifdef SYNC_PTY_SIDES
343 /* Used to wait for a SIGUSR1 signal from the subprocess */
344 sigset_t sigusr1_mask, old_mask;
345 #endif
347 /* }}} */
349 /* Take the current (hopefully pristine) tty mode and make */
350 /* a raw mode based on it now, before we do anything else with it */
351 init_raw_mode ();
353 if (subshell_pty == 0) /* First time through */
355 /* {{{ Find out what type of shell we have */
357 if (strstr (shell, "/zsh"))
358 subshell_type = ZSH;
359 else if (strstr (shell, "/tcsh"))
360 subshell_type = TCSH;
361 else if (strstr (shell, "/bash") || getenv ("BASH"))
362 subshell_type = BASH;
363 else
365 use_subshell = FALSE;
366 return;
369 /* }}} */
370 /* {{{ Open a pty for talking to the subshell */
372 /* FIXME: We may need to open a fresh pty each time on SVR4 */
374 subshell_pty = pty_open_master (pty_name);
375 if (subshell_pty == -1)
377 fputs (__FILE__": couldn't open master side of pty\n", stderr);
378 perror ("pty_open_master");
379 use_subshell = FALSE;
380 return;
382 pty_slave = pty_open_slave (pty_name);
383 if (pty_slave == -1)
385 fprintf (stderr, "couldn't open slave side of pty (%s)\n\r",
386 pty_name);
387 use_subshell = FALSE;
388 return;
392 /* }}} */
393 /* {{{ Initialise the pty's I/O buffer */
395 pty_buffer_size = INITIAL_PTY_BUFFER_SIZE;
396 pty_buffer = (char *) g_malloc (pty_buffer_size);
398 /* }}} */
399 /* {{{ Create a pipe for receiving the subshell's CWD */
401 if (subshell_type == TCSH)
403 g_snprintf (tcsh_fifo, sizeof (tcsh_fifo), "%s/mc.pipe.%d",
404 mc_tmpdir(), getpid ());
405 if (mkfifo (tcsh_fifo, 0600) == -1)
407 perror (__FILE__": mkfifo");
408 use_subshell = FALSE;
409 return;
412 /* Opening the FIFO as O_RDONLY or O_WRONLY causes deadlock */
414 if ((subshell_pipe[READ] = open (tcsh_fifo, O_RDWR)) == -1 ||
415 (subshell_pipe[WRITE] = open (tcsh_fifo, O_RDWR)) == -1)
417 fprintf (stderr, _("Couldn't open named pipe %s\n"), tcsh_fifo);
418 perror (__FILE__": open");
419 use_subshell = FALSE;
420 return;
423 else /* subshell_type is BASH or ZSH */
424 if (pipe (subshell_pipe))
426 perror (__FILE__": couldn't create pipe");
427 use_subshell = FALSE;
428 return;
431 /* }}} */
434 /* {{{ Define a handler for the sigusr1 signal */
436 #ifdef SYNC_PTY_SIDES
437 sigemptyset (&sigusr1_mask);
438 sigaddset (&sigusr1_mask, SIGUSR1);
439 sigprocmask (SIG_BLOCK, &sigusr1_mask, &old_mask);
440 signal (SIGUSR1, sigusr1_handler);
441 #endif
443 /* }}} */
444 /* {{{ Fork the subshell */
446 subshell_alive = TRUE;
447 subshell_stopped = FALSE;
448 subshell_pid = fork ();
450 if (subshell_pid == -1)
452 perror (__FILE__": couldn't spawn the subshell process");
453 /* We exit here because, if the process table is full, the */
454 /* other method of running user commands won't work either */
455 exit (1);
458 /* }}} */
460 if (subshell_pid == 0) /* We are in the child process */
462 init_subshell_child (pty_name);
465 /* pty_slave is only opened when called the first time */
466 if (pty_slave != -1) {
467 close(pty_slave);
470 #ifdef SYNC_PTY_SIDES
471 sigsuspend (&old_mask);
472 signal (SIGUSR1, SIG_DFL);
473 sigprocmask (SIG_SETMASK, &old_mask, NULL);
474 /* ...before installing our handler for SIGCHLD. */
475 #endif
477 #if 0
478 /* {{{ Install our handler for SIGCHLD */
480 init_sigchld ();
482 /* We could have received the SIGCHLD signal for the subshell
483 * before installing the init_sigchld */
484 pid = waitpid (subshell_pid, &status, WUNTRACED | WNOHANG);
485 if (pid == subshell_pid){
486 use_subshell = FALSE;
487 return;
490 /* }}} */
491 #endif
493 /* {{{ Set up `precmd' or equivalent for reading the subshell's CWD */
495 switch (subshell_type)
497 char precmd[BUF_SMALL];
499 case BASH:
500 g_snprintf (precmd, sizeof (precmd), " PROMPT_COMMAND='pwd>&%d;kill -STOP $$'\n",
501 subshell_pipe[WRITE]);
502 goto write_it;
504 case ZSH:
505 g_snprintf (precmd, sizeof (precmd), "precmd(){ pwd>&%d;kill -STOP $$ }\n",
506 subshell_pipe[WRITE]);
507 goto write_it;
509 case TCSH:
510 g_snprintf (precmd, sizeof (precmd),
511 "set echo_style=both;"
512 "alias precmd 'echo $cwd:q >>%s;kill -STOP $$'\n",
513 tcsh_fifo);
515 write_it:
516 write (subshell_pty, precmd, strlen (precmd));
519 /* }}} */
520 /* {{{ Wait until the subshell has started up and processed the command */
522 subshell_state = RUNNING_COMMAND;
523 enable_interrupt_key ();
524 if (!feed_subshell (QUIETLY, TRUE)){
525 use_subshell = FALSE;
527 disable_interrupt_key ();
528 if (!subshell_alive)
529 use_subshell = FALSE; /* Subshell died instantly, so don't use it */
531 /* }}} */
534 /* }}} */
536 static void init_raw_mode ()
538 static int initialized = 0;
540 /* MC calls reset_shell_mode() in pre_exec() to set the real tty to its */
541 /* original settings. However, here we need to make this tty very raw, */
542 /* so that all keyboard signals, XON/XOFF, etc. will get through to the */
543 /* pty. So, instead of changing the code for execute(), pre_exec(), */
544 /* etc, we just set up the modes we need here, before each command. */
546 if (initialized == 0) /* First time: initialise `raw_mode' */
548 tcgetattr (STDOUT_FILENO, &raw_mode);
549 raw_mode.c_lflag &= ~ICANON; /* Disable line-editing chars, etc. */
550 raw_mode.c_lflag &= ~ISIG; /* Disable intr, quit & suspend chars */
551 raw_mode.c_lflag &= ~ECHO; /* Disable input echoing */
552 raw_mode.c_iflag &= ~IXON; /* Pass ^S/^Q to subshell undisturbed */
553 raw_mode.c_iflag &= ~ICRNL; /* Don't translate CRs into LFs */
554 raw_mode.c_oflag &= ~OPOST; /* Don't postprocess output */
555 raw_mode.c_cc[VTIME] = 0; /* IE: wait forever, and return as */
556 raw_mode.c_cc[VMIN] = 1; /* soon as a character is available */
557 initialized = 1;
561 /* {{{ invoke_subshell */
563 int invoke_subshell (const char *command, int how, char **new_dir)
565 /* {{{ Make the MC terminal transparent */
567 tcsetattr (STDOUT_FILENO, TCSANOW, &raw_mode);
569 /* }}} */
571 /* Make the subshell change to MC's working directory */
572 if (new_dir)
573 do_subshell_chdir (cpanel->cwd, TRUE, 1);
575 if (command == NULL) /* The user has done "C-o" from MC */
577 if (subshell_state == INACTIVE)
579 subshell_state = ACTIVE;
580 /* FIXME: possibly take out this hack; the user can
581 re-play it by hitting C-hyphen a few times! */
582 write (subshell_pty, " \b", 2); /* Hack to make prompt reappear */
585 else /* MC has passed us a user command */
587 if (how == QUIETLY)
588 write (subshell_pty, " ", 1);
589 /* FIXME: if command is long (>8KB ?) we go comma */
590 write (subshell_pty, command, strlen (command));
591 write (subshell_pty, "\n", 1);
592 subshell_state = RUNNING_COMMAND;
593 subshell_ready = FALSE;
596 feed_subshell (how, FALSE);
598 if (new_dir && subshell_alive && strcmp (subshell_cwd, cpanel->cwd))
599 *new_dir = subshell_cwd; /* Make MC change to the subshell's CWD */
601 /* Restart the subshell if it has died by SIGHUP, SIGQUIT, etc. */
602 while (!subshell_alive && !quit && use_subshell)
603 init_subshell ();
605 prompt_pos = 0;
607 return quit;
610 /* }}} */
611 /* {{{ read_subshell_prompt */
613 int read_subshell_prompt (void)
615 /* {{{ Local variables */
617 static int prompt_size = INITIAL_PROMPT_SIZE;
618 int bytes = 0, i, rc = 0;
619 struct timeval timeleft = {0, 0};
621 fd_set tmp;
622 FD_ZERO (&tmp);
623 FD_SET (subshell_pty, &tmp);
625 /* }}} */
627 if (subshell_prompt == NULL) /* First time through */
629 subshell_prompt = (char *) g_malloc (prompt_size);
630 *subshell_prompt = '\0';
631 prompt_pos = 0;
634 while (subshell_alive &&
635 (rc = select (subshell_pty + 1, &tmp, NULL, NULL, &timeleft)))
637 /* {{{ Check for `select' errors */
639 if (rc == -1) {
640 if (errno == EINTR)
641 continue;
642 else {
643 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
644 perror ("\n"__FILE__": select (FD_SETSIZE, &tmp...)");
645 exit (1);
649 /* }}} */
651 bytes = read (subshell_pty, pty_buffer, pty_buffer_size);
653 /* {{{ Extract the prompt from the shell output */
655 for (i=0; i<bytes; ++i)
656 if (pty_buffer[i] == '\n' || pty_buffer[i] == '\r'){
657 prompt_pos = 0;
658 } else {
659 if (!pty_buffer [i])
660 continue;
662 subshell_prompt[prompt_pos++] = pty_buffer[i];
663 if (prompt_pos == prompt_size)
664 subshell_prompt = (char *) g_realloc (subshell_prompt,
665 prompt_size *= 2);
668 subshell_prompt[prompt_pos] = '\0';
670 /* }}} */
672 if (rc == 0 && bytes == 0)
673 return FALSE;
674 return TRUE;
677 /* Resize given terminal using TIOCSWINSZ, return ioctl() result */
678 static int resize_tty (int fd)
680 #if defined TIOCSWINSZ && !defined SCO_FLAVOR
681 struct winsize tty_size;
683 tty_size.ws_row = LINES;
684 tty_size.ws_col = COLS;
685 tty_size.ws_xpixel = tty_size.ws_ypixel = 0;
687 return ioctl (fd, TIOCSWINSZ, &tty_size);
688 #endif
691 /* Resize subshell_pty */
692 void resize_subshell (void)
694 resize_tty (subshell_pty);
697 int exit_subshell (void)
699 int quit = TRUE;
701 if (subshell_state != INACTIVE && subshell_alive)
702 quit = !query_dialog (_(" Warning "), _(" The shell is still active. Quit anyway? "),
703 0, 2, _("&Yes"), _("&No"));
705 if (quit && subshell_type == TCSH)
707 if (unlink (tcsh_fifo) == -1)
708 perror (__FILE__": couldn't remove named pipe /tmp/mc.pipe.NNN");
711 g_free (subshell_prompt);
712 subshell_prompt = NULL;
714 return quit;
717 /* }}} */
720 * Carefully quote directory name to allow entering any directory safely,
721 * no matter what weird characters it may contain in its name.
722 * NOTE: Treat directory name an untrusted data, don't allow it to cause
723 * executing any commands in the shell. Escape all control characters.
724 * Use following technique:
726 * for bash - echo with `-e', 3-digit octal numbers:
727 * cd "`echo -e '\ooo...\ooo'`"
729 * for zsh - echo with `-e', 4-digit octal numbers:
730 * cd "`echo '\oooo...\oooo'`"
732 * for tcsh - echo without `-e', 4-digit octal numbers:
733 * cd "`echo '\oooo...\oooo'`"
735 static char *
736 subshell_name_quote (const char *s)
738 char *ret, *d;
739 const char echo_cmd[] = "\"`echo '";
740 const char echo_e_cmd[] = "\"`echo -e '";
741 const char common_end[] = "'`\"";
742 const char *cmd_start;
743 int len;
746 * Factor 5 because we need \, 0 and 3 other digits per character
747 * in the worst case (tcsh and zsh).
749 d = ret = g_malloc (5 * strlen (s) + 16);
750 if (!d)
751 return NULL;
753 /* Prevent interpreting leading `-' as a switch for `cd' */
754 if (*s == '-') {
755 *d++ = '.';
756 *d++ = '/';
759 /* echo in tcsh doesn't understand the "-e" option */
760 if (subshell_type == TCSH)
761 cmd_start = echo_cmd;
762 else
763 cmd_start = echo_e_cmd;
765 /* Copy the beginning of the command to the buffer */
766 len = strlen (cmd_start);
767 memcpy (d, cmd_start, len);
768 d += len;
771 * Print every character in octal format with the leading backslash.
772 * tcsh and zsh may require 4-digit octals, bash doesn't like them.
774 if (subshell_type == BASH) {
775 for (; *s; s++) {
776 sprintf(d, "\\%03o", (unsigned char) *s);
777 d += 4;
779 } else {
780 for (; *s; s++) {
781 sprintf(d, "\\0%03o", (unsigned char) *s);
782 d += 5;
786 memcpy (d, common_end, sizeof (common_end));
788 return ret;
793 /* {{{ do_subshell_chdir */
794 /* If it actually changed the directory it returns true */
795 void do_subshell_chdir (const char *directory, int do_update, int reset_prompt)
797 if (!(subshell_state == INACTIVE && strcmp (subshell_cwd, cpanel->cwd))){
798 /* We have to repaint the subshell prompt if we read it from
799 * the main program. Please note that in the code after this
800 * if, the cd command that is sent will make the subshell
801 * repaint the prompt, so we don't have to paint it. */
802 if (do_update)
803 do_update_prompt ();
804 return;
807 /* The initial space keeps this out of the command history (in bash
808 because we set "HISTCONTROL=ignorespace") */
809 write (subshell_pty, " cd ", 4);
810 if (*directory) {
811 char *temp;
812 temp = subshell_name_quote (directory);
813 if (temp) {
814 write (subshell_pty, temp, strlen (temp));
815 g_free (temp);
816 } else {
817 /* Should not happen unless the directory name is so long
818 that we don't have memory to quote it. */
819 write (subshell_pty, ".", 1);
821 } else {
822 write (subshell_pty, "/", 1);
824 write (subshell_pty, "\n", 1);
826 subshell_state = RUNNING_COMMAND;
827 feed_subshell (QUIETLY, FALSE);
829 if (subshell_alive && strcmp (subshell_cwd, cpanel->cwd) && strcmp (cpanel->cwd, "."))
830 fprintf (stderr, _("Warning: Couldn't change to %s.\n"), cpanel->cwd);
832 if (reset_prompt)
833 prompt_pos = 0;
834 update_prompt = FALSE;
835 /* Make sure that MC never stores the CWD in a silly format */
836 /* like /usr////lib/../bin, or the strcmp() above will fail */
839 /* }}} */
840 /* {{{ subshell_get_console_attributes */
842 void subshell_get_console_attributes (void)
844 /* {{{ Get our current terminal modes */
846 if (tcgetattr (STDOUT_FILENO, &shell_mode))
848 perror (__FILE__": couldn't get terminal settings");
849 use_subshell = FALSE;
850 return;
853 /* }}} */
856 /* }}} */
857 /* {{{ sigchld_handler */
859 /* Figure out whether the subshell has stopped, exited or been killed */
860 /* Possibly modifies: `subshell_alive', `subshell_stopped' and `quit' */
862 void sigchld_handler (int sig)
864 int status;
865 pid_t pid;
867 pid = waitpid (subshell_pid, &status, WUNTRACED | WNOHANG);
869 if (pid == subshell_pid) {
870 /* {{{ Figure out what has happened to the subshell */
872 if (WIFSTOPPED (status))
874 if (WSTOPSIG (status) == SIGSTOP) {
875 /* The subshell has received a SIGSTOP signal */
876 subshell_stopped = TRUE;
877 } else {
878 /* The user has suspended the subshell. Revive it */
879 kill (subshell_pid, SIGCONT);
882 else /* The subshell has either exited normally or been killed */
884 subshell_alive = FALSE;
885 delete_select_channel (subshell_pty);
886 if (WIFEXITED (status) && WEXITSTATUS (status) != FORK_FAILURE)
887 quit |= SUBSHELL_EXIT; /* Exited normally */
890 /* }}} */
893 #if defined(linux) || defined(__linux__)
894 pid = waitpid (cons_saver_pid, &status, WUNTRACED | WNOHANG);
896 if (pid == cons_saver_pid) {
898 if (WIFSTOPPED (status))
899 /* Someone has stopped cons.saver - restart it */
900 kill (pid, SIGCONT);
901 else
903 /* cons.saver has died - disable confole saving */
904 handle_console (CONSOLE_DONE);
905 console_flag = 0;
909 #endif /* linux || __linux__ */
910 /* If we get here, some other child exited; ignore it */
911 # ifdef __EMX__ /* Need to report */
912 pid = wait(&status);
913 # endif
916 /* }}} */
918 /* {{{ feed_subshell */
920 /* Feed the subshell our keyboard input until it says it's finished */
922 static int feed_subshell (int how, int fail_on_error)
924 /* {{{ Local variables */
925 fd_set read_set; /* For `select' */
926 int maxfdp;
927 int bytes; /* For the return value from `read' */
928 int i; /* Loop counter */
930 struct timeval wtime; /* Maximum time we wait for the subshell */
931 struct timeval *wptr;
932 /* }}} */
934 /* we wait up to 10 seconds if fail_on_error, forever otherwise */
935 wtime.tv_sec = 10;
936 wtime.tv_usec = 0;
937 wptr = fail_on_error ? &wtime : NULL;
939 while (1) {
940 if (!subshell_alive)
941 return FALSE;
943 /* {{{ Prepare the file-descriptor set and call `select' */
945 FD_ZERO (&read_set);
946 FD_SET (subshell_pty, &read_set);
947 FD_SET (subshell_pipe[READ], &read_set);
948 maxfdp = max (subshell_pty, subshell_pipe[READ]);
949 if (how == VISIBLY) {
950 FD_SET (STDIN_FILENO, &read_set);
951 maxfdp = max (maxfdp, STDIN_FILENO);
954 if (select (maxfdp + 1, &read_set, NULL, NULL, wptr) == -1){
956 /* Despite using SA_RESTART, we still have to check for this */
957 if (errno == EINTR)
958 continue; /* try all over again */
959 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
960 perror ("\n"__FILE__": select (FD_SETSIZE, &read_set...)");
961 exit (1);
963 /* }}} */
965 if (FD_ISSET (subshell_pty, &read_set))
966 /* {{{ Read from the subshell, write to stdout */
968 /* This loop improves performance by reducing context switches
969 by a factor of 20 or so... unfortunately, it also hangs MC
970 randomly, because of an apparent Linux bug. Investigate. */
971 /* for (i=0; i<5; ++i) * FIXME -- experimental */
973 bytes = read (subshell_pty, pty_buffer, pty_buffer_size);
975 /* The subshell has died */
976 if (bytes == -1 && errno == EIO && !subshell_alive)
977 return FALSE;
979 if (bytes <= 0)
981 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
982 perror ("\n"__FILE__": read (subshell_pty...)");
983 exit (1);
986 if (how == VISIBLY)
987 write (STDOUT_FILENO, pty_buffer, bytes);
990 /* }}} */
992 else if (FD_ISSET (subshell_pipe[READ], &read_set))
993 /* {{{ Read the subshell's CWD and capture its prompt */
996 bytes = read (subshell_pipe[READ], subshell_cwd, MC_MAXPATHLEN+1);
997 if (bytes <= 0)
999 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
1000 perror ("\n"__FILE__": read (subshell_pipe[READ]...)");
1001 exit (1);
1004 subshell_cwd[bytes-1] = 0; /* Squash the final '\n' */
1006 synchronize ();
1008 subshell_ready = TRUE;
1009 if (subshell_state == RUNNING_COMMAND)
1011 subshell_state = INACTIVE;
1012 return 1;
1016 /* }}} */
1018 else if (FD_ISSET (STDIN_FILENO, &read_set))
1019 /* {{{ Read from stdin, write to the subshell */
1022 bytes = read (STDIN_FILENO, pty_buffer, pty_buffer_size);
1023 if (bytes <= 0)
1025 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
1026 perror ("\n"__FILE__": read (STDIN_FILENO, pty_buffer...)");
1027 exit (1);
1030 for (i=0; i<bytes; ++i)
1031 if (pty_buffer[i] == subshell_switch_key)
1033 write (subshell_pty, pty_buffer, i);
1034 if (subshell_ready)
1035 subshell_state = INACTIVE;
1036 return TRUE;
1039 write (subshell_pty, pty_buffer, bytes);
1040 subshell_ready = FALSE;
1041 } else {
1042 return FALSE;
1045 /* }}} */
1049 /* }}} */
1050 /* {{{ synchronize */
1052 /* Wait until the subshell dies or stops. If it stops, make it resume. */
1053 /* Possibly modifies the globals `subshell_alive' and `subshell_stopped' */
1055 static void synchronize (void)
1057 sigset_t sigchld_mask, old_mask;
1059 sigemptyset (&sigchld_mask);
1060 sigaddset (&sigchld_mask, SIGCHLD);
1061 sigprocmask (SIG_BLOCK, &sigchld_mask, &old_mask);
1064 * SIGCHLD should not be blocked, but we unblock it just in case.
1065 * This is known to be useful for cygwin 1.3.12 and older.
1067 sigdelset (&old_mask, SIGCHLD);
1069 /* Wait until the subshell has stopped */
1070 while (subshell_alive && !subshell_stopped)
1071 sigsuspend (&old_mask);
1073 /* Discard all remaining data from stdin to the subshell */
1074 tcflush (subshell_pty, TCOFLUSH);
1076 subshell_stopped = FALSE;
1077 kill (subshell_pid, SIGCONT);
1079 sigprocmask (SIG_SETMASK, &old_mask, NULL);
1080 /* We can't do any better without modifying the shell(s) */
1083 /* }}} */
1084 /* {{{ pty opening functions */
1086 #ifdef SCO_FLAVOR
1088 /* {{{ SCO version of pty_open_master */
1090 static int pty_open_master (char *pty_name)
1092 int pty_master;
1093 int num;
1094 char *ptr;
1096 strcpy (pty_name, "/dev/ptyp");
1097 ptr = pty_name+9;
1098 for (num=0;;num++)
1100 g_snprintf(ptr, 9, "%d",num); /* surpriiise ... SCO lacks itoa() */
1101 /* Try to open master */
1102 if ((pty_master = open (pty_name, O_RDWR)) == -1) {
1103 if (errno == ENOENT) /* Different from EIO */
1104 return -1; /* Out of pty devices */
1105 else
1106 continue; /* Try next pty device */
1108 pty_name [5] = 't'; /* Change "pty" to "tty" */
1109 if (access (pty_name, 6)){
1110 close (pty_master);
1111 pty_name [5] = 'p';
1112 continue;
1114 return pty_master;
1116 return -1; /* Ran out of pty devices */
1119 /* }}} */
1120 /* {{{ SCO version of pty_open_slave */
1122 static int pty_open_slave (const char *pty_name)
1124 int pty_slave;
1125 struct group *group_info = getgrnam ("terminal");
1127 if (group_info != NULL)
1129 /* The following two calls will only succeed if we are root */
1130 /* [Commented out while permissions problem is investigated] */
1131 /* chown (pty_name, getuid (), group_info->gr_gid); FIXME */
1132 /* chmod (pty_name, S_IRUSR | S_IWUSR | S_IWGRP); FIXME */
1134 if ((pty_slave = open (pty_name, O_RDWR)) == -1)
1135 perror ("open (pty_name, O_RDWR)");
1136 return pty_slave;
1139 /* }}} */
1141 #elif HAVE_GRANTPT /* !HAVE_SCO */
1143 /* {{{ System V version of pty_open_master */
1145 static int pty_open_master (char *pty_name)
1147 char *slave_name;
1148 int pty_master;
1151 #ifdef HAVE_GETPT
1152 /* getpt () is a GNU extension (glibc 2.1.x) */
1153 pty_master = getpt ();
1154 #else
1155 strcpy (pty_name, "/dev/ptmx");
1156 pty_master = open (pty_name, O_RDWR);
1157 #endif
1158 if (pty_master == -1)
1159 return -1;
1161 if (grantpt (pty_master) == -1 /* Grant access to slave */
1162 || unlockpt (pty_master) == -1 /* Clear slave's lock flag */
1163 || !(slave_name = ptsname (pty_master))) /* Get slave's name */
1165 close (pty_master);
1166 return -1;
1168 strcpy (pty_name, slave_name);
1169 return pty_master;
1172 /* }}} */
1173 /* {{{ System V version of pty_open_slave */
1175 static int pty_open_slave (const char *pty_name)
1177 int pty_slave = open (pty_name, O_RDWR);
1179 if (pty_slave == -1)
1181 perror ("open (pty_name, O_RDWR)");
1182 return -1;
1185 #if !defined(__osf__) && !defined(linux) && !defined(__linux__)
1186 #if defined (I_FIND) && defined (I_PUSH)
1187 if (!ioctl (pty_slave, I_FIND, "ptem"))
1188 if (ioctl (pty_slave, I_PUSH, "ptem") == -1)
1190 fprintf (stderr, "ioctl (pty_slave, I_PUSH, \"ptem\") failed\n");
1191 close (pty_slave);
1192 return -1;
1195 if (!ioctl (pty_slave, I_FIND, "ldterm"))
1196 if (ioctl (pty_slave, I_PUSH, "ldterm") == -1)
1198 fprintf (stderr, "ioctl (pty_slave, I_PUSH, \"ldterm\") failed\n");
1199 close (pty_slave);
1200 return -1;
1203 #if !defined(sgi) && !defined(__sgi)
1204 if (!ioctl (pty_slave, I_FIND, "ttcompat"))
1205 if (ioctl (pty_slave, I_PUSH, "ttcompat") == -1)
1207 fprintf (stderr, "ioctl (pty_slave, I_PUSH, \"ttcompat\") failed\n");
1208 close (pty_slave);
1209 return -1;
1211 #endif /* sgi || __sgi */
1212 #endif /* I_FIND && I_PUSH */
1213 #endif /* __osf__ || linux || __linux__ */
1215 return pty_slave;
1218 /* }}} */
1220 #else /* !HAVE_SCO && !HAVE_GRANTPT */
1222 /* {{{ BSD version of pty_open_master */
1224 static int pty_open_master (char *pty_name)
1226 int pty_master;
1227 char *ptr1, *ptr2;
1229 strcpy (pty_name, "/dev/ptyXX");
1230 for (ptr1 = "pqrstuvwxyzPQRST"; *ptr1; ++ptr1)
1232 pty_name [8] = *ptr1;
1233 for (ptr2 = "0123456789abcdef"; *ptr2; ++ptr2)
1235 pty_name [9] = *ptr2;
1237 /* Try to open master */
1238 if ((pty_master = open (pty_name, O_RDWR)) == -1) {
1239 if (errno == ENOENT) /* Different from EIO */
1240 return -1; /* Out of pty devices */
1241 else
1242 continue; /* Try next pty device */
1244 pty_name [5] = 't'; /* Change "pty" to "tty" */
1245 if (access (pty_name, 6)){
1246 close (pty_master);
1247 pty_name [5] = 'p';
1248 continue;
1250 return pty_master;
1253 return -1; /* Ran out of pty devices */
1256 /* }}} */
1257 /* {{{ BSD version of pty_open_slave */
1259 static int pty_open_slave (const char *pty_name)
1261 int pty_slave;
1262 struct group *group_info = getgrnam ("tty");
1264 if (group_info != NULL)
1266 /* The following two calls will only succeed if we are root */
1267 /* [Commented out while permissions problem is investigated] */
1268 /* chown (pty_name, getuid (), group_info->gr_gid); FIXME */
1269 /* chmod (pty_name, S_IRUSR | S_IWUSR | S_IWGRP); FIXME */
1271 if ((pty_slave = open (pty_name, O_RDWR)) == -1)
1272 perror ("open (pty_name, O_RDWR)");
1273 return pty_slave;
1276 /* }}} */
1278 #endif /* !HAVE_SCO && !HAVE_GRANTPT */
1280 /* }}} */
1282 #endif /* HAVE_SUBSHELL_SUPPORT */
1284 /* {{{ Emacs local variables */
1287 Cause emacs to enter folding mode for this file:
1288 Local variables:
1289 end:
1292 /* }}} */