diff options
Diffstat (limited to 'lib/shell.c')
-rw-r--r-- | lib/shell.c | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/lib/shell.c b/lib/shell.c new file mode 100644 index 0000000..0646171 --- /dev/null +++ b/lib/shell.c @@ -0,0 +1,264 @@ +/* + Provides a functions for working with shell. + + Copyright (C) 2006-2023 + Free Software Foundation, Inc. + + Written by: + Slava Zanko <slavazanko@gmail.com>, 2015. + + This file is part of the Midnight Commander. + + The Midnight Commander is free software: you can redistribute it + and/or modify it under the terms of the GNU General Public License as + published by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + The Midnight Commander is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +/** \file shell.c + * \brief Source: provides a functions for working with shell. + */ + +#include <config.h> + +#include <pwd.h> /* for username in xterm title */ +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> + +#include "global.h" +#include "util.h" + +/*** global variables ****************************************************************************/ + +/*** file scope macro definitions ****************************************************************/ + +/*** file scope type declarations ****************************************************************/ + +/*** forward declarations (file scope functions) *************************************************/ + +/*** file scope variables ************************************************************************/ + +static char rp_shell[PATH_MAX]; + +/* --------------------------------------------------------------------------------------------- */ +/*** file scope functions ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ +/** + * Get a system shell. + * + * @return newly allocated mc_shell_t object with shell name + */ + +static mc_shell_t * +mc_shell_get_installed_in_system (void) +{ + mc_shell_t *mc_shell; + + mc_shell = g_new0 (mc_shell_t, 1); + + /* 3rd choice: look for existing shells supported as MC subshells. */ + if (access ("/bin/bash", X_OK) == 0) + mc_shell->path = g_strdup ("/bin/bash"); + else if (access ("/bin/ash", X_OK) == 0) + mc_shell->path = g_strdup ("/bin/ash"); + else if (access ("/bin/dash", X_OK) == 0) + mc_shell->path = g_strdup ("/bin/dash"); + else if (access ("/bin/busybox", X_OK) == 0) + mc_shell->path = g_strdup ("/bin/busybox"); + else if (access ("/bin/zsh", X_OK) == 0) + mc_shell->path = g_strdup ("/bin/zsh"); + else if (access ("/bin/tcsh", X_OK) == 0) + mc_shell->path = g_strdup ("/bin/tcsh"); + else if (access ("/bin/csh", X_OK) == 0) + mc_shell->path = g_strdup ("/bin/csh"); + /* 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 */ + mc_shell->path = g_strdup ("/bin/sh"); + + return mc_shell; +} + +/* --------------------------------------------------------------------------------------------- */ + +static char * +mc_shell_get_name_env (void) +{ + const char *shell_env; + char *shell_name = NULL; + + shell_env = g_getenv ("SHELL"); + if ((shell_env == NULL) || (shell_env[0] == '\0')) + { + /* 2nd choice: user login shell */ + struct passwd *pwd; + + pwd = getpwuid (geteuid ()); + if (pwd != NULL) + shell_name = g_strdup (pwd->pw_shell); + } + else + /* 1st choice: SHELL environment variable */ + shell_name = g_strdup (shell_env); + + return shell_name; +} + +/* --------------------------------------------------------------------------------------------- */ + +static mc_shell_t * +mc_shell_get_from_env (void) +{ + mc_shell_t *mc_shell = NULL; + + char *shell_name; + + shell_name = mc_shell_get_name_env (); + + if (shell_name != NULL) + { + mc_shell = g_new0 (mc_shell_t, 1); + mc_shell->path = shell_name; + } + + return mc_shell; +} + +/* --------------------------------------------------------------------------------------------- */ + +static void +mc_shell_recognize_real_path (mc_shell_t * mc_shell) +{ + if (strstr (mc_shell->path, "/zsh") != NULL || strstr (mc_shell->real_path, "/zsh") != NULL + || getenv ("ZSH_VERSION") != NULL) + { + /* Also detects ksh symlinked to zsh */ + mc_shell->type = SHELL_ZSH; + mc_shell->name = "zsh"; + } + else if (strstr (mc_shell->path, "/tcsh") != NULL + || strstr (mc_shell->real_path, "/tcsh") != NULL) + { + /* Also detects csh symlinked to tcsh */ + mc_shell->type = SHELL_TCSH; + mc_shell->name = "tcsh"; + } + else if (strstr (mc_shell->path, "/csh") != NULL + || strstr (mc_shell->real_path, "/csh") != NULL) + { + mc_shell->type = SHELL_TCSH; + mc_shell->name = "csh"; + } + else if (strstr (mc_shell->path, "/fish") != NULL + || strstr (mc_shell->real_path, "/fish") != NULL) + { + mc_shell->type = SHELL_FISH; + mc_shell->name = "fish"; + } + else if (strstr (mc_shell->path, "/dash") != NULL + || strstr (mc_shell->real_path, "/dash") != NULL) + { + /* Debian ash (also found if symlinked to by ash/sh) */ + mc_shell->type = SHELL_DASH; + mc_shell->name = "dash"; + } + else if (strstr (mc_shell->real_path, "/busybox") != NULL) + { + /* 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. */ + mc_shell->type = SHELL_ASH_BUSYBOX; + mc_shell->name = mc_shell->path; + } + else + mc_shell->type = SHELL_NONE; +} + +/* --------------------------------------------------------------------------------------------- */ + +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) + { + mc_shell->type = SHELL_BASH; + mc_shell->name = "bash"; + } + else if (strstr (mc_shell->path, "/sh") != NULL || getenv ("SH") != NULL) + { + mc_shell->type = SHELL_SH; + mc_shell->name = "sh"; + } + else if (strstr (mc_shell->path, "/ash") != NULL || getenv ("ASH") != NULL) + { + mc_shell->type = SHELL_ASH_BUSYBOX; + mc_shell->name = "ash"; + } + else + mc_shell->type = SHELL_NONE; +} + +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ + +void +mc_shell_init (void) +{ + mc_shell_t *mc_shell; + + mc_shell = mc_shell_get_from_env (); + + if (mc_shell == NULL) + mc_shell = mc_shell_get_installed_in_system (); + + mc_shell->real_path = mc_realpath (mc_shell->path, rp_shell); + + /* 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 (mc_shell->real_path != NULL) + mc_shell_recognize_real_path (mc_shell); + + if (mc_shell->type == SHELL_NONE) + mc_shell_recognize_path (mc_shell); + + if (mc_shell->type == SHELL_NONE) + mc_global.tty.use_subshell = FALSE; + + mc_global.shell = mc_shell; +} + +/* --------------------------------------------------------------------------------------------- */ + +void +mc_shell_deinit (void) +{ + if (mc_global.shell != NULL) + { + g_free (mc_global.shell->path); + MC_PTR_FREE (mc_global.shell); + } +} + +/* --------------------------------------------------------------------------------------------- */ |