Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ticket #3748: add support for ksh in subshell #209

Closed
wants to merge 8 commits into from
Closed
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
12 changes: 12 additions & 0 deletions doc/man/mc.1.in
zyv marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions lib/fileloc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 2 additions & 0 deletions lib/mcconfig/paths.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down
39 changes: 36 additions & 3 deletions lib/shell.c
zyv marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
}
Expand All @@ -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;
}
Expand Down
4 changes: 3 additions & 1 deletion lib/shell.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)*****************************************/
Expand Down
127 changes: 94 additions & 33 deletions src/subshell/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
Loading