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, 6 years ago)
  • lib/global.h

    typedef struct 
    246246 
    247247        /* The user's shell */ 
    248248        char *shell; 
     249        char *shell_realpath; 
    249250 
    250251        /* This flag is set by xterm detection routine in function main() */ 
    251252        /* It is used by function view_other_cmd() */ 
  • lib/global.c

    mc_global_t mc_global = { 
    9595#endif /* !ENABLE_SUBSHELL */ 
    9696 
    9797        .shell = NULL, 
     98        .shell_realpath = NULL, 
    9899 
    99100        .xterm_flag = FALSE, 
    100101        .disable_x11 = FALSE, 
  • src/main.c

     
    8585/*** file scope type declarations ****************************************************************/ 
    8686 
    8787/*** file scope variables ************************************************************************/ 
     88char rp_shell[PATH_MAX]; 
    8889 
    8990/*** file scope functions ************************************************************************/ 
    9091/* --------------------------------------------------------------------------------------------- */ 
    OS_Setup (void) 
    129130    shell_env = getenv ("SHELL"); 
    130131    if ((shell_env == NULL) || (shell_env[0] == '\0')) 
    131132    { 
     133        /* 2nd choice: user login shell */ 
    132134        struct passwd *pwd; 
    133135 
    134136        pwd = getpwuid (geteuid ()); 
    OS_Setup (void) 
    136138            mc_global.tty.shell = g_strdup (pwd->pw_shell); 
    137139    } 
    138140    else 
     141        /* 1st choice: SHELL environment variable */ 
    139142        mc_global.tty.shell = g_strdup (shell_env); 
    140143 
    141144    if ((mc_global.tty.shell == NULL) || (mc_global.tty.shell[0] == '\0')) 
    142145    { 
    143146        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"); 
    145170    } 
     171    mc_global.tty.shell_realpath = mc_realpath (mc_global.tty.shell, rp_shell); 
    146172 
    147173    /* This is the directory, where MC was installed, on Unix this is DATADIR */ 
    148174    /* and can be overriden by the MC_DATADIR environment variable */ 
  • src/subshell.c

    enum 
    125125static enum 
    126126{ 
    127127    BASH, 
     128    ASH_BUSYBOX,    /* BusyBox default shell (ash) */ 
     129    DASH,           /* Debian variant of ash */ 
    128130    TCSH, 
    129131    ZSH, 
    130132    FISH 
    init_subshell_child (const char *pty_nam 
    281283            init_file = g_strdup (".bashrc"); 
    282284        } 
    283285 
    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"); 
    286289 
    287290        /* Allow alternative readline settings for MC */ 
    288291        { 
    init_subshell_child (const char *pty_nam 
    298301 
    299302        break; 
    300303 
    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 */ 
    302324    case TCSH: 
    303325    case ZSH: 
    304326    case FISH: 
    init_subshell_child (const char *pty_nam 
    336358        execl (mc_global.tty.shell, "bash", "-rcfile", init_file, (char *) NULL); 
    337359        break; 
    338360 
    339     case TCSH: 
    340         execl (mc_global.tty.shell, "tcsh", (char *) NULL); 
    341         break; 
    342  
    343361    case ZSH: 
    344362        /* Use -g to exclude cmds beginning with space from history 
    345363         * and -Z to use the line editor on non-interactive term */ 
    346364        execl (mc_global.tty.shell, "zsh", "-Z", "-g", (char *) NULL); 
    347  
    348365        break; 
    349366 
     367    case ASH_BUSYBOX: 
     368    case DASH: 
     369    case TCSH: 
    350370    case FISH: 
    351         execl (mc_global.tty.shell, "fish", (char *) NULL); 
     371        execl (mc_global.tty.shell, mc_global.tty.shell, (char *) NULL); 
    352372        break; 
    353373    } 
    354374 
    init_subshell (void) 
    774794{ 
    775795    /* This must be remembered across calls to init_subshell() */ 
    776796    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]; 
    778799 
    779800    switch (check_sid ()) 
    780801    { 
    init_subshell (void) 
    790811    /* Take the current (hopefully pristine) tty mode and make */ 
    791812    /* a raw mode based on it now, before we do anything else with it */ 
    792813    init_raw_mode (); 
    793  
    794814    if (mc_global.tty.subshell_pty == 0) 
    795815    {                           /* 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. */ 
    797818 
    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 */ 
    799821            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 */ 
    803824            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        } 
    804841        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 */ 
    805843            subshell_type = BASH; 
    806         else if (strstr (mc_global.tty.shell, "/fish")) 
    807             subshell_type = FISH; 
    808844        else 
    809845        { 
    810846            mc_global.tty.use_subshell = FALSE; 
    init_subshell (void) 
    855891                return; 
    856892            } 
    857893        } 
    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)) 
    859895        { 
    860896            perror (__FILE__ ": couldn't create pipe"); 
    861897            mc_global.tty.use_subshell = FALSE; 
    init_subshell (void) 
    883919        init_subshell_child (pty_name); 
    884920    } 
    885921 
    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     */ 
    887928 
    888929    switch (subshell_type) 
    889930    { 
    890931    case BASH: 
    891932        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]); 
    893993        break; 
    894994 
    895995    case ZSH: 
    896996        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]); 
    8981000        break; 
    8991001 
    9001002    case TCSH: 
    9011003        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); 
    9041008        break; 
     1009 
    9051010    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         */ 
    9061022        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]); 
    9091029        break; 
    9101030 
    9111031    } 
    subshell_name_quote (const char *s) 
    11091229        quote_cmd_start = "(printf \"%b\" '"; 
    11101230        quote_cmd_end = "')"; 
    11111231    } 
     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    } */ 
    11121239    else 
    11131240    { 
    11141241        quote_cmd_start = "\"`printf \"%b\" '";