glibcompat: remove g_direct_equal shim
[midnight-commander.git] / lib / shell.c
blobb93486b0e3c09737ad22e3c3890f31ef48b4dc7e
1 /*
2 Provides a functions for working with shell.
4 Copyright (C) 2006-2024
5 Free Software Foundation, Inc.
7 Written by:
8 Slava Zanko <slavazanko@gmail.com>, 2015.
10 This file is part of the Midnight Commander.
12 The Midnight Commander is free software: you can redistribute it
13 and/or modify it under the terms of the GNU General Public License as
14 published by the Free Software Foundation, either version 3 of the License,
15 or (at your option) any later version.
17 The Midnight Commander is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 /** \file shell.c
27 * \brief Source: provides a functions for working with shell.
30 #include <config.h>
32 #include <pwd.h> /* for username in xterm title */
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <stdlib.h>
37 #include "global.h"
38 #include "util.h"
40 /*** global variables ****************************************************************************/
42 /*** file scope macro definitions ****************************************************************/
44 /*** file scope type declarations ****************************************************************/
46 /*** forward declarations (file scope functions) *************************************************/
48 /*** file scope variables ************************************************************************/
50 static char rp_shell[PATH_MAX];
52 /* --------------------------------------------------------------------------------------------- */
53 /*** file scope functions ************************************************************************/
54 /* --------------------------------------------------------------------------------------------- */
55 /**
56 * Get a system shell.
58 * @return newly allocated mc_shell_t object with shell name
61 static mc_shell_t *
62 mc_shell_get_installed_in_system (void)
64 mc_shell_t *mc_shell;
66 mc_shell = g_new0 (mc_shell_t, 1);
68 /* 3rd choice: look for existing shells supported as MC subshells. */
69 if (access ("/bin/bash", X_OK) == 0)
70 mc_shell->path = g_strdup ("/bin/bash");
71 else if (access ("/bin/ash", X_OK) == 0)
72 mc_shell->path = g_strdup ("/bin/ash");
73 else if (access ("/bin/dash", X_OK) == 0)
74 mc_shell->path = g_strdup ("/bin/dash");
75 else if (access ("/bin/busybox", X_OK) == 0)
76 mc_shell->path = g_strdup ("/bin/busybox");
77 else if (access ("/bin/zsh", X_OK) == 0)
78 mc_shell->path = g_strdup ("/bin/zsh");
79 else if (access ("/bin/tcsh", X_OK) == 0)
80 mc_shell->path = g_strdup ("/bin/tcsh");
81 else if (access ("/bin/csh", X_OK) == 0)
82 mc_shell->path = g_strdup ("/bin/csh");
83 /* No fish as fallback because it is so much different from other shells and
84 * in a way exotic (even though user-friendly by name) that we should not
85 * present it as a subshell without the user's explicit intention. We rather
86 * will not use a subshell but just a command line.
87 * else if (access("/bin/fish", X_OK) == 0)
88 * mc_global.tty.shell = g_strdup ("/bin/fish");
90 else
91 /* Fallback and last resort: system default shell */
92 mc_shell->path = g_strdup ("/bin/sh");
94 return mc_shell;
97 /* --------------------------------------------------------------------------------------------- */
99 static char *
100 mc_shell_get_name_env (void)
102 const char *shell_env;
103 char *shell_name = NULL;
105 shell_env = g_getenv ("SHELL");
106 if ((shell_env == NULL) || (shell_env[0] == '\0'))
108 /* 2nd choice: user login shell */
109 struct passwd *pwd;
111 pwd = getpwuid (geteuid ());
112 if (pwd != NULL)
113 shell_name = g_strdup (pwd->pw_shell);
115 else
116 /* 1st choice: SHELL environment variable */
117 shell_name = g_strdup (shell_env);
119 return shell_name;
122 /* --------------------------------------------------------------------------------------------- */
124 static mc_shell_t *
125 mc_shell_get_from_env (void)
127 mc_shell_t *mc_shell = NULL;
129 char *shell_name;
131 shell_name = mc_shell_get_name_env ();
133 if (shell_name != NULL)
135 mc_shell = g_new0 (mc_shell_t, 1);
136 mc_shell->path = shell_name;
139 return mc_shell;
142 /* --------------------------------------------------------------------------------------------- */
144 static void
145 mc_shell_recognize_real_path (mc_shell_t *mc_shell)
147 if (strstr (mc_shell->path, "/zsh") != NULL || strstr (mc_shell->real_path, "/zsh") != NULL
148 || getenv ("ZSH_VERSION") != NULL)
150 /* Also detects ksh symlinked to zsh */
151 mc_shell->type = SHELL_ZSH;
152 mc_shell->name = "zsh";
154 else if (strstr (mc_shell->path, "/tcsh") != NULL
155 || strstr (mc_shell->real_path, "/tcsh") != NULL)
157 /* Also detects csh symlinked to tcsh */
158 mc_shell->type = SHELL_TCSH;
159 mc_shell->name = "tcsh";
161 else if (strstr (mc_shell->path, "/csh") != NULL
162 || strstr (mc_shell->real_path, "/csh") != NULL)
164 mc_shell->type = SHELL_TCSH;
165 mc_shell->name = "csh";
167 else if (strstr (mc_shell->path, "/fish") != NULL
168 || strstr (mc_shell->real_path, "/fish") != NULL)
170 mc_shell->type = SHELL_FISH;
171 mc_shell->name = "fish";
173 else if (strstr (mc_shell->path, "/dash") != NULL
174 || strstr (mc_shell->real_path, "/dash") != NULL)
176 /* Debian ash (also found if symlinked to by ash/sh) */
177 mc_shell->type = SHELL_DASH;
178 mc_shell->name = "dash";
180 else if (strstr (mc_shell->real_path, "/busybox") != NULL)
182 /* If shell is symlinked to busybox, assume it is an ash, even though theoretically
183 * it could also be a hush (a mini shell for non-MMU systems deactivated by default).
184 * For simplicity's sake we assume that busybox always contains an ash, not a hush.
185 * On embedded platforms or on server systems, /bin/sh often points to busybox.
186 * Sometimes even bash is symlinked to busybox (CONFIG_FEATURE_BASH_IS_ASH option),
187 * so we need to check busybox symlinks *before* checking for the name "bash"
188 * in order to avoid that case. */
189 mc_shell->type = SHELL_ASH_BUSYBOX;
190 mc_shell->name = mc_shell->path;
192 else
193 mc_shell->type = SHELL_NONE;
196 /* --------------------------------------------------------------------------------------------- */
198 static void
199 mc_shell_recognize_path (mc_shell_t *mc_shell)
201 /* If shell is not symlinked to busybox, it is safe to assume it is a real shell */
202 if (strstr (mc_shell->path, "/bash") != NULL || getenv ("BASH") != NULL)
204 mc_shell->type = SHELL_BASH;
205 mc_shell->name = "bash";
207 else if (strstr (mc_shell->path, "/sh") != NULL || getenv ("SH") != NULL)
209 mc_shell->type = SHELL_SH;
210 mc_shell->name = "sh";
212 else if (strstr (mc_shell->path, "/ash") != NULL || getenv ("ASH") != NULL)
214 mc_shell->type = SHELL_ASH_BUSYBOX;
215 mc_shell->name = "ash";
217 else
218 mc_shell->type = SHELL_NONE;
221 /* --------------------------------------------------------------------------------------------- */
222 /*** public functions ****************************************************************************/
223 /* --------------------------------------------------------------------------------------------- */
225 void
226 mc_shell_init (void)
228 mc_shell_t *mc_shell;
230 mc_shell = mc_shell_get_from_env ();
232 if (mc_shell == NULL)
233 mc_shell = mc_shell_get_installed_in_system ();
235 mc_shell->real_path = mc_realpath (mc_shell->path, rp_shell);
237 /* Find out what type of shell we have. Also consider real paths (resolved symlinks)
238 * because e.g. csh might point to tcsh, ash to dash or busybox, sh to anything. */
240 if (mc_shell->real_path != NULL)
241 mc_shell_recognize_real_path (mc_shell);
243 if (mc_shell->type == SHELL_NONE)
244 mc_shell_recognize_path (mc_shell);
246 if (mc_shell->type == SHELL_NONE)
247 mc_global.tty.use_subshell = FALSE;
249 mc_global.shell = mc_shell;
252 /* --------------------------------------------------------------------------------------------- */
254 void
255 mc_shell_deinit (void)
257 if (mc_global.shell != NULL)
259 g_free (mc_global.shell->path);
260 MC_PTR_FREE (mc_global.shell);
264 /* --------------------------------------------------------------------------------------------- */