Manual pages: mcmcdiffmceditmcview

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. peek_subshell_switch_key
  10. feed_subshell
  11. pty_open_master
  12. pty_open_slave
  13. pty_open_master
  14. pty_open_slave
  15. init_subshell_precmd
  16. subshell_name_quote
  17. clear_cwd_pipe
  18. do_subshell_chdir
  19. init_subshell
  20. invoke_subshell
  21. flush_subshell
  22. read_subshell_prompt
  23. do_update_prompt
  24. exit_subshell
  25. subshell_chdir
  26. subshell_get_console_attributes
  27. sigchld_handler

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

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