diff --git a/doc/man/mc.1.in b/doc/man/mc.1.in index aebc9ba1f0..b408b2a26f 100644 --- a/doc/man/mc.1.in +++ b/doc/man/mc.1.in @@ -2406,6 +2406,18 @@ and special keyboard maps in ~/.local/share/mc/inputrc (fallback ~/.inputrc). .B ash/dash users (BusyBox or Debian) may specify startup commands in ~/.local/share/mc/ashrc (fallback ~/.profile). .PP +.B ksh/oksh +users (PD ksh variants) may specify startup commands in ~/.local/share/mc/kshrc +(fallback +.I ENV +or ~/.profile). +.PP +.B mksh +users (MirBSD ksh) may specify startup commands in ~/.local/share/mc/mkshrc +(fallback +.I ENV +or ~/.mkshrc). +.PP .B zsh users may specify startup commands in ~/.local/share/mc/.zshrc (fallback ~/.zshrc). .PP diff --git a/lib/fileloc.h b/lib/fileloc.h index a075d030f9..fad3181bcb 100644 --- a/lib/fileloc.h +++ b/lib/fileloc.h @@ -53,6 +53,8 @@ #define MC_BASHRC_FILE "bashrc" #define MC_ZSHRC_FILE ".zshrc" #define MC_ASHRC_FILE "ashrc" +#define MC_KSHRC_FILE "kshrc" +#define MC_MKSHRC_FILE "mkshrc" #define MC_INPUTRC_FILE "inputrc" #define MC_CONFIG_FILE "ini" #define MC_EXT_FILE "mc.ext.ini" diff --git a/lib/mcconfig/paths.c b/lib/mcconfig/paths.c index 1baca54b8e..f33ea99330 100644 --- a/lib/mcconfig/paths.c +++ b/lib/mcconfig/paths.c @@ -75,6 +75,8 @@ static const struct { &mc_data_str, MC_SKINS_DIR }, { &mc_data_str, VFS_SHELL_PREFIX }, { &mc_data_str, MC_ASHRC_FILE }, + { &mc_data_str, MC_KSHRC_FILE }, + { &mc_data_str, MC_MKSHRC_FILE }, { &mc_data_str, MC_BASHRC_FILE }, { &mc_data_str, MC_INPUTRC_FILE }, { &mc_data_str, MC_ZSHRC_FILE }, diff --git a/lib/shell.c b/lib/shell.c index 730ded43ce..72411c8a58 100644 --- a/lib/shell.c +++ b/lib/shell.c @@ -80,6 +80,12 @@ mc_shell_get_installed_in_system (void) mc_shell->path = g_strdup ("/bin/tcsh"); else if (access ("/bin/csh", X_OK) == 0) mc_shell->path = g_strdup ("/bin/csh"); + else if (access ("/bin/ksh", X_OK) == 0) + mc_shell->path = g_strdup ("/bin/ksh"); + else if (access ("/bin/oksh", X_OK) == 0) + mc_shell->path = g_strdup ("/bin/oksh"); + else if (access ("/bin/mksh", X_OK) == 0) + mc_shell->path = g_strdup ("/bin/mksh"); /* 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 @@ -189,6 +195,20 @@ mc_shell_recognize_real_path (mc_shell_t *mc_shell) mc_shell->type = SHELL_ASH_BUSYBOX; mc_shell->name = mc_shell->path; } + else if (strstr (mc_shell->path, "/ksh") != NULL + || strstr (mc_shell->real_path, "/ksh") != NULL + || strstr (mc_shell->path, "/oksh") != NULL + || strstr (mc_shell->real_path, "/oksh") != NULL) + { + mc_shell->type = SHELL_KSH; + mc_shell->name = "ksh"; + } + else if (strstr (mc_shell->path, "/mksh") != NULL + || strstr (mc_shell->real_path, "/mksh") != NULL) + { + mc_shell->type = SHELL_MKSH; + mc_shell->name = "mksh"; + } else mc_shell->type = SHELL_NONE; } @@ -199,21 +219,34 @@ static void mc_shell_recognize_path (mc_shell_t *mc_shell) { /* If shell is not symlinked to busybox, it is safe to assume it is a real shell */ - if (strstr (mc_shell->path, "/bash") != NULL || getenv ("BASH") != NULL) + if (strstr (mc_shell->path, "/bash") != NULL || getenv ("BASH_VERSION") != NULL) { mc_shell->type = SHELL_BASH; mc_shell->name = "bash"; } - else if (strstr (mc_shell->path, "/sh") != NULL || getenv ("SH") != NULL) + else if (strstr (mc_shell->path, "/sh") != NULL) { mc_shell->type = SHELL_SH; mc_shell->name = "sh"; } - else if (strstr (mc_shell->path, "/ash") != NULL || getenv ("ASH") != NULL) + else if (strstr (mc_shell->path, "/ash") != NULL || getenv ("BB_ASH_VERSION") != NULL) { mc_shell->type = SHELL_ASH_BUSYBOX; mc_shell->name = "ash"; } + else if (strstr (mc_shell->path, "/ksh") != NULL + || strstr (mc_shell->path, "/oksh") != NULL + || (getenv ("KSH_VERSION") != NULL && strstr (getenv ("KSH_VERSION"), "PD KSH") != NULL)) + { + mc_shell->type = SHELL_KSH; + mc_shell->name = "ksh"; + } + else if (strstr (mc_shell->path, "/mksh") != NULL + || (getenv ("KSH_VERSION") != NULL && strstr (getenv ("KSH_VERSION"), "MIRBSD KSH") != NULL)) + { + mc_shell->type = SHELL_MKSH; + mc_shell->name = "mksh"; + } else mc_shell->type = SHELL_NONE; } diff --git a/lib/shell.h b/lib/shell.h index 7ba82dc078..bf5ada3adc 100644 --- a/lib/shell.h +++ b/lib/shell.h @@ -18,7 +18,9 @@ typedef enum SHELL_DASH, /* Debian variant of ash */ SHELL_TCSH, SHELL_ZSH, - SHELL_FISH + SHELL_FISH, + SHELL_KSH, /* Public Domain Korn shell (pdksh) and variants */ + SHELL_MKSH /* MirBSD Korn shell (mksh) */ } shell_type_t; /*** structures declarations (and typedefs of structures)*****************************************/ diff --git a/src/subshell/common.c b/src/subshell/common.c index 69b1f30e4e..7beeebc8b3 100644 --- a/src/subshell/common.c +++ b/src/subshell/common.c @@ -366,6 +366,45 @@ init_subshell_child (const char *pty_name) break; + case SHELL_KSH: + /* Do we have a custom init file ~/.local/share/mc/kshrc? */ + init_file = mc_config_get_full_path (MC_KSHRC_FILE); + + /* Otherwise use ~/.profile */ + if (!exist_file (init_file)) + { + g_free (init_file); + init_file = g_strdup (".profile"); + } + + /* Put init file to ENV variable used by ksh but only if it + * is not already set. */ + g_setenv ("ENV", init_file, FALSE); + + /* Make MC's special commands not show up in history */ + putenv ((char *) "HISTCONTROL=ignorespace"); + + break; + + case SHELL_MKSH: + /* Do we have a custom init file ~/.local/share/mc/mkshrc? */ + init_file = mc_config_get_full_path (MC_MKSHRC_FILE); + + /* Otherwise use ~/.mkshrc (default behavior of mksh) */ + if (!exist_file (init_file)) + { + g_free (init_file); + init_file = g_strdup (".mkshrc"); + } + + /* Put init file to ENV variable used by mksh but only if it + * is not already set. */ + g_setenv ("ENV", init_file, FALSE); + + /* Note mksh doesn't support HISTCONTROL. */ + + break; + case SHELL_ZSH: /* ZDOTDIR environment variable is the only way to point zsh * to an other rc file than the default. */ @@ -439,6 +478,8 @@ init_subshell_child (const char *pty_name) case SHELL_ASH_BUSYBOX: case SHELL_DASH: case SHELL_TCSH: + case SHELL_KSH: + case SHELL_MKSH: execl (mc_global.shell->path, mc_global.shell->path, (char *) NULL); break; @@ -1093,6 +1134,39 @@ pty_open_slave (const char *pty_name) static void init_subshell_precmd (char *precmd, size_t buff_size) { + /* Attention! Make sure that the buffer for precmd is big enough. */ + + /* Fallback precmd emulation that should work with virtually any shell. + * No real precmd functionality is required, no support for \x substitutions + * in PS1 is needed. For convenience, $HOME is replaced by ~ in PS1. + * + * 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", + */ + static const char *precmd_fallback = + " " /* Useful if the shell supports HISTCONTROL=ignorespace like functionality */ + "MC_PS1_SAVED=\"$PS1\"; " /* Save custom PS1 */ + "precmd() { " + "if [ ! \"${PWD##$HOME}\" ]; then " + "MC_PWD=\"~\"; " + "else " + "[ \"${PWD##$HOME/}\" = \"$PWD\" ] && MC_PWD=\"$PWD\" || MC_PWD=\"~/${PWD##$HOME/}\"; " + "fi; " + "echo \"${MC_PS1_SAVED:-$USER@$(hostname -s):$MC_PWD\\$ }\"; " + "pwd>&%d; " + "kill -STOP $$; " + "}; " + "PRECMD=precmd; " + "PS1='$($PRECMD)'\n"; + switch (mc_global.shell->type) { case SHELL_BASH: @@ -1126,44 +1200,31 @@ init_subshell_precmd (char *precmd, size_t buff_size) * 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", + * the falback version. */ + g_snprintf (precmd, buff_size, precmd_fallback, subshell_pipe[WRITE]); + break; + case SHELL_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]); + g_snprintf (precmd, buff_size, precmd_fallback, subshell_pipe[WRITE]); break; + case SHELL_MKSH: + /* mksh doesn't support \x placeholders in PS1 and needs precmd emulation via PS1 */ + g_snprintf (precmd, buff_size, precmd_fallback, subshell_pipe[WRITE]); + break; + + case SHELL_KSH: + /* pdksh based variants support \x placeholders but not any "precmd" functionality. */ + g_snprintf (precmd, buff_size, + " PS1='$(pwd>&%d; kill -STOP $$)'" + "\"${PS1:-\\u@\\h:\\w\\$ }\"\n", + subshell_pipe[WRITE]); + break; + case SHELL_ZSH: g_snprintf (precmd, buff_size, " mc_print_command_buffer () { printf \"%%s\\\\n\" \"$BUFFER\" >&%d; }\n" @@ -1209,11 +1270,11 @@ init_subshell_precmd (char *precmd, size_t buff_size) * Use following technique: * * printf(1) with format string containing a single conversion specifier, - * "b", and an argument which contains a copy of the string passed to + * "b", and an argument which contains a copy of the string passed to * subshell_name_quote() with all characters, except digits and letters, * replaced by the backslash-escape sequence \0nnn, where "nnn" is the * numeric value of the character converted to octal number. - * + * * cd "`printf '%b' 'ABC\0nnnDEF\0nnnXYZ'`" * * N.B.: Use single quotes for conversion specifier to work around