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

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