Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -727,6 +727,7 @@ tests/lib/vfs/mc.charsets
tests/lib/widget/Makefile
tests/src/Makefile
tests/src/filemanager/Makefile
tests/src/subshell/Makefile
tests/src/editor/Makefile
tests/src/editor/edit_complete_word_cmd_test_data.txt
tests/src/vfs/Makefile
Expand Down
79 changes: 69 additions & 10 deletions src/subshell/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,9 @@ typedef enum
* the current command buffer and the location of the cursor. */
#define SHELL_BUFFER_KEYBINDING "_"

/*** forward declarations (file scope functions) *************************************************/
/*** file scope functions ************************************************************************/

static int subshell_init_ready (int fd, void *info);

/*** file scope variables ************************************************************************/

Expand Down Expand Up @@ -1702,8 +1704,6 @@ do_subshell_chdir (const vfs_path_t *vpath, gboolean force, gboolean update_prom
void
init_subshell (void)
{
vfs_path_t *vfs_subshell_cwd;

// This must be remembered across calls to init_subshell()
static char pty_name[BUF_SMALL];

Expand Down Expand Up @@ -1823,7 +1823,19 @@ init_subshell (void)
g_free (precmd);
}

// Wait until the subshell has started up and processed the command
// Finish the handshake asynchronously from the main loop once the shell is ready
subshell_initialized = FALSE;
add_select_channel (subshell_pipe[READ], subshell_init_ready, NULL);
}

/* --------------------------------------------------------------------------------------------- */
// Second half of init_subshell(): consume the shell's first CWD report, probe the persistent
// command buffer and get a fresh prompt printed. Runs from the main loop as soon as the shell
// is ready, or right away from invoke_subshell() if the user got there first.

static void
subshell_finish_init (void)
{
subshell_state = RUNNING_COMMAND;
tty_enable_interrupt_key ();
if (!feed_subshell (QUIETLY, TRUE))
Expand All @@ -1841,19 +1853,63 @@ init_subshell (void)

/* Force an initial `cd` command, even if the subshell is already in the target directory.
* Testing the persistent command feature might have read and discarded the prompt. Just get
* a new one printed. See #4784#issuecomment-3435834623. */
vfs_subshell_cwd = vfs_path_from_str (subshell_cwd);
do_subshell_chdir (vfs_subshell_cwd, TRUE, FALSE);
vfs_path_free (vfs_subshell_cwd, TRUE);
* a new one printed. See #4784#issuecomment-3435834623.
*
* The panel may have moved on while the shell was starting up (subshell_chdir() is a no-op
* until we get here), so aim at the panel's directory rather than the shell's. */
do_subshell_chdir (subshell_get_cwd (), TRUE, FALSE);

subshell_initialized = TRUE;
}

/* --------------------------------------------------------------------------------------------- */
// Remove both of the subshell's file descriptors from the main event loop; safe even if
// unregistered.

static void
subshell_close_select_channels (void)
{
delete_select_channel (mc_global.tty.subshell_pty);
delete_select_channel (subshell_pipe[READ]);
}

/* --------------------------------------------------------------------------------------------- */
// Event-loop callback: fires once the shell has written its CWD, i.e. reached its first prompt.

static int
subshell_init_ready (int fd, void *info)
{
(void) info;

delete_select_channel (fd);

if (subshell_alive)
subshell_finish_init ();

return 0;
}

/* --------------------------------------------------------------------------------------------- */
// Finish the handshake synchronously if it isn't done yet. Called from
// invoke_subshell(), so Ctrl-O and running a command always get a ready subshell.

static void
subshell_ensure_initialized (void)
{
if (!subshell_initialized)
{
delete_select_channel (subshell_pipe[READ]);
subshell_finish_init ();
}
}

/* --------------------------------------------------------------------------------------------- */

int
invoke_subshell (const char *command, int how, vfs_path_t **new_dir_vpath)
{
subshell_ensure_initialized ();

// Make the MC terminal transparent
tcsetattr (STDOUT_FILENO, TCSANOW, &raw_mode);

Expand Down Expand Up @@ -2099,7 +2155,8 @@ exit_subshell (void)
void
subshell_chdir (const vfs_path_t *vpath)
{
if (mc_global.tty.use_subshell && vfs_current_is_local ())
// Before the handshake is done, subshell_finish_init() takes care of the panel's directory
if (mc_global.tty.use_subshell && subshell_initialized && vfs_current_is_local ())
do_subshell_chdir (vpath, FALSE, FALSE);
}

Expand Down Expand Up @@ -2149,7 +2206,9 @@ sigchld_handler (MC_UNUSED int sig)
{
// The subshell has either exited normally or been killed
subshell_alive = FALSE;
delete_select_channel (mc_global.tty.subshell_pty);
subshell_close_select_channels ();
if (!subshell_initialized)
mc_global.tty.use_subshell = FALSE; // Subshell died instantly, so don't use it
if (WIFEXITED (status) && WEXITSTATUS (status) != FORK_FAILURE)
{
const int subshell_quit =
Expand Down
4 changes: 4 additions & 0 deletions tests/src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ if USE_INTERNAL_EDIT
SUBDIRS += editor
endif

if ENABLE_SUBSHELL
SUBDIRS += subshell
endif

AM_CPPFLAGS = \
$(GLIB_CFLAGS) \
-I$(top_srcdir) \
Expand Down
30 changes: 30 additions & 0 deletions tests/src/subshell/Makefile.am
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
PACKAGE_STRING = "/src/subshell"

AM_CPPFLAGS = \
$(GLIB_CFLAGS) \
-I$(top_srcdir) \
-I$(top_srcdir)/lib/vfs \
@CHECK_CFLAGS@

LIBS = @CHECK_LIBS@ \
$(top_builddir)/src/libinternal.la \
$(top_builddir)/lib/libmc.la

if ENABLE_MCLIB
LIBS += $(GLIB_LIBS)
endif

EXTRA_DIST = \
subshell__common.c

TESTS = \
subshell__peek_subshell_switch_key \
subshell__async_init

check_PROGRAMS = $(TESTS)

subshell__peek_subshell_switch_key_SOURCES = \
subshell__peek_subshell_switch_key.c

subshell__async_init_SOURCES = \
subshell__async_init.c
Loading