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

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