Ticket #4114: persistent_subshell_command_buffer3.patch

File persistent_subshell_command_buffer3.patch, 26.3 KB (added by congest, 2 years ago)
  • src/execute.c

    diff --git a/src/execute.c b/src/execute.c
    index d9b306e67..f35c53d94 100644
    a b toggle_subshell (void) 
    551551    { 
    552552        if (mc_global.mc_run_mode == MC_RUN_FULL) 
    553553        { 
    554             do_load_prompt (); 
    555554            if (new_dir_vpath != NULL) 
    556555                do_possible_cd (new_dir_vpath); 
    557556        } 
  • src/filemanager/layout.c

    diff --git a/src/filemanager/layout.c b/src/filemanager/layout.c
    index dfffa96f5..56318d99d 100644
    a b load_prompt (int fd, void *unused) 
    14971497{ 
    14981498    (void) fd; 
    14991499    (void) unused; 
    1500  
    1501     do_load_prompt (); 
     1500    if (should_read_new_subshell_prompt) 
     1501        do_load_prompt (); 
     1502    else 
     1503        flush_subshell(0, QUIETLY); 
    15021504    return 0; 
    15031505} 
    15041506#endif /* ENABLE_SUBSHELL */ 
  • src/subshell/common.c

    diff --git a/src/subshell/common.c b/src/subshell/common.c
    index 06699233c..9b7e5284d 100644
    a b  
    1515   Claes Nästén <me@pekdon.net> 
    1616   Egmont Koblinger <egmont@gmail.com> 
    1717   Enrico Weigelt, metux IT service <weigelt@metux.de> 
     18   Eric Roberts <ericmrobertsdeveloper@gmail.com> 
    1819   Igor Urazov <z0rc3r@gmail.com> 
    1920   Ilia Maslakov <il.smind@gmail.com> 
    2021   Leonard den Ottolander <leonard@den.ottolander.nl> 
     
    105106#include "lib/util.h" 
    106107#include "lib/widget.h" 
    107108 
     109#include "src/filemanager/layout.h"        /* setup_cmdline() */ 
     110#include "src/filemanager/command.h"        /* cmdline */ 
     111 
    108112#include "subshell.h" 
    109113#include "internal.h" 
    110114 
    GString *subshell_prompt = NULL; 
    123127/* We need to paint it after CONSOLE_RESTORE, see: load_prompt */ 
    124128gboolean update_subshell_prompt = FALSE; 
    125129 
     130/* If set, then a command has just finished executing, and we need */ 
     131/* to be on the lookout for a new prompt string from the subshell. */ 
     132gboolean should_read_new_subshell_prompt; 
     133 
    126134/*** file scope macro definitions ****************************************************************/ 
    127135 
    128136#ifndef WEXITSTATUS 
    enum 
    151159    WRITE = 1 
    152160}; 
    153161 
     162/* This is the keybinding that is sent to the shell, to make the shell send us the contents of 
     163 * the current command buffer. */ 
     164#define SHELL_BUFFER_KEYBINDING "_" 
     165 
     166/* This is the keybinding that is sent to the shell, to make the shell send us the location of 
     167 * the cursor. */ 
     168#define SHELL_CURSOR_KEYBINDING "+" 
     169 
    154170/*** file scope variables ************************************************************************/ 
    155171 
    156172/* tcsh closes all non-standard file descriptors, so we have to use a pipe */ 
    static char pty_buffer[PTY_BUFFER_SIZE] = "\0"; 
    169185/* To pass CWD info from the subshell to MC */ 
    170186static int subshell_pipe[2]; 
    171187 
     188/* To pass command buffer info from the subshell to MC */ 
     189static int command_buffer_pipe[2]; 
     190 
    172191/* The subshell's process ID */ 
    173192static pid_t subshell_pid = 1; 
    174193 
    static char subshell_cwd[MC_MAXPATHLEN + 1]; 
    178197/* Flag to indicate whether the subshell is ready for next command */ 
    179198static int subshell_ready; 
    180199 
     200/* Flag to indicate if the subshell supports the persistent buffer feature. */ 
     201static gboolean use_persistent_buffer = FALSE; 
     202 
     203/* Flag to indicate if the contents of the subshell command line need to be cleared before */ 
     204/* executing a command. This should only end up set to true if there is some sort of error.*/ 
     205/* This allows us to recover gracefully from an error.*/ 
     206static gboolean subshell_should_clear_command_line = FALSE; 
     207 
     208/* This is the local variable where the subshell prompt is stored while we are working on it. */ 
     209static GString *subshell_prompt_temp_buffer = NULL; 
     210 
    181211/* The following two flags can be changed by the SIGCHLD handler. This is */ 
    182212/* OK, because the 'int' type is updated atomically on all known machines */ 
    183213static volatile int subshell_alive, subshell_stopped; 
    init_subshell_child (const char *pty_name) 
    362392    dup2 (subshell_pty_slave, STDERR_FILENO); 
    363393 
    364394    close (subshell_pipe[READ]); 
     395    if (use_persistent_buffer == TRUE) 
     396        close (command_buffer_pipe[READ]); 
    365397    close (subshell_pty_slave); /* These may be FD_CLOEXEC, but just in case... */ 
    366398    /* Close master side of pty.  This is important; apart from */ 
    367399    /* freeing up the descriptor for use in the subshell, it also       */ 
    synchronize (void) 
    469501} 
    470502 
    471503/* --------------------------------------------------------------------------------------------- */ 
     504/* Get the contents of the current subshell command line buffer, and */ 
     505/* transfer the contents to the panel command prompt. */ 
     506 
     507static gboolean 
     508read_command_line_buffer(gboolean test_mode) 
     509{ 
     510    fd_set read_set; 
     511    int i; 
     512    ssize_t bytes; 
     513    char subshell_command_buffer[BUF_LARGE]; 
     514    char subshell_cursor_buffer[BUF_SMALL]; 
     515    struct timeval subshell_prompt_timer = { 0, 0 }; 
     516    int command_buffer_length; 
     517    int command_buffer_char_length; 
     518    int cursor_position; 
     519    int maxfdp; 
     520    int rc; 
     521    char *end; 
     522 
     523    if (use_persistent_buffer == TRUE) 
     524    { 
     525        FD_ZERO (&read_set); 
     526        FD_SET (command_buffer_pipe[READ], &read_set); 
     527        maxfdp = command_buffer_pipe[READ]; 
     528        /* First, flush the command buffer pipe. This pipe shouldn't be written 
     529         * to under normal circumstances, but if it somehow does get written 
     530         * to, we need to make sure to discard whatever data is there before 
     531         * we try to use it. */ 
     532        while ((rc = select (maxfdp + 1, &read_set, NULL, NULL, &subshell_prompt_timer)) != 1) 
     533        { 
     534            if (rc == -1) 
     535            { 
     536                if (errno == EINTR) 
     537                    continue; 
     538                else 
     539                    return FALSE; 
     540            } 
     541            else if (rc == 1) 
     542                read (command_buffer_pipe[READ], subshell_command_buffer, sizeof(subshell_command_buffer)); 
     543            else if (rc == 0) 
     544                break; 
     545        } 
     546        /* get contents of command line buffer */ 
     547        write_all (mc_global.tty.subshell_pty, ESC_STR SHELL_BUFFER_KEYBINDING, 
     548            sizeof(ESC_STR SHELL_CURSOR_KEYBINDING) - 1); 
     549        subshell_prompt_timer.tv_sec = 1; 
     550        FD_ZERO (&read_set); 
     551        FD_SET (command_buffer_pipe[READ], &read_set); 
     552        while ((rc = select (maxfdp + 1, &read_set, NULL, NULL, &subshell_prompt_timer)) != 1) 
     553        { 
     554            if (rc == -1) 
     555            { 
     556                if (errno == EINTR) 
     557                    continue; 
     558                else 
     559                    return FALSE; 
     560            } 
     561            else if (rc == 0) 
     562                return FALSE; 
     563        } 
     564 
     565        bytes = read (command_buffer_pipe[READ], subshell_command_buffer, sizeof(subshell_command_buffer)); 
     566        if (bytes == sizeof(subshell_command_buffer)) 
     567            return FALSE; 
     568        if (bytes == 0) 
     569            return FALSE; 
     570        subshell_command_buffer[bytes - 1] = 0; 
     571        command_buffer_char_length = bytes - 1; 
     572        command_buffer_length = str_length(subshell_command_buffer); 
     573 
     574        /* get cursor position */ 
     575        write_all (mc_global.tty.subshell_pty, ESC_STR SHELL_CURSOR_KEYBINDING, 
     576            sizeof(ESC_STR SHELL_CURSOR_KEYBINDING) - 1); 
     577        subshell_prompt_timer.tv_sec = 1; 
     578        subshell_prompt_timer.tv_usec = 0; 
     579        FD_ZERO (&read_set); 
     580        FD_SET (command_buffer_pipe[READ], &read_set); 
     581        while ((rc = select (maxfdp + 1, &read_set, NULL, NULL, &subshell_prompt_timer)) != 1) 
     582        { 
     583            if (rc == -1) 
     584            { 
     585                if (errno == EINTR) 
     586                    continue; 
     587                else 
     588                    return FALSE; 
     589            } 
     590            else if (rc == 0) 
     591                return FALSE; 
     592        } 
     593        bytes = read (command_buffer_pipe[READ], subshell_cursor_buffer, sizeof(subshell_cursor_buffer)); 
     594        if (bytes == 0) 
     595            return FALSE; 
     596        subshell_cursor_buffer[bytes - 1] = 0; 
     597        cursor_position = strtol(subshell_cursor_buffer, &end, 10); 
     598        if (end == subshell_cursor_buffer) 
     599            return FALSE; 
     600 
     601        if (test_mode == TRUE) 
     602            return TRUE; 
     603        /* Erase non-text characters in the command buffer, such as tab, or newline, as this 
     604         * could cause problems. */ 
     605        for(i = 0;i < command_buffer_char_length;i++) 
     606        { 
     607            if ((unsigned char) subshell_command_buffer[i] < 32 
     608                || (unsigned char)subshell_command_buffer[i] == 127) 
     609                subshell_command_buffer[i] = ' '; 
     610        } 
     611        input_assign_text (cmdline, ""); 
     612        input_insert (cmdline, subshell_command_buffer, 0); 
     613 
     614        if (mc_global.shell->type == SHELL_BASH) 
     615        { 
     616            /* We need to do this because bash gives the cursor position in a utf-8 string based 
     617             * on the location in bytes, not in unicode characters. */ 
     618            for (i = 0;i < command_buffer_length;i++) 
     619                if (str_offset_to_pos(subshell_command_buffer, i) == cursor_position) 
     620                    break; 
     621            cursor_position = i; 
     622        } 
     623        if (cursor_position > command_buffer_length) 
     624            cursor_position = command_buffer_length; 
     625        cmdline->point = cursor_position; 
     626        /* We send any remaining data to STDOUT before we finish. */ 
     627        flush_subshell(0, VISIBLY); 
     628 
     629        /* Now we erase the current contents of the command line buffer */ 
     630        if (mc_global.shell->type != SHELL_ZSH) 
     631        /* In zsh, we can just press c-u to clear the line, without needing to go to the end of 
     632         * the line first first. In all other shells, we must go to the end of the line first. */ 
     633        { 
     634            /* If we are not at the end of the line, we go to the end. */ 
     635            if (cursor_position != command_buffer_length) 
     636            { 
     637                write_all (mc_global.tty.subshell_pty, "\005", 1); 
     638                if (flush_subshell(1, VISIBLY) != 1) 
     639                    return FALSE; 
     640            } 
     641        } 
     642        if (command_buffer_length > 0)    /* Now we clear the line. */ 
     643        { 
     644            write_all (mc_global.tty.subshell_pty, "\025", 1); 
     645            if (flush_subshell(1, VISIBLY) != 1) 
     646                return FALSE; 
     647        } 
     648    } 
     649    return TRUE; 
     650} 
     651 
     652/* --------------------------------------------------------------------------------------------- */ 
     653 
     654static void 
     655clear_subshell_prompt_string (void) 
     656{ 
     657    if(subshell_prompt_temp_buffer != NULL) 
     658        g_string_set_size (subshell_prompt_temp_buffer, 0); 
     659} 
     660 
     661/* --------------------------------------------------------------------------------------------- */ 
     662 
     663static void 
     664parse_subshell_prompt_string (const char *buffer, int bytes) 
     665{ 
     666    int i; 
     667 
     668    /* First time through */ 
     669    if (subshell_prompt == NULL) 
     670    { 
     671        subshell_prompt = g_string_sized_new (INITIAL_PROMPT_SIZE); 
     672    } 
     673    if (subshell_prompt_temp_buffer == NULL) 
     674    { 
     675        subshell_prompt_temp_buffer = g_string_sized_new (INITIAL_PROMPT_SIZE); 
     676    } 
     677 
     678    /* Extract the prompt from the shell output */ 
     679    for (i = 0; i < bytes; i++) 
     680        if (buffer[i] == '\n' || buffer[i] == '\r') 
     681        { 
     682            g_string_set_size (subshell_prompt_temp_buffer, 0); 
     683        } 
     684        else if (buffer[i] != '\0') 
     685            g_string_append_c (subshell_prompt_temp_buffer, buffer[i]); 
     686} 
     687 
     688/* --------------------------------------------------------------------------------------------- */ 
     689 
     690static void 
     691set_prompt_string (void) 
     692{ 
     693    if (subshell_prompt_temp_buffer->len != 0) 
     694        g_string_assign (subshell_prompt, subshell_prompt_temp_buffer->str); 
     695 
     696    setup_cmdline(); 
     697} 
     698 
     699/* --------------------------------------------------------------------------------------------- */ 
    472700/** Feed the subshell our keyboard input until it says it's finished */ 
    473701 
    474702static gboolean 
    feed_subshell (int how, gboolean fail_on_error) 
    481709    struct timeval wtime;       /* Maximum time we wait for the subshell */ 
    482710    struct timeval *wptr; 
    483711 
     712    should_read_new_subshell_prompt = FALSE; 
     713    subshell_should_clear_command_line = FALSE; 
     714 
    484715    /* we wait up to 10 seconds if fail_on_error, forever otherwise */ 
    485716    wtime.tv_sec = 10; 
    486717    wtime.tv_usec = 0; 
    feed_subshell (int how, gboolean fail_on_error) 
    549780 
    550781            if (how == VISIBLY) 
    551782                write_all (STDOUT_FILENO, pty_buffer, bytes); 
     783            if (should_read_new_subshell_prompt == TRUE) 
     784            { 
     785                parse_subshell_prompt_string(pty_buffer, bytes); 
     786            } 
     787 
    552788        } 
    553789 
    554790        else if (FD_ISSET (subshell_pipe[READ], &read_set)) 
    feed_subshell (int how, gboolean fail_on_error) 
    567803 
    568804            synchronize (); 
    569805 
     806            clear_subshell_prompt_string(); 
     807            should_read_new_subshell_prompt = TRUE; 
    570808            subshell_ready = TRUE; 
    571809            if (subshell_state == RUNNING_COMMAND) 
    572810            { 
    feed_subshell (int how, gboolean fail_on_error) 
    578816        else if (FD_ISSET (STDIN_FILENO, &read_set)) 
    579817            /* Read from stdin, write to the subshell */ 
    580818        { 
     819            should_read_new_subshell_prompt = FALSE; 
    581820            bytes = read (STDIN_FILENO, pty_buffer, sizeof (pty_buffer)); 
    582821            if (bytes <= 0) 
    583822            { 
    feed_subshell (int how, gboolean fail_on_error) 
    592831                { 
    593832                    write_all (mc_global.tty.subshell_pty, pty_buffer, i); 
    594833                    if (subshell_ready) 
     834                    { 
    595835                        subshell_state = INACTIVE; 
     836                        set_prompt_string (); 
     837                        if (subshell_ready == TRUE) 
     838                        { 
     839                            if (!read_command_line_buffer(FALSE)) 
     840                            { 
     841                                /* If we got here, some unforseen error must have occurred. */ 
     842                                flush_subshell(0, VISIBLY); 
     843                                input_assign_text (cmdline, ""); 
     844                                subshell_should_clear_command_line = TRUE; 
     845                                return TRUE; 
     846                            } 
     847                        } 
     848                    } 
    596849                    return TRUE; 
    597850                } 
    598851 
    599852            write_all (mc_global.tty.subshell_pty, pty_buffer, bytes); 
    600853 
    601854            if (pty_buffer[bytes - 1] == '\n' || pty_buffer[bytes - 1] == '\r') 
     855            { 
     856                /* We should only clear the command line if we are using a shell that works 
     857                 * with persistent command buffer, otherwise we get awkward results. */ 
     858                if (use_persistent_buffer == TRUE) 
     859                    input_assign_text (cmdline, ""); 
    602860                subshell_ready = FALSE; 
     861            } 
    603862        } 
    604863        else 
    605864            return FALSE; 
    init_subshell_precmd (char *precmd, size_t buff_size) 
    7841043    { 
    7851044    case SHELL_BASH: 
    7861045        g_snprintf (precmd, buff_size, 
     1046                    " bind -x '\"\\e" SHELL_BUFFER_KEYBINDING "\":\"echo $READLINE_LINE>&%d\"'\n" 
     1047                    " bind -x '\"\\e" SHELL_CURSOR_KEYBINDING "\":\"echo $READLINE_POINT>&%d\"'\n" 
    7871048                    " PROMPT_COMMAND=${PROMPT_COMMAND:+$PROMPT_COMMAND\n}'pwd>&%d;kill -STOP $$'\n" 
    788                     "PS1='\\u@\\h:\\w\\$ '\n", subshell_pipe[WRITE]); 
     1049                    "PS1='\\u@\\h:\\w\\$ '\n", 
     1050                    command_buffer_pipe[WRITE], command_buffer_pipe[WRITE], subshell_pipe[WRITE]); 
    7891051        break; 
    7901052 
    7911053    case SHELL_ASH_BUSYBOX: 
    init_subshell_precmd (char *precmd, size_t buff_size) 
    8441106 
    8451107    case SHELL_ZSH: 
    8461108        g_snprintf (precmd, buff_size, 
     1109                    " mc_print_command_buffer () { echo $BUFFER >&%d}\n" 
     1110                    " zle -N mc_print_command_buffer\n" 
     1111                    " bindkey '^[" SHELL_BUFFER_KEYBINDING "' mc_print_command_buffer\n" 
     1112                    " mc_print_cursor_position () { echo $CURSOR >&%d}\n" 
     1113                    " zle -N mc_print_cursor_position\n" 
     1114                    " bindkey '^[" SHELL_CURSOR_KEYBINDING "' mc_print_cursor_position\n" 
    8471115                    " _mc_precmd(){ pwd>&%d;kill -STOP $$ }; precmd_functions+=(_mc_precmd)\n" 
    848                     "PS1='%%n@%%m:%%~%%# '\n", subshell_pipe[WRITE]); 
     1116                    "PS1='%%n@%%m:%%~%%# '\n", 
     1117                    command_buffer_pipe[WRITE], command_buffer_pipe[WRITE], subshell_pipe[WRITE]); 
    8491118        break; 
    8501119 
    8511120    case SHELL_TCSH: 
    init_subshell_precmd (char *precmd, size_t buff_size) 
    8541123                    "set prompt='%%n@%%m:%%~%%# '; " 
    8551124                    "alias precmd 'echo $cwd:q >>%s; kill -STOP $$'\n", tcsh_fifo); 
    8561125        break; 
    857  
    8581126    case SHELL_FISH: 
    8591127        g_snprintf (precmd, buff_size, 
    8601128                    " if not functions -q fish_prompt_mc;" 
    8611129                    "functions -e fish_right_prompt;" 
    8621130                    "functions -c fish_prompt fish_prompt_mc; end;" 
    8631131                    "function fish_prompt;" 
     1132                    "bind \\e\\" SHELL_BUFFER_KEYBINDING " 'echo (commandline)>&%d'\n" 
     1133                    "bind \\e\\" SHELL_CURSOR_KEYBINDING " 'echo (commandline -C)>&%d'\n" 
    8641134                    "echo \"$PWD\">&%d; fish_prompt_mc; kill -STOP %%self; end\n", 
    865                     subshell_pipe[WRITE]); 
     1135                    command_buffer_pipe[WRITE], command_buffer_pipe[WRITE], subshell_pipe[WRITE]); 
    8661136        break; 
    8671137 
    8681138    default: 
    init_subshell (void) 
    10401310            mc_global.tty.use_subshell = FALSE; 
    10411311            return; 
    10421312        } 
     1313        if (mc_global.shell->type == SHELL_BASH 
     1314            || mc_global.shell->type == SHELL_ZSH 
     1315            || mc_global.shell->type == SHELL_FISH) 
     1316            use_persistent_buffer = TRUE; 
     1317        if (use_persistent_buffer == TRUE) 
     1318        { 
     1319            if (pipe (command_buffer_pipe)) 
     1320            { 
     1321                perror (__FILE__ ": couldn't create pipe"); 
     1322                mc_global.tty.use_subshell = FALSE; 
     1323                return; 
     1324            } 
     1325        } 
    10431326    } 
    10441327 
    10451328    /* Fork the subshell */ 
    init_subshell (void) 
    10611344        /* We are in the child process */ 
    10621345        init_subshell_child (pty_name); 
    10631346    } 
    1064  
    10651347    init_subshell_precmd (precmd, BUF_MEDIUM); 
    10661348 
    10671349    write_all (mc_global.tty.subshell_pty, precmd, strlen (precmd)); 
    init_subshell (void) 
    10771359    tty_disable_interrupt_key (); 
    10781360    if (!subshell_alive) 
    10791361        mc_global.tty.use_subshell = FALSE;     /* Subshell died instantly, so don't use it */ 
     1362 
     1363    /* Try out the persistent command buffer feature. If it doesn't work the first time, we 
     1364     * assume there must be something wrong with the shell, and we turn persistent buffer off 
     1365     * for good. This will save the user the trouble of having to wait for the persistent 
     1366     * buffer function to time out every time they try to close the subshell. */ 
     1367    if (use_persistent_buffer == TRUE) 
     1368    { 
     1369        if (read_command_line_buffer(TRUE) == FALSE) 
     1370            use_persistent_buffer = FALSE; 
     1371    } 
    10801372} 
    10811373 
    10821374/* --------------------------------------------------------------------------------------------- */ 
    init_subshell (void) 
    10841376int 
    10851377invoke_subshell (const char *command, int how, vfs_path_t ** new_dir_vpath) 
    10861378{ 
     1379    size_t i; 
    10871380    /* Make the MC terminal transparent */ 
    10881381    tcsetattr (STDOUT_FILENO, TCSANOW, &raw_mode); 
    10891382 
    invoke_subshell (const char *command, int how, vfs_path_t ** new_dir_vpath) 
    11001393            /* FIXME: possibly take out this hack; the user can re-play it by hitting C-hyphen a few times! */ 
    11011394            if (subshell_ready && mc_global.mc_run_mode == MC_RUN_FULL) 
    11021395                write_all (mc_global.tty.subshell_pty, " \b", 2);       /* Hack to make prompt reappear */ 
     1396  
     1397            if (use_persistent_buffer == TRUE) 
     1398            { 
     1399                /* Check to make sure there are no non text characters in the command buffer, 
     1400                 * such as tab, or newline, as this could cause problems. */ 
     1401                for(i = 0;i < strlen(cmdline->buffer);i++) 
     1402                { 
     1403                    if ((unsigned char) cmdline->buffer[i] < 32 
     1404                        || (unsigned char)cmdline->buffer[i] == 127) 
     1405                        cmdline->buffer[i] = ' '; 
     1406                } 
     1407 
     1408                /* Write the command buffer to the subshell. */ 
     1409                write_all (mc_global.tty.subshell_pty, cmdline->buffer, strlen (cmdline->buffer)); 
     1410 
     1411                /* Put the cursor in the correct place in the subshell. */ 
     1412                for (int j = 0;j < str_length (cmdline->buffer) - cmdline->point;j++) 
     1413                    write_all (mc_global.tty.subshell_pty, ESC_STR "[D", 3); 
     1414            } 
    11031415        } 
    11041416    } 
    11051417    else                        /* MC has passed us a user command */ 
    11061418    { 
     1419        /* Before we write to the command prompt, we need to clear whatever */ 
     1420        /* data is there, but only if we are using one of the shells that */ 
     1421        /* doesn't support keeping command buffer contents, OR if there was */ 
     1422        /* some sort of error. */ 
     1423        if (use_persistent_buffer == FALSE || subshell_should_clear_command_line == TRUE) 
     1424        { 
     1425            write_all (mc_global.tty.subshell_pty, "\003", 1); 
     1426            subshell_state = RUNNING_COMMAND; 
     1427            /* We need to call feed_subshell here if we are using fish, because of a quirk 
     1428             * in the behavioral of that particular shell. */ 
     1429            if (mc_global.shell->type != SHELL_FISH) 
     1430                feed_subshell (QUIETLY, FALSE); 
     1431        } 
    11071432        if (how == QUIETLY) 
    11081433            write_all (mc_global.tty.subshell_pty, " ", 1); 
    11091434        /* FIXME: if command is long (>8KB ?) we go comma */ 
    invoke_subshell (const char *command, int how, vfs_path_t ** new_dir_vpath) 
    11311456    return subshell_get_mainloop_quit (); 
    11321457} 
    11331458 
    1134  
    11351459/* --------------------------------------------------------------------------------------------- */ 
    11361460 
    11371461gboolean 
    1138 read_subshell_prompt (void) 
     1462flush_subshell (int max_wait_length, int how) 
    11391463{ 
    11401464    int rc = 0; 
    11411465    ssize_t bytes = 0; 
    11421466    struct timeval timeleft = { 0, 0 }; 
    1143     GString *p; 
    1144     gboolean prompt_was_reset = FALSE; 
    1145  
     1467    int return_value = FALSE; 
    11461468    fd_set tmp; 
     1469 
     1470    timeleft.tv_sec = max_wait_length; 
    11471471    FD_ZERO (&tmp); 
    11481472    FD_SET (mc_global.tty.subshell_pty, &tmp); 
    11491473 
    1150     /* First time through */ 
    1151     if (subshell_prompt == NULL) 
    1152         subshell_prompt = g_string_sized_new (INITIAL_PROMPT_SIZE); 
    1153  
    1154     p = g_string_sized_new (INITIAL_PROMPT_SIZE); 
    1155  
    11561474    while (subshell_alive 
    11571475           && (rc = select (mc_global.tty.subshell_pty + 1, &tmp, NULL, NULL, &timeleft)) != 0) 
    11581476    { 
    1159         ssize_t i; 
    1160  
    11611477        /* Check for 'select' errors */ 
    11621478        if (rc == -1) 
    11631479        { 
    read_subshell_prompt (void) 
    11721488            fprintf (stderr, "select (FD_SETSIZE, &tmp...): %s\r\n", unix_error_string (errno)); 
    11731489            exit (EXIT_FAILURE); 
    11741490        } 
     1491        return_value = TRUE; 
     1492        timeleft.tv_sec = 0; 
     1493        timeleft.tv_usec = 0; 
    11751494 
    11761495        bytes = read (mc_global.tty.subshell_pty, pty_buffer, sizeof (pty_buffer)); 
     1496        if (how == VISIBLY) 
     1497            write_all (STDOUT_FILENO, pty_buffer, bytes); 
     1498    } 
     1499    return return_value; 
     1500} 
     1501 
     1502/* --------------------------------------------------------------------------------------------- */ 
     1503 
     1504gboolean 
     1505read_subshell_prompt (void) 
     1506{ 
     1507    int rc = 0; 
     1508    ssize_t bytes = 0; 
     1509    struct timeval timeleft = { 0, 0 }; 
     1510    int should_reset_prompt = TRUE; 
     1511    int got_new_prompt = FALSE; 
     1512 
     1513    fd_set tmp; 
     1514    FD_ZERO (&tmp); 
     1515    FD_SET (mc_global.tty.subshell_pty, &tmp); 
    11771516 
    1178         /* Extract the prompt from the shell output */ 
    1179         for (i = 0; i < bytes; i++) 
    1180             if (pty_buffer[i] == '\n' || pty_buffer[i] == '\r') 
     1517    while (subshell_alive 
     1518           && (rc = select (mc_global.tty.subshell_pty + 1, &tmp, NULL, NULL, &timeleft)) != 0) 
     1519    { 
     1520        /* Check for 'select' errors */ 
     1521        if (rc == -1) 
     1522        { 
     1523            if (errno == EINTR) 
    11811524            { 
    1182                 g_string_set_size (p, 0); 
    1183                 prompt_was_reset = TRUE; 
     1525                if (tty_got_winch ()) 
     1526                    tty_change_screen_size (); 
     1527 
     1528                continue; 
    11841529            } 
    1185             else if (pty_buffer[i] != '\0') 
    1186                 g_string_append_c (p, pty_buffer[i]); 
    1187     } 
    11881530 
    1189     if (p->len != 0 || prompt_was_reset) 
    1190         g_string_assign (subshell_prompt, p->str); 
     1531            fprintf (stderr, "select (FD_SETSIZE, &tmp...): %s\r\n", unix_error_string (errno)); 
     1532            exit (EXIT_FAILURE); 
     1533        } 
    11911534 
    1192     g_string_free (p, TRUE); 
     1535        bytes = read (mc_global.tty.subshell_pty, pty_buffer, sizeof (pty_buffer)); 
     1536        if (should_reset_prompt) 
     1537        { 
     1538            should_reset_prompt = FALSE; 
     1539            clear_subshell_prompt_string(); 
     1540        } 
     1541        parse_subshell_prompt_string (pty_buffer, bytes); 
     1542        got_new_prompt = TRUE; 
     1543    } 
     1544    if (got_new_prompt == TRUE) 
     1545    { 
     1546        set_prompt_string (); 
     1547    } 
    11931548 
    11941549    return (rc != 0 || bytes != 0); 
    11951550} 
    exit_subshell (void) 
    12311586 
    12321587        g_string_free (subshell_prompt, TRUE); 
    12331588        subshell_prompt = NULL; 
     1589        g_string_free (subshell_prompt_temp_buffer, TRUE); 
     1590        subshell_prompt_temp_buffer = NULL; 
    12341591        pty_buffer[0] = '\0'; 
    12351592    } 
    12361593 
    do_subshell_chdir (const vfs_path_t * vpath, gboolean update_prompt) 
    12591616        return; 
    12601617    } 
    12611618 
     1619    /* If we are using a shell that doesn't support persistent command buffer, we need to clear 
     1620     * the command prompt before we send the cd command. */ 
     1621    if (use_persistent_buffer == FALSE || subshell_should_clear_command_line == TRUE) 
     1622    { 
     1623        write_all (mc_global.tty.subshell_pty, "\003", 1); 
     1624        subshell_state = RUNNING_COMMAND; 
     1625        if (mc_global.shell->type != SHELL_FISH) 
     1626            feed_subshell (QUIETLY, FALSE); 
     1627    } 
    12621628    /* The initial space keeps this out of the command history (in bash 
    12631629       because we set "HISTCONTROL=ignorespace") */ 
    12641630    write_all (mc_global.tty.subshell_pty, " cd ", 4); 
  • src/subshell/subshell.h

    diff --git a/src/subshell/subshell.h b/src/subshell/subshell.h
    index e0fdfb13e..bde19c469 100644
    a b extern GString *subshell_prompt; 
    3636 
    3737extern gboolean update_subshell_prompt; 
    3838 
     39extern gboolean should_read_new_subshell_prompt; 
     40 
    3941/*** declarations of public functions ************************************************************/ 
    4042 
    4143void init_subshell (void); 
    4244int invoke_subshell (const char *command, int how, vfs_path_t ** new_dir); 
     45gboolean flush_subshell (int max_wait_length, int how); 
    4346gboolean read_subshell_prompt (void); 
    4447void do_update_prompt (void); 
    4548gboolean exit_subshell (void);