Ticket #2742: 040-ash_as_subshell.patch
File 040-ash_as_subshell.patch, 13.3 KB (added by kriegaex, 12 years ago) |
---|
-
src/main.h
72 72 #endif /* !HAVE_CHARSET */ 73 73 74 74 extern char *shell; 75 extern char *shell_realpath; 75 76 extern const char *mc_prompt; 76 77 77 78 /* index to record_macro_buf[], -1 if not recording a macro */ -
src/main.c
101 101 102 102 /* The user's shell */ 103 103 char *shell = NULL; 104 char rp_shell[PATH_MAX]; 105 char *shell_realpath = NULL; 104 106 105 107 /* The prompt */ 106 108 const char *mc_prompt = NULL; … … 165 167 166 168 if ((shell_env == NULL) || (shell_env[0] == '\0')) 167 169 { 170 /* 2nd choice: user login shell */ 168 171 struct passwd *pwd; 169 172 pwd = getpwuid (geteuid ()); 170 173 if (pwd != NULL) 171 174 shell = g_strdup (pwd->pw_shell); 172 175 } 173 176 else 177 /* 1st choice: SHELL environment variable */ 174 178 shell = g_strdup (shell_env); 175 179 176 180 if ((shell == NULL) || (shell[0] == '\0')) 177 181 { 178 182 g_free (shell); 179 shell = g_strdup ("/bin/sh"); 183 /* 3rd choice: look for existing shells supported as MC subshells. */ 184 if (access("/bin/bash", X_OK) == 0) 185 shell = g_strdup ("/bin/bash"); 186 else if (access("/bin/ash", X_OK) == 0) 187 shell = g_strdup ("/bin/ash"); 188 else if (access("/bin/dash", X_OK) == 0) 189 shell = g_strdup ("/bin/dash"); 190 else if (access("/bin/busybox", X_OK) == 0) 191 shell = g_strdup ("/bin/busybox"); 192 else if (access("/bin/zsh", X_OK) == 0) 193 shell = g_strdup ("/bin/zsh"); 194 else if (access("/bin/tcsh", X_OK) == 0) 195 shell = g_strdup ("/bin/tcsh"); 196 /* No fish as fallback because it is so much different from other shells and 197 * in a way exotic (even though user-friendly by name) that we should not 198 * present it as a subshell without the user's explicit intention. We rather 199 * will not use a subshell but just a command line. 200 * else if (access("/bin/fish", X_OK) == 0) 201 * shell = g_strdup ("/bin/fish"); 202 */ 203 else 204 /* Fallback and last resort: system default shell */ 205 shell = g_strdup ("/bin/sh"); 180 206 } 207 shell_realpath = mc_realpath (shell, rp_shell); 181 208 } 182 209 183 210 /* --------------------------------------------------------------------------------------------- */ -
src/subshell.c
126 126 static enum 127 127 { 128 128 BASH, 129 ASH_BUSYBOX, /* BusyBox default shell (ash) */ 130 DASH, /* Debian variant of ash */ 129 131 TCSH, 130 132 ZSH, 131 133 FISH … … 280 282 init_file = g_strdup (".bashrc"); 281 283 } 282 284 283 /* Make MC's special commands not show up in bash's history */ 284 putenv ((char *) "HISTCONTROL=ignorespace"); 285 /* Make MC's special commands not show up in bash's history and also suppress 286 * consecutive identical commands*/ 287 putenv ((char *) "HISTCONTROL=ignoreboth"); 285 288 286 289 /* Allow alternative readline settings for MC */ 287 290 { … … 297 300 298 301 break; 299 302 300 /* TODO: Find a way to pass initfile to TCSH and ZSH */ 303 case ASH_BUSYBOX: 304 case DASH: 305 /* Do we have a custom init file ~/.local/share/mc/ashrc? */ 306 init_file = mc_config_get_full_path ("ashrc"); 307 308 /* Otherwise use ~/.profile */ 309 if (access (init_file, R_OK) == -1) 310 { 311 g_free (init_file); 312 init_file = g_strdup (".profile"); 313 } 314 315 /* Put init file to ENV variable used by ash */ 316 putenv_str = g_strconcat ("ENV=", init_file, NULL); 317 putenv (putenv_str); 318 /* Do not use "g_free (putenv_str)" here, otherwise ENV will be undefined! */ 319 320 break; 321 322 /* TODO: Find a way to pass initfile to TCSH, ZSH and FISH */ 301 323 case TCSH: 302 324 case ZSH: 303 325 case FISH: … … 335 357 execl (shell, "bash", "-rcfile", init_file, (char *) NULL); 336 358 break; 337 359 338 case TCSH:339 execl (shell, "tcsh", (char *) NULL);340 break;341 342 360 case ZSH: 343 361 /* Use -g to exclude cmds beginning with space from history 344 362 * and -Z to use the line editor on non-interactive term */ 345 363 execl (shell, "zsh", "-Z", "-g", (char *) NULL); 346 347 364 break; 348 365 366 case ASH_BUSYBOX: 367 case DASH: 368 case TCSH: 349 369 case FISH: 350 execl (shell, "fish", (char *) NULL);370 execl (shell, shell, (char *) NULL); 351 371 break; 352 372 } 353 373 … … 769 789 { 770 790 /* This must be remembered across calls to init_subshell() */ 771 791 static char pty_name[BUF_SMALL]; 772 char precmd[BUF_SMALL]; 792 /* Must be considerably longer than BUF_SMALL (128) to support fancy shell prompts */ 793 char precmd[300]; 773 794 774 795 switch (check_sid ()) 775 796 { … … 785 806 /* Take the current (hopefully pristine) tty mode and make */ 786 807 /* a raw mode based on it now, before we do anything else with it */ 787 808 init_raw_mode (); 788 789 809 if (mc_global.tty.subshell_pty == 0) 790 810 { /* First time through */ 791 /* Find out what type of shell we have */ 811 /* Find out what type of shell we have. Also consider real paths (resolved symlinks) 812 * because e.g. csh might point to tcsh, ash to dash or busybox, sh to anything. */ 792 813 793 if (strstr (shell, "/zsh") || getenv ("ZSH_VERSION")) 814 if (strstr (shell, "/zsh") || strstr (shell_realpath, "/zsh") || getenv ("ZSH_VERSION")) 815 /* Also detects ksh symlinked to zsh */ 794 816 subshell_type = ZSH; 795 else if (strstr (shell, "/tcsh")) 796 subshell_type = TCSH; 797 else if (strstr (shell, "/csh")) 817 else if (strstr (shell, "/tcsh") || strstr (shell_realpath, "/tcsh")) 818 /* Also detects csh symlinked to tcsh */ 798 819 subshell_type = TCSH; 820 else if (strstr (shell, "/fish") || strstr (shell_realpath, "/fish")) 821 subshell_type = FISH; 822 else if (strstr (shell, "/dash") || strstr (shell_realpath, "/dash")) 823 /* Debian ash (also found if symlinked to by ash/sh) */ 824 subshell_type = DASH; 825 else if (strstr (shell_realpath, "/busybox")) 826 { 827 /* If shell is symlinked to busybox, assume it is an ash, even though theoretically 828 * it could also be a hush (a mini shell for non-MMU systems deactivated by default). 829 * For simplicity's sake we assume that busybox always contains an ash, not a hush. 830 * On embedded platforms or on server systems, /bin/sh often points to busybox. 831 * Sometimes even bash is symlinked to busybox (CONFIG_FEATURE_BASH_IS_ASH option), 832 * so we need to check busybox symlinks *before* checking for the name "bash" 833 * in order to avoid that case. */ 834 subshell_type = ASH_BUSYBOX; 835 } 799 836 else if (strstr (shell, "/bash") || getenv ("BASH")) 837 /* If bash is not symlinked to busybox, it is safe to assume it is a real bash */ 800 838 subshell_type = BASH; 801 else if (strstr (shell, "/fish"))802 subshell_type = FISH;803 839 else 804 840 { 805 841 mc_global.tty.use_subshell = FALSE; … … 850 886 return; 851 887 } 852 888 } 853 else /* subshell_type is BASH or ZSH */ if (pipe (subshell_pipe))889 else /* subshell_type is BASH, ASH_BUSYBOX, DASH or ZSH */ if (pipe (subshell_pipe)) 854 890 { 855 891 perror (__FILE__ ": couldn't create pipe"); 856 892 mc_global.tty.use_subshell = FALSE; … … 878 914 init_subshell_child (pty_name); 879 915 } 880 916 881 /* Set up `precmd' or equivalent for reading the subshell's CWD */ 917 /* Set up `precmd' or equivalent for reading the subshell's CWD 918 * 919 * Attention! Never forget that these are *one-liners* even though the concatenated 920 * substrings contain line breaks and indentation for better understanding of the 921 * shell code. It is vital that each one-liner ends with a line feed character ("\n" ). 922 */ 882 923 883 924 switch (subshell_type) 884 925 { 885 926 case BASH: 886 927 g_snprintf (precmd, sizeof (precmd), 887 " PROMPT_COMMAND='pwd>&%d;kill -STOP $$'\n", subshell_pipe[WRITE]); 928 " PROMPT_COMMAND='pwd>&%d; kill -STOP $$'; " 929 "PS1='\\u@\\h:\\w\\$ '\n", 930 subshell_pipe[WRITE]); 931 break; 932 933 case ASH_BUSYBOX: 934 /* BusyBox ash needs a somewhat complicated precmd emulation via PS1, and it is vital 935 * that BB be built with active CONFIG_ASH_EXPAND_PRMT, but this is the default anyway. 936 * 937 * A: This leads to a stopped subshell (=frozen mc) if user calls "ash" command 938 * "PS1='$(pwd>&%d; kill -STOP $$)\\u@\\h:\\w\\$ '\n", 939 * 940 * B: This leads to "sh: precmd: not found" in sub-subshell if user calls "ash" command 941 * "precmd() { pwd>&%d; kill -STOP $$; }; " 942 * "PS1='$(precmd)\\u@\\h:\\w\\$ '\n", 943 * 944 * C: This works if user calls "ash" command because in sub-subshell 945 * PRECMD is unfedined, thus evaluated to empty string - no damage done. 946 * Attention: BusyBox must be built with FEATURE_EDITING_FANCY_PROMPT to 947 * permit \u, \w, \h, \$ escape sequences. Unfortunately this cannot be guaranteed, 948 * especially on embedded systems where people try to save space, so let's use 949 * the dash version below. It should work on virtually all systems. 950 * "precmd() { pwd>&%d; kill -STOP $$; }; " 951 * "PRECMD=precmd; " 952 * "PS1='$(eval $PRECMD)\\u@\\h:\\w\\$ '\n", 953 */ 954 case DASH: 955 /* Debian ash needs a precmd emulation via PS1, similar to BusyBox ash, 956 * but does not support escape sequences for user, host and cwd in prompt. 957 * Attention! Make sure that the buffer for precmd is big enough. 958 * 959 * We want to have a fancy dynamic prompt with user@host:cwd just like in the BusyBox 960 * examples above, but because replacing the home directory part of the path by "~" is 961 * complicated, it bloats the precmd to a size > BUF_SMALL (128). 962 * 963 * The following example is a little less fancy (home directory not replaced) 964 * and shows the basic workings of our prompt for easier understanding: 965 * 966 * "precmd() { " 967 * "echo \"$USER@$(hostname -s):$PWD\"; " 968 * "pwd>&%d; " 969 * "kill -STOP $$; " 970 * "}; " 971 * "PRECMD=precmd; " 972 * "PS1='$($PRECMD)$ '\n", 973 */ 974 g_snprintf (precmd, sizeof (precmd), 975 "precmd() { " 976 "if [ ! \"${PWD##$HOME}\" ]; then " 977 "MC_PWD=\"~\"; " 978 "else " 979 "[ \"${PWD##$HOME/}\" = \"$PWD\" ] && MC_PWD=\"$PWD\" || MC_PWD=\"~/${PWD##$HOME/}\"; " 980 "fi; " 981 "echo \"$USER@$(hostname -s):$MC_PWD\"; " 982 "pwd>&%d; " 983 "kill -STOP $$; " 984 "}; " 985 "PRECMD=precmd; " 986 "PS1='$($PRECMD)$ '\n", 987 subshell_pipe[WRITE]); 888 988 break; 889 989 890 990 case ZSH: 891 991 g_snprintf (precmd, sizeof (precmd), 892 " precmd(){ pwd>&%d;kill -STOP $$ }\n", subshell_pipe[WRITE]); 992 " precmd() { pwd>&%d; kill -STOP $$; }; " 993 "PS1='%%n@%%m:%%~%%# '\n", 994 subshell_pipe[WRITE]); 893 995 break; 894 996 895 997 case TCSH: 896 998 g_snprintf (precmd, sizeof (precmd), 897 "set echo_style=both;" 898 "alias precmd 'echo $cwd:q >>%s;kill -STOP $$'\n", tcsh_fifo); 999 "set echo_style=both; " 1000 "set prompt='%%n@%%m:%%~%%# '; " 1001 "alias precmd 'echo $cwd:q >>%s; kill -STOP $$'\n", 1002 tcsh_fifo); 899 1003 break; 1004 900 1005 case FISH: 1006 /* We also want a fancy user@host:cwd prompt here, but fish makes it very easy to also 1007 * use colours, which is what we will do. But first here is a simpler, uncoloured version: 1008 * "function fish_prompt; " 1009 * "echo (whoami)@(hostname -s):(pwd)\\$\\ ; " 1010 * "echo \"$PWD\">&%d; " 1011 * "kill -STOP %%self; " 1012 * "end\n", 1013 * 1014 * TODO: fish prompt is shown when panel is hidden (Ctrl-O), but not when it is visible. 1015 * Find out how to fix this. 1016 */ 901 1017 g_snprintf (precmd, sizeof (precmd), 902 "function fish_prompt ; pwd>&%d;kill -STOP %%self; end\n", 903 subshell_pipe[WRITE]); 1018 "function fish_prompt; " 1019 "echo (whoami)@(hostname -s):(set_color $fish_color_cwd)(pwd)(set_color normal)\\$\\ ; " 1020 "echo \"$PWD\">&%d; " 1021 "kill -STOP %%self; " 1022 "end\n", 1023 subshell_pipe[WRITE]); 904 1024 break; 905 1025 906 1026 } … … 1107 1227 quote_cmd_start = "(printf \"%b\" '"; 1108 1228 quote_cmd_end = "')"; 1109 1229 } 1230 /* TODO: When BusyBox printf is fixed, get rid of this "else if", see 1231 http://lists.busybox.net/pipermail/busybox/2012-March/077460.html */ 1232 else if (subshell_type == ASH_BUSYBOX) 1233 { 1234 quote_cmd_start = "\"`echo -en '"; 1235 quote_cmd_end = "'`\""; 1236 } 1110 1237 else 1111 1238 { 1112 1239 quote_cmd_start = "\"`printf \"%b\" '";