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

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