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

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