Ticket #2742: 040-ash_as_subshell___mc-4.8.8.patch
File 040-ash_as_subshell___mc-4.8.8.patch, 14.5 KB (added by er13, 10 years ago) |
---|
-
lib/global.h
typedef struct 246 246 247 247 /* The user's shell */ 248 248 char *shell; 249 char *shell_realpath; 249 250 250 251 /* This flag is set by xterm detection routine in function main() */ 251 252 /* It is used by function view_other_cmd() */ -
lib/global.c
mc_global_t mc_global = { 95 95 #endif /* !ENABLE_SUBSHELL */ 96 96 97 97 .shell = NULL, 98 .shell_realpath = NULL, 98 99 99 100 .xterm_flag = FALSE, 100 101 .disable_x11 = FALSE, -
src/main.c
85 85 /*** file scope type declarations ****************************************************************/ 86 86 87 87 /*** file scope variables ************************************************************************/ 88 char rp_shell[PATH_MAX]; 88 89 89 90 /*** file scope functions ************************************************************************/ 90 91 /* --------------------------------------------------------------------------------------------- */ … … OS_Setup (void) 129 130 shell_env = getenv ("SHELL"); 130 131 if ((shell_env == NULL) || (shell_env[0] == '\0')) 131 132 { 133 /* 2nd choice: user login shell */ 132 134 struct passwd *pwd; 133 135 134 136 pwd = getpwuid (geteuid ()); … … OS_Setup (void) 136 138 mc_global.tty.shell = g_strdup (pwd->pw_shell); 137 139 } 138 140 else 141 /* 1st choice: SHELL environment variable */ 139 142 mc_global.tty.shell = g_strdup (shell_env); 140 143 141 144 if ((mc_global.tty.shell == NULL) || (mc_global.tty.shell[0] == '\0')) 142 145 { 143 146 g_free (mc_global.tty.shell); 144 mc_global.tty.shell = g_strdup ("/bin/sh"); 147 /* 3rd choice: look for existing shells supported as MC subshells. */ 148 if (access("/bin/bash", X_OK) == 0) 149 mc_global.tty.shell = g_strdup ("/bin/bash"); 150 else if (access("/bin/ash", X_OK) == 0) 151 mc_global.tty.shell = g_strdup ("/bin/ash"); 152 else if (access("/bin/dash", X_OK) == 0) 153 mc_global.tty.shell = g_strdup ("/bin/dash"); 154 else if (access("/bin/busybox", X_OK) == 0) 155 mc_global.tty.shell = g_strdup ("/bin/busybox"); 156 else if (access("/bin/zsh", X_OK) == 0) 157 mc_global.tty.shell = g_strdup ("/bin/zsh"); 158 else if (access("/bin/tcsh", X_OK) == 0) 159 mc_global.tty.shell = g_strdup ("/bin/tcsh"); 160 /* No fish as fallback because it is so much different from other shells and 161 * in a way exotic (even though user-friendly by name) that we should not 162 * present it as a subshell without the user's explicit intention. We rather 163 * will not use a subshell but just a command line. 164 * else if (access("/bin/fish", X_OK) == 0) 165 * mc_global.tty.shell = g_strdup ("/bin/fish"); 166 */ 167 else 168 /* Fallback and last resort: system default shell */ 169 mc_global.tty.shell = g_strdup ("/bin/sh"); 145 170 } 171 mc_global.tty.shell_realpath = mc_realpath (mc_global.tty.shell, rp_shell); 146 172 147 173 /* This is the directory, where MC was installed, on Unix this is DATADIR */ 148 174 /* and can be overriden by the MC_DATADIR environment variable */ -
src/subshell.c
enum 125 125 static enum 126 126 { 127 127 BASH, 128 ASH_BUSYBOX, /* BusyBox default shell (ash) */ 129 DASH, /* Debian variant of ash */ 128 130 TCSH, 129 131 ZSH, 130 132 FISH … … init_subshell_child (const char *pty_nam 281 283 init_file = g_strdup (".bashrc"); 282 284 } 283 285 284 /* Make MC's special commands not show up in bash's history */ 285 putenv ((char *) "HISTCONTROL=ignorespace"); 286 /* Make MC's special commands not show up in bash's history and also suppress 287 * consecutive identical commands*/ 288 putenv ((char *) "HISTCONTROL=ignoreboth"); 286 289 287 290 /* Allow alternative readline settings for MC */ 288 291 { … … init_subshell_child (const char *pty_nam 298 301 299 302 break; 300 303 301 /* TODO: Find a way to pass initfile to TCSH and ZSH */ 304 case ASH_BUSYBOX: 305 case DASH: 306 /* Do we have a custom init file ~/.local/share/mc/ashrc? */ 307 init_file = mc_config_get_full_path ("ashrc"); 308 309 /* Otherwise use ~/.profile */ 310 if (access (init_file, R_OK) == -1) 311 { 312 g_free (init_file); 313 init_file = g_strdup (".profile"); 314 } 315 316 /* Put init file to ENV variable used by ash */ 317 putenv_str = g_strconcat ("ENV=", init_file, NULL); 318 putenv (putenv_str); 319 /* Do not use "g_free (putenv_str)" here, otherwise ENV will be undefined! */ 320 321 break; 322 323 /* TODO: Find a way to pass initfile to TCSH, ZSH and FISH */ 302 324 case TCSH: 303 325 case ZSH: 304 326 case FISH: … … init_subshell_child (const char *pty_nam 336 358 execl (mc_global.tty.shell, "bash", "-rcfile", init_file, (char *) NULL); 337 359 break; 338 360 339 case TCSH:340 execl (mc_global.tty.shell, "tcsh", (char *) NULL);341 break;342 343 361 case ZSH: 344 362 /* Use -g to exclude cmds beginning with space from history 345 363 * and -Z to use the line editor on non-interactive term */ 346 364 execl (mc_global.tty.shell, "zsh", "-Z", "-g", (char *) NULL); 347 348 365 break; 349 366 367 case ASH_BUSYBOX: 368 case DASH: 369 case TCSH: 350 370 case FISH: 351 execl (mc_global.tty.shell, "fish", (char *) NULL);371 execl (mc_global.tty.shell, mc_global.tty.shell, (char *) NULL); 352 372 break; 353 373 } 354 374 … … init_subshell (void) 774 794 { 775 795 /* This must be remembered across calls to init_subshell() */ 776 796 static char pty_name[BUF_SMALL]; 777 char precmd[BUF_SMALL]; 797 /* Must be considerably longer than BUF_SMALL (128) to support fancy shell prompts */ 798 char precmd[300]; 778 799 779 800 switch (check_sid ()) 780 801 { … … init_subshell (void) 790 811 /* Take the current (hopefully pristine) tty mode and make */ 791 812 /* a raw mode based on it now, before we do anything else with it */ 792 813 init_raw_mode (); 793 794 814 if (mc_global.tty.subshell_pty == 0) 795 815 { /* First time through */ 796 /* Find out what type of shell we have */ 816 /* Find out what type of shell we have. Also consider real paths (resolved symlinks) 817 * because e.g. csh might point to tcsh, ash to dash or busybox, sh to anything. */ 797 818 798 if (strstr (mc_global.tty.shell, "/zsh") || getenv ("ZSH_VERSION")) 819 if (strstr (mc_global.tty.shell, "/zsh") || strstr (mc_global.tty.shell_realpath, "/zsh") || getenv ("ZSH_VERSION")) 820 /* Also detects ksh symlinked to zsh */ 799 821 subshell_type = ZSH; 800 else if (strstr (mc_global.tty.shell, "/tcsh")) 801 subshell_type = TCSH; 802 else if (strstr (mc_global.tty.shell, "/csh")) 822 else if (strstr (mc_global.tty.shell, "/tcsh") || strstr (mc_global.tty.shell_realpath, "/tcsh")) 823 /* Also detects csh symlinked to tcsh */ 803 824 subshell_type = TCSH; 825 else if (strstr (mc_global.tty.shell, "/fish") || strstr (mc_global.tty.shell_realpath, "/fish")) 826 subshell_type = FISH; 827 else if (strstr (mc_global.tty.shell, "/dash") || strstr (mc_global.tty.shell_realpath, "/dash")) 828 /* Debian ash (also found if symlinked to by ash/sh) */ 829 subshell_type = DASH; 830 else if (strstr (mc_global.tty.shell_realpath, "/busybox")) 831 { 832 /* If shell is symlinked to busybox, assume it is an ash, even though theoretically 833 * it could also be a hush (a mini shell for non-MMU systems deactivated by default). 834 * For simplicity's sake we assume that busybox always contains an ash, not a hush. 835 * On embedded platforms or on server systems, /bin/sh often points to busybox. 836 * Sometimes even bash is symlinked to busybox (CONFIG_FEATURE_BASH_IS_ASH option), 837 * so we need to check busybox symlinks *before* checking for the name "bash" 838 * in order to avoid that case. */ 839 subshell_type = ASH_BUSYBOX; 840 } 804 841 else if (strstr (mc_global.tty.shell, "/bash") || getenv ("BASH")) 842 /* If bash is not symlinked to busybox, it is safe to assume it is a real bash */ 805 843 subshell_type = BASH; 806 else if (strstr (mc_global.tty.shell, "/fish"))807 subshell_type = FISH;808 844 else 809 845 { 810 846 mc_global.tty.use_subshell = FALSE; … … init_subshell (void) 855 891 return; 856 892 } 857 893 } 858 else /* subshell_type is BASH or ZSH */ if (pipe (subshell_pipe))894 else /* subshell_type is BASH, ASH_BUSYBOX, DASH or ZSH */ if (pipe (subshell_pipe)) 859 895 { 860 896 perror (__FILE__ ": couldn't create pipe"); 861 897 mc_global.tty.use_subshell = FALSE; … … init_subshell (void) 883 919 init_subshell_child (pty_name); 884 920 } 885 921 886 /* Set up `precmd' or equivalent for reading the subshell's CWD */ 922 /* Set up `precmd' or equivalent for reading the subshell's CWD 923 * 924 * Attention! Never forget that these are *one-liners* even though the concatenated 925 * substrings contain line breaks and indentation for better understanding of the 926 * shell code. It is vital that each one-liner ends with a line feed character ("\n" ). 927 */ 887 928 888 929 switch (subshell_type) 889 930 { 890 931 case BASH: 891 932 g_snprintf (precmd, sizeof (precmd), 892 " PROMPT_COMMAND='pwd>&%d;kill -STOP $$'\n", subshell_pipe[WRITE]); 933 " PROMPT_COMMAND='pwd>&%d; kill -STOP $$'; " 934 "PS1='\\u@\\h:\\w\\$ '\n", 935 subshell_pipe[WRITE]); 936 break; 937 938 case ASH_BUSYBOX: 939 /* BusyBox ash needs a somewhat complicated precmd emulation via PS1, and it is vital 940 * that BB be built with active CONFIG_ASH_EXPAND_PRMT, but this is the default anyway. 941 * 942 * A: This leads to a stopped subshell (=frozen mc) if user calls "ash" command 943 * "PS1='$(pwd>&%d; kill -STOP $$)\\u@\\h:\\w\\$ '\n", 944 * 945 * B: This leads to "sh: precmd: not found" in sub-subshell if user calls "ash" command 946 * "precmd() { pwd>&%d; kill -STOP $$; }; " 947 * "PS1='$(precmd)\\u@\\h:\\w\\$ '\n", 948 * 949 * C: This works if user calls "ash" command because in sub-subshell 950 * PRECMD is unfedined, thus evaluated to empty string - no damage done. 951 * Attention: BusyBox must be built with FEATURE_EDITING_FANCY_PROMPT to 952 * permit \u, \w, \h, \$ escape sequences. Unfortunately this cannot be guaranteed, 953 * especially on embedded systems where people try to save space, so let's use 954 * the dash version below. It should work on virtually all systems. 955 * "precmd() { pwd>&%d; kill -STOP $$; }; " 956 * "PRECMD=precmd; " 957 * "PS1='$(eval $PRECMD)\\u@\\h:\\w\\$ '\n", 958 */ 959 case DASH: 960 /* Debian ash needs a precmd emulation via PS1, similar to BusyBox ash, 961 * but does not support escape sequences for user, host and cwd in prompt. 962 * Attention! Make sure that the buffer for precmd is big enough. 963 * 964 * We want to have a fancy dynamic prompt with user@host:cwd just like in the BusyBox 965 * examples above, but because replacing the home directory part of the path by "~" is 966 * complicated, it bloats the precmd to a size > BUF_SMALL (128). 967 * 968 * The following example is a little less fancy (home directory not replaced) 969 * and shows the basic workings of our prompt for easier understanding: 970 * 971 * "precmd() { " 972 * "echo \"$USER@$(hostname -s):$PWD\"; " 973 * "pwd>&%d; " 974 * "kill -STOP $$; " 975 * "}; " 976 * "PRECMD=precmd; " 977 * "PS1='$($PRECMD)$ '\n", 978 */ 979 g_snprintf (precmd, sizeof (precmd), 980 "precmd() { " 981 "if [ ! \"${PWD##$HOME}\" ]; then " 982 "MC_PWD=\"~\"; " 983 "else " 984 "[ \"${PWD##$HOME/}\" = \"$PWD\" ] && MC_PWD=\"$PWD\" || MC_PWD=\"~/${PWD##$HOME/}\"; " 985 "fi; " 986 "echo \"$USER@$(hostname -s):$MC_PWD\"; " 987 "pwd>&%d; " 988 "kill -STOP $$; " 989 "}; " 990 "PRECMD=precmd; " 991 "PS1='$($PRECMD)$ '\n", 992 subshell_pipe[WRITE]); 893 993 break; 894 994 895 995 case ZSH: 896 996 g_snprintf (precmd, sizeof (precmd), 897 " precmd(){ pwd>&%d;kill -STOP $$ }\n", subshell_pipe[WRITE]); 997 " precmd() { pwd>&%d; kill -STOP $$; }; " 998 "PS1='%%n@%%m:%%~%%# '\n", 999 subshell_pipe[WRITE]); 898 1000 break; 899 1001 900 1002 case TCSH: 901 1003 g_snprintf (precmd, sizeof (precmd), 902 "set echo_style=both;" 903 "alias precmd 'echo $cwd:q >>%s;kill -STOP $$'\n", tcsh_fifo); 1004 "set echo_style=both; " 1005 "set prompt='%%n@%%m:%%~%%# '; " 1006 "alias precmd 'echo $cwd:q >>%s; kill -STOP $$'\n", 1007 tcsh_fifo); 904 1008 break; 1009 905 1010 case FISH: 1011 /* We also want a fancy user@host:cwd prompt here, but fish makes it very easy to also 1012 * use colours, which is what we will do. But first here is a simpler, uncoloured version: 1013 * "function fish_prompt; " 1014 * "echo (whoami)@(hostname -s):(pwd)\\$\\ ; " 1015 * "echo \"$PWD\">&%d; " 1016 * "kill -STOP %%self; " 1017 * "end\n", 1018 * 1019 * TODO: fish prompt is shown when panel is hidden (Ctrl-O), but not when it is visible. 1020 * Find out how to fix this. 1021 */ 906 1022 g_snprintf (precmd, sizeof (precmd), 907 "function fish_prompt ; pwd>&%d;kill -STOP %%self; end\n", 908 subshell_pipe[WRITE]); 1023 "function fish_prompt; " 1024 "echo (whoami)@(hostname -s):(set_color $fish_color_cwd)(pwd)(set_color normal)\\$\\ ; " 1025 "echo \"$PWD\">&%d; " 1026 "kill -STOP %%self; " 1027 "end\n", 1028 subshell_pipe[WRITE]); 909 1029 break; 910 1030 911 1031 } … … subshell_name_quote (const char *s) 1109 1229 quote_cmd_start = "(printf \"%b\" '"; 1110 1230 quote_cmd_end = "')"; 1111 1231 } 1232 /* TODO: When BusyBox printf is fixed, get rid of this "else if", see 1233 http://lists.busybox.net/pipermail/busybox/2012-March/077460.html */ 1234 /* else if (subshell_type == ASH_BUSYBOX) 1235 { 1236 quote_cmd_start = "\"`echo -en '"; 1237 quote_cmd_end = "'`\""; 1238 } */ 1112 1239 else 1113 1240 { 1114 1241 quote_cmd_start = "\"`printf \"%b\" '";