Take care of one BSOD in NtGdiDdCreateDirectDrawObject, it is not correct fix, it...
[reactos.git] / rosapps / mc / src / subshell.c
1 /* {{{ Copyright notice */
2
3 /* Concurrent shell support for the Midnight Commander
4 Copyright (C) 1994, 1995 Dugan Porter
5
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.
9
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.
14
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., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20 /* }}} */
21
22 #include <config.h>
23 #ifdef HAVE_SUBSHELL_SUPPORT
24
25 /* {{{ Declarations */
26
27 #include <stdio.h>
28 #include <stdlib.h> /* For errno, putenv, etc. */
29 #include <errno.h> /* For errno on SunOS systems */
30 #include <termios.h> /* tcgetattr(), struct termios, etc. */
31 #if (!defined(__IBMC__) && !defined(__IBMCPP__))
32 #include <sys/types.h> /* Required by unistd.h below */
33 #endif
34 #include <sys/ioctl.h> /* For ioctl() (surprise, surprise) */
35 #include <fcntl.h> /* For open(), etc. */
36 #include <string.h> /* strstr(), strcpy(), etc. */
37 #include <signal.h> /* sigaction(), sigprocmask(), etc. */
38 #ifndef SCO_FLAVOR
39 # include <sys/time.h> /* select(), gettimeofday(), etc. */
40 #endif /* SCO_FLAVOR */
41 #include <sys/stat.h> /* Required by dir.h & panel.h below */
42 #include <sys/param.h> /* Required by panel.h below */
43 #include "tty.h"
44
45 #ifdef HAVE_UNISTD_H
46 # include <unistd.h> /* For pipe, fork, setsid, access etc */
47 #endif
48
49 #ifdef HAVE_SYS_SELECT_H
50 # include <sys/select.h>
51 #endif
52
53 #ifdef HAVE_SYS_WAIT_H
54 # include <sys/wait.h> /* For waitpid() */
55 #endif
56
57 #ifndef WEXITSTATUS
58 # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
59 #endif
60
61 #ifndef WIFEXITED
62 # define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
63 #endif
64
65 #ifdef HAVE_GRANTPT
66 # include <stropts.h> /* For I_PUSH */
67 #else
68 # include <grp.h> /* For the group struct & getgrnam() */
69 #endif
70
71 #ifdef SCO_FLAVOR
72 # include <grp.h> /* For the group struct & getgrnam() */
73 #endif /* SCO_FLAVOR */
74
75 #ifdef __QNX__
76 # include <unix.h> /* exec*() from <process.h> */
77 #endif
78
79 #include "dir.h" /* Required by panel.h below */
80 #include "util.h" /* Required by panel.h */
81 #include "panel.h" /* For WPanel and current_panel */
82 #include "dialog.h" /* For query_dialog() */
83 #include "main.h" /* For cpanel, quit & init_sigchld() */
84 #include "global.h" /* For home_dir */
85 #include "cons.saver.h" /* For handle_console(), etc. */
86 #include "key.h" /* XCTRL and ALT macros */
87 #include "subshell.h"
88
89 /* Local functions */
90 static int feed_subshell (int how, int fail_on_error);
91 static void synchronize (void);
92 static int pty_open_master (char *pty_name);
93 static int pty_open_slave (const char *pty_name);
94
95 /* }}} */
96 /* {{{ Definitions */
97
98 #ifndef STDIN_FILENO
99 # define STDIN_FILENO 0
100 #endif
101
102 #ifndef STDOUT_FILENO
103 # define STDOUT_FILENO 1
104 #endif
105
106 #ifndef STDERR_FILENO
107 # define STDERR_FILENO 2
108 #endif
109
110 /* If using a subshell for evaluating commands this is true */
111 int use_subshell =
112 #ifdef SUBSHELL_OPTIONAL
113 FALSE;
114 #else
115 TRUE;
116 #endif
117
118 /* File descriptor of the pseudoterminal used by the subshell */
119 int subshell_pty = 0;
120
121 /* If true, the child forked in init_subshell will wait in a loop to be attached by gdb */
122 int debug_subshell = 0;
123
124 /* The key for switching back to MC from the subshell */
125 char subshell_switch_key = XCTRL('o');
126
127 /* State of the subshell:
128 * INACTIVE: the default state; awaiting a command
129 * ACTIVE: remain in the shell until the user hits `subshell_switch_key'
130 * RUNNING_COMMAND: return to MC when the current command finishes */
131 enum subshell_state_enum subshell_state;
132
133 /* Holds the latest prompt captured from the subshell */
134 char *subshell_prompt = NULL;
135
136 /* Initial length of the buffer for the subshell's prompt */
137 #define INITIAL_PROMPT_SIZE 10
138
139 /* Used by the child process to indicate failure to start the subshell */
140 #define FORK_FAILURE 69 /* Arbitrary */
141
142 /* Initial length of the buffer for all I/O with the subshell */
143 #define INITIAL_PTY_BUFFER_SIZE 100 /* Arbitrary; but keep it >= 80 */
144
145 /* For pipes */
146 enum {READ=0, WRITE=1};
147
148
149 /* Local variables */
150
151 static char *pty_buffer; /* For reading/writing on the subshell's pty */
152 static int pty_buffer_size; /* The buffer grows as needed */
153 static int subshell_pipe[2]; /* To pass CWD info from the subshell to MC */
154 static pid_t subshell_pid = 1; /* The subshell's process ID */
155 static char subshell_cwd[MC_MAXPATHLEN+1]; /* One extra char for final '\n' */
156
157 /* Subshell type (gleaned from the SHELL environment variable, if available) */
158 static enum {BASH, TCSH, ZSH} subshell_type;
159
160 /* Flag to indicate whether the subshell is ready for next command */
161 static int subshell_ready;
162
163 /* The following two flags can be changed by the SIGCHLD handler. This is */
164 /* OK, because the `int' type is updated atomically on all known machines */
165 static volatile int subshell_alive, subshell_stopped;
166
167 /* We store the terminal's initial mode here so that we can configure
168 the pty similarly, and also so we can restore the real terminal to
169 sanity if we have to exit abruptly */
170 static struct termios shell_mode;
171
172 /* This counter indicates how many characters of prompt we have read */
173 /* FIXME: try to figure out why this had to become global */
174 static int prompt_pos;
175
176 /* }}} */
177
178 /* {{{ init_subshell */
179
180 /*
181 * Fork the subshell, and set up many, many things.
182 *
183 * Possibly modifies the global variables:
184 * shell_mode
185 * subshell_type, subshell_alive, subshell_stopped, subshell_pid
186 * use_subshell - Is set to FALSE if we can't run the subshell
187 * quit - Can be set to SUBSHELL_EXIT by the SIGCHLD handler
188 */
189
190 #ifdef HAVE_GRANTPT
191 # define SYNC_PTY_SIDES
192 #else
193 # define SYNC_PTY_SIDES
194 #endif
195
196 #undef SYNC_PTY_SIDES
197
198 #ifdef SYNC_PTY_SIDES
199 /* Handler for SIGUSR1 (used below), does nothing but accept the signal */
200 static void sigusr1_handler (int sig)
201 {
202 }
203 #endif
204
205 void init_subshell (void)
206 {
207 /* {{{ Local variables */
208
209 /* This must be remembered across calls to init_subshell() */
210 static char pty_name[40];
211 int pty_slave;
212
213 /* Braindead tcsh can't redirect output to a file descriptor? */
214 char tcsh_fifo[sizeof "/tmp/mc.pipe.1234567890"];
215
216
217 #ifdef SYNC_PTY_SIDES
218 /* Used to wait for a SIGUSR1 signal from the subprocess */
219 sigset_t sigusr1_mask, old_mask;
220 #endif
221
222 /* }}} */
223
224 if (subshell_pty == 0) /* First time through */
225 {
226 /* {{{ Find out what type of shell we have */
227
228 if (strstr (shell, "/zsh"))
229 subshell_type = ZSH;
230 else if (strstr (shell, "/tcsh"))
231 subshell_type = TCSH;
232 else if (strstr (shell, "/bash") || getenv ("BASH"))
233 subshell_type = BASH;
234 else
235 {
236 use_subshell = FALSE;
237 return;
238 }
239
240 /* }}} */
241 /* {{{ Open a pty for talking to the subshell */
242
243 /* FIXME: We may need to open a fresh pty each time on SVR4 */
244
245 subshell_pty = pty_open_master (pty_name);
246 if (subshell_pty == -1)
247 {
248 fputs (__FILE__": couldn't open master side of pty\n", stderr);
249 perror ("pty_open_master");
250 use_subshell = FALSE;
251 return;
252 }
253 pty_slave = pty_open_slave (pty_name);
254 if (pty_slave == -1)
255 {
256 fprintf (stderr, "couldn't open slave side of pty (%s)\n\r",
257 pty_name);
258 use_subshell = FALSE;
259 return;
260 }
261
262
263 /* }}} */
264 /* {{{ Initialise the pty's I/O buffer */
265
266 pty_buffer_size = INITIAL_PTY_BUFFER_SIZE;
267 pty_buffer = (char *) malloc (pty_buffer_size);
268
269 /* }}} */
270 /* {{{ Create a pipe for receiving the subshell's CWD */
271
272 if (subshell_type == TCSH)
273 {
274 sprintf (tcsh_fifo, "/tmp/mc.pipe.%d", getpid ());
275 if (mkfifo (tcsh_fifo, 0600) == -1)
276 {
277 perror (__FILE__": mkfifo");
278 use_subshell = FALSE;
279 return;
280 }
281
282 /* Opening the FIFO as O_RDONLY or O_WRONLY causes deadlock */
283
284 if ((subshell_pipe[READ] = open (tcsh_fifo, O_RDWR)) == -1 ||
285 (subshell_pipe[WRITE] = open (tcsh_fifo, O_RDWR)) == -1)
286 {
287 fprintf (stderr, _("Couldn't open named pipe %s\n"), tcsh_fifo);
288 perror (__FILE__": open");
289 use_subshell = FALSE;
290 return;
291 }
292 }
293 else /* subshell_type is BASH or ZSH */
294 if (pipe (subshell_pipe))
295 {
296 perror (__FILE__": couldn't create pipe");
297 use_subshell = FALSE;
298 return;
299 }
300
301 /* }}} */
302 }
303
304 /* {{{ Define a handler for the sigusr1 signal */
305
306 #ifdef SYNC_PTY_SIDES
307 sigemptyset (&sigusr1_mask);
308 sigaddset (&sigusr1_mask, SIGUSR1);
309 sigprocmask (SIG_BLOCK, &sigusr1_mask, &old_mask);
310 signal (SIGUSR1, sigusr1_handler);
311 #endif
312
313 /* }}} */
314 /* {{{ Fork the subshell */
315
316 subshell_alive = TRUE;
317 subshell_stopped = FALSE;
318 subshell_pid = fork ();
319
320 if (subshell_pid == -1)
321 {
322 perror (__FILE__": couldn't spawn the subshell process");
323 /* We exit here because, if the process table is full, the */
324 /* other method of running user commands won't work either */
325 exit (1);
326 }
327
328 /* }}} */
329
330 if (subshell_pid == 0) /* We are in the child process */
331 {
332 char *init_file = NULL;
333
334 setsid (); /* Get a fresh terminal session */
335
336 /* {{{ Open the slave side of the pty: again */
337 pty_slave = pty_open_slave (pty_name);
338
339 /* This must be done before closing the master side of the pty, */
340 /* or it will fail on certain idiotic systems, such as Solaris. */
341
342 /* Close master side of pty. This is important; apart from */
343 /* freeing up the descriptor for use in the subshell, it also */
344 /* means that when MC exits, the subshell will get a SIGHUP and */
345 /* exit too, because there will be no more descriptors pointing */
346 /* at the master side of the pty and so it will disappear. */
347
348 close (subshell_pty);
349
350 #ifdef SYNC_PTY_SIDES
351 /* Give our parent process (MC) the go-ahead */
352 kill (getppid (), SIGUSR1);
353 #endif
354
355 /* }}} */
356 /* {{{ Make sure that it has become our controlling terminal */
357
358 /* Redundant on Linux and probably most systems, but just in case: */
359
360 # ifdef TIOCSCTTY
361 ioctl (pty_slave, TIOCSCTTY, 0);
362 # endif
363
364 /* }}} */
365 /* {{{ Configure its terminal modes and window size */
366
367 /* Set up the pty with the same termios flags as our own tty, plus */
368 /* TOSTOP, which keeps background processes from writing to the pty */
369
370 shell_mode.c_lflag |= TOSTOP; /* So background writers get SIGTTOU */
371 if (tcsetattr (pty_slave, TCSANOW, &shell_mode))
372 {
373 perror (__FILE__": couldn't set pty terminal modes");
374 _exit (FORK_FAILURE);
375 }
376
377 /* Set the pty's size (80x25 by default on Linux) according to the */
378 /* size of the real terminal as calculated by ncurses, if possible */
379 # if defined TIOCSWINSZ && !defined SCO_FLAVOR
380 {
381 struct winsize tty_size;
382 tty_size.ws_row = LINES;
383 tty_size.ws_col = COLS;
384 tty_size.ws_xpixel = tty_size.ws_ypixel = 0;
385
386 if (ioctl (pty_slave, TIOCSWINSZ, &tty_size))
387 perror (__FILE__": couldn't set pty size");
388 }
389 # endif
390
391 /* }}} */
392 /* {{{ Set up the subshell's environment and init file name */
393
394 /* It simplifies things to change to our home directory here, */
395 /* and the user's startup file may do a `cd' command anyway */
396 chdir (home_dir); /* FIXME? What about when we re-run the subshell? */
397
398 switch (subshell_type)
399 {
400 case BASH:
401 init_file = ".mc/bashrc";
402 if (access (init_file, R_OK) == -1)
403 init_file = ".bashrc";
404
405 /* Make MC's special commands not show up in bash's history */
406 putenv ("HISTCONTROL=ignorespace");
407
408 /* Allow alternative readline settings for MC */
409 if (access (".mc/inputrc", R_OK) == 0)
410 putenv ("INPUTRC=.mc/inputrc");
411
412 break;
413
414 case TCSH:
415 init_file = ".mc/tcshrc";
416 if (access (init_file, R_OK) == -1)
417 init_file += 3;
418 break;
419
420 case ZSH:
421 break;
422
423 default:
424 fprintf (stderr, __FILE__": unimplemented subshell type %d\n",
425 subshell_type);
426 _exit (FORK_FAILURE);
427 }
428
429 /* }}} */
430 /* {{{ Attach all our standard file descriptors to the pty */
431
432 /* This is done just before the fork, because stderr must still */
433 /* be connected to the real tty during the above error messages; */
434 /* otherwise the user will never see them. */
435
436 dup2 (pty_slave, STDIN_FILENO);
437 dup2 (pty_slave, STDOUT_FILENO);
438 dup2 (pty_slave, STDERR_FILENO);
439
440 /* }}} */
441 /* {{{ Execute the subshell at last */
442
443 close (subshell_pipe[READ]);
444 close (pty_slave); /* These may be FD_CLOEXEC, but just in case... */
445
446 switch (subshell_type)
447 {
448 case BASH:
449 execl (shell, "bash", "-rcfile", init_file, NULL);
450 break;
451
452 case TCSH:
453 execl (shell, "tcsh", NULL); /* What's the -rcfile equivalent? */
454 break;
455
456 case ZSH:
457 execl (shell, "zsh", "+Z", NULL);
458 break;
459 }
460
461 /* If we get this far, everything failed miserably */
462 _exit (FORK_FAILURE);
463
464 /* }}} */
465 }
466
467 close(pty_slave);
468
469 #ifdef SYNC_PTY_SIDES
470 sigsuspend (&old_mask);
471 signal (SIGUSR1, SIG_DFL);
472 sigprocmask (SIG_SETMASK, &old_mask, NULL);
473 /* ...before installing our handler for SIGCHLD. */
474 #endif
475
476 #if 0
477 /* {{{ Install our handler for SIGCHLD */
478
479 init_sigchld ();
480
481 /* We could have received the SIGCHLD signal for the subshell
482 * before installing the init_sigchld */
483 pid = waitpid (subshell_pid, &status, WUNTRACED | WNOHANG);
484 if (pid == subshell_pid){
485 use_subshell = FALSE;
486 return;
487 }
488
489 /* }}} */
490 #endif
491
492 /* {{{ Set up `precmd' or equivalent for reading the subshell's CWD */
493
494 switch (subshell_type)
495 {
496 char precmd[80];
497
498 case BASH:
499 sprintf (precmd, " PROMPT_COMMAND='pwd>&%d;kill -STOP $$'\n",
500 subshell_pipe[WRITE]);
501 goto write_it;
502
503 case ZSH:
504 sprintf (precmd, "precmd(){ pwd>&%d;kill -STOP $$ }\n",
505 subshell_pipe[WRITE]);
506 goto write_it;
507
508 case TCSH:
509 sprintf (precmd, "alias precmd 'echo $cwd:q >>%s;kill -STOP $$'\n", tcsh_fifo);
510
511 write_it:
512 write (subshell_pty, precmd, strlen (precmd));
513 }
514
515 /* }}} */
516 /* {{{ Wait until the subshell has started up and processed the command */
517
518 subshell_state = RUNNING_COMMAND;
519 enable_interrupt_key ();
520 if (!feed_subshell (QUIETLY, TRUE)){
521 use_subshell = FALSE;
522 }
523 disable_interrupt_key ();
524 if (!subshell_alive)
525 use_subshell = FALSE; /* Subshell died instantly, so don't use it */
526
527 /* }}} */
528 }
529
530 /* }}} */
531 /* {{{ invoke_subshell */
532
533 int invoke_subshell (const char *command, int how, char **new_dir)
534 {
535 /* {{{ Fiddle with terminal modes */
536
537 static struct termios raw_mode = {0};
538
539 /* MC calls reset_shell_mode() in pre_exec() to set the real tty to its */
540 /* original settings. However, here we need to make this tty very raw, */
541 /* so that all keyboard signals, XON/XOFF, etc. will get through to the */
542 /* pty. So, instead of changing the code for execute(), pre_exec(), */
543 /* etc, we just set up the modes we need here, before each command. */
544
545 if (raw_mode.c_iflag == 0) /* First time: initialise `raw_mode' */
546 {
547 tcgetattr (STDOUT_FILENO, &raw_mode);
548 raw_mode.c_lflag &= ~ICANON; /* Disable line-editing chars, etc. */
549 raw_mode.c_lflag &= ~ISIG; /* Disable intr, quit & suspend chars */
550 raw_mode.c_lflag &= ~ECHO; /* Disable input echoing */
551 raw_mode.c_iflag &= ~IXON; /* Pass ^S/^Q to subshell undisturbed */
552 raw_mode.c_iflag &= ~ICRNL; /* Don't translate CRs into LFs */
553 raw_mode.c_oflag &= ~OPOST; /* Don't postprocess output */
554 raw_mode.c_cc[VTIME] = 0; /* IE: wait forever, and return as */
555 raw_mode.c_cc[VMIN] = 1; /* soon as a character is available */
556 }
557
558 tcsetattr (STDOUT_FILENO, TCSANOW, &raw_mode);
559
560 /* }}} */
561
562 /* Make the subshell change to MC's working directory */
563 if (new_dir)
564 do_subshell_chdir (cpanel->cwd, TRUE, 1);
565
566 if (command == NULL) /* The user has done "C-o" from MC */
567 {
568 if (subshell_state == INACTIVE)
569 {
570 subshell_state = ACTIVE;
571 /* FIXME: possibly take out this hack; the user can
572 re-play it by hitting C-hyphen a few times! */
573 write (subshell_pty, " \b", 2); /* Hack to make prompt reappear */
574 }
575 }
576 else /* MC has passed us a user command */
577 {
578 if (how == QUIETLY)
579 write (subshell_pty, " ", 1);
580 write (subshell_pty, command, strlen (command));
581 write (subshell_pty, "\n", 1);
582 subshell_state = RUNNING_COMMAND;
583 subshell_ready = FALSE;
584 }
585
586 feed_subshell (how, FALSE);
587
588 if (new_dir && subshell_alive && strcmp (subshell_cwd, cpanel->cwd))
589 *new_dir = subshell_cwd; /* Make MC change to the subshell's CWD */
590
591 /* Restart the subshell if it has died by SIGHUP, SIGQUIT, etc. */
592 while (!subshell_alive && !quit && use_subshell)
593 init_subshell ();
594
595 prompt_pos = 0;
596
597 return quit;
598 }
599
600 /* }}} */
601 /* {{{ read_subshell_prompt */
602
603 int read_subshell_prompt (int how)
604 {
605 /* {{{ Local variables */
606
607 int clear_now = FALSE;
608 static int prompt_size = INITIAL_PROMPT_SIZE;
609 int bytes = 0, i, rc = 0;
610 struct timeval timeleft = {0, 0};
611
612 fd_set tmp;
613 FD_ZERO (&tmp);
614 FD_SET (subshell_pty, &tmp);
615
616 /* }}} */
617
618 if (subshell_prompt == NULL) /* First time through */
619 {
620 subshell_prompt = (char *) malloc (prompt_size);
621 *subshell_prompt = '\0';
622 prompt_pos = 0;
623 }
624
625 while (subshell_alive &&
626 (rc = select (FD_SETSIZE, &tmp, NULL, NULL, &timeleft)))
627 {
628 /* {{{ Check for `select' errors */
629
630 if (rc == -1)
631 if (errno == EINTR)
632 continue;
633 else
634 {
635 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
636 perror ("\n"__FILE__": select (FD_SETSIZE, &tmp...)");
637 exit (1);
638 }
639
640 /* }}} */
641
642 bytes = read (subshell_pty, pty_buffer, pty_buffer_size);
643 if (how == VISIBLY)
644 write (STDOUT_FILENO, pty_buffer, bytes);
645
646 /* {{{ Extract the prompt from the shell output */
647
648 for (i=0; i<bytes; ++i)
649 if (pty_buffer[i] == '\n' || pty_buffer[i] == '\r'){
650 prompt_pos = 0;
651 clear_now = FALSE;
652 } else {
653 clear_now = TRUE;
654 if (!pty_buffer [i])
655 continue;
656
657 subshell_prompt[prompt_pos++] = pty_buffer[i];
658 if (prompt_pos == prompt_size)
659 subshell_prompt = (char *) realloc (subshell_prompt,
660 prompt_size *= 2);
661 }
662
663 /* Sometimes we get an empty new line and then nothing,
664 * we better just keep the old prompt instead. */
665 if (clear_now)
666 subshell_prompt[prompt_pos] = '\0';
667
668 /* }}} */
669 }
670 if (rc == 0 && bytes == 0)
671 return FALSE;
672 return TRUE;
673 }
674
675 /* }}} */
676 /* {{{ resize_subshell */
677
678 void resize_subshell (void)
679 {
680 #if defined TIOCSWINSZ && !defined SCO_FLAVOR
681 struct winsize tty_size;
682
683 tty_size.ws_row = LINES;
684 tty_size.ws_col = COLS;
685 tty_size.ws_xpixel = tty_size.ws_ypixel = 0;
686
687 if (ioctl (subshell_pty, TIOCSWINSZ, &tty_size))
688 perror (__FILE__": couldn't set pty size");
689 #endif
690 }
691
692 /* }}} */
693 /* {{{ exit_subshell */
694
695 int exit_subshell (void)
696 {
697 int quit = TRUE;
698
699 if (subshell_state != INACTIVE && subshell_alive)
700 quit = !query_dialog (_(" Warning "), _(" The shell is still active. Quit anyway? "),
701 0, 2, _("&Yes"), _("&No"));
702
703 #if AIX_TCSH_CODE_BELOW_IS_IT_FIXED
704 /* New Test code */
705 else
706 {
707 if (subshell_type == TCSH)
708 sprintf (pty_buffer, " echo -n Jobs:>/tmp/mc.pipe.%d;jobs>/tmp/"
709 "mc.pipe.%d;kill -STOP $$\n", getpid (), getpid ());
710 else
711 sprintf (pty_buffer, " echo -n Jobs:>&%d;jobs>&%d;kill -STOP $$\n",
712 subshell_pipe[WRITE], subshell_pipe[WRITE]);
713 write (subshell_pty, pty_buffer, strlen (pty_buffer));
714
715 #ifndef HAVE_GRANTPT /* FIXME */
716 if (subshell_type == ZSH)
717 /* Here we have to drain the shell output, because zsh does a */
718 /* tcsetattr(SHTTY, TCSADRAIN...) which will block if we don't */
719 read (subshell_pty, pty_buffer, pty_buffer_size);
720 #endif
721
722 /* TCSH + AIX hang here, fix this before removing the ifdef above */
723 if (read (subshell_pipe[READ], pty_buffer, pty_buffer_size) == 5)
724 quit = TRUE;
725 else
726 quit = !query_dialog (_(" Warning "), _(" There are stopped jobs.")
727 _(" Quit anyway? "), 0, 2, _("&Yes"), _("&No"));
728
729 synchronize ();
730 subshell_state = RUNNING_COMMAND;
731 feed_subshell (QUIETLY, FALSE); /* Drain the shell output (again) */
732 }
733 #endif
734
735 if (quit && subshell_type == TCSH)
736 {
737 /* We abuse of pty_buffer here, but it doesn't matter at this stage */
738 sprintf (pty_buffer, "/tmp/mc.pipe.%d", getpid ());
739 if (unlink (pty_buffer) == -1)
740 perror (__FILE__": couldn't remove named pipe /tmp/mc.pipe.NNN");
741 }
742
743 return quit;
744 }
745
746 /* }}} */
747
748 /* {{{ do_subshell_chdir */
749 /* If it actually changed the directory it returns true */
750 void do_subshell_chdir (char *directory, int do_update, int reset_prompt)
751 {
752 char *temp;
753
754 if (!(subshell_state == INACTIVE && strcmp (subshell_cwd, cpanel->cwd))){
755 /* We have to repaint the subshell prompt if we read it from
756 * the main program. Please note that in the code after this
757 * if, the cd command that is sent will make the subshell
758 * repaint the prompt, so we don't have to paint it. */
759 if (do_update)
760 do_update_prompt ();
761 return;
762 }
763
764 /* The initial space keeps this out of the command history (in bash
765 because we set "HISTCONTROL=ignorespace") */
766 write (subshell_pty, " cd ", 4);
767 if (*directory) {
768 temp = name_quote (directory, 0);
769 write (subshell_pty, temp, strlen (temp));
770 free (temp);
771 } else {
772 write (subshell_pty, "/", 1);
773 }
774 write (subshell_pty, "\n", 1);
775
776 subshell_state = RUNNING_COMMAND;
777 feed_subshell (QUIETLY, FALSE);
778
779 if (subshell_alive && strcmp (subshell_cwd, cpanel->cwd) && strcmp (cpanel->cwd, "."))
780 fprintf (stderr, _("Warning: Couldn't change to %s.\n"), cpanel->cwd);
781
782 if (reset_prompt)
783 prompt_pos = 0;
784 update_prompt = FALSE;
785 /* Make sure that MC never stores the CWD in a silly format */
786 /* like /usr////lib/../bin, or the strcmp() above will fail */
787 }
788
789 /* }}} */
790 /* {{{ subshell_get_console_attributes */
791
792 void subshell_get_console_attributes (void)
793 {
794 /* {{{ Get our current terminal modes */
795
796 if (tcgetattr (STDOUT_FILENO, &shell_mode))
797 {
798 perror (__FILE__": couldn't get terminal settings");
799 use_subshell = FALSE;
800 return;
801 }
802
803 /* }}} */
804 }
805
806 /* }}} */
807 /* {{{ sigchld_handler */
808
809 /* Figure out whether the subshell has stopped, exited or been killed */
810 /* Possibly modifies: `subshell_alive', `subshell_stopped' and `quit' */
811
812 void sigchld_handler (int sig)
813 {
814 int pid, status;
815
816 pid = waitpid (subshell_pid, &status, WUNTRACED | WNOHANG);
817
818 if (pid == subshell_pid) {
819 /* {{{ Figure out what has happened to the subshell */
820
821 if (WIFSTOPPED (status))
822 {
823 if (WSTOPSIG (status) == SIGTSTP)
824 /* The user has suspended the subshell. Revive it */
825 kill (subshell_pid, SIGCONT);
826 else
827 /* The subshell has received a SIGSTOP signal */
828 subshell_stopped = TRUE;
829 }
830 else /* The subshell has either exited normally or been killed */
831 {
832 subshell_alive = FALSE;
833 if (WIFEXITED (status) && WEXITSTATUS (status) != FORK_FAILURE)
834 quit |= SUBSHELL_EXIT; /* Exited normally */
835 }
836
837 /* }}} */
838 }
839
840 #ifndef HAVE_X
841 #ifndef SCO_FLAVOR
842 pid = waitpid (cons_saver_pid, &status, WUNTRACED | WNOHANG);
843
844 if (pid == cons_saver_pid) {
845 /* {{{ Someone has stopped or killed cons.saver; restart it */
846
847 if (WIFSTOPPED (status))
848 kill (pid, SIGCONT);
849 else
850 {
851 handle_console (CONSOLE_DONE);
852 handle_console (CONSOLE_INIT);
853 /* Ought to do: if (in_subshell) handle_console (CONSOLE_SAVE)
854 Can't do this without adding a new variable `in_subshell';
855 it hardly seems to be worth the trouble. */
856 }
857
858 /* }}} */
859 }
860 #endif /* ! SCO_FLAVOR */
861 #endif /* ! HAVE_X */
862 /* If we get here, some other child exited; ignore it */
863 }
864
865 /* }}} */
866
867 /* {{{ feed_subshell */
868
869 /* Feed the subshell our keyboard input until it says it's finished */
870
871 static int feed_subshell (int how, int fail_on_error)
872 {
873 /* {{{ Local variables */
874 fd_set read_set; /* For `select' */
875 int bytes; /* For the return value from `read' */
876 int i; /* Loop counter */
877
878 struct timeval wtime; /* Maximum time we wait for the subshell */
879 struct timeval *wptr;
880 /* }}} */
881
882 /* we wait up to 10 seconds if fail_on_error */
883 wtime.tv_sec = 10;
884 wtime.tv_usec = 0;
885
886 for (wptr = fail_on_error ? &wtime : NULL;;)
887 {
888 if (!subshell_alive)
889 return FALSE;
890
891 /* {{{ Prepare the file-descriptor set and call `select' */
892
893 FD_ZERO (&read_set);
894 FD_SET (subshell_pty, &read_set);
895 FD_SET (subshell_pipe[READ], &read_set);
896 if (how == VISIBLY)
897 FD_SET (STDIN_FILENO, &read_set);
898
899 if (select (FD_SETSIZE, &read_set, NULL, NULL, wptr) == -1){
900
901 /* Despite using SA_RESTART, we still have to check for this */
902 if (errno == EINTR)
903 continue; /* try all over again */
904 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
905 perror ("\n"__FILE__": select (FD_SETSIZE, &read_set...)");
906 exit (1);
907 }
908 /* }}} */
909
910 /* From now on: block forever on the select call */
911 wptr = NULL;
912
913 if (FD_ISSET (subshell_pty, &read_set))
914 /* {{{ Read from the subshell, write to stdout */
915
916 /* This loop improves performance by reducing context switches
917 by a factor of 20 or so... unfortunately, it also hangs MC
918 randomly, because of an apparent Linux bug. Investigate. */
919 /* for (i=0; i<5; ++i) * FIXME -- experimental */
920 {
921 bytes = read (subshell_pty, pty_buffer, pty_buffer_size);
922 if (bytes == -1 && errno != EIO)
923 {
924 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
925 perror ("\n"__FILE__": read (subshell_pty...)");
926 exit (1);
927 }
928 if (how == VISIBLY)
929 write (STDOUT_FILENO, pty_buffer, bytes);
930 }
931
932 /* }}} */
933
934 else if (FD_ISSET (subshell_pipe[READ], &read_set))
935 /* {{{ Read the subshell's CWD and capture its prompt */
936
937 {
938 bytes = read (subshell_pipe[READ], subshell_cwd, MC_MAXPATHLEN+1);
939 if (bytes == -1)
940 {
941 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
942 perror ("\n"__FILE__": read (subshell_pipe[READ]...)");
943 exit (1);
944 }
945 if (bytes >= 1)
946 subshell_cwd[bytes-1] = 0; /* Squash the final '\n' */
947
948 synchronize ();
949
950 subshell_ready = TRUE;
951 if (subshell_state == RUNNING_COMMAND)
952 {
953 subshell_state = INACTIVE;
954 return 1;
955 }
956 }
957
958 /* }}} */
959
960 else if (FD_ISSET (STDIN_FILENO, &read_set))
961 /* {{{ Read from stdin, write to the subshell */
962
963 {
964 bytes = read (STDIN_FILENO, pty_buffer, pty_buffer_size);
965 if (bytes == -1)
966 {
967 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
968 perror ("\n"__FILE__": read (STDIN_FILENO, pty_buffer...)");
969 exit (1);
970 }
971
972 for (i=0; i<bytes; ++i)
973 if (pty_buffer[i] == subshell_switch_key)
974 {
975 write (subshell_pty, pty_buffer, i);
976 if (subshell_ready)
977 subshell_state = INACTIVE;
978 return TRUE;
979 }
980
981 write (subshell_pty, pty_buffer, bytes);
982 subshell_ready = FALSE;
983 } else {
984 return FALSE;
985 }
986
987 /* }}} */
988 }
989 }
990
991 /* }}} */
992 /* {{{ synchronize */
993
994 /* Wait until the subshell dies or stops. If it stops, make it resume. */
995 /* Possibly modifies the globals `subshell_alive' and `subshell_stopped' */
996
997 static void synchronize (void)
998 {
999 sigset_t sigchld_mask, old_mask;
1000
1001 sigemptyset (&sigchld_mask);
1002 sigaddset (&sigchld_mask, SIGCHLD);
1003 sigprocmask (SIG_BLOCK, &sigchld_mask, &old_mask);
1004
1005 /* Wait until the subshell has stopped */
1006 while (subshell_alive && !subshell_stopped)
1007 sigsuspend (&old_mask);
1008 subshell_stopped = FALSE;
1009 kill (subshell_pid, SIGCONT);
1010
1011 sigprocmask (SIG_SETMASK, &old_mask, NULL);
1012 /* We can't do any better without modifying the shell(s) */
1013 }
1014
1015 /* }}} */
1016 /* {{{ pty opening functions */
1017
1018 #ifdef SCO_FLAVOR
1019
1020 /* {{{ SCO version of pty_open_master */
1021
1022 static int pty_open_master (char *pty_name)
1023 {
1024 int pty_master;
1025 int num;
1026 char *ptr;
1027
1028 strcpy (pty_name, "/dev/ptyp");
1029 ptr = pty_name+9;
1030 for (num=0;;num++)
1031 {
1032 sprintf(ptr,"%d",num); /* surpriiise ... SCO lacks itoa() */
1033 /* Try to open master */
1034 if ((pty_master = open (pty_name, O_RDWR)) == -1)
1035 if (errno == ENOENT) /* Different from EIO */
1036 return -1; /* Out of pty devices */
1037 else
1038 continue; /* Try next pty device */
1039 pty_name [5] = 't'; /* Change "pty" to "tty" */
1040 if (access (pty_name, 6)){
1041 close (pty_master);
1042 pty_name [5] = 'p';
1043 continue;
1044 }
1045 return pty_master;
1046 }
1047 return -1; /* Ran out of pty devices */
1048 }
1049
1050 /* }}} */
1051 /* {{{ SCO version of pty_open_slave */
1052
1053 static int pty_open_slave (const char *pty_name)
1054 {
1055 int pty_slave;
1056 struct group *group_info = getgrnam ("terminal");
1057
1058 if (group_info != NULL)
1059 {
1060 /* The following two calls will only succeed if we are root */
1061 /* [Commented out while permissions problem is investigated] */
1062 /* chown (pty_name, getuid (), group_info->gr_gid); FIXME */
1063 /* chmod (pty_name, S_IRUSR | S_IWUSR | S_IWGRP); FIXME */
1064 }
1065 if ((pty_slave = open (pty_name, O_RDWR)) == -1)
1066 perror ("open (pty_name, O_RDWR)");
1067 return pty_slave;
1068 }
1069
1070 /* }}} */
1071
1072 #elif HAVE_GRANTPT
1073
1074 /* {{{ System V version of pty_open_master */
1075
1076 static int pty_open_master (char *pty_name)
1077 {
1078 char *slave_name;
1079 int pty_master;
1080
1081 strcpy (pty_name, "/dev/ptmx");
1082 if ((pty_master = open (pty_name, O_RDWR)) == -1
1083 || grantpt (pty_master) == -1 /* Grant access to slave */
1084 || unlockpt (pty_master) == -1 /* Clear slave's lock flag */
1085 || !(slave_name = ptsname (pty_master))) /* Get slave's name */
1086 {
1087 close (pty_master);
1088 return -1;
1089 }
1090 strcpy (pty_name, slave_name);
1091 return pty_master;
1092 }
1093
1094 /* }}} */
1095 /* {{{ System V version of pty_open_slave */
1096
1097 static int pty_open_slave (const char *pty_name)
1098 {
1099 int pty_slave = open (pty_name, O_RDWR);
1100
1101 if (pty_slave == -1)
1102 {
1103 perror ("open (pty_name, O_RDWR)");
1104 return -1;
1105 }
1106
1107 #if !defined(__osf__)
1108 if (!ioctl (pty_slave, I_FIND, "ptem"))
1109 if (ioctl (pty_slave, I_PUSH, "ptem") == -1)
1110 {
1111 perror ("ioctl (pty_slave, I_PUSH, \"ptem\")");
1112 close (pty_slave);
1113 return -1;
1114 }
1115
1116 if (!ioctl (pty_slave, I_FIND, "ldterm"))
1117 if (ioctl (pty_slave, I_PUSH, "ldterm") == -1)
1118 {
1119 perror ("ioctl (pty_slave, I_PUSH, \"ldterm\")");
1120 close (pty_slave);
1121 return -1;
1122 }
1123
1124 #if !defined(sgi) && !defined(__sgi)
1125 if (!ioctl (pty_slave, I_FIND, "ttcompat"))
1126 if (ioctl (pty_slave, I_PUSH, "ttcompat") == -1)
1127 {
1128 perror ("ioctl (pty_slave, I_PUSH, \"ttcompat\")");
1129 close (pty_slave);
1130 return -1;
1131 }
1132 #endif /* sgi || __sgi */
1133 #endif /* __osf__ */
1134
1135 return pty_slave;
1136 }
1137
1138 /* }}} */
1139
1140 #else
1141
1142 /* {{{ BSD version of pty_open_master */
1143
1144 static int pty_open_master (char *pty_name)
1145 {
1146 int pty_master;
1147 char *ptr1, *ptr2;
1148
1149 strcpy (pty_name, "/dev/ptyXX");
1150 for (ptr1 = "pqrstuvwxyzPQRST"; *ptr1; ++ptr1)
1151 {
1152 pty_name [8] = *ptr1;
1153 for (ptr2 = "0123456789abcdef"; *ptr2; ++ptr2)
1154 {
1155 pty_name [9] = *ptr2;
1156
1157 /* Try to open master */
1158 if ((pty_master = open (pty_name, O_RDWR)) == -1)
1159 if (errno == ENOENT) /* Different from EIO */
1160 return -1; /* Out of pty devices */
1161 else
1162 continue; /* Try next pty device */
1163 pty_name [5] = 't'; /* Change "pty" to "tty" */
1164 if (access (pty_name, 6)){
1165 close (pty_master);
1166 pty_name [5] = 'p';
1167 continue;
1168 }
1169 return pty_master;
1170 }
1171 }
1172 return -1; /* Ran out of pty devices */
1173 }
1174
1175 /* }}} */
1176 /* {{{ BSD version of pty_open_slave */
1177
1178 static int pty_open_slave (const char *pty_name)
1179 {
1180 int pty_slave;
1181 struct group *group_info = getgrnam ("tty");
1182
1183 if (group_info != NULL)
1184 {
1185 /* The following two calls will only succeed if we are root */
1186 /* [Commented out while permissions problem is investigated] */
1187 /* chown (pty_name, getuid (), group_info->gr_gid); FIXME */
1188 /* chmod (pty_name, S_IRUSR | S_IWUSR | S_IWGRP); FIXME */
1189 }
1190 if ((pty_slave = open (pty_name, O_RDWR)) == -1)
1191 perror ("open (pty_name, O_RDWR)");
1192 return pty_slave;
1193 }
1194
1195 /* }}} */
1196
1197 #endif
1198
1199 /* }}} */
1200
1201 #endif /* HAVE_SUBSHELL_SUPPORT */
1202
1203 /* {{{ Emacs local variables */
1204
1205 /*
1206 Cause emacs to enter folding mode for this file:
1207 Local variables:
1208 end:
1209 */
1210
1211 /* }}} */