Ticket #3692: 3692-mc_subshell.patch

File 3692-mc_subshell.patch, 21.7 KB (added by alllexx88, 8 years ago)
  • lib/shell.c

    Author: alllexx88 <opotapenko@gmail.com>
    Date:   Sat Sep 24 09:41:02 2016 -0700
    
        More sophisticated shell type detection method
    
        * Make tests on shell binary instead of trying to guess shell type from
          path. Most supported shells set specific variables, hence by testing
          whether such variables are set, we can guess shell type in a more
          reliable way. This works with bash, zsh, tcsh and fish. For guessing
          dash or BusyBox ash (which are treated the same), we run a more peculiar
          test on whether shell supports expansion in PS1 as a prompt string. The
          latter test is also designed to diffirentiate a legacy pre 1.20 BusyBox
          ash, which allows to apply printf workaround in the case of such shell.
        * Remove chdir command from subshell initialization code, and use full
          paths for init_file instead. Changing dir only allows to use relative
          init_file paths, but can instead potentially lead to some problems, as
          previously noted in the comments; so by not doing this we add additional
          layer of protection against bugs.
        * Remove unneded SHELL_SH shell type, and 'name' mc_shell_t  field, since
          the latter was only being used as arg0 when initializing subshell, and
          it looks like all shells work fine regardless of arg0, except for zsh,
          (so we just leave arg0 as "zsh" for it), and we use path as arg0
        * Also add a little error verbosity in scope of detecting shell type and
          subshell initialization
    
    ---
     lib/shell.c           | 229 ++++++++++++++++++++++++++++++++++++--------------
     lib/shell.h           |  24 +++++-
     src/subshell/common.c |  58 ++++---------
     3 files changed, 200 insertions(+), 109 deletions(-)
    
    diff --git a/lib/shell.c b/lib/shell.c
    index 6f07cb0..c036c01 100644
    a b  
    3333#include <stdarg.h> 
    3434#include <stdio.h> 
    3535#include <stdlib.h> 
     36#include <sys/wait.h> 
     37#include <unistd.h> 
     38#include <sys/stat.h> 
     39#include <fcntl.h> 
    3640 
    3741#include "global.h" 
    3842#include "util.h" 
     
    4246 
    4347/*** file scope macro definitions ****************************************************************/ 
    4448 
     49#ifndef WIFEXITED 
     50#define WIFEXITED(stat_val) (((stat_val) & 255) == 0) 
     51#endif 
     52 
     53#ifndef WIFSIGNALED 
     54#define WIFSIGNALED(stat_val) ((((stat_val) & 255) != 255) && !WIFEXITED(stat_val)) 
     55#endif 
     56 
     57#ifndef WEXITSTATUS 
     58#define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) 
     59#endif 
     60 
    4561/*** file scope type declarations ****************************************************************/ 
    4662 
    4763/*** file scope variables ************************************************************************/ 
    mc_shell_get_from_env (void) 
    137153    return mc_shell; 
    138154} 
    139155 
     156/* --------------------------------------------------------------------------------------------- 
     157   This function returns TRUE for a shell if it sets a variable with respective name. We unset 
     158   environmental variable of the same name in the child fork to make sure it's not inherited. 
     159   We use three different commands for the respective shell syntaxes: bourne, C and fish. 
     160   If we test a shell with a wrong syntax, it returns error code, and function returns FALSE, 
     161   so in fact we test for syntax first, and only then for shell setting the variable. 
     162   --------------------------------------------------------------------------------------------- */ 
     163static gboolean 
     164mc_shell_internal_variable_set (mc_shell_t * mc_shell, const char * name, const shell_syntax_t shell_syntax) 
     165{ 
     166    pid_t cpid, w; 
     167    int status, devNull; 
     168    char *command; 
     169 
     170 
     171    if (shell_syntax == SHELL_SYNTAX_BOURNE) 
     172        command = g_strdup_printf ("if [ -z ${%s+x} ]; then exit 1; else exit 0; fi", name); 
     173    else if (shell_syntax == SHELL_SYNTAX_C) 
     174        command = g_strdup_printf ("if !( $?%s ) exit 1", name); 
     175    else /* shell_syntax == SHELL_SYNTAX_FISH */ 
     176        command = g_strdup_printf ("if set -q %s; exit 0; else; exit 1; end", name); 
     177 
     178 
     179    cpid = fork (); 
     180    if (cpid == -1) { 
     181        /* failed to fork */ 
     182        g_free (command); 
     183        return FALSE; 
     184    } 
     185 
     186   if (cpid == 0) {            /* Code executed by child */ 
     187        unsetenv (name); 
     188        /* silence stdout and stderr */ 
     189        devNull = open ("/dev/null", O_WRONLY); 
     190        dup2 (devNull, STDERR_FILENO); 
     191        dup2 (devNull, STDOUT_FILENO); 
     192        /* execute test command */ 
     193        execl (mc_shell->path, mc_shell->path, "-c", command, (char *) NULL); 
     194        /* execl failed */ 
     195        exit (1); 
     196   } else {                    /* Code executed by parent */ 
     197        g_free (command); 
     198        do { 
     199            w = waitpid (cpid, &status, WUNTRACED | WCONTINUED); 
     200            if (w == -1) { 
     201                /* waitpid error */ 
     202                return FALSE; 
     203            } 
     204        } while (!WIFEXITED(status) && !WIFSIGNALED(status)); 
     205 
     206        return (WIFEXITED(status)) && (WEXITSTATUS(status) == 0); 
     207    } 
     208} 
     209 
    140210/* --------------------------------------------------------------------------------------------- */ 
    141211 
    142212static void 
    143 mc_shell_recognize_real_path (mc_shell_t * mc_shell) 
     213mc_shell_recognize_from_internal_variable (mc_shell_t * mc_shell) 
    144214{ 
    145     if (strstr (mc_shell->path, "/zsh") != NULL || strstr (mc_shell->real_path, "/zsh") != NULL 
    146         || getenv ("ZSH_VERSION") != NULL) 
     215    /* These tests recognize bash, zsh, tcsh and fish by testing for 
     216       variables that only these shells set */ 
     217    if (mc_shell_internal_variable_set (mc_shell, "BASH", SHELL_SYNTAX_BOURNE)) 
    147218    { 
    148         /* Also detects ksh symlinked to zsh */ 
    149         mc_shell->type = SHELL_ZSH; 
    150         mc_shell->name = "zsh"; 
     219        mc_shell->type = SHELL_BASH; 
    151220    } 
    152     else if (strstr (mc_shell->path, "/tcsh") != NULL 
    153              || strstr (mc_shell->real_path, "/tcsh") != NULL) 
     221    else if (mc_shell_internal_variable_set (mc_shell, "ZSH_NAME", SHELL_SYNTAX_BOURNE)) 
    154222    { 
    155         /* Also detects csh symlinked to tcsh */ 
    156         mc_shell->type = SHELL_TCSH; 
    157         mc_shell->name = "tcsh"; 
     223        mc_shell->type = SHELL_ZSH; 
    158224    } 
    159     else if (strstr (mc_shell->path, "/csh") != NULL 
    160              || strstr (mc_shell->real_path, "/csh") != NULL) 
     225    else if (mc_shell_internal_variable_set (mc_shell, "tcsh", SHELL_SYNTAX_C)) 
    161226    { 
    162227        mc_shell->type = SHELL_TCSH; 
    163         mc_shell->name = "csh"; 
    164228    } 
    165     else if (strstr (mc_shell->path, "/fish") != NULL 
    166              || strstr (mc_shell->real_path, "/fish") != NULL) 
     229    else if (mc_shell_internal_variable_set (mc_shell, "fish_greeting", SHELL_SYNTAX_FISH)) 
    167230    { 
    168231        mc_shell->type = SHELL_FISH; 
    169         mc_shell->name = "fish"; 
    170     } 
    171     else if (strstr (mc_shell->path, "/dash") != NULL 
    172              || strstr (mc_shell->real_path, "/dash") != NULL) 
    173     { 
    174         /* Debian ash (also found if symlinked to by ash/sh) */ 
    175         mc_shell->type = SHELL_DASH; 
    176         mc_shell->name = "dash"; 
    177     } 
    178     else if (strstr (mc_shell->real_path, "/busybox") != NULL) 
    179     { 
    180         /* If shell is symlinked to busybox, assume it is an ash, even though theoretically 
    181          * it could also be a hush (a mini shell for non-MMU systems deactivated by default). 
    182          * For simplicity's sake we assume that busybox always contains an ash, not a hush. 
    183          * On embedded platforms or on server systems, /bin/sh often points to busybox. 
    184          * Sometimes even bash is symlinked to busybox (CONFIG_FEATURE_BASH_IS_ASH option), 
    185          * so we need to check busybox symlinks *before* checking for the name "bash" 
    186          * in order to avoid that case. */ 
    187         mc_shell->type = SHELL_ASH_BUSYBOX; 
    188         mc_shell->name = mc_shell->path; 
    189232    } 
    190233    else 
    191234        mc_shell->type = SHELL_NONE; 
    192235} 
    193236 
    194 /* --------------------------------------------------------------------------------------------- */ 
    195  
     237/* --------------------------------------------------------------------------------------------- 
     238   This function tests whether a shell treats PS1 as prompt string that is being expanded. 
     239   We test for an old BusyBox ash 4-digit octal codes bug in printf along the way too. 
     240   mc_shell->type will be set to: 
     241      SHELL_DASH: Test for PS1 expansion succeeds fully. This can mean dash, or BusyBox ash 
     242                  with CONFIG_ASH_EXPAND_PRMT enabled, or something other compatible 
     243      SHELL_ASH_BUSYBOX_LEGACY: PS1 is being expanded, but printf suffers from the 4-digit octal 
     244                                codes bug, so apply the printf workaround 
     245      SHELL_NONE: Test failed. Possible reasons: PS1 is not being treated as a prompt string, 
     246                  PS1 is not being expanded (no CONFIG_ASH_EXPAND_PRMT in BusyBox ash?), 
     247                  shell doesn't recognize syntax, failed to execute shell, etc. 
     248   --------------------------------------------------------------------------------------------- */ 
    196249static void 
    197 mc_shell_recognize_path (mc_shell_t * mc_shell) 
     250mc_shell_test_prompt_expansion (mc_shell_t * mc_shell) 
    198251{ 
    199     /* If shell is not symlinked to busybox, it is safe to assume it is a real shell */ 
    200     if (strstr (mc_shell->path, "/bash") != NULL || getenv ("BASH") != NULL) 
    201     { 
    202         mc_shell->type = SHELL_BASH; 
    203         mc_shell->name = "bash"; 
    204     } 
    205     else if (strstr (mc_shell->path, "/sh") != NULL || getenv ("SH") != NULL) 
    206     { 
    207         mc_shell->type = SHELL_SH; 
    208         mc_shell->name = "sh"; 
    209     } 
    210     else if (strstr (mc_shell->path, "/ash") != NULL || getenv ("ASH") != NULL) 
    211     { 
    212         mc_shell->type = SHELL_ASH_BUSYBOX; 
    213         mc_shell->name = "ash"; 
    214     } 
    215     else 
    216         mc_shell->type = SHELL_NONE; 
     252    pid_t cpid, w; 
     253    int status, devNull; 
     254    char *command; 
     255    gboolean firstrun = TRUE; 
     256 
     257 
     258    do { 
     259        /* Now this looks complicated, but the idea is simple: to check if 
     260           after setting PS1='$(printf "%b" "\\0057a\\0057\\n" >&3)' in interactive mode, 
     261           it gets evaluated, by capturing 3-rd descriptor output, and comparing it to the expected 
     262           output for dash / BusyBox ash ("/a/") during first run, and if it doesn't match - 
     263           test again to compare to BusyBox pre 1.20 broken printf output ("7a7") */ 
     264        if (firstrun) 
     265            command = g_strdup_printf ("str=$( (printf \"PS1='\"'$(printf \"%%%%b\" \"\\\\0057a\\\\0057\\\\n\" >&3)'\"'\\nexit 0\\n\" | %s -i 1>/dev/null) 3>&1); if [ \"$str\" = \"/a/\" ]; then exit 0; else exit 1; fi", mc_shell->path); 
     266        else 
     267            command = g_strdup_printf ("str=$( (printf \"PS1='\"'$(printf \"%%%%b\" \"\\\\0057a\\\\0057\\\\n\" >&3)'\"'\\nexit 0\\n\" | %s -i 1>/dev/null) 3>&1); if [ \"$str\" = \"7a7\" ]; then exit 0; else exit 1; fi", mc_shell->path); 
     268 
     269        cpid = fork (); 
     270        if (cpid == -1) { 
     271            /* failed to fork */ 
     272            g_free (command); 
     273            mc_shell->type = SHELL_NONE; 
     274            return; 
     275        } 
     276 
     277       if (cpid == 0) {            /* Code executed by child */ 
     278            /* silence stdout and stderr */ 
     279            devNull = open ("/dev/null", O_WRONLY); 
     280            dup2 (devNull, STDERR_FILENO); 
     281            dup2 (devNull, STDOUT_FILENO); 
     282            /* execute test command */ 
     283            execl (mc_shell->path, mc_shell->path, "-c", command, (char *) NULL); 
     284            /* execl failed */ 
     285            exit (1); 
     286       } else {                    /* Code executed by parent */ 
     287            g_free (command); 
     288            do { 
     289                w = waitpid (cpid, &status, WUNTRACED | WCONTINUED); 
     290                if (w == -1) { 
     291                    /* waitpid error */ 
     292                    mc_shell->type = SHELL_NONE; 
     293                    return; 
     294                } 
     295            } while (!WIFEXITED(status) && !WIFSIGNALED(status)); 
     296 
     297            if ((WIFEXITED(status)) && (WEXITSTATUS(status) == 0)) { 
     298                if (firstrun) 
     299                    mc_shell->type = SHELL_DASH; 
     300                else 
     301                    mc_shell->type = SHELL_ASH_BUSYBOX_LEGACY; 
     302                return; 
     303            } 
     304        } 
     305        firstrun = !firstrun; 
     306    } while (!firstrun); 
     307 
     308    /* both tests failed */ 
     309    mc_shell->type = SHELL_NONE; 
    217310} 
    218311 
    219312/* --------------------------------------------------------------------------------------------- */ 
    mc_shell_init (void) 
    232325 
    233326    mc_shell->real_path = mc_realpath (mc_shell->path, rp_shell); 
    234327 
    235     /* Find out what type of shell we have. Also consider real paths (resolved symlinks) 
    236      * because e.g. csh might point to tcsh, ash to dash or busybox, sh to anything. */ 
     328    /* Find out what type of shell we have. Use tests for specific variables that 
     329     * different shells set for most shell types. To recognize dash, or compatible 
     330     * BusyBox ash, we test whether prompt expansion works. */ 
    237331 
    238     if (mc_shell->real_path != NULL) 
    239         mc_shell_recognize_real_path (mc_shell); 
     332    if (mc_shell->real_path != NULL) { 
     333       mc_shell_recognize_from_internal_variable (mc_shell); 
    240334 
    241     if (mc_shell->type == SHELL_NONE) 
    242         mc_shell_recognize_path (mc_shell); 
     335       if (mc_shell->type == SHELL_NONE) 
     336           mc_shell_test_prompt_expansion (mc_shell); 
    243337 
    244     if (mc_shell->type == SHELL_NONE) 
    245         mc_global.tty.use_subshell = FALSE; 
     338       if (mc_shell->type == SHELL_NONE) 
     339           fprintf (stderr, __FILE__ ": failed to recognize shell \"%s\" as supported subshell. Supported shells are: bash, zsh, tcsh, fish, dash and BusyBox ash with enabled CONFIG_ASH_EXPAND_PRMT\r\n", mc_shell->path); 
     340   } else { 
     341       mc_shell->type = SHELL_NONE; 
     342       fprintf (stderr, __FILE__ ": wrong \"%s\" shell: No such file\r\n", mc_shell->path); 
     343   } 
    246344 
     345    mc_global.tty.use_subshell = mc_shell->type != SHELL_NONE; 
    247346    mc_global.shell = mc_shell; 
    248347} 
    249348 
  • lib/shell.h

    diff --git a/lib/shell.h b/lib/shell.h
    index 9afcd90..e48f52c 100644
    a b  
    77 
    88/*** typedefs(not structures) and defined constants **********************************************/ 
    99 
     10#define SHELL_TYPE_STRING(shell_type) (\ 
     11    shell_type == SHELL_NONE               ? "NONE"               : (\ 
     12    shell_type == SHELL_BASH               ? "BASH"               : (\ 
     13    shell_type == SHELL_DASH               ? "DASH"               : (\ 
     14    shell_type == SHELL_ASH_BUSYBOX_LEGACY ? "ASH_BUSYBOX_LEGACY" : (\ 
     15    shell_type == SHELL_TCSH               ? "TCSH"               : (\ 
     16    shell_type == SHELL_ZSH                ? "ZSH"                : (\ 
     17    shell_type == SHELL_FISH               ? "FISH"               : \ 
     18                                             "UNKNOWN"              \ 
     19))))))) 
     20 
    1021/*** enums ***************************************************************************************/ 
    1122 
    1223typedef enum 
    1324{ 
    1425    SHELL_NONE, 
    15     SHELL_SH, 
    1626    SHELL_BASH, 
    17     SHELL_ASH_BUSYBOX,          /* BusyBox default shell (ash) */ 
    18     SHELL_DASH,                 /* Debian variant of ash */ 
     27    SHELL_DASH,                 /* Debian variant of ash, or BusyBox ash shell with CONFIG_ASH_EXPAND_PRMT */ 
     28    SHELL_ASH_BUSYBOX_LEGACY,   /* Legacy BusyBox ash shell with broken printf */ 
    1929    SHELL_TCSH, 
    2030    SHELL_ZSH, 
    2131    SHELL_FISH 
    2232} shell_type_t; 
    2333 
     34typedef enum 
     35{ 
     36    SHELL_SYNTAX_BOURNE, 
     37    SHELL_SYNTAX_C, 
     38    SHELL_SYNTAX_FISH 
     39} shell_syntax_t; 
     40 
    2441/*** structures declarations (and typedefs of structures)*****************************************/ 
    2542 
    2643typedef struct 
    2744{ 
    2845    shell_type_t type; 
    29     const char *name; 
    3046    char *path; 
    3147    char *real_path; 
    3248} mc_shell_t; 
  • src/subshell/common.c

    diff --git a/src/subshell/common.c b/src/subshell/common.c
    index 98968fa..085b805 100644
    a b init_subshell_child (const char *pty_name) 
    249249    tty_resize (subshell_pty_slave); 
    250250 
    251251    /* Set up the subshell's environment and init file name */ 
    252  
    253     /* It simplifies things to change to our home directory here, */ 
    254     /* and the user's startup file may do a 'cd' command anyway   */ 
    255252    { 
    256253        int ret; 
    257254 
    258         ret = chdir (mc_config_get_home_dir ());        /* FIXME? What about when we re-run the subshell? */ 
    259255        (void) ret; 
    260256    } 
    261257 
    init_subshell_child (const char *pty_name) 
    279275        if (!exist_file (init_file)) 
    280276        { 
    281277            g_free (init_file); 
    282             init_file = g_strdup (".bashrc"); 
     278            init_file = g_build_filename (g_getenv ("HOME"), ".bashrc", (char *) NULL); 
    283279        } 
    284280 
    285281        /* Make MC's special commands not show up in bash's history and also suppress 
    init_subshell_child (const char *pty_name) 
    301297 
    302298        break; 
    303299 
    304     case SHELL_ASH_BUSYBOX: 
     300    case SHELL_ASH_BUSYBOX_LEGACY: 
    305301    case SHELL_DASH: 
    306302        /* Do we have a custom init file ~/.local/share/mc/ashrc? */ 
    307303        init_file = mc_config_get_full_path ("ashrc"); 
    init_subshell_child (const char *pty_name) 
    310306        if (!exist_file (init_file)) 
    311307        { 
    312308            g_free (init_file); 
    313             init_file = g_strdup (".profile"); 
     309            init_file = g_build_filename (g_getenv ("HOME"), ".profile", (char *) NULL); 
    314310        } 
    315311 
    316312        /* Put init file to ENV variable used by ash */ 
    init_subshell_child (const char *pty_name) 
    327323        break; 
    328324 
    329325    default: 
    330         fprintf (stderr, __FILE__ ": unimplemented subshell type %d\r\n", mc_global.shell->type); 
     326        fprintf (stderr, __FILE__ ": unimplemented subshell type %s\r\n", SHELL_TYPE_STRING(mc_global.shell->type)); 
    331327        my_exit (FORK_FAILURE); 
    332328    } 
    333329 
    init_subshell_child (const char *pty_name) 
    355351    switch (mc_global.shell->type) 
    356352    { 
    357353    case SHELL_BASH: 
    358         execl (mc_global.shell->path, "bash", "-rcfile", init_file, (char *) NULL); 
     354        execl (mc_global.shell->path, mc_global.shell->path, "-rcfile", init_file, (char *) NULL); 
    359355        break; 
    360356 
    361357    case SHELL_ZSH: 
    362358        /* Use -g to exclude cmds beginning with space from history 
    363359         * and -Z to use the line editor on non-interactive term */ 
    364360        execl (mc_global.shell->path, "zsh", "-Z", "-g", (char *) NULL); 
    365361 
    366362        break; 
    367363 
    368     case SHELL_ASH_BUSYBOX: 
     364    case SHELL_ASH_BUSYBOX_LEGACY: 
    369365    case SHELL_DASH: 
    370366    case SHELL_TCSH: 
    371367    case SHELL_FISH: 
    init_subshell_precmd (char *precmd, size_t buff_size) 
    801797                    "PS1='\\u@\\h:\\w\\$ '\n", subshell_pipe[WRITE]); 
    802798        break; 
    803799 
    804     case SHELL_ASH_BUSYBOX: 
    805         /* BusyBox ash needs a somewhat complicated precmd emulation via PS1, and it is vital 
    806          * that BB be built with active CONFIG_ASH_EXPAND_PRMT, but this is the default anyway. 
    807          * 
    808          * A: This leads to a stopped subshell (=frozen mc) if user calls "ash" command 
    809          *    "PS1='$(pwd>&%d; kill -STOP $$)\\u@\\h:\\w\\$ '\n", 
    810          * 
    811          * B: This leads to "sh: precmd: not found" in sub-subshell if user calls "ash" command 
    812          *    "precmd() { pwd>&%d; kill -STOP $$; }; " 
    813          *    "PS1='$(precmd)\\u@\\h:\\w\\$ '\n", 
    814          * 
    815          * C: This works if user calls "ash" command because in sub-subshell 
    816          *    PRECMD is unfedined, thus evaluated to empty string - no damage done. 
    817          *    Attention: BusyBox must be built with FEATURE_EDITING_FANCY_PROMPT to 
    818          *    permit \u, \w, \h, \$ escape sequences. Unfortunately this cannot be guaranteed, 
    819          *    especially on embedded systems where people try to save space, so let's use 
    820          *    the dash version below. It should work on virtually all systems. 
    821          *    "precmd() { pwd>&%d; kill -STOP $$; }; " 
    822          *    "PRECMD=precmd; " 
    823          *    "PS1='$(eval $PRECMD)\\u@\\h:\\w\\$ '\n", 
    824          */ 
     800    case SHELL_ASH_BUSYBOX_LEGACY: 
    825801    case SHELL_DASH: 
    826         /* Debian ash needs a precmd emulation via PS1, similar to BusyBox ash, 
    827          * but does not support escape sequences for user, host and cwd in prompt. 
     802        /* Debian ash needs a precmd emulation via PS1. 
    828803         * Attention! Make sure that the buffer for precmd is big enough. 
    829804         * 
    830          * We want to have a fancy dynamic prompt with user@host:cwd just like in the BusyBox 
    831          * examples above, but because replacing the home directory part of the path by "~" is 
     805         * We want to have a fancy dynamic prompt with user@host:cwd, 
     806         * but because replacing the home directory part of the path by "~" is 
    832807         * complicated, it bloats the precmd to a size > BUF_SMALL (128). 
    833808         * 
    834809         * The following example is a little less fancy (home directory not replaced) 
    subshell_name_quote (const char *s) 
    924899        quote_cmd_start = "(printf \"%b\" '"; 
    925900        quote_cmd_end = "')"; 
    926901    } 
    927     /* TODO: When BusyBox printf is fixed, get rid of this "else if", see 
    928        http://lists.busybox.net/pipermail/busybox/2012-March/077460.html */ 
    929     /* else if (subshell_type == ASH_BUSYBOX) 
     902    /* see http://lists.busybox.net/pipermail/busybox/2012-March/077460.html */ 
     903    else if (mc_global.shell->type == SHELL_ASH_BUSYBOX_LEGACY) 
    930904       { 
    931905       quote_cmd_start = "\"`echo -en '"; 
    932906       quote_cmd_end = "'`\""; 
    933        } */ 
     907       } 
    934908    else 
    935909    { 
    936910        quote_cmd_start = "\"`printf \"%b\" '"; 
    init_subshell (void) 
    10591033                return; 
    10601034            } 
    10611035        } 
    1062         else if (pipe (subshell_pipe))  /* subshell_type is BASH, ASH_BUSYBOX, DASH or ZSH */ 
     1036        else if (pipe (subshell_pipe))  /* subshell_type is BASH, ASH_BUSYBOX_LEGACY, DASH or ZSH */ 
    10631037        { 
    10641038            perror (__FILE__ ": couldn't create pipe"); 
    10651039            mc_global.tty.use_subshell = FALSE; 
    init_subshell (void) 
    11021076    tty_disable_interrupt_key (); 
    11031077    if (!subshell_alive) 
    11041078        mc_global.tty.use_subshell = FALSE;     /* Subshell died instantly, so don't use it */ 
     1079    if (!mc_global.tty.use_subshell) 
     1080        fprintf (stderr, __FILE__ ": failed to initialize \"%s\" shell of type %s\r\n", mc_global.shell->path, SHELL_TYPE_STRING(mc_global.shell->type)); 
    11051081} 
    11061082 
    11071083/* --------------------------------------------------------------------------------------------- */