rcaloras / bash-preexec

⚡ preexec and precmd functions for Bash just like Zsh.
MIT License
862 stars 94 forks source link

Functions called in opposite order in midnight commander subshell #140

Closed josef-kyrian closed 1 year ago

josef-kyrian commented 1 year ago

I have defined simple functions for test and added to .bashrc. They print command to run and it's return code

preexec() { echo "RUN: $1"; }
precmd() { echo "EXIT: $?"; }
source ~/.bash-preexec.sh

In bash shell it's working ok:

# ls /
RUN: ls /
bin  boot  cdrom  dev  etc  home  initrd.img  lib  lib32  lib64  lxc  media  mnt  opt  proc  root  run  sbin  snap  srv  sys  tmp  usr  var  vmlinuz  www
EXIT: 0

But in midnight commander subshell functions are called in opposite order

# ls /
bin  boot  cdrom  dev  etc  home  initrd.img  lib  lib32  lib64  lxc  media  mnt  opt  proc  root  run  sbin  snap  srv  sys  tmp  usr  var  vmlinuz  www
EXIT: 0
RUN: ls /

What's wrong? Thanks for help

akinomyoga commented 1 year ago

bash-preexec doesn't support the case where PROMPT_COMMAND is modified after the shell startup and the first prompt. However, Midnight Commander installs its hook by directly inputting PROMPT_COMMAND+=..., etc. as user commands from stdin after starting the shell.

More specifically, bash-preexec requires that all the other PROMPT_COMMAND hooks are enclosed between __bp_precmd_invoke_cmd and __bp_interactive_mode. This is normally achieved through the initial DEBUG trap (__bp_install) installed by bash-preexec. However, Midnight Commander injects codes after the shell startup and modifies PROMPT_COMMAND. This results in the following value of PROMPT_COMMAND:

$ declare -p PROMPT_COMMAND
declare -- PROMPT_COMMAND=$'__bp_precmd_invoke_cmd\n__bp_interactive_mode\npwd>&11;kill -STOP $$'

The substring pwd>&11;kill -STOP $$ is the hook installed by mc. We can see that the mc hook string is not enclosed within __bp_precmd_invoke_cmd and __bp_interactive_mode. Instead, this should look like the following:

PROMPT_COMMAND=$'__bp_precmd_invoke_cmd\npwd>&11;kill -STOP $$\n__bp_interactive_mode'

I'm not sure if there is a solution at the user side.

josef-kyrian commented 1 year ago

Thanks, this info is enough for me.

After patching subshell/common.c in the mc sources it works like a charm!

--- common.c    2023-03-06 13:14:44.536493089 +0100
+++ common.c.patched    2023-03-06 13:13:51.448906309 +0100
@@ -825,7 +825,7 @@
     {
     case SHELL_BASH:
         g_snprintf (precmd, buff_size,
-                    " PROMPT_COMMAND=${PROMPT_COMMAND:+$PROMPT_COMMAND\n}'pwd>&%d;kill -STOP $$'\n"
+                    " PROMPT_COMMAND='__bp_precmd_invoke_cmd\npwd>&%d;kill -STOP $$\n__bp_interactive_mode'\n"
                     "PS1='\\u@\\h:\\w\\$ '\n", subshell_pipe[WRITE]);
         break;