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

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