summaryrefslogtreecommitdiffstats
path: root/lib/shell.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/shell.c')
-rw-r--r--lib/shell.c262
1 files changed, 262 insertions, 0 deletions
diff --git a/lib/shell.c b/lib/shell.c
new file mode 100644
index 0000000..41b58fb
--- /dev/null
+++ b/lib/shell.c
@@ -0,0 +1,262 @@
+/*
+ Provides a functions for working with shell.
+
+ Copyright (C) 2006-2022
+ 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 ****************************************************************/
+
+/*** 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);
+ }
+}
+
+/* --------------------------------------------------------------------------------------------- */