root/src/subshell/common.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. write_all
  2. init_subshell_child
  3. init_raw_mode
  4. synchronize
  5. read_command_line_buffer
  6. clear_subshell_prompt_string
  7. parse_subshell_prompt_string
  8. set_prompt_string
  9. feed_subshell
  10. pty_open_master
  11. pty_open_slave
  12. pty_open_master
  13. pty_open_slave
  14. init_subshell_precmd
  15. subshell_name_quote
  16. init_subshell
  17. invoke_subshell
  18. flush_subshell
  19. read_subshell_prompt
  20. do_update_prompt
  21. exit_subshell
  22. do_subshell_chdir
  23. subshell_get_console_attributes
  24. sigchld_handler

   1 /*
   2    Concurrent shell support for the Midnight Commander
   3 
   4    Copyright (C) 1994-2021
   5    Free Software Foundation, Inc.
   6 
   7    Written by:
   8    Alexander Kriegisch <Alexander@Kriegisch.name>
   9    Aliaksey Kandratsenka <alk@tut.by>
  10    Andreas Mohr <and@gmx.li>
  11    Andrew Borodin <aborodin@vmail.ru>
  12    Andrew V. Samoilov <sav@bcs.zp.ua>
  13    Chris Owen <chris@candu.co.uk>
  14    Claes Nästén <me@pekdon.net>
  15    Egmont Koblinger <egmont@gmail.com>
  16    Enrico Weigelt, metux IT service <weigelt@metux.de>
  17    Eric Roberts <ericmrobertsdeveloper@gmail.com>
  18    Igor Urazov <z0rc3r@gmail.com>
  19    Ilia Maslakov <il.smind@gmail.com>
  20    Leonard den Ottolander <leonard@den.ottolander.nl>
  21    Miguel de Icaza <miguel@novell.com>
  22    Mikhail S. Pobolovets <styx.mp@gmail.com>
  23    Norbert Warmuth <nwarmuth@privat.circular.de>
  24    Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
  25    Patrick Winnertz <winnie@debian.org>
  26    Pavel Machek <pavel@suse.cz>
  27    Pavel Roskin <proski@gnu.org>
  28    Pavel Tsekov <ptsekov@gmx.net>
  29    Roland Illig <roland.illig@gmx.de>
  30    Sergei Trofimovich <slyfox@inbox.ru>
  31    Slava Zanko <slavazanko@gmail.com>
  32    Timur Bakeyev <mc@bat.ru>
  33    Vit Rosin <vit_r@list.ru>
  34 
  35    This file is part of the Midnight Commander.
  36 
  37    The Midnight Commander is free software: you can redistribute it
  38    and/or modify it under the terms of the GNU General Public License as
  39    published by the Free Software Foundation, either version 3 of the License,
  40    or (at your option) any later version.
  41 
  42    The Midnight Commander is distributed in the hope that it will be useful,
  43    but WITHOUT ANY WARRANTY; without even the implied warranty of
  44    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  45    GNU General Public License for more details.
  46 
  47    You should have received a copy of the GNU General Public License
  48    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  49  */
  50 
  51 /** \file subshell.c
  52  *  \brief Source: concurrent shell support
  53  */
  54 
  55 #include <config.h>
  56 
  57 #ifndef _GNU_SOURCE
  58 #define _GNU_SOURCE 1
  59 #endif
  60 
  61 #include <ctype.h>
  62 #include <stdio.h>
  63 #include <stdlib.h>
  64 #include <errno.h>
  65 #include <string.h>
  66 #include <signal.h>
  67 #ifdef HAVE_SYS_SELECT_H
  68 #include <sys/select.h>
  69 #else
  70 #include <sys/time.h>
  71 #include <unistd.h>
  72 #endif
  73 #include <sys/types.h>
  74 #include <sys/wait.h>
  75 #ifdef HAVE_SYS_IOCTL_H
  76 #include <sys/ioctl.h>
  77 #endif
  78 #include <termios.h>
  79 
  80 #ifdef HAVE_STROPTS_H
  81 #include <stropts.h>            /* For I_PUSH */
  82 #endif /* HAVE_STROPTS_H */
  83 
  84 #ifdef HAVE_OPENPTY
  85 /* includes for openpty() */
  86 #ifdef HAVE_PTY_H
  87 #include <pty.h>
  88 #endif
  89 #ifdef HAVE_UTIL_H
  90 #include <util.h>
  91 #endif
  92 /* <sys/types.h> is a prerequisite of <libutil.h> on FreeBSD 8.0.  */
  93 #ifdef HAVE_LIBUTIL_H
  94 #include <libutil.h>
  95 #endif
  96 #endif /* HAVE_OPENPTY */
  97 
  98 #include "lib/global.h"
  99 
 100 #include "lib/fileloc.h"
 101 #include "lib/unixcompat.h"
 102 #include "lib/tty/tty.h"        /* LINES */
 103 #include "lib/tty/key.h"        /* XCTRL */
 104 #include "lib/vfs/vfs.h"
 105 #include "lib/strutil.h"
 106 #include "lib/mcconfig.h"
 107 #include "lib/util.h"
 108 #include "lib/widget.h"
 109 
 110 #include "src/filemanager/layout.h"     /* setup_cmdline() */
 111 #include "src/filemanager/command.h"    /* cmdline */
 112 
 113 #include "subshell.h"
 114 #include "internal.h"
 115 
 116 /*** global variables ****************************************************************************/
 117 
 118 /* State of the subshell:
 119  * INACTIVE: the default state; awaiting a command
 120  * ACTIVE: remain in the shell until the user hits 'subshell_switch_key'
 121  * RUNNING_COMMAND: return to MC when the current command finishes */
 122 enum subshell_state_enum subshell_state;
 123 
 124 /* Holds the latest prompt captured from the subshell */
 125 GString *subshell_prompt = NULL;
 126 
 127 /* Subshell: if set, then the prompt was not saved on CONSOLE_SAVE */
 128 /* We need to paint it after CONSOLE_RESTORE, see: load_prompt */
 129 gboolean update_subshell_prompt = FALSE;
 130 
 131 /* If set, then a command has just finished executing, and we need */
 132 /* to be on the lookout for a new prompt string from the subshell. */
 133 gboolean should_read_new_subshell_prompt;
 134 
 135 /*** file scope macro definitions ****************************************************************/
 136 
 137 #ifndef WEXITSTATUS
 138 #define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8)
 139 #endif
 140 
 141 #ifndef WIFEXITED
 142 #define WIFEXITED(stat_val) (((stat_val) & 255) == 0)
 143 #endif
 144 
 145 /* Initial length of the buffer for the subshell's prompt */
 146 #define INITIAL_PROMPT_SIZE 10
 147 
 148 /* Used by the child process to indicate failure to start the subshell */
 149 #define FORK_FAILURE 69         /* Arbitrary */
 150 
 151 /* Length of the buffer for all I/O with the subshell */
 152 #define PTY_BUFFER_SIZE BUF_SMALL       /* Arbitrary; but keep it >= 80 */
 153 
 154 /*** file scope type declarations ****************************************************************/
 155 
 156 /* For pipes */
 157 enum
 158 {
 159     READ = 0,
 160     WRITE = 1
 161 };
 162 
 163 /* This is the keybinding that is sent to the shell, to make the shell send us the contents of
 164  * the current command buffer. */
 165 #define SHELL_BUFFER_KEYBINDING "_"
 166 
 167 /* This is the keybinding that is sent to the shell, to make the shell send us the location of
 168  * the cursor. */
 169 #define SHELL_CURSOR_KEYBINDING "+"
 170 
 171 /*** file scope variables ************************************************************************/
 172 
 173 /* tcsh closes all non-standard file descriptors, so we have to use a pipe */
 174 static char tcsh_fifo[128];
 175 
 176 static int subshell_pty_slave = -1;
 177 
 178 /* The key for switching back to MC from the subshell */
 179 /* *INDENT-OFF* */
 180 static const char subshell_switch_key = XCTRL ('o') & 255;
 181 /* *INDENT-ON* */
 182 
 183 /* For reading/writing on the subshell's pty */
 184 static char pty_buffer[PTY_BUFFER_SIZE] = "\0";
 185 
 186 /* To pass CWD info from the subshell to MC */
 187 static int subshell_pipe[2];
 188 
 189 /* To pass command buffer info from the subshell to MC */
 190 static int command_buffer_pipe[2];
 191 
 192 /* The subshell's process ID */
 193 static pid_t subshell_pid = 1;
 194 
 195 /* One extra char for final '\n' */
 196 static char subshell_cwd[MC_MAXPATHLEN + 1];
 197 
 198 /* Flag to indicate whether the subshell is ready for next command */
 199 static int subshell_ready;
 200 
 201 /* Flag to indicate if the subshell supports the persistent buffer feature. */
 202 static gboolean use_persistent_buffer = FALSE;
 203 
 204 /* Flag to indicate if the contents of the subshell command line need to be cleared before */
 205 /* executing a command. This should only end up set to true if there is some sort of error. */
 206 /* This allows us to recover gracefully from an error. */
 207 static gboolean subshell_should_clear_command_line = FALSE;
 208 
 209 /* This is the local variable where the subshell prompt is stored while we are working on it. */
 210 static GString *subshell_prompt_temp_buffer = NULL;
 211 
 212 /* The following two flags can be changed by the SIGCHLD handler. This is */
 213 /* OK, because the 'int' type is updated atomically on all known machines */
 214 static volatile int subshell_alive, subshell_stopped;
 215 
 216 /* We store the terminal's initial mode here so that we can configure
 217    the pty similarly, and also so we can restore the real terminal to
 218    sanity if we have to exit abruptly */
 219 static struct termios shell_mode;
 220 
 221 /* This is a transparent mode for the terminal where MC is running on */
 222 /* It is used when the shell is active, so that the control signals */
 223 /* are delivered to the shell pty */
 224 static struct termios raw_mode;
 225 
 226 /* --------------------------------------------------------------------------------------------- */
 227 /*** file scope functions ************************************************************************/
 228 /* --------------------------------------------------------------------------------------------- */
 229 /**
 230  *  Write all data, even if the write() call is interrupted.
 231  */
 232 
 233 static ssize_t
 234 write_all (int fd, const void *buf, size_t count)
     /* [previous][next][first][last][top][bottom][index][help]  */
 235 {
 236     ssize_t written = 0;
 237 
 238     while (count > 0)
 239     {
 240         ssize_t ret;
 241 
 242         ret = write (fd, (const unsigned char *) buf + written, count);
 243         if (ret < 0)
 244         {
 245             if (errno == EINTR)
 246             {
 247                 if (tty_got_winch ())
 248                     tty_change_screen_size ();
 249 
 250                 continue;
 251             }
 252 
 253             return written > 0 ? written : ret;
 254         }
 255         count -= ret;
 256         written += ret;
 257     }
 258     return written;
 259 }
 260 
 261 /* --------------------------------------------------------------------------------------------- */
 262 /**
 263  *  Prepare child process to running the shell and run it.
 264  *
 265  *  Modifies the global variables (in the child process only):
 266  *      shell_mode
 267  *
 268  *  Returns: never.
 269  */
 270 
 271 static void
 272 init_subshell_child (const char *pty_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
 273 {
 274     char *init_file = NULL;
 275     pid_t mc_sid;
 276 
 277     (void) pty_name;
 278     setsid ();                  /* Get a fresh terminal session */
 279 
 280     /* Make sure that it has become our controlling terminal */
 281 
 282     /* Redundant on Linux and probably most systems, but just in case: */
 283 
 284 #ifdef TIOCSCTTY
 285     ioctl (subshell_pty_slave, TIOCSCTTY, 0);
 286 #endif
 287 
 288     /* Configure its terminal modes and window size */
 289 
 290     /* Set up the pty with the same termios flags as our own tty */
 291     if (tcsetattr (subshell_pty_slave, TCSANOW, &shell_mode))
 292     {
 293         fprintf (stderr, "Cannot set pty terminal modes: %s\r\n", unix_error_string (errno));
 294         my_exit (FORK_FAILURE);
 295     }
 296 
 297     /* Set the pty's size (80x25 by default on Linux) according to the */
 298     /* size of the real terminal as calculated by ncurses, if possible */
 299     tty_resize (subshell_pty_slave);
 300 
 301     /* Set up the subshell's environment and init file name */
 302 
 303     /* It simplifies things to change to our home directory here, */
 304     /* and the user's startup file may do a 'cd' command anyway   */
 305     {
 306         int ret;
 307 
 308         ret = chdir (mc_config_get_home_dir ());        /* FIXME? What about when we re-run the subshell? */
 309         (void) ret;
 310     }
 311 
 312     /* Set MC_SID to prevent running one mc from another */
 313     mc_sid = getsid (0);
 314     if (mc_sid != -1)
 315     {
 316         char sid_str[BUF_SMALL];
 317 
 318         g_snprintf (sid_str, sizeof (sid_str), "MC_SID=%ld", (long) mc_sid);
 319         putenv (g_strdup (sid_str));
 320     }
 321 
 322     switch (mc_global.shell->type)
 323     {
 324     case SHELL_BASH:
 325         /* Do we have a custom init file ~/.local/share/mc/bashrc? */
 326         init_file = mc_config_get_full_path (MC_BASHRC_FILE);
 327 
 328         /* Otherwise use ~/.bashrc */
 329         if (!exist_file (init_file))
 330         {
 331             g_free (init_file);
 332             init_file = g_strdup (".bashrc");
 333         }
 334 
 335         /* Make MC's special commands not show up in bash's history and also suppress
 336          * consecutive identical commands*/
 337         putenv ((char *) "HISTCONTROL=ignoreboth");
 338 
 339         /* Allow alternative readline settings for MC */
 340         {
 341             char *input_file;
 342 
 343             input_file = mc_config_get_full_path (MC_INPUTRC_FILE);
 344             if (exist_file (input_file))
 345                 g_setenv ("INPUTRC", input_file, TRUE);
 346             g_free (input_file);
 347         }
 348 
 349         break;
 350 
 351     case SHELL_ASH_BUSYBOX:
 352     case SHELL_DASH:
 353         /* Do we have a custom init file ~/.local/share/mc/ashrc? */
 354         init_file = mc_config_get_full_path (MC_ASHRC_FILE);
 355 
 356         /* Otherwise use ~/.profile */
 357         if (!exist_file (init_file))
 358         {
 359             g_free (init_file);
 360             init_file = g_strdup (".profile");
 361         }
 362 
 363         /* Put init file to ENV variable used by ash */
 364         g_setenv ("ENV", init_file, TRUE);
 365 
 366         break;
 367 
 368     case SHELL_ZSH:
 369         /* ZDOTDIR environment variable is the only way to point zsh
 370          * to an other rc file than the default. */
 371 
 372         /* Don't overwrite $ZDOTDIR */
 373         if (g_getenv ("ZDOTDIR") != NULL)
 374         {
 375             /* Do we have a custom init file ~/.local/share/mc/.zshrc?
 376              * Otherwise use standard ~/.zshrc */
 377             init_file = mc_config_get_full_path (MC_ZSHRC_FILE);
 378             if (exist_file (init_file))
 379             {
 380                 /* Set ZDOTDIR to ~/.local/share/mc */
 381                 g_setenv ("ZDOTDIR", mc_config_get_data_path (), TRUE);
 382             }
 383         }
 384         break;
 385 
 386         /* TODO: Find a way to pass initfile to TCSH and FISH */
 387     case SHELL_TCSH:
 388     case SHELL_FISH:
 389         break;
 390 
 391     default:
 392         fprintf (stderr, __FILE__ ": unimplemented subshell type %u\r\n", mc_global.shell->type);
 393         my_exit (FORK_FAILURE);
 394     }
 395 
 396     /* Attach all our standard file descriptors to the pty */
 397 
 398     /* This is done just before the exec, because stderr must still      */
 399     /* be connected to the real tty during the above error messages; */
 400     /* otherwise the user will never see them.                   */
 401 
 402     dup2 (subshell_pty_slave, STDIN_FILENO);
 403     dup2 (subshell_pty_slave, STDOUT_FILENO);
 404     dup2 (subshell_pty_slave, STDERR_FILENO);
 405 
 406     close (subshell_pipe[READ]);
 407 
 408     if (use_persistent_buffer)
 409         close (command_buffer_pipe[READ]);
 410 
 411     close (subshell_pty_slave); /* These may be FD_CLOEXEC, but just in case... */
 412     /* Close master side of pty.  This is important; apart from */
 413     /* freeing up the descriptor for use in the subshell, it also       */
 414     /* means that when MC exits, the subshell will get a SIGHUP and     */
 415     /* exit too, because there will be no more descriptors pointing     */
 416     /* at the master side of the pty and so it will disappear.  */
 417     close (mc_global.tty.subshell_pty);
 418 
 419     /* Execute the subshell at last */
 420 
 421     switch (mc_global.shell->type)
 422     {
 423     case SHELL_BASH:
 424         execl (mc_global.shell->path, "bash", "-rcfile", init_file, (char *) NULL);
 425         break;
 426 
 427     case SHELL_ZSH:
 428         /* Use -g to exclude cmds beginning with space from history
 429          * and -Z to use the line editor on non-interactive term */
 430         execl (mc_global.shell->path, "zsh", "-Z", "-g", (char *) NULL);
 431         break;
 432 
 433     case SHELL_ASH_BUSYBOX:
 434     case SHELL_DASH:
 435     case SHELL_TCSH:
 436     case SHELL_FISH:
 437         execl (mc_global.shell->path, mc_global.shell->path, (char *) NULL);
 438         break;
 439 
 440     default:
 441         break;
 442     }
 443 
 444     /* If we get this far, everything failed miserably */
 445     g_free (init_file);
 446     my_exit (FORK_FAILURE);
 447 }
 448 
 449 /* --------------------------------------------------------------------------------------------- */
 450 
 451 static void
 452 init_raw_mode (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 453 {
 454     static gboolean initialized = FALSE;
 455 
 456     /* MC calls tty_reset_shell_mode() in pre_exec() to set the real tty to its */
 457     /* original settings.  However, here we need to make this tty very raw,     */
 458     /* so that all keyboard signals, XON/XOFF, etc. will get through to the     */
 459     /* pty.  So, instead of changing the code for execute(), pre_exec(),        */
 460     /* etc, we just set up the modes we need here, before each command.         */
 461 
 462     if (!initialized)           /* First time: initialise 'raw_mode' */
 463     {
 464         tcgetattr (STDOUT_FILENO, &raw_mode);
 465         raw_mode.c_lflag &= ~ICANON;    /* Disable line-editing chars, etc.   */
 466         raw_mode.c_lflag &= ~ISIG;      /* Disable intr, quit & suspend chars */
 467         raw_mode.c_lflag &= ~ECHO;      /* Disable input echoing              */
 468         raw_mode.c_iflag &= ~IXON;      /* Pass ^S/^Q to subshell undisturbed */
 469         raw_mode.c_iflag &= ~ICRNL;     /* Don't translate CRs into LFs       */
 470         raw_mode.c_oflag &= ~OPOST;     /* Don't postprocess output           */
 471         raw_mode.c_cc[VTIME] = 0;       /* IE: wait forever, and return as    */
 472         raw_mode.c_cc[VMIN] = 1;        /* soon as a character is available   */
 473         initialized = TRUE;
 474     }
 475 }
 476 
 477 /* --------------------------------------------------------------------------------------------- */
 478 /**
 479  * Wait until the subshell dies or stops.  If it stops, make it resume.
 480  * Possibly modifies the globals 'subshell_alive' and 'subshell_stopped'
 481  */
 482 
 483 static void
 484 synchronize (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 485 {
 486     sigset_t sigchld_mask, old_mask;
 487 
 488     sigemptyset (&sigchld_mask);
 489     sigaddset (&sigchld_mask, SIGCHLD);
 490     sigprocmask (SIG_BLOCK, &sigchld_mask, &old_mask);
 491 
 492     /*
 493      * SIGCHLD should not be blocked, but we unblock it just in case.
 494      * This is known to be useful for cygwin 1.3.12 and older.
 495      */
 496     sigdelset (&old_mask, SIGCHLD);
 497 
 498     /* Wait until the subshell has stopped */
 499     while (subshell_alive && !subshell_stopped)
 500         sigsuspend (&old_mask);
 501 
 502     if (subshell_state != ACTIVE)
 503     {
 504         /* Discard all remaining data from stdin to the subshell */
 505         tcflush (subshell_pty_slave, TCIFLUSH);
 506     }
 507 
 508     subshell_stopped = FALSE;
 509     kill (subshell_pid, SIGCONT);
 510 
 511     sigprocmask (SIG_SETMASK, &old_mask, NULL);
 512     /* We can't do any better without modifying the shell(s) */
 513 }
 514 
 515 /* --------------------------------------------------------------------------------------------- */
 516 /* Get the contents of the current subshell command line buffer, and */
 517 /* transfer the contents to the panel command prompt. */
 518 
 519 static gboolean
 520 read_command_line_buffer (gboolean test_mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
 521 {
 522     char subshell_command_buffer[BUF_LARGE];
 523     char subshell_cursor_buffer[BUF_SMALL];
 524 
 525     fd_set read_set;
 526     int i;
 527     ssize_t bytes;
 528     struct timeval subshell_prompt_timer = { 0, 0 };
 529     int command_buffer_length;
 530     int command_buffer_char_length;
 531     int bash_version;
 532     int cursor_position;
 533     int maxfdp;
 534     int rc;
 535 
 536     if (!use_persistent_buffer)
 537         return TRUE;
 538 
 539     FD_ZERO (&read_set);
 540     FD_SET (command_buffer_pipe[READ], &read_set);
 541     maxfdp = command_buffer_pipe[READ];
 542 
 543     /* First, flush the command buffer pipe. This pipe shouldn't be written
 544      * to under normal circumstances, but if it somehow does get written
 545      * to, we need to make sure to discard whatever data is there before
 546      * we try to use it. */
 547     while ((rc = select (maxfdp + 1, &read_set, NULL, NULL, &subshell_prompt_timer)) != 0)
 548     {
 549         if (rc == -1)
 550         {
 551             if (errno == EINTR)
 552                 continue;
 553 
 554             return FALSE;
 555         }
 556 
 557         if (rc == 1)
 558         {
 559             bytes = read (command_buffer_pipe[READ], subshell_command_buffer,
 560                           sizeof (subshell_command_buffer));
 561             (void) bytes;
 562         }
 563     }
 564 
 565     /* get contents of command line buffer */
 566     write_all (mc_global.tty.subshell_pty, ESC_STR SHELL_BUFFER_KEYBINDING,
 567                sizeof (ESC_STR SHELL_CURSOR_KEYBINDING) - 1);
 568 
 569     subshell_prompt_timer.tv_sec = 1;
 570     FD_ZERO (&read_set);
 571     FD_SET (command_buffer_pipe[READ], &read_set);
 572 
 573     while ((rc = select (maxfdp + 1, &read_set, NULL, NULL, &subshell_prompt_timer)) != 1)
 574     {
 575         if (rc == -1)
 576         {
 577             if (errno == EINTR)
 578                 continue;
 579 
 580             return FALSE;
 581         }
 582 
 583         if (rc == 0)
 584             return FALSE;
 585     }
 586 
 587     bytes =
 588         read (command_buffer_pipe[READ], subshell_command_buffer, sizeof (subshell_command_buffer));
 589     if (bytes == 0 || bytes == sizeof (subshell_command_buffer))
 590         return FALSE;
 591 
 592     command_buffer_char_length = bytes - 1;
 593     subshell_command_buffer[command_buffer_char_length] = '\0';
 594     command_buffer_length = str_length (subshell_command_buffer);
 595 
 596     /* get cursor position */
 597     write_all (mc_global.tty.subshell_pty, ESC_STR SHELL_CURSOR_KEYBINDING,
 598                sizeof (ESC_STR SHELL_CURSOR_KEYBINDING) - 1);
 599 
 600     subshell_prompt_timer.tv_sec = 1;
 601     subshell_prompt_timer.tv_usec = 0;
 602     FD_ZERO (&read_set);
 603     FD_SET (command_buffer_pipe[READ], &read_set);
 604 
 605     while ((rc = select (maxfdp + 1, &read_set, NULL, NULL, &subshell_prompt_timer)) != 1)
 606     {
 607         if (rc == -1)
 608         {
 609             if (errno == EINTR)
 610                 continue;
 611 
 612             return FALSE;
 613         }
 614 
 615         if (rc == 0)
 616             return FALSE;
 617     }
 618 
 619     bytes =
 620         read (command_buffer_pipe[READ], subshell_cursor_buffer, sizeof (subshell_cursor_buffer));
 621     if (bytes == 0)
 622         return FALSE;
 623 
 624     subshell_cursor_buffer[bytes - 1] = '\0';
 625     if (mc_global.shell->type == SHELL_BASH)
 626     {
 627         if (sscanf (subshell_cursor_buffer, "%d:%d", &bash_version, &cursor_position) != 2)
 628             return FALSE;
 629     }
 630     else
 631     {
 632         if (sscanf (subshell_cursor_buffer, "%d", &cursor_position) != 1)
 633             return FALSE;
 634         bash_version = 1000;
 635     }
 636 
 637     if (test_mode)
 638         return TRUE;
 639 
 640     /* Substitute non-text characters in the command buffer, such as tab, or newline, as this
 641      * could cause problems. */
 642     for (i = 0; i < command_buffer_char_length; i++)
 643         if ((unsigned char) subshell_command_buffer[i] < 32
 644             || (unsigned char) subshell_command_buffer[i] == 127)
 645             subshell_command_buffer[i] = ' ';
 646 
 647     input_assign_text (cmdline, "");
 648     input_insert (cmdline, subshell_command_buffer, FALSE);
 649 
 650     if (bash_version < 5)       /* implies SHELL_BASH */
 651     {
 652         /* We need to do this because bash < v5 gives the cursor position in a utf-8 string based
 653          * on the location in bytes, not in unicode characters. */
 654         char *curr, *stop;
 655 
 656         curr = subshell_command_buffer;
 657         stop = curr + cursor_position;
 658 
 659         for (cursor_position = 0; curr < stop; cursor_position++)
 660             str_next_char_safe (&curr);
 661     }
 662     if (cursor_position > command_buffer_length)
 663         cursor_position = command_buffer_length;
 664     cmdline->point = cursor_position;
 665     /* We send any remaining data to STDOUT before we finish. */
 666     flush_subshell (0, VISIBLY);
 667 
 668     /* Now we erase the current contents of the command line buffer */
 669     if (mc_global.shell->type != SHELL_ZSH)
 670     {
 671         /* In zsh, we can just press c-u to clear the line, without needing to go to the end of
 672          * the line first first. In all other shells, we must go to the end of the line first. */
 673 
 674         /* If we are not at the end of the line, we go to the end. */
 675         if (cursor_position != command_buffer_length)
 676         {
 677             write_all (mc_global.tty.subshell_pty, "\005", 1);
 678             if (flush_subshell (1, VISIBLY) != 1)
 679                 return FALSE;
 680         }
 681     }
 682 
 683     if (command_buffer_length > 0)
 684     {
 685         /* Now we clear the line. */
 686         write_all (mc_global.tty.subshell_pty, "\025", 1);
 687         if (flush_subshell (1, VISIBLY) != 1)
 688             return FALSE;
 689     }
 690 
 691     return TRUE;
 692 }
 693 
 694 /* --------------------------------------------------------------------------------------------- */
 695 
 696 static void
 697 clear_subshell_prompt_string (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 698 {
 699     if (subshell_prompt_temp_buffer != NULL)
 700         g_string_set_size (subshell_prompt_temp_buffer, 0);
 701 }
 702 
 703 /* --------------------------------------------------------------------------------------------- */
 704 
 705 static void
 706 parse_subshell_prompt_string (const char *buffer, int bytes)
     /* [previous][next][first][last][top][bottom][index][help]  */
 707 {
 708     int i;
 709 
 710     if (mc_global.mc_run_mode != MC_RUN_FULL)
 711         return;
 712 
 713     /* First time through */
 714     if (subshell_prompt == NULL)
 715         subshell_prompt = g_string_sized_new (INITIAL_PROMPT_SIZE);
 716     if (subshell_prompt_temp_buffer == NULL)
 717         subshell_prompt_temp_buffer = g_string_sized_new (INITIAL_PROMPT_SIZE);
 718 
 719     /* Extract the prompt from the shell output */
 720     for (i = 0; i < bytes; i++)
 721         if (buffer[i] == '\n' || buffer[i] == '\r')
 722             g_string_set_size (subshell_prompt_temp_buffer, 0);
 723         else if (buffer[i] != '\0')
 724             g_string_append_c (subshell_prompt_temp_buffer, buffer[i]);
 725 }
 726 
 727 /* --------------------------------------------------------------------------------------------- */
 728 
 729 static void
 730 set_prompt_string (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 731 {
 732     if (mc_global.mc_run_mode != MC_RUN_FULL)
 733         return;
 734 
 735     if (subshell_prompt_temp_buffer->len != 0)
 736         g_string_assign (subshell_prompt, subshell_prompt_temp_buffer->str);
 737 
 738     setup_cmdline ();
 739 }
 740 
 741 /* --------------------------------------------------------------------------------------------- */
 742 /** Feed the subshell our keyboard input until it says it's finished */
 743 
 744 static gboolean
 745 feed_subshell (int how, gboolean fail_on_error)
     /* [previous][next][first][last][top][bottom][index][help]  */
 746 {
 747     fd_set read_set;            /* For 'select' */
 748     int bytes;                  /* For the return value from 'read' */
 749     int i;                      /* Loop counter */
 750 
 751     struct timeval wtime;       /* Maximum time we wait for the subshell */
 752     struct timeval *wptr;
 753 
 754     should_read_new_subshell_prompt = FALSE;
 755     subshell_should_clear_command_line = FALSE;
 756 
 757     /* we wait up to 10 seconds if fail_on_error, forever otherwise */
 758     wtime.tv_sec = 10;
 759     wtime.tv_usec = 0;
 760     wptr = fail_on_error ? &wtime : NULL;
 761 
 762     while (TRUE)
 763     {
 764         int maxfdp;
 765 
 766         if (!subshell_alive)
 767             return FALSE;
 768 
 769         /* Prepare the file-descriptor set and call 'select' */
 770 
 771         FD_ZERO (&read_set);
 772         FD_SET (mc_global.tty.subshell_pty, &read_set);
 773         FD_SET (subshell_pipe[READ], &read_set);
 774         maxfdp = MAX (mc_global.tty.subshell_pty, subshell_pipe[READ]);
 775         if (how == VISIBLY)
 776         {
 777             FD_SET (STDIN_FILENO, &read_set);
 778             maxfdp = MAX (maxfdp, STDIN_FILENO);
 779         }
 780 
 781         if (select (maxfdp + 1, &read_set, NULL, NULL, wptr) == -1)
 782         {
 783             /* Despite using SA_RESTART, we still have to check for this */
 784             if (errno == EINTR)
 785             {
 786                 if (tty_got_winch ())
 787                     tty_change_screen_size ();
 788 
 789                 continue;       /* try all over again */
 790             }
 791             tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
 792             fprintf (stderr, "select (FD_SETSIZE, &read_set...): %s\r\n",
 793                      unix_error_string (errno));
 794             exit (EXIT_FAILURE);
 795         }
 796 
 797         if (FD_ISSET (mc_global.tty.subshell_pty, &read_set))
 798             /* Read from the subshell, write to stdout */
 799 
 800             /* This loop improves performance by reducing context switches
 801                by a factor of 20 or so... unfortunately, it also hangs MC
 802                randomly, because of an apparent Linux bug.  Investigate. */
 803             /* for (i=0; i<5; ++i)  * FIXME -- experimental */
 804         {
 805             bytes = read (mc_global.tty.subshell_pty, pty_buffer, sizeof (pty_buffer));
 806 
 807             /* The subshell has died */
 808             if (bytes == -1 && errno == EIO && !subshell_alive)
 809                 return FALSE;
 810 
 811             if (bytes <= 0)
 812             {
 813 #ifdef PTY_ZEROREAD
 814                 /* On IBM i, read(1) can return 0 for a non-closed fd */
 815                 continue;
 816 #else
 817                 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
 818                 fprintf (stderr, "read (subshell_pty...): %s\r\n", unix_error_string (errno));
 819                 exit (EXIT_FAILURE);
 820 #endif
 821             }
 822 
 823             if (how == VISIBLY)
 824                 write_all (STDOUT_FILENO, pty_buffer, bytes);
 825 
 826             if (should_read_new_subshell_prompt)
 827                 parse_subshell_prompt_string (pty_buffer, bytes);
 828         }
 829 
 830         else if (FD_ISSET (subshell_pipe[READ], &read_set))
 831             /* Read the subshell's CWD and capture its prompt */
 832         {
 833             bytes = read (subshell_pipe[READ], subshell_cwd, sizeof (subshell_cwd));
 834             if (bytes <= 0)
 835             {
 836                 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
 837                 fprintf (stderr, "read (subshell_pipe[READ]...): %s\r\n",
 838                          unix_error_string (errno));
 839                 exit (EXIT_FAILURE);
 840             }
 841 
 842             subshell_cwd[bytes - 1] = '\0';     /* Squash the final '\n' */
 843 
 844             synchronize ();
 845 
 846             clear_subshell_prompt_string ();
 847             should_read_new_subshell_prompt = TRUE;
 848             subshell_ready = TRUE;
 849             if (subshell_state == RUNNING_COMMAND)
 850             {
 851                 subshell_state = INACTIVE;
 852                 return TRUE;
 853             }
 854         }
 855 
 856         else if (FD_ISSET (STDIN_FILENO, &read_set))
 857             /* Read from stdin, write to the subshell */
 858         {
 859             should_read_new_subshell_prompt = FALSE;
 860             bytes = read (STDIN_FILENO, pty_buffer, sizeof (pty_buffer));
 861             if (bytes <= 0)
 862             {
 863                 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
 864                 fprintf (stderr,
 865                          "read (STDIN_FILENO, pty_buffer...): %s\r\n", unix_error_string (errno));
 866                 exit (EXIT_FAILURE);
 867             }
 868 
 869             for (i = 0; i < bytes; ++i)
 870                 if (pty_buffer[i] == subshell_switch_key)
 871                 {
 872                     write_all (mc_global.tty.subshell_pty, pty_buffer, i);
 873 
 874                     if (subshell_ready)
 875                     {
 876                         subshell_state = INACTIVE;
 877                         set_prompt_string ();
 878                         if (subshell_ready && !read_command_line_buffer (FALSE))
 879                         {
 880                             /* If we got here, some unforseen error must have occurred. */
 881                             flush_subshell (0, VISIBLY);
 882                             input_assign_text (cmdline, "");
 883                             subshell_should_clear_command_line = TRUE;
 884                         }
 885                     }
 886 
 887                     return TRUE;
 888                 }
 889 
 890             write_all (mc_global.tty.subshell_pty, pty_buffer, bytes);
 891 
 892             if (pty_buffer[bytes - 1] == '\n' || pty_buffer[bytes - 1] == '\r')
 893             {
 894                 /* We should only clear the command line if we are using a shell that works
 895                  * with persistent command buffer, otherwise we get awkward results. */
 896                 if (use_persistent_buffer)
 897                     input_assign_text (cmdline, "");
 898                 subshell_ready = FALSE;
 899             }
 900         }
 901         else
 902             return FALSE;
 903     }
 904 }
 905 
 906 /* --------------------------------------------------------------------------------------------- */
 907 /* pty opening functions */
 908 
 909 #ifndef HAVE_OPENPTY
 910 
 911 #ifdef HAVE_GRANTPT
 912 
 913 /* System V version of pty_open_master */
 914 
 915 static int
 916 pty_open_master (char *pty_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
 917 {
 918     char *slave_name;
 919     int pty_master;
 920 
 921 #ifdef HAVE_POSIX_OPENPT
 922     pty_master = posix_openpt (O_RDWR);
 923 #elif defined HAVE_GETPT
 924     /* getpt () is a GNU extension (glibc 2.1.x) */
 925     pty_master = getpt ();
 926 #elif defined IS_AIX
 927     strcpy (pty_name, "/dev/ptc");
 928     pty_master = open (pty_name, O_RDWR);
 929 #else
 930     strcpy (pty_name, "/dev/ptmx");
 931     pty_master = open (pty_name, O_RDWR);
 932 #endif
 933 
 934     if (pty_master == -1)
 935         return -1;
 936 
 937     if (grantpt (pty_master) == -1      /* Grant access to slave */
 938         || unlockpt (pty_master) == -1  /* Clear slave's lock flag */
 939         || !(slave_name = ptsname (pty_master)))        /* Get slave's name */
 940     {
 941         close (pty_master);
 942         return -1;
 943     }
 944     strcpy (pty_name, slave_name);
 945     return pty_master;
 946 }
 947 
 948 /* --------------------------------------------------------------------------------------------- */
 949 /** System V version of pty_open_slave */
 950 
 951 static int
 952 pty_open_slave (const char *pty_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
 953 {
 954     int pty_slave;
 955 
 956     pty_slave = open (pty_name, O_RDWR);
 957     if (pty_slave == -1)
 958     {
 959         fprintf (stderr, "open (%s, O_RDWR): %s\r\n", pty_name, unix_error_string (errno));
 960         return -1;
 961     }
 962 #if !defined(__osf__) && !defined(__linux__)
 963 #if defined (I_FIND) && defined (I_PUSH)
 964     if (ioctl (pty_slave, I_FIND, "ptem") == 0)
 965         if (ioctl (pty_slave, I_PUSH, "ptem") == -1)
 966         {
 967             fprintf (stderr, "ioctl (%d, I_PUSH, \"ptem\") failed: %s\r\n",
 968                      pty_slave, unix_error_string (errno));
 969             close (pty_slave);
 970             return -1;
 971         }
 972 
 973     if (ioctl (pty_slave, I_FIND, "ldterm") == 0)
 974         if (ioctl (pty_slave, I_PUSH, "ldterm") == -1)
 975         {
 976             fprintf (stderr,
 977                      "ioctl (%d, I_PUSH, \"ldterm\") failed: %s\r\n",
 978                      pty_slave, unix_error_string (errno));
 979             close (pty_slave);
 980             return -1;
 981         }
 982 #if !defined(sgi) && !defined(__sgi)
 983     if (ioctl (pty_slave, I_FIND, "ttcompat") == 0)
 984         if (ioctl (pty_slave, I_PUSH, "ttcompat") == -1)
 985         {
 986             fprintf (stderr,
 987                      "ioctl (%d, I_PUSH, \"ttcompat\") failed: %s\r\n",
 988                      pty_slave, unix_error_string (errno));
 989             close (pty_slave);
 990             return -1;
 991         }
 992 #endif /* sgi || __sgi */
 993 #endif /* I_FIND && I_PUSH */
 994 #endif /* __osf__ || __linux__ */
 995 
 996     fcntl (pty_slave, F_SETFD, FD_CLOEXEC);
 997     return pty_slave;
 998 }
 999 
1000 #else /* !HAVE_GRANTPT */
1001 
1002 /* --------------------------------------------------------------------------------------------- */
1003 /** BSD version of pty_open_master */
1004 static int
1005 pty_open_master (char *pty_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
1006 {
1007     int pty_master;
1008     const char *ptr1, *ptr2;
1009 
1010     strcpy (pty_name, "/dev/ptyXX");
1011     for (ptr1 = "pqrstuvwxyzPQRST"; *ptr1; ++ptr1)
1012     {
1013         pty_name[8] = *ptr1;
1014         for (ptr2 = "0123456789abcdef"; *ptr2 != '\0'; ++ptr2)
1015         {
1016             pty_name[9] = *ptr2;
1017 
1018             /* Try to open master */
1019             pty_master = open (pty_name, O_RDWR);
1020             if (pty_master == -1)
1021             {
1022                 if (errno == ENOENT)    /* Different from EIO */
1023                     return -1;  /* Out of pty devices */
1024                 continue;       /* Try next pty device */
1025             }
1026             pty_name[5] = 't';  /* Change "pty" to "tty" */
1027             if (access (pty_name, 6) != 0)
1028             {
1029                 close (pty_master);
1030                 pty_name[5] = 'p';
1031                 continue;
1032             }
1033             return pty_master;
1034         }
1035     }
1036     return -1;                  /* Ran out of pty devices */
1037 }
1038 
1039 /* --------------------------------------------------------------------------------------------- */
1040 /** BSD version of pty_open_slave */
1041 
1042 static int
1043 pty_open_slave (const char *pty_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
1044 {
1045     int pty_slave;
1046     struct group *group_info;
1047 
1048     group_info = getgrnam ("tty");
1049     if (group_info != NULL)
1050     {
1051         /* The following two calls will only succeed if we are root */
1052         /* [Commented out while permissions problem is investigated] */
1053         /* chown (pty_name, getuid (), group_info->gr_gid);  FIXME */
1054         /* chmod (pty_name, S_IRUSR | S_IWUSR | S_IWGRP);   FIXME */
1055     }
1056     pty_slave = open (pty_name, O_RDWR);
1057     if (pty_slave == -1)
1058         fprintf (stderr, "open (pty_name, O_RDWR): %s\r\n", pty_name);
1059     fcntl (pty_slave, F_SETFD, FD_CLOEXEC);
1060     return pty_slave;
1061 }
1062 #endif /* !HAVE_GRANTPT */
1063 
1064 #endif /* !HAVE_OPENPTY */
1065 
1066 /* --------------------------------------------------------------------------------------------- */
1067 /**
1068  * Set up `precmd' or equivalent for reading the subshell's CWD.
1069  *
1070  * Attention! Never forget that these are *one-liners* even though the concatenated
1071  * substrings contain line breaks and indentation for better understanding of the
1072  * shell code. It is vital that each one-liner ends with a line feed character ("\n" ).
1073  *
1074  * @return initialized pre-command string
1075  */
1076 
1077 static void
1078 init_subshell_precmd (char *precmd, size_t buff_size)
     /* [previous][next][first][last][top][bottom][index][help]  */
1079 {
1080     switch (mc_global.shell->type)
1081     {
1082     case SHELL_BASH:
1083         g_snprintf (precmd, buff_size,
1084                     " mc_print_command_buffer () { printf \"%%s\\\\n\" \"$READLINE_LINE\" >&%d; }\n"
1085                     " bind -x '\"\\e" SHELL_BUFFER_KEYBINDING "\":\"mc_print_command_buffer\"'\n"
1086                     " bind -x '\"\\e" SHELL_CURSOR_KEYBINDING
1087                     "\":\"echo $BASH_VERSINFO:$READLINE_POINT >&%d\"'\n"
1088                     " PROMPT_COMMAND=${PROMPT_COMMAND:+$PROMPT_COMMAND\n}'pwd>&%d;kill -STOP $$'\n"
1089                     "PS1='\\u@\\h:\\w\\$ '\n", command_buffer_pipe[WRITE],
1090                     command_buffer_pipe[WRITE], subshell_pipe[WRITE]);
1091         break;
1092 
1093     case SHELL_ASH_BUSYBOX:
1094         /* BusyBox ash needs a somewhat complicated precmd emulation via PS1, and it is vital
1095          * that BB be built with active CONFIG_ASH_EXPAND_PRMT, but this is the default anyway.
1096          *
1097          * A: This leads to a stopped subshell (=frozen mc) if user calls "ash" command
1098          *    "PS1='$(pwd>&%d; kill -STOP $$)\\u@\\h:\\w\\$ '\n",
1099          *
1100          * B: This leads to "sh: precmd: not found" in sub-subshell if user calls "ash" command
1101          *    "precmd() { pwd>&%d; kill -STOP $$; }; "
1102          *    "PS1='$(precmd)\\u@\\h:\\w\\$ '\n",
1103          *
1104          * C: This works if user calls "ash" command because in sub-subshell
1105          *    PRECMD is undefined, thus evaluated to empty string - no damage done.
1106          *    Attention: BusyBox must be built with FEATURE_EDITING_FANCY_PROMPT to
1107          *    permit \u, \w, \h, \$ escape sequences. Unfortunately this cannot be guaranteed,
1108          *    especially on embedded systems where people try to save space, so let's use
1109          *    the dash version below. It should work on virtually all systems.
1110          *    "precmd() { pwd>&%d; kill -STOP $$; }; "
1111          *    "PRECMD=precmd; "
1112          *    "PS1='$(eval $PRECMD)\\u@\\h:\\w\\$ '\n",
1113          */
1114     case SHELL_DASH:
1115         /* Debian ash needs a precmd emulation via PS1, similar to BusyBox ash,
1116          * but does not support escape sequences for user, host and cwd in prompt.
1117          * Attention! Make sure that the buffer for precmd is big enough.
1118          *
1119          * We want to have a fancy dynamic prompt with user@host:cwd just like in the BusyBox
1120          * examples above, but because replacing the home directory part of the path by "~" is
1121          * complicated, it bloats the precmd to a size > BUF_SMALL (128).
1122          *
1123          * The following example is a little less fancy (home directory not replaced)
1124          * and shows the basic workings of our prompt for easier understanding:
1125          *
1126          * "precmd() { "
1127          *     "echo \"$USER@$(hostname -s):$PWD\"; "
1128          *     "pwd>&%d; "
1129          *     "kill -STOP $$; "
1130          * "}; "
1131          * "PRECMD=precmd; "
1132          * "PS1='$($PRECMD)$ '\n",
1133          */
1134         g_snprintf (precmd, buff_size,
1135                     "precmd() { "
1136                     "if [ ! \"${PWD##$HOME}\" ]; then "
1137                     "MC_PWD=\"~\"; "
1138                     "else "
1139                     "[ \"${PWD##$HOME/}\" = \"$PWD\" ] && MC_PWD=\"$PWD\" || MC_PWD=\"~/${PWD##$HOME/}\"; "
1140                     "fi; "
1141                     "echo \"$USER@$(hostname -s):$MC_PWD\"; "
1142                     "pwd>&%d; "
1143                     "kill -STOP $$; "
1144                     "}; " "PRECMD=precmd; " "PS1='$($PRECMD)$ '\n", subshell_pipe[WRITE]);
1145         break;
1146 
1147     case SHELL_ZSH:
1148         g_snprintf (precmd, buff_size,
1149                     " mc_print_command_buffer () { printf \"%%s\\\\n\" \"$BUFFER\" >&%d; }\n"
1150                     " zle -N mc_print_command_buffer\n"
1151                     " bindkey '^[" SHELL_BUFFER_KEYBINDING "' mc_print_command_buffer\n"
1152                     " mc_print_cursor_position () { echo $CURSOR >&%d}\n"
1153                     " zle -N mc_print_cursor_position\n"
1154                     " bindkey '^[" SHELL_CURSOR_KEYBINDING "' mc_print_cursor_position\n"
1155                     " _mc_precmd(){ pwd>&%d;kill -STOP $$ }; precmd_functions+=(_mc_precmd)\n"
1156                     "PS1='%%n@%%m:%%~%%# '\n",
1157                     command_buffer_pipe[WRITE], command_buffer_pipe[WRITE], subshell_pipe[WRITE]);
1158         break;
1159 
1160     case SHELL_TCSH:
1161         g_snprintf (precmd, buff_size,
1162                     "set echo_style=both; "
1163                     "set prompt='%%n@%%m:%%~%%# '; "
1164                     "alias precmd 'echo -n;echo $cwd:q >>%s; kill -STOP $$'\n", tcsh_fifo);
1165         break;
1166     case SHELL_FISH:
1167         g_snprintf (precmd, buff_size,
1168                     " bind \\e" SHELL_BUFFER_KEYBINDING " 'commandline >&%d';"
1169                     "bind \\e" SHELL_CURSOR_KEYBINDING " 'commandline -C >&%d';"
1170                     "if not functions -q fish_prompt_mc;"
1171                     "functions -e fish_right_prompt;"
1172                     "functions -c fish_prompt fish_prompt_mc; end;"
1173                     "function fish_prompt;"
1174                     "echo \"$PWD\">&%d; fish_prompt_mc; kill -STOP %%self; end\n",
1175                     command_buffer_pipe[WRITE], command_buffer_pipe[WRITE], subshell_pipe[WRITE]);
1176         break;
1177 
1178     default:
1179         break;
1180     }
1181 }
1182 
1183 /* --------------------------------------------------------------------------------------------- */
1184 /**
1185  * Carefully quote directory name to allow entering any directory safely,
1186  * no matter what weird characters it may contain in its name.
1187  * NOTE: Treat directory name an untrusted data, don't allow it to cause
1188  * executing any commands in the shell.  Escape all control characters.
1189  * Use following technique:
1190  *
1191  * printf(1) with format string containing a single conversion specifier,
1192  * "b", and an argument which contains a copy of the string passed to 
1193  * subshell_name_quote() with all characters, except digits and letters,
1194  * replaced by the backslash-escape sequence \0nnn, where "nnn" is the
1195  * numeric value of the character converted to octal number.
1196  * 
1197  *   cd "`printf '%b' 'ABC\0nnnDEF\0nnnXYZ'`"
1198  *
1199  * N.B.: Use single quotes for conversion specifier to work around
1200  *       tcsh 6.20+ parser breakage, see ticket #3852 for the details.
1201  */
1202 
1203 static GString *
1204 subshell_name_quote (const char *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
1205 {
1206     GString *ret;
1207     const char *su, *n;
1208     const char *quote_cmd_start, *quote_cmd_end;
1209 
1210     if (mc_global.shell->type == SHELL_FISH)
1211     {
1212         quote_cmd_start = "(printf '%b' '";
1213         quote_cmd_end = "')";
1214     }
1215     /* TODO: When BusyBox printf is fixed, get rid of this "else if", see
1216        http://lists.busybox.net/pipermail/busybox/2012-March/077460.html */
1217     /* else if (subshell_type == ASH_BUSYBOX)
1218        {
1219        quote_cmd_start = "\"`echo -en '";
1220        quote_cmd_end = "'`\"";
1221        } */
1222     else
1223     {
1224         quote_cmd_start = "\"`printf '%b' '";
1225         quote_cmd_end = "'`\"";
1226     }
1227 
1228     ret = g_string_sized_new (64);
1229 
1230     /* Prevent interpreting leading '-' as a switch for 'cd' */
1231     if (s[0] == '-')
1232         g_string_append (ret, "./");
1233 
1234     /* Copy the beginning of the command to the buffer */
1235     g_string_append (ret, quote_cmd_start);
1236 
1237     /*
1238      * Print every character except digits and letters as a backslash-escape
1239      * sequence of the form \0nnn, where "nnn" is the numeric value of the
1240      * character converted to octal number.
1241      */
1242     for (su = s; su[0] != '\0'; su = n)
1243     {
1244         n = str_cget_next_char_safe (su);
1245 
1246         if (str_isalnum (su))
1247             g_string_append_len (ret, su, n - su);
1248         else
1249         {
1250             int c;
1251 
1252             for (c = 0; c < n - su; c++)
1253                 g_string_append_printf (ret, "\\0%03o", (unsigned char) su[c]);
1254         }
1255     }
1256 
1257     g_string_append (ret, quote_cmd_end);
1258 
1259     return ret;
1260 }
1261 
1262 /* --------------------------------------------------------------------------------------------- */
1263 /*** public functions ****************************************************************************/
1264 /* --------------------------------------------------------------------------------------------- */
1265 
1266 /* --------------------------------------------------------------------------------------------- */
1267 /**
1268  *  Fork the subshell, and set up many, many things.
1269  *
1270  *  Possibly modifies the global variables:
1271  *      subshell_type, subshell_alive, subshell_stopped, subshell_pid
1272  *      mc_global.tty.use_subshell - Is set to FALSE if we can't run the subshell
1273  *      quit - Can be set to SUBSHELL_EXIT by the SIGCHLD handler
1274  */
1275 
1276 void
1277 init_subshell (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1278 {
1279     /* This must be remembered across calls to init_subshell() */
1280     static char pty_name[BUF_SMALL];
1281     /* Must be considerably longer than BUF_SMALL (128) to support fancy shell prompts */
1282     char precmd[BUF_MEDIUM];
1283 
1284     /* Take the current (hopefully pristine) tty mode and make */
1285     /* a raw mode based on it now, before we do anything else with it */
1286     init_raw_mode ();
1287 
1288     if (mc_global.tty.subshell_pty == 0)
1289     {                           /* First time through */
1290         if (mc_global.shell->type == SHELL_NONE)
1291             return;
1292 
1293         /* Open a pty for talking to the subshell */
1294 
1295         /* FIXME: We may need to open a fresh pty each time on SVR4 */
1296 
1297 #ifdef HAVE_OPENPTY
1298         if (openpty (&mc_global.tty.subshell_pty, &subshell_pty_slave, NULL, NULL, NULL))
1299         {
1300             fprintf (stderr, "Cannot open master and slave sides of pty: %s\n",
1301                      unix_error_string (errno));
1302             mc_global.tty.use_subshell = FALSE;
1303             return;
1304         }
1305 #else
1306         mc_global.tty.subshell_pty = pty_open_master (pty_name);
1307         if (mc_global.tty.subshell_pty == -1)
1308         {
1309             fprintf (stderr, "Cannot open master side of pty: %s\r\n", unix_error_string (errno));
1310             mc_global.tty.use_subshell = FALSE;
1311             return;
1312         }
1313         subshell_pty_slave = pty_open_slave (pty_name);
1314         if (subshell_pty_slave == -1)
1315         {
1316             fprintf (stderr, "Cannot open slave side of pty %s: %s\r\n",
1317                      pty_name, unix_error_string (errno));
1318             mc_global.tty.use_subshell = FALSE;
1319             return;
1320         }
1321 #endif /* HAVE_OPENPTY */
1322 
1323         /* Create a pipe for receiving the subshell's CWD */
1324 
1325         if (mc_global.shell->type == SHELL_TCSH)
1326         {
1327             g_snprintf (tcsh_fifo, sizeof (tcsh_fifo), "%s/mc.pipe.%d",
1328                         mc_tmpdir (), (int) getpid ());
1329             if (mkfifo (tcsh_fifo, 0600) == -1)
1330             {
1331                 fprintf (stderr, "mkfifo(%s) failed: %s\r\n", tcsh_fifo, unix_error_string (errno));
1332                 mc_global.tty.use_subshell = FALSE;
1333                 return;
1334             }
1335 
1336             /* Opening the FIFO as O_RDONLY or O_WRONLY causes deadlock */
1337 
1338             if ((subshell_pipe[READ] = open (tcsh_fifo, O_RDWR)) == -1
1339                 || (subshell_pipe[WRITE] = open (tcsh_fifo, O_RDWR)) == -1)
1340             {
1341                 fprintf (stderr, _("Cannot open named pipe %s\n"), tcsh_fifo);
1342                 perror (__FILE__ ": open");
1343                 mc_global.tty.use_subshell = FALSE;
1344                 return;
1345             }
1346         }
1347         else if (pipe (subshell_pipe) != 0)     /* subshell_type is BASH, ASH_BUSYBOX, DASH or ZSH */
1348         {
1349             perror (__FILE__ ": couldn't create pipe");
1350             mc_global.tty.use_subshell = FALSE;
1351             return;
1352         }
1353 
1354         if (mc_global.mc_run_mode == MC_RUN_FULL &&
1355             (mc_global.shell->type == SHELL_BASH || mc_global.shell->type == SHELL_ZSH
1356              || mc_global.shell->type == SHELL_FISH))
1357             use_persistent_buffer = TRUE;
1358         if (use_persistent_buffer && pipe (command_buffer_pipe) != 0)
1359         {
1360             perror (__FILE__ ": couldn't create pipe");
1361             mc_global.tty.use_subshell = FALSE;
1362             return;
1363         }
1364     }
1365 
1366     /* Fork the subshell */
1367 
1368     subshell_alive = TRUE;
1369     subshell_stopped = FALSE;
1370     subshell_pid = fork ();
1371 
1372     if (subshell_pid == -1)
1373     {
1374         fprintf (stderr, "Cannot spawn the subshell process: %s\r\n", unix_error_string (errno));
1375         /* We exit here because, if the process table is full, the */
1376         /* other method of running user commands won't work either */
1377         exit (EXIT_FAILURE);
1378     }
1379 
1380     if (subshell_pid == 0)
1381     {
1382         /* We are in the child process */
1383         init_subshell_child (pty_name);
1384     }
1385 
1386     init_subshell_precmd (precmd, BUF_MEDIUM);
1387 
1388     write_all (mc_global.tty.subshell_pty, precmd, strlen (precmd));
1389 
1390     /* Wait until the subshell has started up and processed the command */
1391 
1392     subshell_state = RUNNING_COMMAND;
1393     tty_enable_interrupt_key ();
1394     if (!feed_subshell (QUIETLY, TRUE))
1395         mc_global.tty.use_subshell = FALSE;
1396     tty_disable_interrupt_key ();
1397     if (!subshell_alive)
1398         mc_global.tty.use_subshell = FALSE;     /* Subshell died instantly, so don't use it */
1399 
1400     /* Try out the persistent command buffer feature. If it doesn't work the first time, we
1401      * assume there must be something wrong with the shell, and we turn persistent buffer off
1402      * for good. This will save the user the trouble of having to wait for the persistent
1403      * buffer function to time out every time they try to close the subshell. */
1404     if (use_persistent_buffer && !read_command_line_buffer (TRUE))
1405         use_persistent_buffer = FALSE;
1406 }
1407 
1408 /* --------------------------------------------------------------------------------------------- */
1409 
1410 int
1411 invoke_subshell (const char *command, int how, vfs_path_t ** new_dir_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1412 {
1413     /* Make the MC terminal transparent */
1414     tcsetattr (STDOUT_FILENO, TCSANOW, &raw_mode);
1415 
1416     /* Make the subshell change to MC's working directory */
1417     if (new_dir_vpath != NULL)
1418         do_subshell_chdir (subshell_get_cwd (), TRUE);
1419 
1420     if (command == NULL)        /* The user has done "C-o" from MC */
1421     {
1422         if (subshell_state == INACTIVE)
1423         {
1424             subshell_state = ACTIVE;
1425 
1426             /* FIXME: possibly take out this hack; the user can re-play it by hitting C-hyphen a few times! */
1427             if (subshell_ready && mc_global.mc_run_mode == MC_RUN_FULL)
1428                 write_all (mc_global.tty.subshell_pty, " \b", 2);       /* Hack to make prompt reappear */
1429 
1430             if (use_persistent_buffer)
1431             {
1432                 size_t i;
1433                 int pos;
1434 
1435                 /* Check to make sure there are no non text characters in the command buffer,
1436                  * such as tab, or newline, as this could cause problems. */
1437                 for (i = 0; cmdline->buffer[i] != '\0'; i++)
1438                     if ((unsigned char) cmdline->buffer[i] < 32
1439                         || (unsigned char) cmdline->buffer[i] == 127)
1440                         cmdline->buffer[i] = ' ';
1441 
1442                 /* Write the command buffer to the subshell. */
1443                 write_all (mc_global.tty.subshell_pty, cmdline->buffer, strlen (cmdline->buffer));
1444 
1445                 /* Put the cursor in the correct place in the subshell. */
1446                 pos = str_length (cmdline->buffer) - cmdline->point;
1447                 for (i = 0; i < (size_t) pos; i++)
1448                     write_all (mc_global.tty.subshell_pty, ESC_STR "[D", 3);
1449             }
1450         }
1451     }
1452     else                        /* MC has passed us a user command */
1453     {
1454         /* Before we write to the command prompt, we need to clear whatever */
1455         /* data is there, but only if we are using one of the shells that */
1456         /* doesn't support keeping command buffer contents, OR if there was */
1457         /* some sort of error. */
1458         if (!use_persistent_buffer || subshell_should_clear_command_line)
1459         {
1460             write_all (mc_global.tty.subshell_pty, "\003", 1);
1461             subshell_state = RUNNING_COMMAND;
1462             /* We need to call feed_subshell here if we are using fish, because of a quirk
1463              * in the behavioral of that particular shell. */
1464             if (mc_global.shell->type != SHELL_FISH)
1465                 feed_subshell (QUIETLY, FALSE);
1466         }
1467 
1468         if (how == QUIETLY)
1469             write_all (mc_global.tty.subshell_pty, " ", 1);
1470         /* FIXME: if command is long (>8KB ?) we go comma */
1471         write_all (mc_global.tty.subshell_pty, command, strlen (command));
1472         write_all (mc_global.tty.subshell_pty, "\n", 1);
1473         subshell_state = RUNNING_COMMAND;
1474         subshell_ready = FALSE;
1475     }
1476 
1477     feed_subshell (how, FALSE);
1478 
1479     if (new_dir_vpath != NULL && subshell_alive)
1480     {
1481         const char *pcwd;
1482 
1483         pcwd = vfs_translate_path (vfs_path_as_str (subshell_get_cwd ()));
1484         if (strcmp (subshell_cwd, pcwd) != 0)
1485             *new_dir_vpath = vfs_path_from_str (subshell_cwd);  /* Make MC change to the subshell's CWD */
1486     }
1487 
1488     /* Restart the subshell if it has died by SIGHUP, SIGQUIT, etc. */
1489     while (!subshell_alive && subshell_get_mainloop_quit () == 0 && mc_global.tty.use_subshell)
1490         init_subshell ();
1491 
1492     return subshell_get_mainloop_quit ();
1493 }
1494 
1495 /* --------------------------------------------------------------------------------------------- */
1496 
1497 gboolean
1498 flush_subshell (int max_wait_length, int how)
     /* [previous][next][first][last][top][bottom][index][help]  */
1499 {
1500     int rc = 0;
1501     ssize_t bytes = 0;
1502     struct timeval timeleft = { 0, 0 };
1503     gboolean return_value = FALSE;
1504     fd_set tmp;
1505 
1506     timeleft.tv_sec = max_wait_length;
1507     FD_ZERO (&tmp);
1508     FD_SET (mc_global.tty.subshell_pty, &tmp);
1509 
1510     while (subshell_alive
1511            && (rc = select (mc_global.tty.subshell_pty + 1, &tmp, NULL, NULL, &timeleft)) != 0)
1512     {
1513         /* Check for 'select' errors */
1514         if (rc == -1)
1515         {
1516             if (errno == EINTR)
1517             {
1518                 if (tty_got_winch ())
1519                     tty_change_screen_size ();
1520 
1521                 continue;
1522             }
1523 
1524             fprintf (stderr, "select (FD_SETSIZE, &tmp...): %s\r\n", unix_error_string (errno));
1525             exit (EXIT_FAILURE);
1526         }
1527 
1528         return_value = TRUE;
1529         timeleft.tv_sec = 0;
1530         timeleft.tv_usec = 0;
1531 
1532         bytes = read (mc_global.tty.subshell_pty, pty_buffer, sizeof (pty_buffer));
1533         if (how == VISIBLY)
1534             write_all (STDOUT_FILENO, pty_buffer, bytes);
1535     }
1536 
1537     return return_value;
1538 }
1539 
1540 /* --------------------------------------------------------------------------------------------- */
1541 
1542 gboolean
1543 read_subshell_prompt (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1544 {
1545     int rc = 0;
1546     ssize_t bytes = 0;
1547     struct timeval timeleft = { 0, 0 };
1548     gboolean should_reset_prompt = TRUE;
1549     gboolean got_new_prompt = FALSE;
1550 
1551     fd_set tmp;
1552     FD_ZERO (&tmp);
1553     FD_SET (mc_global.tty.subshell_pty, &tmp);
1554 
1555     while (subshell_alive
1556            && (rc = select (mc_global.tty.subshell_pty + 1, &tmp, NULL, NULL, &timeleft)) != 0)
1557     {
1558         /* Check for 'select' errors */
1559         if (rc == -1)
1560         {
1561             if (errno == EINTR)
1562             {
1563                 if (tty_got_winch ())
1564                     tty_change_screen_size ();
1565 
1566                 continue;
1567             }
1568 
1569             fprintf (stderr, "select (FD_SETSIZE, &tmp...): %s\r\n", unix_error_string (errno));
1570             exit (EXIT_FAILURE);
1571         }
1572 
1573         bytes = read (mc_global.tty.subshell_pty, pty_buffer, sizeof (pty_buffer));
1574         if (should_reset_prompt)
1575         {
1576             should_reset_prompt = FALSE;
1577             clear_subshell_prompt_string ();
1578         }
1579 
1580         parse_subshell_prompt_string (pty_buffer, bytes);
1581         got_new_prompt = TRUE;
1582     }
1583 
1584     if (got_new_prompt)
1585         set_prompt_string ();
1586 
1587     return (rc != 0 || bytes != 0);
1588 }
1589 
1590 /* --------------------------------------------------------------------------------------------- */
1591 
1592 void
1593 do_update_prompt (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1594 {
1595     if (update_subshell_prompt)
1596     {
1597         if (subshell_prompt != NULL)
1598         {
1599             printf ("\r\n%s", subshell_prompt->str);
1600             fflush (stdout);
1601         }
1602         update_subshell_prompt = FALSE;
1603     }
1604 }
1605 
1606 /* --------------------------------------------------------------------------------------------- */
1607 
1608 gboolean
1609 exit_subshell (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1610 {
1611     gboolean subshell_quit = TRUE;
1612 
1613     if (subshell_state != INACTIVE && subshell_alive)
1614         subshell_quit =
1615             query_dialog (_("Warning"),
1616                           _("The shell is still active. Quit anyway?"),
1617                           D_NORMAL, 2, _("&Yes"), _("&No")) == 0;
1618 
1619     if (subshell_quit)
1620     {
1621         if (mc_global.shell->type == SHELL_TCSH)
1622         {
1623             if (unlink (tcsh_fifo) == -1)
1624                 fprintf (stderr, "Cannot remove named pipe %s: %s\r\n",
1625                          tcsh_fifo, unix_error_string (errno));
1626         }
1627 
1628         if (subshell_prompt != NULL)
1629         {
1630             g_string_free (subshell_prompt, TRUE);
1631             subshell_prompt = NULL;
1632         }
1633 
1634         if (subshell_prompt_temp_buffer != NULL)
1635         {
1636             g_string_free (subshell_prompt_temp_buffer, TRUE);
1637             subshell_prompt_temp_buffer = NULL;
1638         }
1639 
1640         pty_buffer[0] = '\0';
1641     }
1642 
1643     return subshell_quit;
1644 }
1645 
1646 /* --------------------------------------------------------------------------------------------- */
1647 
1648 /** If it actually changed the directory it returns true */
1649 void
1650 do_subshell_chdir (const vfs_path_t * vpath, gboolean update_prompt)
     /* [previous][next][first][last][top][bottom][index][help]  */
1651 {
1652     char *pcwd;
1653 
1654     pcwd = vfs_path_to_str_flags (subshell_get_cwd (), 0, VPF_RECODE);
1655 
1656     if (!(subshell_state == INACTIVE && strcmp (subshell_cwd, pcwd) != 0))
1657     {
1658         /* We have to repaint the subshell prompt if we read it from
1659          * the main program.  Please note that in the code after this
1660          * if, the cd command that is sent will make the subshell
1661          * repaint the prompt, so we don't have to paint it. */
1662         if (update_prompt)
1663             do_update_prompt ();
1664         g_free (pcwd);
1665         return;
1666     }
1667 
1668     /* If we are using a shell that doesn't support persistent command buffer, we need to clear
1669      * the command prompt before we send the cd command. */
1670     if (!use_persistent_buffer || subshell_should_clear_command_line)
1671     {
1672         write_all (mc_global.tty.subshell_pty, "\003", 1);
1673         subshell_state = RUNNING_COMMAND;
1674         if (mc_global.shell->type != SHELL_FISH)
1675             feed_subshell (QUIETLY, FALSE);
1676     }
1677     /* The initial space keeps this out of the command history (in bash
1678        because we set "HISTCONTROL=ignorespace") */
1679     write_all (mc_global.tty.subshell_pty, " cd ", 4);
1680 
1681     if (vpath != NULL)
1682     {
1683         const char *translate;
1684 
1685         translate = vfs_translate_path (vfs_path_as_str (vpath));
1686         if (translate != NULL)
1687         {
1688             GString *temp;
1689 
1690             temp = subshell_name_quote (translate);
1691             write_all (mc_global.tty.subshell_pty, temp->str, temp->len);
1692             g_string_free (temp, TRUE);
1693         }
1694         else
1695         {
1696             write_all (mc_global.tty.subshell_pty, ".", 1);
1697         }
1698     }
1699     else
1700     {
1701         write_all (mc_global.tty.subshell_pty, "/", 1);
1702     }
1703     write_all (mc_global.tty.subshell_pty, "\n", 1);
1704 
1705     subshell_state = RUNNING_COMMAND;
1706     feed_subshell (QUIETLY, FALSE);
1707 
1708     if (subshell_alive)
1709     {
1710         gboolean bPathNotEq;
1711 
1712         bPathNotEq = strcmp (subshell_cwd, pcwd) != 0;
1713 
1714         if (bPathNotEq && mc_global.shell->type == SHELL_TCSH)
1715         {
1716             char rp_subshell_cwd[PATH_MAX];
1717             char rp_current_panel_cwd[PATH_MAX];
1718             char *p_subshell_cwd, *p_current_panel_cwd;
1719 
1720             p_subshell_cwd = mc_realpath (subshell_cwd, rp_subshell_cwd);
1721             p_current_panel_cwd = mc_realpath (pcwd, rp_current_panel_cwd);
1722 
1723             if (p_subshell_cwd == NULL)
1724                 p_subshell_cwd = subshell_cwd;
1725             if (p_current_panel_cwd == NULL)
1726                 p_current_panel_cwd = pcwd;
1727             bPathNotEq = strcmp (p_subshell_cwd, p_current_panel_cwd) != 0;
1728         }
1729 
1730         if (bPathNotEq && !DIR_IS_DOT (pcwd))
1731         {
1732             char *cwd;
1733 
1734             cwd = vfs_path_to_str_flags (subshell_get_cwd (), 0, VPF_STRIP_PASSWORD);
1735             vfs_print_message (_("Warning: Cannot change to %s.\n"), cwd);
1736             g_free (cwd);
1737         }
1738     }
1739 
1740     /* Really escape Zsh history */
1741     if (mc_global.shell->type == SHELL_ZSH)
1742     {
1743         /* Per Zsh documentation last command prefixed with space lingers in the internal history
1744          * until the next command is entered before it vanishes. To make it vanish right away,
1745          * type a space and press return. */
1746         write_all (mc_global.tty.subshell_pty, " \n", 2);
1747         subshell_state = RUNNING_COMMAND;
1748         feed_subshell (QUIETLY, FALSE);
1749     }
1750 
1751     update_subshell_prompt = FALSE;
1752 
1753     g_free (pcwd);
1754     /* Make sure that MC never stores the CWD in a silly format */
1755     /* like /usr////lib/../bin, or the strcmp() above will fail */
1756 }
1757 
1758 /* --------------------------------------------------------------------------------------------- */
1759 
1760 void
1761 subshell_get_console_attributes (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1762 {
1763     /* Get our current terminal modes */
1764 
1765     if (tcgetattr (STDOUT_FILENO, &shell_mode))
1766     {
1767         fprintf (stderr, "Cannot get terminal settings: %s\r\n", unix_error_string (errno));
1768         mc_global.tty.use_subshell = FALSE;
1769     }
1770 }
1771 
1772 /* --------------------------------------------------------------------------------------------- */
1773 /**
1774  * Figure out whether the subshell has stopped, exited or been killed
1775  * Possibly modifies: 'subshell_alive', 'subshell_stopped' and 'quit' */
1776 
1777 void
1778 sigchld_handler (int sig)
     /* [previous][next][first][last][top][bottom][index][help]  */
1779 {
1780     int status;
1781     pid_t pid;
1782 
1783     (void) sig;
1784 
1785     pid = waitpid (subshell_pid, &status, WUNTRACED | WNOHANG);
1786 
1787     if (pid == subshell_pid)
1788     {
1789         /* Figure out what has happened to the subshell */
1790 
1791         if (WIFSTOPPED (status))
1792         {
1793             if (WSTOPSIG (status) == SIGSTOP)
1794             {
1795                 /* The subshell has received a SIGSTOP signal */
1796                 subshell_stopped = TRUE;
1797             }
1798             else
1799             {
1800                 /* The user has suspended the subshell.  Revive it */
1801                 kill (subshell_pid, SIGCONT);
1802             }
1803         }
1804         else
1805         {
1806             /* The subshell has either exited normally or been killed */
1807             subshell_alive = FALSE;
1808             delete_select_channel (mc_global.tty.subshell_pty);
1809             if (WIFEXITED (status) && WEXITSTATUS (status) != FORK_FAILURE)
1810             {
1811                 int subshell_quit;
1812                 subshell_quit = subshell_get_mainloop_quit () | SUBSHELL_EXIT;  /* Exited normally */
1813                 subshell_set_mainloop_quit (subshell_quit);
1814             }
1815         }
1816     }
1817     subshell_handle_cons_saver ();
1818 
1819     /* If we got here, some other child exited; ignore it */
1820 }
1821 
1822 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */