Ticket #2742: 040-ash_as_subshell.patch

File 040-ash_as_subshell.patch, 13.3 KB (added by kriegaex, 7 years ago)

New subshell types Busybox ash + Debian ash (dash) and some more enhancements plus fish chdir bugfix

  • src/main.h

     
    7272#endif /* !HAVE_CHARSET */ 
    7373 
    7474extern char *shell; 
     75extern char *shell_realpath; 
    7576extern const char *mc_prompt; 
    7677 
    7778/* index to record_macro_buf[], -1 if not recording a macro */ 
  • src/main.c

     
    101101 
    102102/* The user's shell */ 
    103103char *shell = NULL; 
     104char rp_shell[PATH_MAX]; 
     105char *shell_realpath = NULL; 
    104106 
    105107/* The prompt */ 
    106108const char *mc_prompt = NULL; 
     
    165167 
    166168    if ((shell_env == NULL) || (shell_env[0] == '\0')) 
    167169    { 
     170        /* 2nd choice: user login shell */ 
    168171        struct passwd *pwd; 
    169172        pwd = getpwuid (geteuid ()); 
    170173        if (pwd != NULL) 
    171174            shell = g_strdup (pwd->pw_shell); 
    172175    } 
    173176    else 
     177        /* 1st choice: SHELL environment variable */ 
    174178        shell = g_strdup (shell_env); 
    175179 
    176180    if ((shell == NULL) || (shell[0] == '\0')) 
    177181    { 
    178182        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"); 
    180206    } 
     207    shell_realpath = mc_realpath (shell, rp_shell); 
    181208} 
    182209 
    183210/* --------------------------------------------------------------------------------------------- */ 
  • src/subshell.c

     
    126126static enum 
    127127{ 
    128128    BASH, 
     129    ASH_BUSYBOX,    /* BusyBox default shell (ash) */ 
     130    DASH,           /* Debian variant of ash */ 
    129131    TCSH, 
    130132    ZSH, 
    131133    FISH 
     
    280282            init_file = g_strdup (".bashrc"); 
    281283        } 
    282284 
    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"); 
    285288 
    286289        /* Allow alternative readline settings for MC */ 
    287290        { 
     
    297300 
    298301        break; 
    299302 
    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 */ 
    301323    case TCSH: 
    302324    case ZSH: 
    303325    case FISH: 
     
    335357        execl (shell, "bash", "-rcfile", init_file, (char *) NULL); 
    336358        break; 
    337359 
    338     case TCSH: 
    339         execl (shell, "tcsh", (char *) NULL); 
    340         break; 
    341  
    342360    case ZSH: 
    343361        /* Use -g to exclude cmds beginning with space from history 
    344362         * and -Z to use the line editor on non-interactive term */ 
    345363        execl (shell, "zsh", "-Z", "-g", (char *) NULL); 
    346  
    347364        break; 
    348365 
     366    case ASH_BUSYBOX: 
     367    case DASH: 
     368    case TCSH: 
    349369    case FISH: 
    350         execl (shell, "fish", (char *) NULL); 
     370        execl (shell, shell, (char *) NULL); 
    351371        break; 
    352372    } 
    353373 
     
    769789{ 
    770790    /* This must be remembered across calls to init_subshell() */ 
    771791    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]; 
    773794 
    774795    switch (check_sid ()) 
    775796    { 
     
    785806    /* Take the current (hopefully pristine) tty mode and make */ 
    786807    /* a raw mode based on it now, before we do anything else with it */ 
    787808    init_raw_mode (); 
    788  
    789809    if (mc_global.tty.subshell_pty == 0) 
    790810    {                           /* 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. */ 
    792813 
    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 */ 
    794816            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 */ 
    798819            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        } 
    799836        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 */ 
    800838            subshell_type = BASH; 
    801         else if (strstr (shell, "/fish")) 
    802             subshell_type = FISH; 
    803839        else 
    804840        { 
    805841            mc_global.tty.use_subshell = FALSE; 
     
    850886                return; 
    851887            } 
    852888        } 
    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)) 
    854890        { 
    855891            perror (__FILE__ ": couldn't create pipe"); 
    856892            mc_global.tty.use_subshell = FALSE; 
     
    878914        init_subshell_child (pty_name); 
    879915    } 
    880916 
    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     */ 
    882923 
    883924    switch (subshell_type) 
    884925    { 
    885926    case BASH: 
    886927        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]); 
    888988        break; 
    889989 
    890990    case ZSH: 
    891991        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]); 
    893995        break; 
    894996 
    895997    case TCSH: 
    896998        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); 
    8991003        break; 
     1004 
    9001005    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         */ 
    9011017        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]); 
    9041024        break; 
    9051025 
    9061026    } 
     
    11071227        quote_cmd_start = "(printf \"%b\" '"; 
    11081228        quote_cmd_end = "')"; 
    11091229    } 
     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    } 
    11101237    else 
    11111238    { 
    11121239        quote_cmd_start = "\"`printf \"%b\" '";