Manual pages: mcmcdiffmceditmcview

root/src/subshell/common.c

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

DEFINITIONS

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

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

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