corrected some file operations dialog msgs
[midnight-commander.git] / src / subshell.c
blob38949ad70da0debec5fe2c622337a2213dcb0a4a
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 <fcntl.h> /* For open(), etc. */
40 #include <string.h> /* strstr(), strcpy(), etc. */
41 #include <signal.h> /* sigaction(), sigprocmask(), etc. */
42 #include <sys/stat.h> /* Required by dir.h & panel.h below */
44 #ifdef HAVE_UNISTD_H
45 # include <unistd.h> /* For pipe, fork, setsid, access etc */
46 #endif
48 #ifdef HAVE_STROPTS_H
49 # include <stropts.h> /* For I_PUSH */
50 #endif /* HAVE_STROPTS_H */
52 #include "global.h" /* For home_dir */
53 #include "tty.h"
54 #include "dir.h" /* Required by panel.h below */
55 #include "util.h" /* Required by panel.h */
56 #include "panel.h" /* For WPanel and current_panel */
57 #include "dialog.h" /* For query_dialog() */
58 #include "main.h" /* For cpanel, quit & init_sigchld() */
59 #include "cons.saver.h" /* For handle_console(), etc. */
60 #include "key.h" /* XCTRL and ALT macros */
61 #include "subshell.h"
63 #ifndef WEXITSTATUS
64 # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
65 #endif
67 #ifndef WIFEXITED
68 # define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
69 #endif
71 /* tcsh closes all non-standard file descriptors, so we have to use a pipe */
72 static char tcsh_fifo[128];
74 /* Local functions */
75 static void init_raw_mode (void);
76 static int feed_subshell (int how, int fail_on_error);
77 static void synchronize (void);
78 static int pty_open_master (char *pty_name);
79 static int pty_open_slave (const char *pty_name);
80 static int resize_tty (int fd);
82 /* }}} */
83 /* {{{ Definitions */
85 #ifndef STDIN_FILENO
86 # define STDIN_FILENO 0
87 #endif
89 #ifndef STDOUT_FILENO
90 # define STDOUT_FILENO 1
91 #endif
93 #ifndef STDERR_FILENO
94 # define STDERR_FILENO 2
95 #endif
97 /* If using a subshell for evaluating commands this is true */
98 int use_subshell =
99 #ifdef SUBSHELL_OPTIONAL
100 FALSE;
101 #else
102 TRUE;
103 #endif
105 /* File descriptor of the pseudoterminal used by the subshell */
106 int subshell_pty = 0;
108 /* The key for switching back to MC from the subshell */
109 char subshell_switch_key = XCTRL('o');
111 /* State of the subshell:
112 * INACTIVE: the default state; awaiting a command
113 * ACTIVE: remain in the shell until the user hits `subshell_switch_key'
114 * RUNNING_COMMAND: return to MC when the current command finishes */
115 enum subshell_state_enum subshell_state;
117 /* Holds the latest prompt captured from the subshell */
118 char *subshell_prompt = NULL;
120 /* Initial length of the buffer for the subshell's prompt */
121 #define INITIAL_PROMPT_SIZE 10
123 /* Used by the child process to indicate failure to start the subshell */
124 #define FORK_FAILURE 69 /* Arbitrary */
126 /* Initial length of the buffer for all I/O with the subshell */
127 #define INITIAL_PTY_BUFFER_SIZE 100 /* Arbitrary; but keep it >= 80 */
129 /* For pipes */
130 enum {READ=0, WRITE=1};
133 /* Local variables */
135 static char *pty_buffer; /* For reading/writing on the subshell's pty */
136 static int pty_buffer_size; /* The buffer grows as needed */
137 static int subshell_pipe[2]; /* To pass CWD info from the subshell to MC */
138 static pid_t subshell_pid = 1; /* The subshell's process ID */
139 static char subshell_cwd[MC_MAXPATHLEN+1]; /* One extra char for final '\n' */
141 /* Subshell type (gleaned from the SHELL environment variable, if available) */
142 static enum {BASH, TCSH, ZSH} subshell_type;
144 /* Flag to indicate whether the subshell is ready for next command */
145 static int subshell_ready;
147 /* The following two flags can be changed by the SIGCHLD handler. This is */
148 /* OK, because the `int' type is updated atomically on all known machines */
149 static volatile int subshell_alive, subshell_stopped;
151 /* We store the terminal's initial mode here so that we can configure
152 the pty similarly, and also so we can restore the real terminal to
153 sanity if we have to exit abruptly */
154 static struct termios shell_mode;
156 /* This is a transparent mode for the terminal where MC is running on */
157 /* It is used when the shell is active, so that the control signals */
158 /* are delivered to the shell pty */
159 static struct termios raw_mode;
161 /* This counter indicates how many characters of prompt we have read */
162 /* FIXME: try to figure out why this had to become global */
163 static int prompt_pos;
165 /* }}} */
167 #ifdef HAVE_GRANTPT
168 # define SYNC_PTY_SIDES
169 #else
170 # define SYNC_PTY_SIDES
171 #endif
173 #undef SYNC_PTY_SIDES
175 #ifdef SYNC_PTY_SIDES
176 /* Handler for SIGUSR1 (used below), does nothing but accept the signal */
177 static void sigusr1_handler (int sig)
180 #endif
183 * Prepare child process to running the shell and run it.
185 * Modifies the global variables (in the child process only):
186 * shell_mode
188 * Returns: never.
190 static void init_subshell_child (const char *pty_name)
192 int pty_slave;
193 char *init_file = NULL;
195 setsid (); /* Get a fresh terminal session */
197 /* {{{ Open the slave side of the pty: again */
198 pty_slave = pty_open_slave (pty_name);
200 /* This must be done before closing the master side of the pty, */
201 /* or it will fail on certain idiotic systems, such as Solaris. */
203 /* Close master side of pty. This is important; apart from */
204 /* freeing up the descriptor for use in the subshell, it also */
205 /* means that when MC exits, the subshell will get a SIGHUP and */
206 /* exit too, because there will be no more descriptors pointing */
207 /* at the master side of the pty and so it will disappear. */
209 close (subshell_pty);
211 #ifdef SYNC_PTY_SIDES
212 /* Give our parent process (MC) the go-ahead */
213 kill (getppid (), SIGUSR1);
214 #endif
216 /* }}} */
217 /* {{{ Make sure that it has become our controlling terminal */
219 /* Redundant on Linux and probably most systems, but just in case: */
221 #ifdef TIOCSCTTY
222 ioctl (pty_slave, TIOCSCTTY, 0);
223 #endif
225 /* }}} */
226 /* {{{ Configure its terminal modes and window size */
228 /* Set up the pty with the same termios flags as our own tty, plus */
229 /* TOSTOP, which keeps background processes from writing to the pty */
231 shell_mode.c_lflag |= TOSTOP; /* So background writers get SIGTTOU */
232 if (tcsetattr (pty_slave, TCSANOW, &shell_mode))
234 perror (__FILE__": couldn't set pty terminal modes");
235 _exit (FORK_FAILURE);
238 /* Set the pty's size (80x25 by default on Linux) according to the */
239 /* size of the real terminal as calculated by ncurses, if possible */
240 resize_tty (pty_slave);
242 /* }}} */
243 /* {{{ Set up the subshell's environment and init file name */
245 /* It simplifies things to change to our home directory here, */
246 /* and the user's startup file may do a `cd' command anyway */
247 chdir (home_dir); /* FIXME? What about when we re-run the subshell? */
249 switch (subshell_type)
251 case BASH:
252 init_file = ".mc/bashrc";
253 if (access (init_file, R_OK) == -1)
254 init_file = ".bashrc";
256 /* Make MC's special commands not show up in bash's history */
257 putenv ("HISTCONTROL=ignorespace");
259 /* Allow alternative readline settings for MC */
260 if (access (".mc/inputrc", R_OK) == 0)
261 putenv ("INPUTRC=.mc/inputrc");
263 break;
265 case TCSH:
266 init_file = ".mc/tcshrc";
267 if (access (init_file, R_OK) == -1)
268 init_file += 3;
269 break;
271 case ZSH:
272 break;
274 default:
275 fprintf (stderr, __FILE__": unimplemented subshell type %d\n",
276 subshell_type);
277 _exit (FORK_FAILURE);
280 /* }}} */
281 /* {{{ Attach all our standard file descriptors to the pty */
283 /* This is done just before the fork, because stderr must still */
284 /* be connected to the real tty during the above error messages; */
285 /* otherwise the user will never see them. */
287 dup2 (pty_slave, STDIN_FILENO);
288 dup2 (pty_slave, STDOUT_FILENO);
289 dup2 (pty_slave, STDERR_FILENO);
291 /* }}} */
292 /* {{{ Execute the subshell at last */
294 close (subshell_pipe[READ]);
295 close (pty_slave); /* These may be FD_CLOEXEC, but just in case... */
297 switch (subshell_type)
299 case BASH:
300 execl (shell, "bash", "-rcfile", init_file, NULL);
301 break;
303 case TCSH:
304 execl (shell, "tcsh", NULL); /* What's the -rcfile equivalent? */
305 break;
307 case ZSH:
308 /* change from "+Z" to "-Z" by Michael Bramer
309 * (Debian-mc-maintainer) <grisu@debian.org> from a patch from
310 * Radovan Garabik <garabik@center.fmph.uniba.sk>
312 execl (shell, "zsh", "-Z", NULL);
314 break;
317 /* If we get this far, everything failed miserably */
318 _exit (FORK_FAILURE);
320 /* }}} */
323 /* {{{ init_subshell */
326 * Fork the subshell, and set up many, many things.
328 * Possibly modifies the global variables:
329 * subshell_type, subshell_alive, subshell_stopped, subshell_pid
330 * use_subshell - Is set to FALSE if we can't run the subshell
331 * quit - Can be set to SUBSHELL_EXIT by the SIGCHLD handler
334 void init_subshell (void)
336 /* {{{ Local variables */
338 /* This must be remembered across calls to init_subshell() */
339 static char pty_name[BUF_SMALL];
340 int pty_slave = -1;
343 #ifdef SYNC_PTY_SIDES
344 /* Used to wait for a SIGUSR1 signal from the subprocess */
345 sigset_t sigusr1_mask, old_mask;
346 #endif
348 /* }}} */
350 /* Take the current (hopefully pristine) tty mode and make */
351 /* a raw mode based on it now, before we do anything else with it */
352 init_raw_mode ();
354 if (subshell_pty == 0) /* First time through */
356 /* {{{ Find out what type of shell we have */
358 if (strstr (shell, "/zsh"))
359 subshell_type = ZSH;
360 else if (strstr (shell, "/tcsh"))
361 subshell_type = TCSH;
362 else if (strstr (shell, "/bash") || getenv ("BASH"))
363 subshell_type = BASH;
364 else
366 use_subshell = FALSE;
367 return;
370 /* }}} */
371 /* {{{ Open a pty for talking to the subshell */
373 /* FIXME: We may need to open a fresh pty each time on SVR4 */
375 subshell_pty = pty_open_master (pty_name);
376 if (subshell_pty == -1)
378 fputs (__FILE__": couldn't open master side of pty\n", stderr);
379 perror ("pty_open_master");
380 use_subshell = FALSE;
381 return;
383 pty_slave = pty_open_slave (pty_name);
384 if (pty_slave == -1)
386 fprintf (stderr, "couldn't open slave side of pty (%s)\n\r",
387 pty_name);
388 use_subshell = FALSE;
389 return;
393 /* }}} */
394 /* {{{ Initialise the pty's I/O buffer */
396 pty_buffer_size = INITIAL_PTY_BUFFER_SIZE;
397 pty_buffer = (char *) g_malloc (pty_buffer_size);
399 /* }}} */
400 /* {{{ Create a pipe for receiving the subshell's CWD */
402 if (subshell_type == TCSH)
404 g_snprintf (tcsh_fifo, sizeof (tcsh_fifo), "%s/mc.pipe.%d",
405 mc_tmpdir(), getpid ());
406 if (mkfifo (tcsh_fifo, 0600) == -1)
408 perror (__FILE__": mkfifo");
409 use_subshell = FALSE;
410 return;
413 /* Opening the FIFO as O_RDONLY or O_WRONLY causes deadlock */
415 if ((subshell_pipe[READ] = open (tcsh_fifo, O_RDWR)) == -1 ||
416 (subshell_pipe[WRITE] = open (tcsh_fifo, O_RDWR)) == -1)
418 fprintf (stderr, _("Couldn't open named pipe %s\n"), tcsh_fifo);
419 perror (__FILE__": open");
420 use_subshell = FALSE;
421 return;
424 else /* subshell_type is BASH or ZSH */
425 if (pipe (subshell_pipe))
427 perror (__FILE__": couldn't create pipe");
428 use_subshell = FALSE;
429 return;
432 /* }}} */
435 /* {{{ Define a handler for the sigusr1 signal */
437 #ifdef SYNC_PTY_SIDES
438 sigemptyset (&sigusr1_mask);
439 sigaddset (&sigusr1_mask, SIGUSR1);
440 sigprocmask (SIG_BLOCK, &sigusr1_mask, &old_mask);
441 signal (SIGUSR1, sigusr1_handler);
442 #endif
444 /* }}} */
445 /* {{{ Fork the subshell */
447 subshell_alive = TRUE;
448 subshell_stopped = FALSE;
449 subshell_pid = fork ();
451 if (subshell_pid == -1)
453 perror (__FILE__": couldn't spawn the subshell process");
454 /* We exit here because, if the process table is full, the */
455 /* other method of running user commands won't work either */
456 exit (1);
459 /* }}} */
461 if (subshell_pid == 0) /* We are in the child process */
463 init_subshell_child (pty_name);
466 /* pty_slave is only opened when called the first time */
467 if (pty_slave != -1) {
468 close(pty_slave);
471 #ifdef SYNC_PTY_SIDES
472 sigsuspend (&old_mask);
473 signal (SIGUSR1, SIG_DFL);
474 sigprocmask (SIG_SETMASK, &old_mask, NULL);
475 /* ...before installing our handler for SIGCHLD. */
476 #endif
478 #if 0
479 /* {{{ Install our handler for SIGCHLD */
481 init_sigchld ();
483 /* We could have received the SIGCHLD signal for the subshell
484 * before installing the init_sigchld */
485 pid = waitpid (subshell_pid, &status, WUNTRACED | WNOHANG);
486 if (pid == subshell_pid){
487 use_subshell = FALSE;
488 return;
491 /* }}} */
492 #endif
494 /* {{{ Set up `precmd' or equivalent for reading the subshell's CWD */
496 switch (subshell_type)
498 char precmd[BUF_SMALL];
500 case BASH:
501 g_snprintf (precmd, sizeof (precmd), " PROMPT_COMMAND='pwd>&%d;kill -STOP $$'\n",
502 subshell_pipe[WRITE]);
503 goto write_it;
505 case ZSH:
506 g_snprintf (precmd, sizeof (precmd), "precmd(){ pwd>&%d;kill -STOP $$ }\n",
507 subshell_pipe[WRITE]);
508 goto write_it;
510 case TCSH:
511 g_snprintf (precmd, sizeof (precmd),
512 "set echo_style=both;"
513 "alias precmd 'echo $cwd:q >>%s;kill -STOP $$'\n",
514 tcsh_fifo);
516 write_it:
517 write (subshell_pty, precmd, strlen (precmd));
520 /* }}} */
521 /* {{{ Wait until the subshell has started up and processed the command */
523 subshell_state = RUNNING_COMMAND;
524 enable_interrupt_key ();
525 if (!feed_subshell (QUIETLY, TRUE)){
526 use_subshell = FALSE;
528 disable_interrupt_key ();
529 if (!subshell_alive)
530 use_subshell = FALSE; /* Subshell died instantly, so don't use it */
532 /* }}} */
535 /* }}} */
537 static void init_raw_mode ()
539 static int initialized = 0;
541 /* MC calls reset_shell_mode() in pre_exec() to set the real tty to its */
542 /* original settings. However, here we need to make this tty very raw, */
543 /* so that all keyboard signals, XON/XOFF, etc. will get through to the */
544 /* pty. So, instead of changing the code for execute(), pre_exec(), */
545 /* etc, we just set up the modes we need here, before each command. */
547 if (initialized == 0) /* First time: initialise `raw_mode' */
549 tcgetattr (STDOUT_FILENO, &raw_mode);
550 raw_mode.c_lflag &= ~ICANON; /* Disable line-editing chars, etc. */
551 raw_mode.c_lflag &= ~ISIG; /* Disable intr, quit & suspend chars */
552 raw_mode.c_lflag &= ~ECHO; /* Disable input echoing */
553 raw_mode.c_iflag &= ~IXON; /* Pass ^S/^Q to subshell undisturbed */
554 raw_mode.c_iflag &= ~ICRNL; /* Don't translate CRs into LFs */
555 raw_mode.c_oflag &= ~OPOST; /* Don't postprocess output */
556 raw_mode.c_cc[VTIME] = 0; /* IE: wait forever, and return as */
557 raw_mode.c_cc[VMIN] = 1; /* soon as a character is available */
558 initialized = 1;
562 /* {{{ invoke_subshell */
564 int invoke_subshell (const char *command, int how, char **new_dir)
566 /* {{{ Make the MC terminal transparent */
568 tcsetattr (STDOUT_FILENO, TCSANOW, &raw_mode);
570 /* }}} */
572 /* Make the subshell change to MC's working directory */
573 if (new_dir)
574 do_subshell_chdir (cpanel->cwd, TRUE, 1);
576 if (command == NULL) /* The user has done "C-o" from MC */
578 if (subshell_state == INACTIVE)
580 subshell_state = ACTIVE;
581 /* FIXME: possibly take out this hack; the user can
582 re-play it by hitting C-hyphen a few times! */
583 write (subshell_pty, " \b", 2); /* Hack to make prompt reappear */
586 else /* MC has passed us a user command */
588 if (how == QUIETLY)
589 write (subshell_pty, " ", 1);
590 /* FIXME: if command is long (>8KB ?) we go comma */
591 write (subshell_pty, command, strlen (command));
592 write (subshell_pty, "\n", 1);
593 subshell_state = RUNNING_COMMAND;
594 subshell_ready = FALSE;
597 feed_subshell (how, FALSE);
599 if (new_dir && subshell_alive && strcmp (subshell_cwd, cpanel->cwd))
600 *new_dir = subshell_cwd; /* Make MC change to the subshell's CWD */
602 /* Restart the subshell if it has died by SIGHUP, SIGQUIT, etc. */
603 while (!subshell_alive && !quit && use_subshell)
604 init_subshell ();
606 prompt_pos = 0;
608 return quit;
611 /* }}} */
612 /* {{{ read_subshell_prompt */
614 int read_subshell_prompt (void)
616 /* {{{ Local variables */
618 static int prompt_size = INITIAL_PROMPT_SIZE;
619 int bytes = 0, i, rc = 0;
620 struct timeval timeleft = {0, 0};
622 fd_set tmp;
623 FD_ZERO (&tmp);
624 FD_SET (subshell_pty, &tmp);
626 /* }}} */
628 if (subshell_prompt == NULL) /* First time through */
630 subshell_prompt = (char *) g_malloc (prompt_size);
631 *subshell_prompt = '\0';
632 prompt_pos = 0;
635 while (subshell_alive &&
636 (rc = select (subshell_pty + 1, &tmp, NULL, NULL, &timeleft)))
638 /* {{{ Check for `select' errors */
640 if (rc == -1) {
641 if (errno == EINTR)
642 continue;
643 else {
644 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
645 perror ("\n"__FILE__": select (FD_SETSIZE, &tmp...)");
646 exit (1);
650 /* }}} */
652 bytes = read (subshell_pty, pty_buffer, pty_buffer_size);
654 /* {{{ Extract the prompt from the shell output */
656 for (i=0; i<bytes; ++i)
657 if (pty_buffer[i] == '\n' || pty_buffer[i] == '\r'){
658 prompt_pos = 0;
659 } else {
660 if (!pty_buffer [i])
661 continue;
663 subshell_prompt[prompt_pos++] = pty_buffer[i];
664 if (prompt_pos == prompt_size)
665 subshell_prompt = (char *) g_realloc (subshell_prompt,
666 prompt_size *= 2);
669 subshell_prompt[prompt_pos] = '\0';
671 /* }}} */
673 if (rc == 0 && bytes == 0)
674 return FALSE;
675 return TRUE;
678 /* Resize given terminal using TIOCSWINSZ, return ioctl() result */
679 static int resize_tty (int fd)
681 #if defined TIOCSWINSZ && !defined SCO_FLAVOR
682 struct winsize tty_size;
684 tty_size.ws_row = LINES;
685 tty_size.ws_col = COLS;
686 tty_size.ws_xpixel = tty_size.ws_ypixel = 0;
688 return ioctl (fd, TIOCSWINSZ, &tty_size);
689 #endif
692 /* Resize subshell_pty */
693 void resize_subshell (void)
695 resize_tty (subshell_pty);
698 int exit_subshell (void)
700 int quit = TRUE;
702 if (subshell_state != INACTIVE && subshell_alive)
703 quit = !query_dialog (_(" Warning "), _(" The shell is still active. Quit anyway? "),
704 0, 2, _("&Yes"), _("&No"));
706 if (quit && subshell_type == TCSH)
708 if (unlink (tcsh_fifo) == -1)
709 perror (__FILE__": couldn't remove named pipe /tmp/mc.pipe.NNN");
712 g_free (subshell_prompt);
713 subshell_prompt = NULL;
715 return quit;
718 /* }}} */
721 * Carefully quote directory name to allow entering any directory safely,
722 * no matter what weird characters it may contain in its name.
723 * NOTE: Treat directory name an untrusted data, don't allow it to cause
724 * executing any commands in the shell. Escape all control characters.
725 * Use following technique:
727 * for bash - echo with `-e', 3-digit octal numbers:
728 * cd "`echo -e '\ooo...\ooo'`"
730 * for zsh - echo with `-e', 4-digit octal numbers:
731 * cd "`echo '\oooo...\oooo'`"
733 * for tcsh - echo without `-e', 4-digit octal numbers:
734 * cd "`echo '\oooo...\oooo'`"
736 static char *
737 subshell_name_quote (const char *s)
739 char *ret, *d;
740 const char echo_cmd[] = "\"`echo '";
741 const char echo_e_cmd[] = "\"`echo -e '";
742 const char common_end[] = "'`\"";
743 const char *cmd_start;
744 int len;
747 * Factor 5 because we need \, 0 and 3 other digits per character
748 * in the worst case (tcsh and zsh).
750 d = ret = g_malloc (5 * strlen (s) + 16);
751 if (!d)
752 return NULL;
754 /* Prevent interpreting leading `-' as a switch for `cd' */
755 if (*s == '-') {
756 *d++ = '.';
757 *d++ = '/';
760 /* echo in tcsh doesn't understand the "-e" option */
761 if (subshell_type == TCSH)
762 cmd_start = echo_cmd;
763 else
764 cmd_start = echo_e_cmd;
766 /* Copy the beginning of the command to the buffer */
767 len = strlen (cmd_start);
768 memcpy (d, cmd_start, len);
769 d += len;
772 * Print every character in octal format with the leading backslash.
773 * tcsh and zsh may require 4-digit octals, bash doesn't like them.
775 if (subshell_type == BASH) {
776 for (; *s; s++) {
777 sprintf(d, "\\%03o", (unsigned char) *s);
778 d += 4;
780 } else {
781 for (; *s; s++) {
782 sprintf(d, "\\0%03o", (unsigned char) *s);
783 d += 5;
787 memcpy (d, common_end, sizeof (common_end));
789 return ret;
794 /* {{{ do_subshell_chdir */
795 /* If it actually changed the directory it returns true */
796 void do_subshell_chdir (const char *directory, int do_update, int reset_prompt)
798 if (!(subshell_state == INACTIVE && strcmp (subshell_cwd, cpanel->cwd))){
799 /* We have to repaint the subshell prompt if we read it from
800 * the main program. Please note that in the code after this
801 * if, the cd command that is sent will make the subshell
802 * repaint the prompt, so we don't have to paint it. */
803 if (do_update)
804 do_update_prompt ();
805 return;
808 /* The initial space keeps this out of the command history (in bash
809 because we set "HISTCONTROL=ignorespace") */
810 write (subshell_pty, " cd ", 4);
811 if (*directory) {
812 char *temp;
813 temp = subshell_name_quote (directory);
814 if (temp) {
815 write (subshell_pty, temp, strlen (temp));
816 g_free (temp);
817 } else {
818 /* Should not happen unless the directory name is so long
819 that we don't have memory to quote it. */
820 write (subshell_pty, ".", 1);
822 } else {
823 write (subshell_pty, "/", 1);
825 write (subshell_pty, "\n", 1);
827 subshell_state = RUNNING_COMMAND;
828 feed_subshell (QUIETLY, FALSE);
830 if (subshell_alive && strcmp (subshell_cwd, cpanel->cwd) && strcmp (cpanel->cwd, "."))
831 fprintf (stderr, _("Warning: Couldn't change to %s.\n"), cpanel->cwd);
833 if (reset_prompt)
834 prompt_pos = 0;
835 update_prompt = FALSE;
836 /* Make sure that MC never stores the CWD in a silly format */
837 /* like /usr////lib/../bin, or the strcmp() above will fail */
840 /* }}} */
841 /* {{{ subshell_get_console_attributes */
843 void subshell_get_console_attributes (void)
845 /* {{{ Get our current terminal modes */
847 if (tcgetattr (STDOUT_FILENO, &shell_mode))
849 perror (__FILE__": couldn't get terminal settings");
850 use_subshell = FALSE;
851 return;
854 /* }}} */
857 /* }}} */
858 /* {{{ sigchld_handler */
860 /* Figure out whether the subshell has stopped, exited or been killed */
861 /* Possibly modifies: `subshell_alive', `subshell_stopped' and `quit' */
863 void sigchld_handler (int sig)
865 int status;
866 pid_t pid;
868 pid = waitpid (subshell_pid, &status, WUNTRACED | WNOHANG);
870 if (pid == subshell_pid) {
871 /* {{{ Figure out what has happened to the subshell */
873 if (WIFSTOPPED (status))
875 if (WSTOPSIG (status) == SIGSTOP) {
876 /* The subshell has received a SIGSTOP signal */
877 subshell_stopped = TRUE;
878 } else {
879 /* The user has suspended the subshell. Revive it */
880 kill (subshell_pid, SIGCONT);
883 else /* The subshell has either exited normally or been killed */
885 subshell_alive = FALSE;
886 delete_select_channel (subshell_pty);
887 if (WIFEXITED (status) && WEXITSTATUS (status) != FORK_FAILURE)
888 quit |= SUBSHELL_EXIT; /* Exited normally */
891 /* }}} */
894 #if defined(linux) || defined(__linux__)
895 pid = waitpid (cons_saver_pid, &status, WUNTRACED | WNOHANG);
897 if (pid == cons_saver_pid) {
899 if (WIFSTOPPED (status))
900 /* Someone has stopped cons.saver - restart it */
901 kill (pid, SIGCONT);
902 else
904 /* cons.saver has died - disable confole saving */
905 handle_console (CONSOLE_DONE);
906 console_flag = 0;
910 #endif /* linux || __linux__ */
911 /* If we get here, some other child exited; ignore it */
912 # ifdef __EMX__ /* Need to report */
913 pid = wait(&status);
914 # endif
917 /* }}} */
919 /* {{{ feed_subshell */
921 /* Feed the subshell our keyboard input until it says it's finished */
923 static int feed_subshell (int how, int fail_on_error)
925 /* {{{ Local variables */
926 fd_set read_set; /* For `select' */
927 int maxfdp;
928 int bytes; /* For the return value from `read' */
929 int i; /* Loop counter */
931 struct timeval wtime; /* Maximum time we wait for the subshell */
932 struct timeval *wptr;
933 /* }}} */
935 /* we wait up to 10 seconds if fail_on_error, forever otherwise */
936 wtime.tv_sec = 10;
937 wtime.tv_usec = 0;
938 wptr = fail_on_error ? &wtime : NULL;
940 while (1) {
941 if (!subshell_alive)
942 return FALSE;
944 /* {{{ Prepare the file-descriptor set and call `select' */
946 FD_ZERO (&read_set);
947 FD_SET (subshell_pty, &read_set);
948 FD_SET (subshell_pipe[READ], &read_set);
949 maxfdp = max (subshell_pty, subshell_pipe[READ]);
950 if (how == VISIBLY) {
951 FD_SET (STDIN_FILENO, &read_set);
952 maxfdp = max (maxfdp, STDIN_FILENO);
955 if (select (maxfdp + 1, &read_set, NULL, NULL, wptr) == -1){
957 /* Despite using SA_RESTART, we still have to check for this */
958 if (errno == EINTR)
959 continue; /* try all over again */
960 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
961 perror ("\n"__FILE__": select (FD_SETSIZE, &read_set...)");
962 exit (1);
964 /* }}} */
966 if (FD_ISSET (subshell_pty, &read_set))
967 /* {{{ Read from the subshell, write to stdout */
969 /* This loop improves performance by reducing context switches
970 by a factor of 20 or so... unfortunately, it also hangs MC
971 randomly, because of an apparent Linux bug. Investigate. */
972 /* for (i=0; i<5; ++i) * FIXME -- experimental */
974 bytes = read (subshell_pty, pty_buffer, pty_buffer_size);
976 /* The subshell has died */
977 if (bytes == -1 && errno == EIO && !subshell_alive)
978 return FALSE;
980 if (bytes <= 0)
982 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
983 perror ("\n"__FILE__": read (subshell_pty...)");
984 exit (1);
987 if (how == VISIBLY)
988 write (STDOUT_FILENO, pty_buffer, bytes);
991 /* }}} */
993 else if (FD_ISSET (subshell_pipe[READ], &read_set))
994 /* {{{ Read the subshell's CWD and capture its prompt */
997 bytes = read (subshell_pipe[READ], subshell_cwd, MC_MAXPATHLEN+1);
998 if (bytes <= 0)
1000 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
1001 perror ("\n"__FILE__": read (subshell_pipe[READ]...)");
1002 exit (1);
1005 subshell_cwd[bytes-1] = 0; /* Squash the final '\n' */
1007 synchronize ();
1009 subshell_ready = TRUE;
1010 if (subshell_state == RUNNING_COMMAND)
1012 subshell_state = INACTIVE;
1013 return 1;
1017 /* }}} */
1019 else if (FD_ISSET (STDIN_FILENO, &read_set))
1020 /* {{{ Read from stdin, write to the subshell */
1023 bytes = read (STDIN_FILENO, pty_buffer, pty_buffer_size);
1024 if (bytes <= 0)
1026 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
1027 perror ("\n"__FILE__": read (STDIN_FILENO, pty_buffer...)");
1028 exit (1);
1031 for (i=0; i<bytes; ++i)
1032 if (pty_buffer[i] == subshell_switch_key)
1034 write (subshell_pty, pty_buffer, i);
1035 if (subshell_ready)
1036 subshell_state = INACTIVE;
1037 return TRUE;
1040 write (subshell_pty, pty_buffer, bytes);
1041 subshell_ready = FALSE;
1042 } else {
1043 return FALSE;
1046 /* }}} */
1050 /* }}} */
1051 /* {{{ synchronize */
1053 /* Wait until the subshell dies or stops. If it stops, make it resume. */
1054 /* Possibly modifies the globals `subshell_alive' and `subshell_stopped' */
1056 static void synchronize (void)
1058 sigset_t sigchld_mask, old_mask;
1060 sigemptyset (&sigchld_mask);
1061 sigaddset (&sigchld_mask, SIGCHLD);
1062 sigprocmask (SIG_BLOCK, &sigchld_mask, &old_mask);
1065 * SIGCHLD should not be blocked, but we unblock it just in case.
1066 * This is known to be useful for cygwin 1.3.12 and older.
1068 sigdelset (&old_mask, SIGCHLD);
1070 /* Wait until the subshell has stopped */
1071 while (subshell_alive && !subshell_stopped)
1072 sigsuspend (&old_mask);
1074 /* Discard all remaining data from stdin to the subshell */
1075 tcflush (subshell_pty, TCOFLUSH);
1077 subshell_stopped = FALSE;
1078 kill (subshell_pid, SIGCONT);
1080 sigprocmask (SIG_SETMASK, &old_mask, NULL);
1081 /* We can't do any better without modifying the shell(s) */
1084 /* }}} */
1085 /* {{{ pty opening functions */
1087 #ifdef SCO_FLAVOR
1089 /* {{{ SCO version of pty_open_master */
1091 static int pty_open_master (char *pty_name)
1093 int pty_master;
1094 int num;
1095 char *ptr;
1097 strcpy (pty_name, "/dev/ptyp");
1098 ptr = pty_name+9;
1099 for (num=0;;num++)
1101 g_snprintf(ptr, 9, "%d",num); /* surpriiise ... SCO lacks itoa() */
1102 /* Try to open master */
1103 if ((pty_master = open (pty_name, O_RDWR)) == -1) {
1104 if (errno == ENOENT) /* Different from EIO */
1105 return -1; /* Out of pty devices */
1106 else
1107 continue; /* Try next pty device */
1109 pty_name [5] = 't'; /* Change "pty" to "tty" */
1110 if (access (pty_name, 6)){
1111 close (pty_master);
1112 pty_name [5] = 'p';
1113 continue;
1115 return pty_master;
1117 return -1; /* Ran out of pty devices */
1120 /* }}} */
1121 /* {{{ SCO version of pty_open_slave */
1123 static int pty_open_slave (const char *pty_name)
1125 int pty_slave;
1126 struct group *group_info = getgrnam ("terminal");
1128 if (group_info != NULL)
1130 /* The following two calls will only succeed if we are root */
1131 /* [Commented out while permissions problem is investigated] */
1132 /* chown (pty_name, getuid (), group_info->gr_gid); FIXME */
1133 /* chmod (pty_name, S_IRUSR | S_IWUSR | S_IWGRP); FIXME */
1135 if ((pty_slave = open (pty_name, O_RDWR)) == -1)
1136 perror ("open (pty_name, O_RDWR)");
1137 return pty_slave;
1140 /* }}} */
1142 #elif HAVE_GRANTPT /* !HAVE_SCO */
1144 /* {{{ System V version of pty_open_master */
1146 static int pty_open_master (char *pty_name)
1148 char *slave_name;
1149 int pty_master;
1152 #ifdef HAVE_GETPT
1153 /* getpt () is a GNU extension (glibc 2.1.x) */
1154 pty_master = getpt ();
1155 #else
1156 strcpy (pty_name, "/dev/ptmx");
1157 pty_master = open (pty_name, O_RDWR);
1158 #endif
1159 if (pty_master == -1)
1160 return -1;
1162 if (grantpt (pty_master) == -1 /* Grant access to slave */
1163 || unlockpt (pty_master) == -1 /* Clear slave's lock flag */
1164 || !(slave_name = ptsname (pty_master))) /* Get slave's name */
1166 close (pty_master);
1167 return -1;
1169 strcpy (pty_name, slave_name);
1170 return pty_master;
1173 /* }}} */
1174 /* {{{ System V version of pty_open_slave */
1176 static int pty_open_slave (const char *pty_name)
1178 int pty_slave = open (pty_name, O_RDWR);
1180 if (pty_slave == -1)
1182 perror ("open (pty_name, O_RDWR)");
1183 return -1;
1186 #if !defined(__osf__) && !defined(linux) && !defined(__linux__)
1187 #if defined (I_FIND) && defined (I_PUSH)
1188 if (!ioctl (pty_slave, I_FIND, "ptem"))
1189 if (ioctl (pty_slave, I_PUSH, "ptem") == -1)
1191 fprintf (stderr, "ioctl (pty_slave, I_PUSH, \"ptem\") failed\n");
1192 close (pty_slave);
1193 return -1;
1196 if (!ioctl (pty_slave, I_FIND, "ldterm"))
1197 if (ioctl (pty_slave, I_PUSH, "ldterm") == -1)
1199 fprintf (stderr, "ioctl (pty_slave, I_PUSH, \"ldterm\") failed\n");
1200 close (pty_slave);
1201 return -1;
1204 #if !defined(sgi) && !defined(__sgi)
1205 if (!ioctl (pty_slave, I_FIND, "ttcompat"))
1206 if (ioctl (pty_slave, I_PUSH, "ttcompat") == -1)
1208 fprintf (stderr, "ioctl (pty_slave, I_PUSH, \"ttcompat\") failed\n");
1209 close (pty_slave);
1210 return -1;
1212 #endif /* sgi || __sgi */
1213 #endif /* I_FIND && I_PUSH */
1214 #endif /* __osf__ || linux || __linux__ */
1216 return pty_slave;
1219 /* }}} */
1221 #else /* !HAVE_SCO && !HAVE_GRANTPT */
1223 /* {{{ BSD version of pty_open_master */
1225 static int pty_open_master (char *pty_name)
1227 int pty_master;
1228 char *ptr1, *ptr2;
1230 strcpy (pty_name, "/dev/ptyXX");
1231 for (ptr1 = "pqrstuvwxyzPQRST"; *ptr1; ++ptr1)
1233 pty_name [8] = *ptr1;
1234 for (ptr2 = "0123456789abcdef"; *ptr2; ++ptr2)
1236 pty_name [9] = *ptr2;
1238 /* Try to open master */
1239 if ((pty_master = open (pty_name, O_RDWR)) == -1) {
1240 if (errno == ENOENT) /* Different from EIO */
1241 return -1; /* Out of pty devices */
1242 else
1243 continue; /* Try next pty device */
1245 pty_name [5] = 't'; /* Change "pty" to "tty" */
1246 if (access (pty_name, 6)){
1247 close (pty_master);
1248 pty_name [5] = 'p';
1249 continue;
1251 return pty_master;
1254 return -1; /* Ran out of pty devices */
1257 /* }}} */
1258 /* {{{ BSD version of pty_open_slave */
1260 static int pty_open_slave (const char *pty_name)
1262 int pty_slave;
1263 struct group *group_info = getgrnam ("tty");
1265 if (group_info != NULL)
1267 /* The following two calls will only succeed if we are root */
1268 /* [Commented out while permissions problem is investigated] */
1269 /* chown (pty_name, getuid (), group_info->gr_gid); FIXME */
1270 /* chmod (pty_name, S_IRUSR | S_IWUSR | S_IWGRP); FIXME */
1272 if ((pty_slave = open (pty_name, O_RDWR)) == -1)
1273 perror ("open (pty_name, O_RDWR)");
1274 return pty_slave;
1277 /* }}} */
1279 #endif /* !HAVE_SCO && !HAVE_GRANTPT */
1281 /* }}} */
1283 #endif /* HAVE_SUBSHELL_SUPPORT */
1285 /* {{{ Emacs local variables */
1288 Cause emacs to enter folding mode for this file:
1289 Local variables:
1290 end:
1293 /* }}} */