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         sigsuspend (&old_mask);
 546 
 547     if (subshell_state != ACTIVE)
 548     {
 549         // Discard all remaining data from stdin to the subshell
 550         tcflush (subshell_pty_slave, TCIFLUSH);
 551     }
 552 
 553     subshell_stopped = FALSE;
 554     kill (subshell_pid, SIGCONT);
 555 
 556     sigprocmask (SIG_SETMASK, &old_mask, NULL);
 557     // We can't do any better without modifying the shell(s)
 558 }
 559 
 560 /* --------------------------------------------------------------------------------------------- */
 561 /* Get the contents of the current subshell command line buffer, and */
 562 /* transfer the contents to the panel command prompt. */
 563 
 564 static gboolean
 565 read_command_line_buffer (gboolean test_mode)
     /* [previous][next][first][last][top][bottom][index][help]  */
 566 {
 567     char subshell_command_buffer[BUF_LARGE];
 568     char subshell_cursor_buffer[BUF_SMALL];
 569 
 570     fd_set read_set;
 571     ssize_t bytes;
 572     struct timeval subshell_prompt_timer = {
 573         .tv_sec = 0,
 574         .tv_usec = 0,
 575     };
 576     int command_buffer_length;
 577     size_t command_buffer_char_length;
 578     int bash_version;
 579     int cursor_position;
 580     int rc;
 581 
 582     if (!use_persistent_buffer)
 583         return TRUE;
 584 
 585     FD_ZERO (&read_set);
 586     FD_SET (command_buffer_pipe[READ], &read_set);
 587 
 588     const int maxfdp = command_buffer_pipe[READ];
 589 
 590     /* First, flush the command buffer pipe. This pipe shouldn't be written
 591      * to under normal circumstances, but if it somehow does get written
 592      * to, we need to make sure to discard whatever data is there before
 593      * we try to use it. */
 594     while ((rc = select (maxfdp + 1, &read_set, NULL, NULL, &subshell_prompt_timer)) != 0)
 595     {
 596         if (rc == -1)
 597         {
 598             if (errno == EINTR)
 599                 continue;
 600 
 601             return FALSE;
 602         }
 603 
 604         if (rc == 1)
 605         {
 606             bytes = read (command_buffer_pipe[READ], subshell_command_buffer,
 607                           sizeof (subshell_command_buffer));
 608             (void) bytes;
 609         }
 610     }
 611 
 612     // get contents of command line buffer
 613     write_all (mc_global.tty.subshell_pty, ESC_STR SHELL_BUFFER_KEYBINDING,
 614                sizeof (ESC_STR SHELL_BUFFER_KEYBINDING) - 1);
 615 
 616     subshell_prompt_timer.tv_sec = 1;
 617     FD_ZERO (&read_set);
 618     FD_SET (command_buffer_pipe[READ], &read_set);
 619 
 620     while ((rc = select (maxfdp + 1, &read_set, NULL, NULL, &subshell_prompt_timer)) != 1)
 621     {
 622         if (rc == -1)
 623         {
 624             if (errno == EINTR)
 625                 continue;
 626 
 627             return FALSE;
 628         }
 629 
 630         if (rc == 0)
 631             return FALSE;
 632     }
 633 
 634     bytes =
 635         read (command_buffer_pipe[READ], subshell_command_buffer, sizeof (subshell_command_buffer));
 636     // FIXME: what about bytes < 0?
 637     if (bytes == 0 || (size_t) bytes == sizeof (subshell_command_buffer))
 638         return FALSE;
 639 
 640     command_buffer_char_length = (size_t) bytes - 1;
 641     subshell_command_buffer[command_buffer_char_length] = '\0';
 642     command_buffer_length = str_length (subshell_command_buffer);
 643 
 644     // get cursor position
 645     write_all (mc_global.tty.subshell_pty, ESC_STR SHELL_CURSOR_KEYBINDING,
 646                sizeof (ESC_STR SHELL_CURSOR_KEYBINDING) - 1);
 647 
 648     subshell_prompt_timer.tv_sec = 1;
 649     subshell_prompt_timer.tv_usec = 0;
 650     FD_ZERO (&read_set);
 651     FD_SET (command_buffer_pipe[READ], &read_set);
 652 
 653     while ((rc = select (maxfdp + 1, &read_set, NULL, NULL, &subshell_prompt_timer)) != 1)
 654     {
 655         if (rc == -1)
 656         {
 657             if (errno == EINTR)
 658                 continue;
 659 
 660             return FALSE;
 661         }
 662 
 663         if (rc == 0)
 664             return FALSE;
 665     }
 666 
 667     bytes =
 668         read (command_buffer_pipe[READ], subshell_cursor_buffer, sizeof (subshell_cursor_buffer));
 669     // FIXME: what about bytes < 0?
 670     if (bytes == 0)
 671         return FALSE;
 672 
 673     subshell_cursor_buffer[(size_t) bytes - 1] = '\0';
 674     if (mc_global.shell->type == SHELL_BASH)
 675     {
 676         if (sscanf (subshell_cursor_buffer, "%d:%d", &bash_version, &cursor_position) != 2)
 677             return FALSE;
 678     }
 679     else
 680     {
 681         if (sscanf (subshell_cursor_buffer, "%d", &cursor_position) != 1)
 682             return FALSE;
 683         bash_version = 1000;
 684     }
 685 
 686     if (test_mode)
 687         return TRUE;
 688 
 689     /* Substitute non-text characters in the command buffer, such as tab, or newline, as this
 690      * could cause problems. */
 691     for (size_t i = 0; i < command_buffer_char_length; i++)
 692         if ((unsigned char) subshell_command_buffer[i] < 32
 693             || (unsigned char) subshell_command_buffer[i] == 127)
 694             subshell_command_buffer[i] = ' ';
 695 
 696     input_assign_text (cmdline, "");
 697     input_insert (cmdline, subshell_command_buffer, FALSE);
 698 
 699     if (bash_version < 5)  // implies SHELL_BASH
 700     {
 701         /* We need to do this because bash < v5 gives the cursor position in a utf-8 string based
 702          * on the location in bytes, not in unicode characters. */
 703         char *curr, *stop;
 704 
 705         curr = subshell_command_buffer;
 706         stop = curr + cursor_position;
 707 
 708         for (cursor_position = 0; curr < stop; cursor_position++)
 709             str_next_char_safe (&curr);
 710     }
 711     if (cursor_position > command_buffer_length)
 712         cursor_position = command_buffer_length;
 713     cmdline->point = cursor_position;
 714     // We send any remaining data to STDOUT before we finish.
 715     flush_subshell (0, VISIBLY);
 716 
 717     // Now we erase the current contents of the command line buffer
 718     if (mc_global.shell->type != SHELL_ZSH)
 719     {
 720         /* In zsh, we can just press c-u to clear the line, without needing to go to the end of
 721          * the line first first. In all other shells, we must go to the end of the line first. */
 722 
 723         // If we are not at the end of the line, we go to the end.
 724         if (cursor_position != command_buffer_length)
 725         {
 726             write_all (mc_global.tty.subshell_pty, "\005", 1);
 727             if (flush_subshell (1, VISIBLY) != 1)
 728                 return FALSE;
 729         }
 730     }
 731 
 732     if (command_buffer_length > 0)
 733     {
 734         // Now we clear the line.
 735         write_all (mc_global.tty.subshell_pty, "\025", 1);
 736         if (flush_subshell (1, VISIBLY) != 1)
 737             return FALSE;
 738     }
 739 
 740     return TRUE;
 741 }
 742 
 743 /* --------------------------------------------------------------------------------------------- */
 744 
 745 static void
 746 clear_subshell_prompt_string (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 747 {
 748     if (subshell_prompt_temp_buffer != NULL)
 749         g_string_set_size (subshell_prompt_temp_buffer, 0);
 750 }
 751 
 752 /* --------------------------------------------------------------------------------------------- */
 753 
 754 static void
 755 parse_subshell_prompt_string (const char *buffer, size_t bytes)
     /* [previous][next][first][last][top][bottom][index][help]  */
 756 {
 757     if (mc_global.mc_run_mode != MC_RUN_FULL)
 758         return;
 759 
 760     // First time through
 761     if (subshell_prompt == NULL)
 762         subshell_prompt = g_string_sized_new (INITIAL_PROMPT_SIZE);
 763     if (subshell_prompt_temp_buffer == NULL)
 764         subshell_prompt_temp_buffer = g_string_sized_new (INITIAL_PROMPT_SIZE);
 765 
 766     // Extract the prompt from the shell output
 767     for (size_t i = 0; i < bytes; i++)
 768         if (buffer[i] == '\n' || buffer[i] == '\r')
 769             g_string_set_size (subshell_prompt_temp_buffer, 0);
 770         else if (buffer[i] != '\0')
 771             g_string_append_c (subshell_prompt_temp_buffer, buffer[i]);
 772 }
 773 
 774 /* --------------------------------------------------------------------------------------------- */
 775 
 776 static void
 777 set_prompt_string (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
 778 {
 779     if (mc_global.mc_run_mode != MC_RUN_FULL)
 780         return;
 781 
 782     if (subshell_prompt_temp_buffer->len != 0)
 783         mc_g_string_copy (subshell_prompt, subshell_prompt_temp_buffer);
 784 
 785     setup_cmdline ();
 786 }
 787 
 788 /* --------------------------------------------------------------------------------------------- */
 789 
 790 static gboolean
 791 peek_subshell_switch_key (const char *buffer, size_t len)
     /* [previous][next][first][last][top][bottom][index][help]  */
 792 {
 793     csi_command_t csi;
 794 
 795     if (len == 0)
 796         return FALSE;
 797     if (buffer[0] == (XCTRL ('o') & 255))
 798         return TRUE;
 799 
 800     // Also check if ctrl-o is encoded as per the kitty keyboard protocol.
 801     if (len == 1)
 802         return FALSE;
 803     if (buffer[0] != ESC_CHAR || buffer[1] != '[')  // CSI
 804         return FALSE;
 805 
 806     buffer += 2;
 807     len -= 2;
 808 
 809     if (!parse_csi (&csi, &buffer, buffer + len))
 810         return FALSE;
 811     if (csi.private_mode != '\0' || buffer[-1] != 'u')
 812         return FALSE;
 813     if (csi.param_count != 2)  // ctrl-o must have the modifier field
 814         return FALSE;
 815     if (csi.params[1][0] == 0)  // Bad modifier.
 816         return FALSE;
 817 
 818     const uint32_t codepoint = csi.params[0][0];
 819     const uint32_t modifiers = csi.params[1][0] - 1;
 820     const uint32_t event = csi.params[1][1];
 821 
 822     if (event != 0 && event != EVENT_TYPE_PRESS && event != EVENT_TYPE_REPEAT)
 823         return FALSE;
 824 
 825     return codepoint == 'o'
 826         && (modifiers & ~(MODIFIER_CAPS_LOCK | MODIFIER_NUM_LOCK)) == MODIFIER_CTRL;
 827 }
 828 
 829 /* --------------------------------------------------------------------------------------------- */
 830 /** Feed the subshell our keyboard input until it says it's finished */
 831 
 832 static gboolean
 833 feed_subshell (int how, gboolean fail_on_error)
     /* [previous][next][first][last][top][bottom][index][help]  */
 834 {
 835     fd_set read_set;  // For 'select'
 836 
 837     struct timeval wtime;  // Maximum time we wait for the subshell
 838     struct timeval *wptr;
 839 
 840     should_read_new_subshell_prompt = FALSE;
 841 
 842     /* have more than enough time to run subshell:
 843        wait up to 10 second if fail_on_error, forever otherwise */
 844     wtime.tv_sec = 10;
 845     wtime.tv_usec = 0;
 846     wptr = fail_on_error ? &wtime : NULL;
 847 
 848     while (TRUE)
 849     {
 850         int maxfdp;
 851 
 852         if (!subshell_alive)
 853             return FALSE;
 854 
 855         // Prepare the file-descriptor set and call 'select'
 856 
 857         FD_ZERO (&read_set);
 858         FD_SET (mc_global.tty.subshell_pty, &read_set);
 859         FD_SET (subshell_pipe[READ], &read_set);
 860         maxfdp = MAX (mc_global.tty.subshell_pty, subshell_pipe[READ]);
 861         if (how == VISIBLY)
 862         {
 863             FD_SET (STDIN_FILENO, &read_set);
 864             maxfdp = MAX (maxfdp, STDIN_FILENO);
 865         }
 866 
 867         if (select (maxfdp + 1, &read_set, NULL, NULL, wptr) == -1)
 868         {
 869             // Despite using SA_RESTART, we still have to check for this
 870             if (errno == EINTR)
 871             {
 872                 if (tty_got_winch ())
 873                     tty_change_screen_size ();
 874 
 875                 continue;  // try all over again
 876             }
 877             tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
 878             fprintf (stderr, "select (FD_SETSIZE, &read_set...): %s\r\n",
 879                      unix_error_string (errno));
 880             exit (EXIT_FAILURE);
 881         }
 882 
 883         if (FD_ISSET (mc_global.tty.subshell_pty, &read_set))
 884         // Read from the subshell, write to stdout
 885 
 886         /* This loop improves performance by reducing context switches
 887            by a factor of 20 or so... unfortunately, it also hangs MC
 888            randomly, because of an apparent Linux bug.  Investigate. */
 889         // for (i=0; i<5; ++i)  * FIXME -- experimental
 890         {
 891             const ssize_t bytes =
 892                 read (mc_global.tty.subshell_pty, pty_buffer, sizeof (pty_buffer));
 893 
 894             // The subshell has died
 895             if (bytes == -1 && errno == EIO && !subshell_alive)
 896                 return FALSE;
 897 
 898             if (bytes <= 0)
 899             {
 900 #ifdef PTY_ZEROREAD
 901                 // On IBM i, read(1) can return 0 for a non-closed fd
 902                 continue;
 903 #else
 904                 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
 905                 fprintf (stderr, "read (subshell_pty...): %s\r\n", unix_error_string (errno));
 906                 exit (EXIT_FAILURE);
 907 #endif
 908             }
 909 
 910             if (how == VISIBLY)
 911                 write_all (STDOUT_FILENO, pty_buffer, (size_t) bytes);
 912 
 913             if (should_read_new_subshell_prompt)
 914                 parse_subshell_prompt_string (pty_buffer, (size_t) bytes);
 915         }
 916 
 917         else if (FD_ISSET (subshell_pipe[READ], &read_set))
 918         // Read the subshell's CWD and capture its prompt
 919         {
 920             const ssize_t bytes = read (subshell_pipe[READ], subshell_cwd, sizeof (subshell_cwd));
 921 
 922             if (bytes <= 0)
 923             {
 924                 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
 925                 fprintf (stderr, "read (subshell_pipe[READ]...): %s\r\n",
 926                          unix_error_string (errno));
 927                 exit (EXIT_FAILURE);
 928             }
 929 
 930             subshell_cwd[(size_t) bytes - 1] = '\0';  // Squash the final '\n'
 931 
 932             synchronize ();
 933 
 934             clear_subshell_prompt_string ();
 935             should_read_new_subshell_prompt = TRUE;
 936             subshell_ready = TRUE;
 937             if (subshell_state == RUNNING_COMMAND)
 938             {
 939                 subshell_state = INACTIVE;
 940                 return TRUE;
 941             }
 942         }
 943 
 944         else if (FD_ISSET (STDIN_FILENO, &read_set))
 945         // Read from stdin, write to the subshell
 946         {
 947             should_read_new_subshell_prompt = FALSE;
 948 
 949             const ssize_t bytes = read (STDIN_FILENO, pty_buffer, sizeof (pty_buffer));
 950 
 951             if (bytes <= 0)
 952             {
 953                 tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
 954                 fprintf (stderr, "read (STDIN_FILENO, pty_buffer...): %s\r\n",
 955                          unix_error_string (errno));
 956                 exit (EXIT_FAILURE);
 957             }
 958 
 959             // just type casting
 960             const size_t ubytes = (size_t) bytes;
 961 
 962             for (size_t i = 0; i < ubytes; i++)
 963                 if (peek_subshell_switch_key (pty_buffer + i, ubytes - i))
 964                 {
 965                     write_all (mc_global.tty.subshell_pty, pty_buffer, i);
 966 
 967                     if (subshell_ready)
 968                     {
 969                         subshell_state = INACTIVE;
 970                         set_prompt_string ();
 971                         if (subshell_ready && !read_command_line_buffer (FALSE))
 972                         {
 973                             // If we got here, some unforeseen error must have occurred.
 974                             if (mc_global.shell->type != SHELL_FISH)
 975                             {
 976                                 write_all (mc_global.tty.subshell_pty, "\003", 1);
 977                                 subshell_state = RUNNING_COMMAND;
 978                                 if (feed_subshell (QUIETLY, TRUE)
 979                                     && read_command_line_buffer (FALSE))
 980                                     return TRUE;
 981                             }
 982 
 983                             subshell_state = ACTIVE;
 984                             flush_subshell (0, VISIBLY);
 985                             input_assign_text (cmdline, "");
 986                         }
 987                     }
 988 
 989                     return TRUE;
 990                 }
 991 
 992             write_all (mc_global.tty.subshell_pty, pty_buffer, ubytes);
 993 
 994             if (pty_buffer[ubytes - 1] == '\n' || pty_buffer[ubytes - 1] == '\r')
 995             {
 996                 /* We should only clear the command line if we are using a shell that works
 997                  * with persistent command buffer, otherwise we get awkward results. */
 998                 if (use_persistent_buffer)
 999                     input_assign_text (cmdline, "");
1000                 subshell_ready = FALSE;
1001             }
1002         }
1003         else
1004             return FALSE;
1005     }
1006 }
1007 
1008 /* --------------------------------------------------------------------------------------------- */
1009 /* pty opening functions */
1010 
1011 #ifndef HAVE_OPENPTY
1012 
1013 #ifdef HAVE_GRANTPT
1014 
1015 /* System V version of pty_open_master */
1016 
1017 static int
1018 pty_open_master (char *pty_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
1019 {
1020     char *slave_name;
1021 
1022 #ifdef HAVE_POSIX_OPENPT
1023     const int pty_master = posix_openpt (O_RDWR);
1024 #elif defined HAVE_GETPT
1025     // getpt () is a GNU extension (glibc 2.1.x)
1026     const int pty_master = getpt ();
1027 #elif defined IS_AIX
1028     strcpy (pty_name, "/dev/ptc");
1029     const int pty_master = open (pty_name, O_RDWR);
1030 #else
1031     strcpy (pty_name, "/dev/ptmx");
1032     const int pty_master = open (pty_name, O_RDWR);
1033 #endif
1034 
1035     if (pty_master == -1)
1036         return -1;
1037 
1038     if (grantpt (pty_master) == -1                       // Grant access to slave
1039         || unlockpt (pty_master) == -1                   // Clear slave's lock flag
1040         || (slave_name = ptsname (pty_master)) == NULL)  // Get slave's name
1041     {
1042         close (pty_master);
1043         return -1;
1044     }
1045     strcpy (pty_name, slave_name);
1046     return pty_master;
1047 }
1048 
1049 /* --------------------------------------------------------------------------------------------- */
1050 /** System V version of pty_open_slave */
1051 
1052 static int
1053 pty_open_slave (const char *pty_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
1054 {
1055     const int pty_slave = open (pty_name, O_RDWR);
1056 
1057     if (pty_slave == -1)
1058     {
1059         fprintf (stderr, "open (%s, O_RDWR): %s\r\n", pty_name, unix_error_string (errno));
1060         return -1;
1061     }
1062 #if !defined(__osf__) && !defined(__linux__)
1063 #if defined(I_FIND) && defined(I_PUSH)
1064     if (ioctl (pty_slave, I_FIND, "ptem") == 0 && ioctl (pty_slave, I_PUSH, "ptem") == -1)
1065     {
1066         fprintf (stderr, "ioctl (%d, I_PUSH, \"ptem\") failed: %s\r\n", pty_slave,
1067                  unix_error_string (errno));
1068         close (pty_slave);
1069         return -1;
1070     }
1071 
1072     if (ioctl (pty_slave, I_FIND, "ldterm") == 0 && ioctl (pty_slave, I_PUSH, "ldterm") == -1)
1073     {
1074         fprintf (stderr, "ioctl (%d, I_PUSH, \"ldterm\") failed: %s\r\n", pty_slave,
1075                  unix_error_string (errno));
1076         close (pty_slave);
1077         return -1;
1078     }
1079 #if !defined(sgi) && !defined(__sgi)
1080     if (ioctl (pty_slave, I_FIND, "ttcompat") == 0 && ioctl (pty_slave, I_PUSH, "ttcompat") == -1)
1081     {
1082         fprintf (stderr, "ioctl (%d, I_PUSH, \"ttcompat\") failed: %s\r\n", pty_slave,
1083                  unix_error_string (errno));
1084         close (pty_slave);
1085         return -1;
1086     }
1087 #endif
1088 #endif
1089 #endif
1090 
1091     fcntl (pty_slave, F_SETFD, FD_CLOEXEC);
1092     return pty_slave;
1093 }
1094 
1095 #else  // !HAVE_GRANTPT
1096 
1097 /* --------------------------------------------------------------------------------------------- */
1098 /** BSD version of pty_open_master */
1099 
1100 static int
1101 pty_open_master (char *pty_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
1102 {
1103     strcpy (pty_name, "/dev/ptyXX");
1104 
1105     for (const char *ptr1 = "pqrstuvwxyzPQRST"; *ptr1 != '\0'; ++ptr1)
1106     {
1107         pty_name[8] = *ptr1;
1108 
1109         for (const char *ptr2 = "0123456789abcdef"; *ptr2 != '\0'; ++ptr2)
1110         {
1111             pty_name[9] = *ptr2;
1112 
1113             // Try to open master
1114             const int pty_master = open (pty_name, O_RDWR);
1115 
1116             if (pty_master == -1)
1117             {
1118                 if (errno == ENOENT)  // Different from EIO
1119                     return -1;        // Out of pty devices
1120                 continue;             // Try next pty device
1121             }
1122             pty_name[5] = 't';  // Change "pty" to "tty"
1123             if (access (pty_name, 6) != 0)
1124             {
1125                 close (pty_master);
1126                 pty_name[5] = 'p';
1127                 continue;
1128             }
1129             return pty_master;
1130         }
1131     }
1132     return -1;  // Ran out of pty devices
1133 }
1134 
1135 /* --------------------------------------------------------------------------------------------- */
1136 /** BSD version of pty_open_slave */
1137 
1138 static int
1139 pty_open_slave (const char *pty_name)
     /* [previous][next][first][last][top][bottom][index][help]  */
1140 {
1141     struct group *group_info = getgrnam ("tty");
1142 
1143     if (group_info != NULL)
1144     {
1145         // The following two calls will only succeed if we are root
1146         // [Commented out while permissions problem is investigated]
1147         // chown (pty_name, getuid (), group_info->gr_gid);  FIXME
1148         // chmod (pty_name, S_IRUSR | S_IWUSR | S_IWGRP);   FIXME
1149     }
1150 
1151     const int pty_slave = open (pty_name, O_RDWR);
1152 
1153     if (pty_slave == -1)
1154         fprintf (stderr, "open (pty_name, O_RDWR): %s\r\n", pty_name);
1155     fcntl (pty_slave, F_SETFD, FD_CLOEXEC);
1156     return pty_slave;
1157 }
1158 #endif
1159 
1160 #endif
1161 
1162 /* --------------------------------------------------------------------------------------------- */
1163 /**
1164  * Set up `precmd' or equivalent for reading the subshell's CWD.
1165  *
1166  * Attention! Never forget that these are *one-liners* even though the concatenated
1167  * substrings contain line breaks and indentation for better understanding of the
1168  * shell code. It is vital that each one-liner ends with a line feed character ("\n" ).
1169  *
1170  * @return initialized pre-command string
1171  */
1172 static gchar *
1173 init_subshell_precmd (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1174 {
1175     /*
1176      * Fallback precmd emulation that should work with virtually any shell.
1177      * No real precmd functionality is required, no support for \x substitutions
1178      * in PS1 is needed. For convenience, $HOME is replaced by ~ in PS1.
1179      *
1180      * The following example is a little less fancy (home directory not replaced)
1181      * and shows the basic workings of our prompt for easier understanding:
1182      *
1183      * "precmd() { "
1184      *     "echo \"$USER@$(hostname -s):$PWD\"; "
1185      *     "pwd>&%d; "
1186      *     "kill -STOP $$; "
1187      * "}; "
1188      * "PRECMD=precmd; "
1189      * "PS1='$($PRECMD)$ '\n",
1190      *
1191      * Explanations:
1192      *
1193      * A: This leads to a stopped subshell (=frozen mc) if user calls "ash" command
1194      *    "PS1='$(pwd>&%d; kill -STOP $$)\\u@\\h:\\w\\$ '\n",
1195      *
1196      * B: This leads to "sh: precmd: not found" in sub-subshell if user calls "ash" command
1197      *    "precmd() { pwd>&%d; kill -STOP $$; }; "
1198      *    "PS1='$(precmd)\\u@\\h:\\w\\$ '\n",
1199      *
1200      * C: This works if user calls "ash" command because in sub-subshell
1201      *    PRECMD is undefined, thus evaluated to empty string - no damage done.
1202      *    Attention: BusyBox must be built with FEATURE_EDITING_FANCY_PROMPT to
1203      *    permit \u, \w, \h, \$ escape sequences. Unfortunately this cannot be guaranteed,
1204      *    especially on embedded systems where people try to save space, so let's use
1205      *    the fallback version.
1206      *
1207      */
1208     static const char *precmd_fallback =
1209         " "  // Useful if the shell supports HISTCONTROL=ignorespace like functionality
1210         "MC_PS1_SAVED=\"$PS1\"; "  // Save custom PS1
1211         "precmd() { "
1212         "  if [ ! \"${PWD##$HOME}\" ]; then "
1213         "    MC_PWD=\"~\"; "
1214         "  else "
1215         "    [ \"${PWD##$HOME/}\" = \"$PWD\" ] && MC_PWD=\"$PWD\" || MC_PWD=\"~/${PWD##$HOME/}\"; "
1216         "  fi; "
1217         "  echo \"${MC_PS1_SAVED:-$USER@$(hostname -s):$MC_PWD\\$ }\"; "
1218         "  pwd>&%d; "
1219         "  kill -STOP $$; "
1220         "}; "
1221         "PRECMD=precmd; "
1222         "PS1='$($PRECMD)'\n";
1223 
1224     switch (mc_global.shell->type)
1225     {
1226     case SHELL_BASH:
1227         return g_strdup_printf (
1228             " mc_print_command_buffer () { printf \"%%s\\\\n\" \"$READLINE_LINE\" >&%d; }\n"
1229             " bind -x '\"\\e" SHELL_BUFFER_KEYBINDING "\":\"mc_print_command_buffer\"'\n"
1230             " bind -x '\"\\e" SHELL_CURSOR_KEYBINDING
1231             "\":\"echo $BASH_VERSINFO:$READLINE_POINT >&%d\"'\n"
1232             " if test ${BASH_VERSION%%%%.*} -ge 5 && [[ ${PROMPT_COMMAND@a} == *a* ]] 2> "
1233             "/dev/null; then\n"
1234             "   eval \"PROMPT_COMMAND+=( 'pwd>&%d;kill -STOP $$' )\"\n"
1235             " else\n"
1236             "   PROMPT_COMMAND=${PROMPT_COMMAND:+$PROMPT_COMMAND\n}'pwd>&%d;kill -STOP $$'\n"
1237             " fi\n"
1238             "PS1='\\u@\\h:\\w\\$ '\n",
1239             command_buffer_pipe[WRITE], command_buffer_pipe[WRITE], subshell_pipe[WRITE],
1240             subshell_pipe[WRITE]);
1241 
1242     case SHELL_ASH_BUSYBOX:
1243         // BusyBox ash needs a somewhat complicated precmd emulation via PS1, and it is vital
1244         // that BB be built with active CONFIG_ASH_EXPAND_PRMT, but this is the default anyway
1245         return g_strdup_printf (precmd_fallback, subshell_pipe[WRITE]);
1246 
1247     case SHELL_DASH:
1248         // Debian ash needs a precmd emulation via PS1, similar to BusyBox ash,
1249         // but does not support escape sequences for user, host and cwd in prompt
1250         return g_strdup_printf (precmd_fallback, subshell_pipe[WRITE]);
1251 
1252     case SHELL_MKSH:
1253         // mksh doesn't support \x placeholders in PS1 and needs precmd emulation via PS1
1254         return g_strdup_printf (precmd_fallback, subshell_pipe[WRITE]);
1255 
1256     case SHELL_KSH:
1257         // pdksh based variants support \x placeholders but not any "precmd" functionality
1258         return g_strdup_printf (" PS1='$(pwd>&%d; kill -STOP $$)'"
1259                                 "\"${PS1:-\\u@\\h:\\w\\$ }\"\n",
1260                                 subshell_pipe[WRITE]);
1261 
1262     case SHELL_ZSH:
1263         return g_strdup_printf (
1264             " mc_print_command_buffer () { printf \"%%s\\\\n\" \"$BUFFER\" >&%d; }\n"
1265             " zle -N mc_print_command_buffer\n"
1266             " bindkey '^[" SHELL_BUFFER_KEYBINDING "' mc_print_command_buffer\n"
1267             " mc_print_cursor_position () { echo $CURSOR >&%d}\n"
1268             " zle -N mc_print_cursor_position\n"
1269             " bindkey '^[" SHELL_CURSOR_KEYBINDING "' mc_print_cursor_position\n"
1270             " _mc_precmd(){ pwd>&%d;kill -STOP $$ }; precmd_functions+=(_mc_precmd)\n"
1271             "PS1='%%n@%%m:%%~%%# '\n",
1272             command_buffer_pipe[WRITE], command_buffer_pipe[WRITE], subshell_pipe[WRITE]);
1273 
1274     case SHELL_TCSH:
1275         return g_strdup_printf ("set echo_style=both; "
1276                                 "set prompt='%%n@%%m:%%~%%# '; "
1277                                 "alias precmd 'echo -n;echo $cwd:q >>%s; kill -STOP $$'\n",
1278                                 tcsh_fifo);
1279 
1280     case SHELL_FISH:
1281         return g_strdup_printf (" bind \\e" SHELL_BUFFER_KEYBINDING " 'commandline >&%d';"
1282                                 "bind \\e" SHELL_CURSOR_KEYBINDING " 'commandline -C >&%d';"
1283                                 "if not functions -q fish_prompt_mc;"
1284                                 "functions -e fish_right_prompt;"
1285                                 "functions -c fish_prompt fish_prompt_mc; end;"
1286                                 "function fish_prompt;"
1287                                 "echo \"$PWD\">&%d; fish_prompt_mc; kill -STOP $fish_pid; end\n",
1288                                 command_buffer_pipe[WRITE], command_buffer_pipe[WRITE],
1289                                 subshell_pipe[WRITE]);
1290     default:
1291         fprintf (stderr, "subshell: unknown shell type (%u), aborting!\r\n", mc_global.shell->type);
1292         exit (EXIT_FAILURE);
1293     }
1294 }
1295 
1296 /* --------------------------------------------------------------------------------------------- */
1297 /**
1298  * Carefully quote directory name to allow entering any directory safely,
1299  * no matter what weird characters it may contain in its name.
1300  * NOTE: Treat directory name an untrusted data, don't allow it to cause
1301  * executing any commands in the shell.  Escape all control characters.
1302  * Use following technique:
1303  *
1304  * printf(1) with format string containing a single conversion specifier,
1305  * "b", and an argument which contains a copy of the string passed to
1306  * subshell_name_quote() with all characters, except digits and letters,
1307  * replaced by the backslash-escape sequence \0nnn, where "nnn" is the
1308  * numeric value of the character converted to octal number.
1309  *
1310  *   cd "`printf '%b' 'ABC\0nnnDEF\0nnnXYZ'`"
1311  *
1312  * N.B.: Use single quotes for conversion specifier to work around
1313  *       tcsh 6.20+ parser breakage, see ticket #3852 for the details.
1314  */
1315 
1316 static GString *
1317 subshell_name_quote (const char *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
1318 {
1319     GString *ret;
1320     const char *n;
1321     const char *quote_cmd_start, *quote_cmd_end;
1322 
1323     if (mc_global.shell->type == SHELL_FISH)
1324     {
1325         quote_cmd_start = "(printf '%b' '";
1326         quote_cmd_end = "')";
1327     }
1328     /* TODO: When BusyBox printf is fixed, get rid of this "else if", see
1329        https://lists.busybox.net/pipermail/busybox/2012-March/077460.html */
1330     /* else if (subshell_type == ASH_BUSYBOX)
1331        {
1332        quote_cmd_start = "\"`echo -en '";
1333        quote_cmd_end = "'`\"";
1334        } */
1335     else
1336     {
1337         quote_cmd_start = "\"`printf '%b' '";
1338         quote_cmd_end = "'`\"";
1339     }
1340 
1341     ret = g_string_sized_new (64);
1342 
1343     // Prevent interpreting leading '-' as a switch for 'cd'
1344     if (s[0] == '-')
1345         g_string_append (ret, "./");
1346 
1347     // Copy the beginning of the command to the buffer
1348     g_string_append (ret, quote_cmd_start);
1349 
1350     /*
1351      * Print every character except digits and letters as a backslash-escape
1352      * sequence of the form \0nnn, where "nnn" is the numeric value of the
1353      * character converted to octal number.
1354      */
1355     for (const char *su = s; su[0] != '\0'; su = n)
1356     {
1357         n = str_cget_next_char_safe (su);
1358 
1359         if (str_isalnum (su))
1360             g_string_append_len (ret, su, (size_t) (n - su));
1361         else
1362             for (size_t c = 0; c < (size_t) (n - su); c++)
1363                 g_string_append_printf (ret, "\\0%03o", (unsigned char) su[c]);
1364     }
1365 
1366     g_string_append (ret, quote_cmd_end);
1367 
1368     return ret;
1369 }
1370 
1371 /* --------------------------------------------------------------------------------------------- */
1372 /**
1373  * This function checks the pipe from which we receive data about the current working directory.
1374  * If there is any data waiting, we clear it.
1375  */
1376 
1377 static void
1378 clear_cwd_pipe (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1379 {
1380     fd_set read_set;
1381     struct timeval wtime = { 0, 0 };
1382 
1383     FD_ZERO (&read_set);
1384     FD_SET (subshell_pipe[READ], &read_set);
1385 
1386     const int maxfdp = subshell_pipe[READ];
1387 
1388     if (select (maxfdp + 1, &read_set, NULL, NULL, &wtime) > 0
1389         && FD_ISSET (subshell_pipe[READ], &read_set))
1390     {
1391         if (read (subshell_pipe[READ], subshell_cwd, sizeof (subshell_cwd)) <= 0)
1392         {
1393             tcsetattr (STDOUT_FILENO, TCSANOW, &shell_mode);
1394             fprintf (stderr, "read (subshell_pipe[READ]...): %s\r\n", unix_error_string (errno));
1395             exit (EXIT_FAILURE);
1396         }
1397 
1398         synchronize ();
1399     }
1400 }
1401 
1402 /* --------------------------------------------------------------------------------------------- */
1403 
1404 static void
1405 do_subshell_chdir (const vfs_path_t *vpath, gboolean update_prompt)
     /* [previous][next][first][last][top][bottom][index][help]  */
1406 {
1407     char *pcwd;
1408 
1409     pcwd = vfs_path_to_str_flags (subshell_get_cwd (), 0, VPF_RECODE);
1410 
1411     if (!(subshell_state == INACTIVE && strcmp (subshell_cwd, pcwd) != 0))
1412     {
1413         /* We have to repaint the subshell prompt if we read it from
1414          * the main program.  Please note that in the code after this
1415          * if, the cd command that is sent will make the subshell
1416          * repaint the prompt, so we don't have to paint it. */
1417         if (update_prompt)
1418             do_update_prompt ();
1419         g_free (pcwd);
1420         return;
1421     }
1422 
1423     /* If we are using a shell that doesn't support persistent command buffer, we need to clear
1424      * the command prompt before we send the cd command. */
1425     if (!use_persistent_buffer)
1426     {
1427         write_all (mc_global.tty.subshell_pty, "\003", 1);
1428         subshell_state = RUNNING_COMMAND;
1429         if (mc_global.shell->type != SHELL_FISH && !feed_subshell (QUIETLY, TRUE))
1430         {
1431             subshell_state = ACTIVE;
1432             return;
1433         }
1434     }
1435 
1436     /* A quick and dirty fix for fish shell. For some reason, fish does not
1437      * execute all the commands sent to it from Midnight Commander :(
1438      * An example of such buggy behavior is presented in ticket #4521.
1439      * TODO: Find the real cause and fix it "the right way" */
1440     if (mc_global.shell->type == SHELL_FISH)
1441     {
1442         write_all (mc_global.tty.subshell_pty, "\n", 1);
1443         subshell_state = RUNNING_COMMAND;
1444         feed_subshell (QUIETLY, TRUE);
1445     }
1446 
1447     /* The initial space keeps this out of the command history (in bash
1448        because we set "HISTCONTROL=ignorespace") */
1449     write_all (mc_global.tty.subshell_pty, " cd ", 4);
1450 
1451     if (vpath == NULL)
1452         write_all (mc_global.tty.subshell_pty, "/", 1);
1453     else
1454     {
1455         const char *translate = vfs_translate_path (vfs_path_as_str (vpath));
1456 
1457         if (translate == NULL)
1458             write_all (mc_global.tty.subshell_pty, ".", 1);
1459         else
1460         {
1461             GString *temp;
1462 
1463             temp = subshell_name_quote (translate);
1464             write_all (mc_global.tty.subshell_pty, temp->str, temp->len);
1465             g_string_free (temp, TRUE);
1466         }
1467     }
1468 
1469     write_all (mc_global.tty.subshell_pty, "\n", 1);
1470 
1471     subshell_state = RUNNING_COMMAND;
1472     if (!feed_subshell (QUIETLY, TRUE))
1473     {
1474         subshell_state = ACTIVE;
1475         return;
1476     }
1477 
1478     if (subshell_alive)
1479     {
1480         gboolean bPathNotEq;
1481 
1482         bPathNotEq = strcmp (subshell_cwd, pcwd) != 0;
1483 
1484         if (bPathNotEq && mc_global.shell->type == SHELL_TCSH)
1485         {
1486             char rp_subshell_cwd[PATH_MAX];
1487             char rp_current_panel_cwd[PATH_MAX];
1488             char *p_subshell_cwd, *p_current_panel_cwd;
1489 
1490             p_subshell_cwd = mc_realpath (subshell_cwd, rp_subshell_cwd);
1491             p_current_panel_cwd = mc_realpath (pcwd, rp_current_panel_cwd);
1492 
1493             if (p_subshell_cwd == NULL)
1494                 p_subshell_cwd = subshell_cwd;
1495             if (p_current_panel_cwd == NULL)
1496                 p_current_panel_cwd = pcwd;
1497             bPathNotEq = strcmp (p_subshell_cwd, p_current_panel_cwd) != 0;
1498         }
1499 
1500         if (bPathNotEq && !DIR_IS_DOT (pcwd))
1501         {
1502             char *cwd;
1503 
1504             cwd = vfs_path_to_str_flags (subshell_get_cwd (), 0, VPF_STRIP_PASSWORD);
1505             vfs_print_message (_ ("Warning: Cannot change to %s.\n"), cwd);
1506             g_free (cwd);
1507         }
1508     }
1509 
1510     // Really escape Zsh/Fish history
1511     if (mc_global.shell->type == SHELL_ZSH || mc_global.shell->type == SHELL_FISH)
1512     {
1513         /* Per Zsh documentation last command prefixed with space lingers in the internal history
1514          * until the next command is entered before it vanishes. To make it vanish right away,
1515          * type a space and press return.
1516          *
1517          * Fish shell now also provides the same behavior:
1518          * https://github.com/fish-shell/fish-shell/commit/9fdc4f903b8b421b18389a0f290d72cc88c128bb
1519          * */
1520         write_all (mc_global.tty.subshell_pty, " \n", 2);
1521         subshell_state = RUNNING_COMMAND;
1522         feed_subshell (QUIETLY, TRUE);
1523     }
1524 
1525     update_subshell_prompt = FALSE;
1526 
1527     g_free (pcwd);
1528     // Make sure that MC never stores the CWD in a silly format
1529     // like /usr////lib/../bin, or the strcmp() above will fail
1530 }
1531 
1532 /* --------------------------------------------------------------------------------------------- */
1533 /*** public functions ****************************************************************************/
1534 /* --------------------------------------------------------------------------------------------- */
1535 /**
1536  *  Fork the subshell, and set up many, many things.
1537  *
1538  *  Possibly modifies the global variables:
1539  *      subshell_type, subshell_alive, subshell_stopped, subshell_pid
1540  *      mc_global.tty.use_subshell - Is set to FALSE if we can't run the subshell
1541  *      quit - Can be set to SUBSHELL_EXIT by the SIGCHLD handler
1542  */
1543 
1544 void
1545 init_subshell (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1546 {
1547     // This must be remembered across calls to init_subshell()
1548     static char pty_name[BUF_SMALL];
1549 
1550     // Take the current (hopefully pristine) tty mode and make
1551     // a raw mode based on it now, before we do anything else with it
1552     init_raw_mode ();
1553 
1554     if (mc_global.tty.subshell_pty == 0)
1555     {  // First time through
1556         if (mc_global.shell->type == SHELL_NONE)
1557             return;
1558 
1559         // Create a pipe for receiving the subshell's CWD
1560 
1561         if (mc_global.shell->type == SHELL_TCSH)
1562         {
1563             g_snprintf (tcsh_fifo, sizeof (tcsh_fifo), "%s/mc.pipe.%d", mc_tmpdir (),
1564                         (int) getpid ());
1565             if (mkfifo (tcsh_fifo, 0600) == -1)
1566             {
1567                 fprintf (stderr, "mkfifo(%s) failed: %s\r\n", tcsh_fifo, unix_error_string (errno));
1568                 mc_global.tty.use_subshell = FALSE;
1569                 return;
1570             }
1571 
1572             // Opening the FIFO as O_RDONLY or O_WRONLY causes deadlock
1573 
1574             if ((subshell_pipe[READ] = open (tcsh_fifo, O_RDWR)) == -1
1575                 || (subshell_pipe[WRITE] = open (tcsh_fifo, O_RDWR)) == -1)
1576             {
1577                 fprintf (stderr, _ ("Cannot open named pipe %s\n"), tcsh_fifo);
1578                 perror (__FILE__ ": open");
1579                 mc_global.tty.use_subshell = FALSE;
1580                 return;
1581             }
1582         }
1583         else if (pipe (subshell_pipe) != 0)  // subshell_type is BASH, ASH_BUSYBOX, DASH or ZSH
1584         {
1585             perror (__FILE__ ": couldn't create pipe");
1586             mc_global.tty.use_subshell = FALSE;
1587             return;
1588         }
1589 
1590         if (mc_global.mc_run_mode == MC_RUN_FULL
1591             && (mc_global.shell->type == SHELL_BASH || mc_global.shell->type == SHELL_ZSH
1592                 || mc_global.shell->type == SHELL_FISH))
1593             use_persistent_buffer = TRUE;
1594 
1595         if (use_persistent_buffer && pipe (command_buffer_pipe) != 0)
1596         {
1597             perror (__FILE__ ": couldn't create pipe");
1598             mc_global.tty.use_subshell = FALSE;
1599             return;
1600         }
1601 
1602         // Open a pty for talking to the subshell; do this after
1603         // acquiring the pipes, so the latter have a higher chance
1604         // of getting a number lower than 10 so the "pwd >&%d" in
1605         // the mc-injected precmd does not cause a syntax error, a
1606         // ten-second sleep at startup, and other problems.
1607 
1608         // FIXME: We may need to open a fresh pty each time on SVR4
1609 
1610 #ifdef HAVE_OPENPTY
1611         if (openpty (&mc_global.tty.subshell_pty, &subshell_pty_slave, NULL, NULL, NULL))
1612         {
1613             fprintf (stderr, "Cannot open master and slave sides of pty: %s\n",
1614                      unix_error_string (errno));
1615             mc_global.tty.use_subshell = FALSE;
1616             return;
1617         }
1618 #else
1619         mc_global.tty.subshell_pty = pty_open_master (pty_name);
1620         if (mc_global.tty.subshell_pty == -1)
1621         {
1622             fprintf (stderr, "Cannot open master side of pty: %s\r\n", unix_error_string (errno));
1623             mc_global.tty.use_subshell = FALSE;
1624             return;
1625         }
1626 
1627         subshell_pty_slave = pty_open_slave (pty_name);
1628         if (subshell_pty_slave == -1)
1629         {
1630             fprintf (stderr, "Cannot open slave side of pty %s: %s\r\n", pty_name,
1631                      unix_error_string (errno));
1632             mc_global.tty.use_subshell = FALSE;
1633             return;
1634         }
1635 #endif
1636     }
1637 
1638     // Fork the subshell
1639 
1640     subshell_alive = TRUE;
1641     subshell_stopped = FALSE;
1642     subshell_pid = my_fork ();
1643 
1644     if (subshell_pid == -1)
1645     {
1646         fprintf (stderr, "Cannot spawn the subshell process: %s\r\n", unix_error_string (errno));
1647         // We exit here because, if the process table is full, the
1648         // other method of running user commands won't work either
1649         exit (EXIT_FAILURE);
1650     }
1651 
1652     if (subshell_pid == 0)
1653     {
1654         // We are in the child process
1655         init_subshell_child (pty_name);
1656     }
1657 
1658     {
1659         gchar *precmd;
1660 
1661         precmd = init_subshell_precmd ();
1662         write_all (mc_global.tty.subshell_pty, precmd, strlen (precmd));
1663         g_free (precmd);
1664     }
1665 
1666     // Wait until the subshell has started up and processed the command
1667     subshell_state = RUNNING_COMMAND;
1668     tty_enable_interrupt_key ();
1669     if (!feed_subshell (QUIETLY, TRUE))
1670         mc_global.tty.use_subshell = FALSE;
1671     tty_disable_interrupt_key ();
1672     if (!subshell_alive)
1673         mc_global.tty.use_subshell = FALSE;  // Subshell died instantly, so don't use it
1674 
1675     /* Try out the persistent command buffer feature. If it doesn't work the first time, we
1676      * assume there must be something wrong with the shell, and we turn persistent buffer off
1677      * for good. This will save the user the trouble of having to wait for the persistent
1678      * buffer function to time out every time they try to close the subshell. */
1679     if (use_persistent_buffer && !read_command_line_buffer (TRUE))
1680         use_persistent_buffer = FALSE;
1681 }
1682 
1683 /* --------------------------------------------------------------------------------------------- */
1684 
1685 int
1686 invoke_subshell (const char *command, int how, vfs_path_t **new_dir_vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1687 {
1688     // Make the MC terminal transparent
1689     tcsetattr (STDOUT_FILENO, TCSANOW, &raw_mode);
1690 
1691     // Make the subshell change to MC's working directory
1692     if (new_dir_vpath != NULL)
1693         do_subshell_chdir (subshell_get_cwd (), TRUE);
1694 
1695     if (command == NULL)  // The user has done "C-o" from MC
1696     {
1697         if (subshell_state == INACTIVE)
1698         {
1699             subshell_state = ACTIVE;
1700 
1701             // FIXME: possibly take out this hack; the user can re-play it by hitting C-hyphen a few
1702             // times!
1703             if (subshell_ready && mc_global.mc_run_mode == MC_RUN_FULL)
1704                 write_all (mc_global.tty.subshell_pty, " \b", 2);  // Hack to make prompt reappear
1705 
1706             if (use_persistent_buffer)
1707             {
1708                 const char *s = input_get_ctext (cmdline);
1709 
1710                 /* Check to make sure there are no non text characters in the command buffer,
1711                  * such as tab, or newline, as this could cause problems. */
1712                 for (size_t i = 0; i < cmdline->buffer->len; i++)
1713                     if ((unsigned char) s[i] < 32 || (unsigned char) s[i] == 127)
1714                         g_string_overwrite_len (cmdline->buffer, i, " ", 1);
1715 
1716                 // Write the command buffer to the subshell.
1717                 write_all (mc_global.tty.subshell_pty, s, cmdline->buffer->len);
1718 
1719                 // Put the cursor in the correct place in the subshell.
1720                 const int pos = (int) (str_length (s) - cmdline->point);
1721 
1722                 for (int i = 0; i < pos; i++)
1723                     write_all (mc_global.tty.subshell_pty, ESC_STR "[D", 3);
1724             }
1725         }
1726     }
1727     else  // MC has passed us a user command
1728     {
1729         // Before we write to the command prompt, we need to clear whatever
1730         // data is there, but only if we are using one of the shells that
1731         // doesn't support keeping command buffer contents, OR if there was
1732         // some sort of error.
1733         if (use_persistent_buffer)
1734             clear_cwd_pipe ();
1735         else
1736         {
1737             /* We don't need to call feed_subshell here if we are using fish, because of a
1738              * quirk in the behavior of that particular shell. */
1739             if (mc_global.shell->type != SHELL_FISH)
1740             {
1741                 write_all (mc_global.tty.subshell_pty, "\003", 1);
1742                 subshell_state = RUNNING_COMMAND;
1743                 feed_subshell (QUIETLY, FALSE);
1744             }
1745         }
1746 
1747         if (how == QUIETLY)
1748             write_all (mc_global.tty.subshell_pty, " ", 1);
1749         // FIXME: if command is long (>8KB ?) we go comma
1750         write_all (mc_global.tty.subshell_pty, command, strlen (command));
1751         write_all (mc_global.tty.subshell_pty, "\n", 1);
1752         subshell_state = RUNNING_COMMAND;
1753         subshell_ready = FALSE;
1754     }
1755 
1756     feed_subshell (how, FALSE);
1757 
1758     if (new_dir_vpath != NULL && subshell_alive)
1759     {
1760         const char *pcwd = vfs_translate_path (vfs_path_as_str (subshell_get_cwd ()));
1761 
1762         if (strcmp (subshell_cwd, pcwd) != 0)
1763             *new_dir_vpath =
1764                 vfs_path_from_str (subshell_cwd);  // Make MC change to the subshell's CWD
1765     }
1766 
1767     // Restart the subshell if it has died by SIGHUP, SIGQUIT, etc.
1768     while (!subshell_alive && subshell_get_mainloop_quit () == 0 && mc_global.tty.use_subshell)
1769         init_subshell ();
1770 
1771     return subshell_get_mainloop_quit ();
1772 }
1773 
1774 /* --------------------------------------------------------------------------------------------- */
1775 
1776 gboolean
1777 flush_subshell (int max_wait_length, int how)
     /* [previous][next][first][last][top][bottom][index][help]  */
1778 {
1779     int rc = 0;
1780     struct timeval timeleft = {
1781         .tv_sec = max_wait_length,
1782         .tv_usec = 0,
1783     };
1784     gboolean return_value = FALSE;
1785     fd_set tmp;
1786 
1787     FD_ZERO (&tmp);
1788     FD_SET (mc_global.tty.subshell_pty, &tmp);
1789 
1790     while (subshell_alive
1791            && (rc = select (mc_global.tty.subshell_pty + 1, &tmp, NULL, NULL, &timeleft)) != 0)
1792     {
1793         // Check for 'select' errors
1794         if (rc == -1)
1795         {
1796             if (errno == EINTR)
1797             {
1798                 if (tty_got_winch ())
1799                     tty_change_screen_size ();
1800 
1801                 continue;
1802             }
1803 
1804             fprintf (stderr, "select (FD_SETSIZE, &tmp...): %s\r\n", unix_error_string (errno));
1805             exit (EXIT_FAILURE);
1806         }
1807 
1808         return_value = TRUE;
1809         timeleft.tv_sec = 0;
1810         timeleft.tv_usec = 0;
1811 
1812         const ssize_t bytes = read (mc_global.tty.subshell_pty, pty_buffer, sizeof (pty_buffer));
1813 
1814         // FIXME: what about bytes <= 0?
1815         if (how == VISIBLY)
1816             write_all (STDOUT_FILENO, pty_buffer, (size_t) bytes);
1817     }
1818 
1819     return return_value;
1820 }
1821 
1822 /* --------------------------------------------------------------------------------------------- */
1823 
1824 gboolean
1825 read_subshell_prompt (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1826 {
1827     int rc = 0;
1828     ssize_t bytes = 0;
1829     struct timeval timeleft = {
1830         .tv_sec = 0,
1831         .tv_usec = 0,
1832     };
1833     gboolean got_new_prompt = FALSE;
1834     fd_set tmp;
1835 
1836     FD_ZERO (&tmp);
1837     FD_SET (mc_global.tty.subshell_pty, &tmp);
1838 
1839     while (subshell_alive
1840            && (rc = select (mc_global.tty.subshell_pty + 1, &tmp, NULL, NULL, &timeleft)) != 0)
1841     {
1842         // Check for 'select' errors
1843         if (rc == -1)
1844         {
1845             if (errno == EINTR)
1846             {
1847                 if (tty_got_winch ())
1848                     tty_change_screen_size ();
1849 
1850                 continue;
1851             }
1852 
1853             fprintf (stderr, "select (FD_SETSIZE, &tmp...): %s\r\n", unix_error_string (errno));
1854             exit (EXIT_FAILURE);
1855         }
1856 
1857         bytes = read (mc_global.tty.subshell_pty, pty_buffer, sizeof (pty_buffer));
1858 
1859         parse_subshell_prompt_string (pty_buffer, bytes);
1860         got_new_prompt = TRUE;
1861     }
1862 
1863     if (got_new_prompt)
1864         set_prompt_string ();
1865 
1866     return (rc != 0 || bytes != 0);
1867 }
1868 
1869 /* --------------------------------------------------------------------------------------------- */
1870 
1871 void
1872 do_update_prompt (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1873 {
1874     if (update_subshell_prompt)
1875     {
1876         if (subshell_prompt != NULL)
1877         {
1878             printf ("\r\n%s", subshell_prompt->str);
1879             fflush (stdout);
1880         }
1881         update_subshell_prompt = FALSE;
1882     }
1883 }
1884 
1885 /* --------------------------------------------------------------------------------------------- */
1886 
1887 gboolean
1888 exit_subshell (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1889 {
1890     gboolean subshell_quit = TRUE;
1891 
1892     if (subshell_state != INACTIVE && subshell_alive)
1893         subshell_quit = query_dialog (_ ("Warning"), _ ("The shell is still active. Quit anyway?"),
1894                                       D_NORMAL, 2, _ ("&Yes"), _ ("&No"))
1895             == 0;
1896 
1897     if (subshell_quit)
1898     {
1899         if (mc_global.shell->type == SHELL_TCSH)
1900         {
1901             if (unlink (tcsh_fifo) == -1)
1902                 fprintf (stderr, "Cannot remove named pipe %s: %s\r\n", tcsh_fifo,
1903                          unix_error_string (errno));
1904         }
1905 
1906         if (subshell_prompt != NULL)
1907         {
1908             g_string_free (subshell_prompt, TRUE);
1909             subshell_prompt = NULL;
1910         }
1911 
1912         if (subshell_prompt_temp_buffer != NULL)
1913         {
1914             g_string_free (subshell_prompt_temp_buffer, TRUE);
1915             subshell_prompt_temp_buffer = NULL;
1916         }
1917 
1918         pty_buffer[0] = '\0';
1919     }
1920 
1921     return subshell_quit;
1922 }
1923 
1924 /* --------------------------------------------------------------------------------------------- */
1925 
1926 void
1927 subshell_chdir (const vfs_path_t *vpath)
     /* [previous][next][first][last][top][bottom][index][help]  */
1928 {
1929     if (mc_global.tty.use_subshell && vfs_current_is_local ())
1930         do_subshell_chdir (vpath, FALSE);
1931 }
1932 
1933 /* --------------------------------------------------------------------------------------------- */
1934 
1935 void
1936 subshell_get_console_attributes (void)
     /* [previous][next][first][last][top][bottom][index][help]  */
1937 {
1938     // Get our current terminal modes
1939 
1940     if (tcgetattr (STDOUT_FILENO, &shell_mode))
1941     {
1942         fprintf (stderr, "Cannot get terminal settings: %s\r\n", unix_error_string (errno));
1943         mc_global.tty.use_subshell = FALSE;
1944     }
1945 }
1946 
1947 /* --------------------------------------------------------------------------------------------- */
1948 /**
1949  * Figure out whether the subshell has stopped, exited or been killed
1950  * Possibly modifies: 'subshell_alive', 'subshell_stopped' and 'quit' */
1951 
1952 void
1953 sigchld_handler (MC_UNUSED int sig)
     /* [previous][next][first][last][top][bottom][index][help]  */
1954 {
1955     int status;
1956     const pid_t pid = waitpid (subshell_pid, &status, WUNTRACED | WNOHANG);
1957 
1958     if (pid == subshell_pid)
1959     {
1960         // Figure out what has happened to the subshell
1961 
1962         if (WIFSTOPPED (status))
1963         {
1964             if (WSTOPSIG (status) == SIGSTOP)
1965             {
1966                 // The subshell has received a SIGSTOP signal
1967                 subshell_stopped = TRUE;
1968             }
1969             else
1970             {
1971                 // The user has suspended the subshell.  Revive it
1972                 kill (subshell_pid, SIGCONT);
1973             }
1974         }
1975         else
1976         {
1977             // The subshell has either exited normally or been killed
1978             subshell_alive = FALSE;
1979             delete_select_channel (mc_global.tty.subshell_pty);
1980             if (WIFEXITED (status) && WEXITSTATUS (status) != FORK_FAILURE)
1981             {
1982                 const int subshell_quit =
1983                     subshell_get_mainloop_quit () | SUBSHELL_EXIT;  // Exited normally
1984 
1985                 subshell_set_mainloop_quit (subshell_quit);
1986             }
1987         }
1988     }
1989 
1990     subshell_handle_cons_saver ();
1991 
1992     // If we got here, some other child exited; ignore it
1993 }
1994 
1995 /* --------------------------------------------------------------------------------------------- */

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