From 239f695d832e83c57ea8bd8e26f3406d4cb5cee8 Mon Sep 17 00:00:00 2001 From: Alexander Kriegisch Date: Mon, 2 Sep 2013 18:09:14 +0300 Subject: [PATCH 1/7] Ticket #2742: [Subshell] Support for ash + bugfixes for bash, fish Fix non-functional INPUTRC for bash (variable was unset and never used) Signed-off-by: Slava Zanko --- src/subshell.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/subshell.c b/src/subshell.c index 511dad1d6..6c08d66f7 100644 --- a/src/subshell.c +++ b/src/subshell.c @@ -209,6 +209,7 @@ static void init_subshell_child (const char *pty_name) { char *init_file = NULL; + char *putenv_str = NULL; pid_t mc_sid; (void) pty_name; @@ -257,8 +258,10 @@ init_subshell_child (const char *pty_name) switch (subshell_type) { case BASH: + /* Do we have a custom init file ~/.local/share/mc/bashrc? */ init_file = mc_config_get_full_path ("bashrc"); + /* Otherwise use ~/.bashrc */ if (access (init_file, R_OK) == -1) { g_free (init_file); @@ -273,9 +276,8 @@ init_subshell_child (const char *pty_name) char *input_file = mc_config_get_full_path ("inputrc"); if (access (input_file, R_OK) == 0) { - char *putenv_str = g_strconcat ("INPUTRC=", input_file, NULL); + putenv_str = g_strconcat ("INPUTRC=", input_file, NULL); putenv (putenv_str); - g_free (putenv_str); } g_free (input_file); } @@ -341,6 +343,7 @@ init_subshell_child (const char *pty_name) /* If we get this far, everything failed miserably */ g_free (init_file); + g_free (putenv_str); my_exit (FORK_FAILURE); } From 039fd561d26ccfb7b7ec6dd1059516a1ac54af57 Mon Sep 17 00:00:00 2001 From: Alexander Kriegisch Date: Mon, 2 Sep 2013 18:23:09 +0300 Subject: [PATCH 2/7] Make MC's special commands not show up in bash's history and also suppress consecutive identical commands. Signed-off-by: Slava Zanko --- src/subshell.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/subshell.c b/src/subshell.c index 6c08d66f7..a0c184ff2 100644 --- a/src/subshell.c +++ b/src/subshell.c @@ -268,8 +268,9 @@ init_subshell_child (const char *pty_name) init_file = g_strdup (".bashrc"); } - /* Make MC's special commands not show up in bash's history */ - putenv ((char *) "HISTCONTROL=ignorespace"); + /* Make MC's special commands not show up in bash's history and also suppress + * consecutive identical commands*/ + putenv ((char *) "HISTCONTROL=ignoreboth"); /* Allow alternative readline settings for MC */ { From f596c916a42a0868897b3314e557b0a82df37017 Mon Sep 17 00:00:00 2001 From: Alexander Kriegisch Date: Mon, 2 Sep 2013 18:50:34 +0300 Subject: [PATCH 3/7] New subshell types Busybox ash + Debian ash (dash) and some more enhancements plus fish chdir bugfix Signed-off-by: Slava Zanko --- lib/global.c | 1 + lib/global.h | 1 + src/main.c | 47 +++++++++++- src/subshell.c | 202 +++++++++++++++++++++++++++++++++++++++++-------- 4 files changed, 219 insertions(+), 32 deletions(-) diff --git a/lib/global.c b/lib/global.c index bbc4e360d..b52f91a71 100644 --- a/lib/global.c +++ b/lib/global.c @@ -97,6 +97,7 @@ mc_global_t mc_global = { #endif /* !ENABLE_SUBSHELL */ .shell = NULL, + .shell_realpath = NULL, .xterm_flag = FALSE, .disable_x11 = FALSE, diff --git a/lib/global.h b/lib/global.h index a13e61019..fc2c5432e 100644 --- a/lib/global.h +++ b/lib/global.h @@ -255,6 +255,7 @@ typedef struct /* The user's shell */ char *shell; + char *shell_realpath; /* This flag is set by xterm detection routine in function main() */ /* It is used by function view_other_cmd() */ diff --git a/src/main.c b/src/main.c index 29686ed70..68a45336b 100644 --- a/src/main.c +++ b/src/main.c @@ -87,6 +87,9 @@ /*** file scope variables ************************************************************************/ /*** file scope functions ************************************************************************/ + +static char rp_shell[PATH_MAX]; + /* --------------------------------------------------------------------------------------------- */ static void @@ -117,6 +120,44 @@ check_codeset (void) mc_global.utf8_display = str_isutf8 (current_system_codepage); } +/* --------------------------------------------------------------------------------------------- */ +/** + * Get a system shell. + * + * @return newly allocated string with shell name + */ + +static char * +mc_get_system_shell (void) +{ + char *sh_str; + /* 3rd choice: look for existing shells supported as MC subshells. */ + if (access ("/bin/bash", X_OK) == 0) + sh_str = g_strdup ("/bin/bash"); + else if (access ("/bin/ash", X_OK) == 0) + sh_str = g_strdup ("/bin/ash"); + else if (access ("/bin/dash", X_OK) == 0) + sh_str = g_strdup ("/bin/dash"); + else if (access ("/bin/busybox", X_OK) == 0) + sh_str = g_strdup ("/bin/busybox"); + else if (access ("/bin/zsh", X_OK) == 0) + sh_str = g_strdup ("/bin/zsh"); + else if (access ("/bin/tcsh", X_OK) == 0) + sh_str = g_strdup ("/bin/tcsh"); + /* No fish as fallback because it is so much different from other shells and + * in a way exotic (even though user-friendly by name) that we should not + * present it as a subshell without the user's explicit intention. We rather + * will not use a subshell but just a command line. + * else if (access("/bin/fish", X_OK) == 0) + * mc_global.tty.shell = g_strdup ("/bin/fish"); + */ + else + /* Fallback and last resort: system default shell */ + sh_str = g_strdup ("/bin/sh"); + + return sh_str; +} + /* --------------------------------------------------------------------------------------------- */ /** POSIX version. The only version we support. */ @@ -126,9 +167,11 @@ OS_Setup (void) const char *shell_env; const char *datadir_env; + shell_env = getenv ("SHELL"); if ((shell_env == NULL) || (shell_env[0] == '\0')) { + /* 2nd choice: user login shell */ struct passwd *pwd; pwd = getpwuid (geteuid ()); @@ -136,13 +179,15 @@ OS_Setup (void) mc_global.tty.shell = g_strdup (pwd->pw_shell); } else + /* 1st choice: SHELL environment variable */ mc_global.tty.shell = g_strdup (shell_env); if ((mc_global.tty.shell == NULL) || (mc_global.tty.shell[0] == '\0')) { g_free (mc_global.tty.shell); - mc_global.tty.shell = g_strdup ("/bin/sh"); + mc_global.tty.shell = mc_get_system_shell (); } + mc_global.tty.shell_realpath = mc_realpath (mc_global.tty.shell, rp_shell); /* This is the directory, where MC was installed, on Unix this is DATADIR */ /* and can be overriden by the MC_DATADIR environment variable */ diff --git a/src/subshell.c b/src/subshell.c index a0c184ff2..0cc8e7032 100644 --- a/src/subshell.c +++ b/src/subshell.c @@ -114,6 +114,8 @@ enum static enum { BASH, + ASH_BUSYBOX, /* BusyBox default shell (ash) */ + DASH, /* Debian variant of ash */ TCSH, ZSH, FISH @@ -285,7 +287,26 @@ init_subshell_child (const char *pty_name) break; - /* TODO: Find a way to pass initfile to TCSH and ZSH */ + case ASH_BUSYBOX: + case DASH: + /* Do we have a custom init file ~/.local/share/mc/ashrc? */ + init_file = mc_config_get_full_path ("ashrc"); + + /* Otherwise use ~/.profile */ + if (access (init_file, R_OK) == -1) + { + g_free (init_file); + init_file = g_strdup (".profile"); + } + + /* Put init file to ENV variable used by ash */ + putenv_str = g_strconcat ("ENV=", init_file, NULL); + putenv (putenv_str); + /* Do not use "g_free (putenv_str)" here, otherwise ENV will be undefined! */ + + break; + + /* TODO: Find a way to pass initfile to TCSH, ZSH and FISH */ case TCSH: case ZSH: case FISH: @@ -323,10 +344,6 @@ init_subshell_child (const char *pty_name) execl (mc_global.tty.shell, "bash", "-rcfile", init_file, (char *) NULL); break; - case TCSH: - execl (mc_global.tty.shell, "tcsh", (char *) NULL); - break; - case ZSH: /* Use -g to exclude cmds beginning with space from history * and -Z to use the line editor on non-interactive term */ @@ -334,8 +351,11 @@ init_subshell_child (const char *pty_name) break; + case ASH_BUSYBOX: + case DASH: + case TCSH: case FISH: - execl (mc_global.tty.shell, "fish", (char *) NULL); + execl (mc_global.tty.shell, mc_global.tty.shell, (char *) NULL); break; default: @@ -746,6 +766,59 @@ pty_open_slave (const char *pty_name) } #endif /* !HAVE_GRANTPT */ + +/* --------------------------------------------------------------------------------------------- */ +/** + * Get a subshell type and store in subshell_type variable + * + * @return TRUE if subtype was gotten, FALSE otherwise + */ + +static gboolean +init_subshell_type (void) +{ + gboolean result = TRUE; + + /* Find out what type of shell we have. Also consider real paths (resolved symlinks) + * because e.g. csh might point to tcsh, ash to dash or busybox, sh to anything. */ + + if (strstr (mc_global.tty.shell, "/zsh") || strstr (mc_global.tty.shell_realpath, "/zsh") + || getenv ("ZSH_VERSION")) + /* Also detects ksh symlinked to zsh */ + subshell_type = ZSH; + else if (strstr (mc_global.tty.shell, "/tcsh") + || strstr (mc_global.tty.shell_realpath, "/tcsh")) + /* Also detects csh symlinked to tcsh */ + subshell_type = TCSH; + else if (strstr (mc_global.tty.shell, "/fish") + || strstr (mc_global.tty.shell_realpath, "/fish")) + subshell_type = FISH; + else if (strstr (mc_global.tty.shell, "/dash") + || strstr (mc_global.tty.shell_realpath, "/dash")) + /* Debian ash (also found if symlinked to by ash/sh) */ + subshell_type = DASH; + else if (strstr (mc_global.tty.shell_realpath, "/busybox")) + { + /* If shell is symlinked to busybox, assume it is an ash, even though theoretically + * it could also be a hush (a mini shell for non-MMU systems deactivated by default). + * For simplicity's sake we assume that busybox always contains an ash, not a hush. + * On embedded platforms or on server systems, /bin/sh often points to busybox. + * Sometimes even bash is symlinked to busybox (CONFIG_FEATURE_BASH_IS_ASH option), + * so we need to check busybox symlinks *before* checking for the name "bash" + * in order to avoid that case. */ + subshell_type = ASH_BUSYBOX; + } + else if (strstr (mc_global.tty.shell, "/bash") || getenv ("BASH")) + /* If bash is not symlinked to busybox, it is safe to assume it is a real bash */ + subshell_type = BASH; + else + { + mc_global.tty.use_subshell = FALSE; + result = FALSE; + } + return result; +} + /* --------------------------------------------------------------------------------------------- */ /*** public functions ****************************************************************************/ /* --------------------------------------------------------------------------------------------- */ @@ -765,6 +838,7 @@ init_subshell (void) { /* This must be remembered across calls to init_subshell() */ static char pty_name[BUF_SMALL]; + /* Must be considerably longer than BUF_SMALL (128) to support fancy shell prompts */ char precmd[BUF_MEDIUM]; switch (check_sid ()) @@ -786,23 +860,8 @@ init_subshell (void) if (mc_global.tty.subshell_pty == 0) { /* First time through */ - /* Find out what type of shell we have */ - - if (strstr (mc_global.tty.shell, "/zsh") || getenv ("ZSH_VERSION")) - subshell_type = ZSH; - else if (strstr (mc_global.tty.shell, "/tcsh")) - subshell_type = TCSH; - else if (strstr (mc_global.tty.shell, "/csh")) - subshell_type = TCSH; - else if (strstr (mc_global.tty.shell, "/bash") || getenv ("BASH")) - subshell_type = BASH; - else if (strstr (mc_global.tty.shell, "/fish")) - subshell_type = FISH; - else - { - mc_global.tty.use_subshell = FALSE; + if (!init_subshell_type ()) return; - } /* Open a pty for talking to the subshell */ @@ -848,7 +907,7 @@ init_subshell (void) return; } } - else /* subshell_type is BASH or ZSH */ if (pipe (subshell_pipe)) + else if (pipe (subshell_pipe)) /* subshell_type is BASH, ASH_BUSYBOX, DASH or ZSH */ { perror (__FILE__ ": couldn't create pipe"); mc_global.tty.use_subshell = FALSE; @@ -876,33 +935,107 @@ init_subshell (void) init_subshell_child (pty_name); } - /* Set up 'precmd' or equivalent for reading the subshell's CWD */ + /* Set up `precmd' or equivalent for reading the subshell's CWD + * + * Attention! Never forget that these are *one-liners* even though the concatenated + * substrings contain line breaks and indentation for better understanding of the + * shell code. It is vital that each one-liner ends with a line feed character ("\n" ). + */ switch (subshell_type) { case BASH: g_snprintf (precmd, sizeof (precmd), - " PROMPT_COMMAND=${PROMPT_COMMAND:+$PROMPT_COMMAND\n}'pwd>&%d;kill -STOP $$'\n", - subshell_pipe[WRITE]); + " PROMPT_COMMAND=${PROMPT_COMMAND:+$PROMPT_COMMAND\n}'pwd>&%d;kill -STOP $$'\n" + "PS1='\\u@\\h:\\w\\$ '\n", subshell_pipe[WRITE]); + break; + + case ASH_BUSYBOX: + /* BusyBox ash needs a somewhat complicated precmd emulation via PS1, and it is vital + * that BB be built with active CONFIG_ASH_EXPAND_PRMT, but this is the default anyway. + * + * A: This leads to a stopped subshell (=frozen mc) if user calls "ash" command + * "PS1='$(pwd>&%d; kill -STOP $$)\\u@\\h:\\w\\$ '\n", + * + * B: This leads to "sh: precmd: not found" in sub-subshell if user calls "ash" command + * "precmd() { pwd>&%d; kill -STOP $$; }; " + * "PS1='$(precmd)\\u@\\h:\\w\\$ '\n", + * + * C: This works if user calls "ash" command because in sub-subshell + * PRECMD is unfedined, thus evaluated to empty string - no damage done. + * Attention: BusyBox must be built with FEATURE_EDITING_FANCY_PROMPT to + * permit \u, \w, \h, \$ escape sequences. Unfortunately this cannot be guaranteed, + * especially on embedded systems where people try to save space, so let's use + * the dash version below. It should work on virtually all systems. + * "precmd() { pwd>&%d; kill -STOP $$; }; " + * "PRECMD=precmd; " + * "PS1='$(eval $PRECMD)\\u@\\h:\\w\\$ '\n", + */ + case DASH: + /* Debian ash needs a precmd emulation via PS1, similar to BusyBox ash, + * but does not support escape sequences for user, host and cwd in prompt. + * Attention! Make sure that the buffer for precmd is big enough. + * + * We want to have a fancy dynamic prompt with user@host:cwd just like in the BusyBox + * examples above, but because replacing the home directory part of the path by "~" is + * complicated, it bloats the precmd to a size > BUF_SMALL (128). + * + * The following example is a little less fancy (home directory not replaced) + * and shows the basic workings of our prompt for easier understanding: + * + * "precmd() { " + * "echo \"$USER@$(hostname -s):$PWD\"; " + * "pwd>&%d; " + * "kill -STOP $$; " + * "}; " + * "PRECMD=precmd; " + * "PS1='$($PRECMD)$ '\n", + */ + g_snprintf (precmd, sizeof (precmd), + "precmd() { " + "if [ ! \"${PWD##$HOME}\" ]; then " + "MC_PWD=\"~\"; " + "else " + "[ \"${PWD##$HOME/}\" = \"$PWD\" ] && MC_PWD=\"$PWD\" || MC_PWD=\"~/${PWD##$HOME/}\"; " + "fi; " + "echo \"$USER@$(hostname -s):$MC_PWD\"; " + "pwd>&%d; " + "kill -STOP $$; " + "}; " "PRECMD=precmd; " "PS1='$($PRECMD)$ '\n", subshell_pipe[WRITE]); break; case ZSH: g_snprintf (precmd, sizeof (precmd), - " _mc_precmd(){ pwd>&%d;kill -STOP $$ }; precmd_functions+=(_mc_precmd)\n", - subshell_pipe[WRITE]); + " _mc_precmd(){ pwd>&%d;kill -STOP $$ }; precmd_functions+=(_mc_precmd)\n" + "PS1='%%n@%%m:%%~%%# '\n", subshell_pipe[WRITE]); break; case TCSH: g_snprintf (precmd, sizeof (precmd), - "set echo_style=both;" - "alias precmd 'echo $cwd:q >>%s;kill -STOP $$'\n", tcsh_fifo); + "set echo_style=both; " + "set prompt='%%n@%%m:%%~%%# '; " + "alias precmd 'echo $cwd:q >>%s; kill -STOP $$'\n", tcsh_fifo); break; + case FISH: /* Use fish_prompt_mc function for prompt, if not present then copy fish_prompt to it. */ + /* We also want a fancy user@host:cwd prompt here, but fish makes it very easy to also + * use colours, which is what we will do. But first here is a simpler, uncoloured version: + * "function fish_prompt; " + * "echo (whoami)@(hostname -s):(pwd)\\$\\ ; " + * "echo \"$PWD\">&%d; " + * "kill -STOP %%self; " + * "end\n", + * + * TODO: fish prompt is shown when panel is hidden (Ctrl-O), but not when it is visible. + * Find out how to fix this. + */ g_snprintf (precmd, sizeof (precmd), "if not functions -q fish_prompt_mc;" "functions -c fish_prompt fish_prompt_mc; end;" - "function fish_prompt; echo $PWD>&%d; fish_prompt_mc; kill -STOP %%self; end\n", + "function fish_prompt;" + "echo (whoami)@(hostname -s):(set_color $fish_color_cwd)(pwd)(set_color normal)\\$\\ ; " + "echo \"$PWD\">&%d; fish_prompt_mc; kill -STOP %%self; end\n", subshell_pipe[WRITE]); break; @@ -1112,6 +1245,13 @@ subshell_name_quote (const char *s) quote_cmd_start = "(printf \"%b\" '"; quote_cmd_end = "')"; } + /* TODO: When BusyBox printf is fixed, get rid of this "else if", see + http://lists.busybox.net/pipermail/busybox/2012-March/077460.html */ + /* else if (subshell_type == ASH_BUSYBOX) + { + quote_cmd_start = "\"`echo -en '"; + quote_cmd_end = "'`\""; + } */ else { quote_cmd_start = "\"`printf \"%b\" '"; From 09ed02ea853442ea45d87a2d209fbba3f81f82ca Mon Sep 17 00:00:00 2001 From: Alexander Kriegisch Date: Mon, 2 Sep 2013 19:00:53 +0300 Subject: [PATCH 4/7] Minor code changes. Signed-off-by: Slava Zanko --- lib/mcconfig/paths.c | 1 + tests/lib/mcconfig/user_configs_path.c | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/lib/mcconfig/paths.c b/lib/mcconfig/paths.c index 126a5ca8a..041bed2b9 100644 --- a/lib/mcconfig/paths.c +++ b/lib/mcconfig/paths.c @@ -84,6 +84,7 @@ static const struct /* data */ { "skins", &mc_data_str, MC_SKINS_SUBDIR}, { "fish", &mc_data_str, FISH_PREFIX}, + { "ashrc", &mc_data_str, "ashrc"}, { "bashrc", &mc_data_str, "bashrc"}, { "inputrc", &mc_data_str, "inputrc"}, { "extfs.d", &mc_data_str, MC_EXTFS_DIR}, diff --git a/tests/lib/mcconfig/user_configs_path.c b/tests/lib/mcconfig/user_configs_path.c index 3dbdb73be..f44b93016 100644 --- a/tests/lib/mcconfig/user_configs_path.c +++ b/tests/lib/mcconfig/user_configs_path.c @@ -136,6 +136,10 @@ static const struct test_user_config_paths_ds CONF_DATA, FISH_PREFIX }, + { /* 0. */ + CONF_DATA, + "ashrc" + }, { /* 0. */ CONF_DATA, "bashrc" From a6246373b47806a67af20bec3e97ae789a4599b0 Mon Sep 17 00:00:00 2001 From: Alexander Kriegisch Date: Mon, 2 Sep 2013 19:07:57 +0300 Subject: [PATCH 5/7] Improve subshell chapter in online help Signed-off-by: Slava Zanko --- doc/man/mc.1.in | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/doc/man/mc.1.in b/doc/man/mc.1.in index c7e8fbd05..b45cc51c0 100644 --- a/doc/man/mc.1.in +++ b/doc/man/mc.1.in @@ -2457,7 +2457,7 @@ is substituted. .\"NODE " The subshell support" .SH " The subshell support" The subshell support is a compile time option, that works with the -shells: bash, tcsh and zsh. +shells: bash, ash (BusyBox and Debian), tcsh, zsh and fish. .PP When the subshell code is activated the Midnight Commander will spawn a concurrent copy of your shell (the one defined in the @@ -2469,28 +2469,34 @@ subshell as if you had typed it. This also allows you to change the environment variables, use shell functions and define aliases that are valid until you quit the Midnight Commander. .PP -If you are using .B bash -you can specify startup -commands for the subshell in your ~/.local/share/mc/bashrc file and -special keyboard maps in the ~/.local/share/mc/inputrc file. -.B tcsh -users may specify startup commands in the ~/.local/share/mc/tcshrc file. +users may specify startup commands in ~/.local/share/mc/bashrc (fallback ~/.bashrc) +and special keyboard maps in ~/.local/share/mc/inputrc (fallback ~/.inputrc). +.PP +.B ash/dash +users (BusyBox or Debian) may specify startup commands in ~/.local/share/mc/ashrc (fallback ~/.profile). +.PP +.B tcsh, zsh, fish +users cannot specify mc-specific startup commands at present. They have to rely on +shell-specific startup files. .PP When the subshell code is used, you can suspend applications at any time with the sequence C\-o and jump back to the Midnight Commander, if you interrupt an application, you will not be able to run other external commands until you quit the application you interrupted. .PP -An extra added feature of using the subshell is that the prompt -displayed by the Midnight Commander is the same prompt that you are -currently using in your shell. +A special subshell feature is that Midnight Commander displays a dynamic prompt +like "user@host:current_path> " (with known problems for fish which displays the prompt in +full-screen mode (Ctrl-o), but not when the MC panels are visible). .PP The .\"LINK2" OPTIONS .\"OPTIONS" -section has more information on how you can control the subshell code. +section has more information on how you can control subshell usage (-U/-u). +Furthermore, to set a specific subshell different from your current SHELL variable or +login shell defined in /etc/passwd, you may call MC like this: +.B SHELL=/bin/myshell mc .\"NODE "Chmod" .SH "Chmod" The Chmod window is used to change the attribute bits in a group of From bf1ff69fb8066ced89fc8b41484dc00f917934fd Mon Sep 17 00:00:00 2001 From: Slava Zanko Date: Mon, 2 Sep 2013 19:38:29 +0300 Subject: [PATCH 6/7] Code refactoring Signed-off-by: Slava Zanko --- src/subshell.c | 116 +++++++++++++++++++++++++ tests/lib/mcconfig/user_configs_path.c | 48 +++++----- 2 files changed, 140 insertions(+), 24 deletions(-) diff --git a/src/subshell.c b/src/subshell.c index 0cc8e7032..8052d3293 100644 --- a/src/subshell.c +++ b/src/subshell.c @@ -819,6 +819,119 @@ init_subshell_type (void) return result; } +/* --------------------------------------------------------------------------------------------- */ +/** + * Set up `precmd' or equivalent for reading the subshell's CWD. + * + * Attention! Never forget that these are *one-liners* even though the concatenated + * substrings contain line breaks and indentation for better understanding of the + * shell code. It is vital that each one-liner ends with a line feed character ("\n" ). + * + * @return initialized pre-command string + */ + +static void +init_subshell_precmd (char *precmd, size_t buff_size) +{ + + switch (subshell_type) + { + case BASH: + g_snprintf (precmd, buff_size, + " PROMPT_COMMAND='pwd>&%d; kill -STOP $$'; " + "PS1='\\u@\\h:\\w\\$ '\n", subshell_pipe[WRITE]); + break; + + case ASH_BUSYBOX: + /* BusyBox ash needs a somewhat complicated precmd emulation via PS1, and it is vital + * that BB be built with active CONFIG_ASH_EXPAND_PRMT, but this is the default anyway. + * + * A: This leads to a stopped subshell (=frozen mc) if user calls "ash" command + * "PS1='$(pwd>&%d; kill -STOP $$)\\u@\\h:\\w\\$ '\n", + * + * B: This leads to "sh: precmd: not found" in sub-subshell if user calls "ash" command + * "precmd() { pwd>&%d; kill -STOP $$; }; " + * "PS1='$(precmd)\\u@\\h:\\w\\$ '\n", + * + * C: This works if user calls "ash" command because in sub-subshell + * PRECMD is unfedined, thus evaluated to empty string - no damage done. + * Attention: BusyBox must be built with FEATURE_EDITING_FANCY_PROMPT to + * permit \u, \w, \h, \$ escape sequences. Unfortunately this cannot be guaranteed, + * especially on embedded systems where people try to save space, so let's use + * the dash version below. It should work on virtually all systems. + * "precmd() { pwd>&%d; kill -STOP $$; }; " + * "PRECMD=precmd; " + * "PS1='$(eval $PRECMD)\\u@\\h:\\w\\$ '\n", + */ + case DASH: + /* Debian ash needs a precmd emulation via PS1, similar to BusyBox ash, + * but does not support escape sequences for user, host and cwd in prompt. + * Attention! Make sure that the buffer for precmd is big enough. + * + * We want to have a fancy dynamic prompt with user@host:cwd just like in the BusyBox + * examples above, but because replacing the home directory part of the path by "~" is + * complicated, it bloats the precmd to a size > BUF_SMALL (128). + * + * The following example is a little less fancy (home directory not replaced) + * and shows the basic workings of our prompt for easier understanding: + * + * "precmd() { " + * "echo \"$USER@$(hostname -s):$PWD\"; " + * "pwd>&%d; " + * "kill -STOP $$; " + * "}; " + * "PRECMD=precmd; " + * "PS1='$($PRECMD)$ '\n", + */ + g_snprintf (precmd, buff_size, + "precmd() { " + "if [ ! \"${PWD##$HOME}\" ]; then " + "MC_PWD=\"~\"; " + "else " + "[ \"${PWD##$HOME/}\" = \"$PWD\" ] && MC_PWD=\"$PWD\" || MC_PWD=\"~/${PWD##$HOME/}\"; " + "fi; " + "echo \"$USER@$(hostname -s):$MC_PWD\"; " + "pwd>&%d; " + "kill -STOP $$; " + "}; " "PRECMD=precmd; " "PS1='$($PRECMD)$ '\n", subshell_pipe[WRITE]); + break; + + case ZSH: + g_snprintf (precmd, buff_size, + " precmd() { pwd>&%d; kill -STOP $$; }; " + "PS1='%%n@%%m:%%~%%# '\n", subshell_pipe[WRITE]); + break; + + case TCSH: + g_snprintf (precmd, buff_size, + "set echo_style=both; " + "set prompt='%%n@%%m:%%~%%# '; " + "alias precmd 'echo $cwd:q >>%s; kill -STOP $$'\n", tcsh_fifo); + break; + + case FISH: + /* We also want a fancy user@host:cwd prompt here, but fish makes it very easy to also + * use colours, which is what we will do. But first here is a simpler, uncoloured version: + * "function fish_prompt; " + * "echo (whoami)@(hostname -s):(pwd)\\$\\ ; " + * "echo \"$PWD\">&%d; " + * "kill -STOP %%self; " + * "end\n", + * + * TODO: fish prompt is shown when panel is hidden (Ctrl-O), but not when it is visible. + * Find out how to fix this. + */ + g_snprintf (precmd, buff_size, + "function fish_prompt; " + "echo (whoami)@(hostname -s):(set_color $fish_color_cwd)(pwd)(set_color normal)\\$\\ ; " + "echo \"$PWD\">&%d; " "kill -STOP %%self; " "end\n", subshell_pipe[WRITE]); + break; + + default: + break; + } +} + /* --------------------------------------------------------------------------------------------- */ /*** public functions ****************************************************************************/ /* --------------------------------------------------------------------------------------------- */ @@ -935,6 +1048,8 @@ init_subshell (void) init_subshell_child (pty_name); } + init_subshell_precmd (precmd, BUF_MEDIUM); + /* Set up `precmd' or equivalent for reading the subshell's CWD * * Attention! Never forget that these are *one-liners* even though the concatenated @@ -1042,6 +1157,7 @@ init_subshell (void) default: break; } + write_all (mc_global.tty.subshell_pty, precmd, strlen (precmd)); /* Wait until the subshell has started up and processed the command */ diff --git a/tests/lib/mcconfig/user_configs_path.c b/tests/lib/mcconfig/user_configs_path.c index f44b93016..c957fa633 100644 --- a/tests/lib/mcconfig/user_configs_path.c +++ b/tests/lib/mcconfig/user_configs_path.c @@ -88,99 +88,99 @@ static const struct test_user_config_paths_ds CONF_MAIN, MC_CONFIG_FILE }, - { /* 0. */ + { /* 1. */ CONF_MAIN, MC_FHL_INI_FILE }, - { /* 0. */ + { /* 2. */ CONF_MAIN, MC_HOTLIST_FILE }, - { /* 0. */ + { /* 3. */ CONF_MAIN, GLOBAL_KEYMAP_FILE }, - { /* 0. */ + { /* 4. */ CONF_MAIN, MC_USERMENU_FILE }, - { /* 0. */ + { /* 5. */ CONF_MAIN, EDIT_SYNTAX_FILE }, - { /* 0. */ + { /* 6. */ CONF_MAIN, EDIT_HOME_MENU }, - { /* 0. */ + { /* 7. */ CONF_MAIN, EDIT_DIR PATH_SEP_STR "edit.indent.rc" }, - { /* 0. */ + { /* 8. */ CONF_MAIN, EDIT_DIR PATH_SEP_STR "edit.spell.rc" }, - { /* 0. */ + { /* 9. */ CONF_MAIN, MC_PANELS_FILE }, - { /* 0. */ + { /* 10. */ CONF_MAIN, MC_FILEBIND_FILE }, - { /* 0. */ + { /* 11. */ CONF_DATA, MC_SKINS_SUBDIR }, - { /* 0. */ + { /* 12. */ CONF_DATA, FISH_PREFIX }, - { /* 0. */ + { /* 13. */ CONF_DATA, "ashrc" }, - { /* 0. */ + { /* 14. */ CONF_DATA, "bashrc" }, - { /* 0. */ + { /* 15. */ CONF_DATA, "inputrc" }, - { /* 0. */ + { /* 16. */ CONF_DATA, MC_EXTFS_DIR }, - { /* 0. */ + { /* 17. */ CONF_DATA, MC_HISTORY_FILE }, - { /* 0. */ + { /* 18. */ CONF_DATA, MC_FILEPOS_FILE }, - { /* 0. */ + { /* 19. */ CONF_DATA, EDIT_CLIP_FILE }, - { /* 0. */ + { /* 20. */ CONF_DATA, MC_MACRO_FILE }, - { /* 0. */ + { /* 21. */ CONF_CACHE, "mc.log" }, - { /* 0. */ + { /* 22. */ CONF_CACHE, MC_TREESTORE_FILE }, - { /* 0. */ + { /* 23. */ CONF_CACHE, EDIT_TEMP_FILE }, - { /* 0. */ + { /* 24. */ CONF_CACHE, EDIT_BLOCK_FILE }, From 0d6d1d0d5c1fa45602acd03008198ea03d3e9952 Mon Sep 17 00:00:00 2001 From: Slava Zanko Date: Mon, 2 Sep 2013 20:24:59 +0300 Subject: [PATCH 7/7] revert changes related to BASH shell prompt Signed-off-by: Slava Zanko --- doc/man/mc.1.in | 6 +++++- src/subshell.c | 3 +-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/doc/man/mc.1.in b/doc/man/mc.1.in index b45cc51c0..2e79e33e9 100644 --- a/doc/man/mc.1.in +++ b/doc/man/mc.1.in @@ -2485,10 +2485,14 @@ time with the sequence C\-o and jump back to the Midnight Commander, if you interrupt an application, you will not be able to run other external commands until you quit the application you interrupted. .PP -A special subshell feature is that Midnight Commander displays a dynamic prompt +A special subshell feature (except Bash shell) is that Midnight Commander displays a dynamic prompt like "user@host:current_path> " (with known problems for fish which displays the prompt in full-screen mode (Ctrl-o), but not when the MC panels are visible). .PP +An extra added feature for Bash shell of using the subshell is that the prompt +displayed by the Midnight Commander is the same prompt that you are +currently using in your shell. +.PP The .\"LINK2" OPTIONS diff --git a/src/subshell.c b/src/subshell.c index 8052d3293..eaab1470e 100644 --- a/src/subshell.c +++ b/src/subshell.c @@ -838,8 +838,7 @@ init_subshell_precmd (char *precmd, size_t buff_size) { case BASH: g_snprintf (precmd, buff_size, - " PROMPT_COMMAND='pwd>&%d; kill -STOP $$'; " - "PS1='\\u@\\h:\\w\\$ '\n", subshell_pipe[WRITE]); + " PROMPT_COMMAND='pwd>&%d; kill -STOP $$';\n", subshell_pipe[WRITE]); break; case ASH_BUSYBOX: