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. init_subshell
  18. invoke_subshell
  19. flush_subshell
  20. read_subshell_prompt
  21. do_update_prompt
  22. exit_subshell
  23. do_subshell_chdir
  24. subshell_get_console_attributes
  25. sigchld_handler

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

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