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

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