diff --git a/src/viewer/datasource.c b/src/viewer/datasource.c index 3389ee4dd..93cb62aed 100644 --- a/src/viewer/datasource.c +++ b/src/viewer/datasource.c @@ -69,25 +69,26 @@ /*** file scope variables ************************************************************************/ +/* --------------------------------------------------------------------------------------------- */ /*** file scope functions ************************************************************************/ /* --------------------------------------------------------------------------------------------- */ -/* --------------------------------------------------------------------------------------------- */ -/*** public functions ****************************************************************************/ -/* --------------------------------------------------------------------------------------------- */ - static void -mcview_set_datasource_stdio_pipe (mcview_t * view, FILE * fp) +mcview_set_datasource_stdio_pipe (mcview_t * view, mc_pipe_t * p) { -#ifdef HAVE_ASSERT_H - assert (fp != NULL); -#endif + p->out.len = MC_PIPE_BUFSIZE; + p->out.null_term = FALSE; + p->err.len = MC_PIPE_BUFSIZE; + p->err.null_term = TRUE; view->datasource = DS_STDIO_PIPE; - view->ds_stdio_pipe = fp; + view->ds_stdio_pipe = p; + view->pipe_first_err_msg = TRUE; mcview_growbuf_init (view); } +/* --------------------------------------------------------------------------------------------- */ +/*** public functions ****************************************************************************/ /* --------------------------------------------------------------------------------------------- */ void @@ -350,19 +351,14 @@ mcview_close_datasource (mcview_t * view) case DS_STDIO_PIPE: if (view->ds_stdio_pipe != NULL) { - (void) pclose (view->ds_stdio_pipe); + mcview_growbuf_done (view); mcview_display (view); - close_error_pipe (D_NORMAL, NULL); - view->ds_stdio_pipe = NULL; } mcview_growbuf_free (view); break; case DS_VFS_PIPE: if (view->ds_vfs_pipe != -1) - { - (void) mc_close (view->ds_vfs_pipe); - view->ds_vfs_pipe = -1; - } + mcview_growbuf_done (view); mcview_growbuf_free (view); break; case DS_FILE: @@ -401,44 +397,29 @@ mcview_set_datasource_file (mcview_t * view, int fd, const struct stat *st) gboolean mcview_load_command_output (mcview_t * view, const char *command) { - FILE *fp; + mc_pipe_t *pipe; + GError *error = NULL; mcview_close_datasource (view); - open_error_pipe (); - fp = popen (command, "r"); - if (fp == NULL) + pipe = mc_popen (command, &error); + if (pipe == NULL) { - /* Avoid two messages. Message from stderr has priority. */ mcview_display (view); - if (!close_error_pipe (mcview_is_in_panel (view) ? -1 : D_ERROR, NULL)) - mcview_show_error (view, _("Cannot spawn child process")); + mcview_show_error (view, error->message); + g_error_free (error); return FALSE; } - /* First, check if filter produced any output */ - mcview_set_datasource_stdio_pipe (view, fp); + /* Check if filter produced any output */ + mcview_set_datasource_stdio_pipe (view, pipe); if (!mcview_get_byte (view, 0, NULL)) { mcview_close_datasource (view); - - /* Avoid two messages. Message from stderr has priority. */ mcview_display (view); - if (!close_error_pipe (mcview_is_in_panel (view) ? -1 : D_ERROR, NULL)) - mcview_show_error (view, _("Empty output from child filter")); return FALSE; } - else - { - /* - * At least something was read correctly. Close stderr and let - * program die if it will try to write something there. - * - * Ideally stderr should be read asynchronously to prevent programs - * from blocking (poll/select multiplexor). - */ - close_error_pipe (D_NORMAL, NULL); - } + return TRUE; } diff --git a/src/viewer/growbuf.c b/src/viewer/growbuf.c index 81d2f7180..7a3c81b2d 100644 --- a/src/viewer/growbuf.c +++ b/src/viewer/growbuf.c @@ -14,7 +14,7 @@ Pavel Machek, 1998 Roland Illig , 2004, 2005 Slava Zanko , 2009 - Andrew Borodin , 2009 + Andrew Borodin , 2009, 2014 Ilia Maslakov , 2009 This file is part of the Midnight Commander. @@ -72,6 +72,25 @@ mcview_growbuf_init (mcview_t * view) /* --------------------------------------------------------------------------------------------- */ +void +mcview_growbuf_done (mcview_t * view) +{ + view->growbuf_finished = TRUE; + + if (view->datasource == DS_STDIO_PIPE) + { + mc_pclose (view->ds_stdio_pipe, NULL); + view->ds_stdio_pipe = NULL; + } + else /* view->datasource == DS_VFS_PIPE */ + { + (void) mc_close (view->ds_vfs_pipe); + view->ds_vfs_pipe = -1; + } +} + +/* --------------------------------------------------------------------------------------------- */ + void mcview_growbuf_free (mcview_t * view) { @@ -111,8 +130,7 @@ mcview_growbuf_filesize (mcview_t * view) void mcview_growbuf_read_until (mcview_t * view, off_t ofs) { - ssize_t nread; - gboolean short_read; + gboolean short_read = FALSE; #ifdef HAVE_ASSERT_H assert (view->growbuf_in_use); @@ -121,9 +139,9 @@ mcview_growbuf_read_until (mcview_t * view, off_t ofs) if (view->growbuf_finished) return; - short_read = FALSE; while (mcview_growbuf_filesize (view) < ofs || short_read) { + ssize_t nread = 0; byte *p; size_t bytesfree; @@ -144,14 +162,56 @@ mcview_growbuf_read_until (mcview_t * view, off_t ofs) if (view->datasource == DS_STDIO_PIPE) { - nread = fread (p, 1, bytesfree, view->ds_stdio_pipe); - if (nread == 0) + mc_pipe_t *sp = view->ds_stdio_pipe; + GError *error = NULL; + + if (bytesfree > MC_PIPE_BUFSIZE) + bytesfree = MC_PIPE_BUFSIZE; + + sp->out.len = bytesfree; + sp->err.len = MC_PIPE_BUFSIZE; + + mc_pread (sp, &error); + + if (error != NULL) { - view->growbuf_finished = TRUE; - (void) pclose (view->ds_stdio_pipe); + mcview_show_error (view, error->message); + g_error_free (error); + mcview_growbuf_done (view); + return; + } + + if (view->pipe_first_err_msg && sp->err.len > 0) + { + /* ignore possible following errors */ + /* reset this flag before call of mcview_show_error() to break + * endless recursion: mcview_growbuf_read_until() -> mcview_show_error() -> + * MSG_DRAW -> mcview_display() -> mcview_get_byte() -> mcview_growbuf_read_until() + */ + view->pipe_first_err_msg = FALSE; + + mcview_show_error (view, sp->err.buf); + } + + if (sp->out.len > 0) + { + memmove (p, sp->out.buf, sp->out.len); + nread = sp->out.len; + } + else if (sp->out.len == MC_PIPE_STREAM_EOF || sp->out.len == MC_PIPE_ERROR_READ) + { + if (sp->out.len == MC_PIPE_ERROR_READ) + { + char *err_msg; + + err_msg = g_strdup_printf (_("Failed to read data from child stdout:\n%s"), + unix_error_string (sp->out.error)); + mcview_show_error (view, err_msg); + g_free (err_msg); + } + + mcview_growbuf_done (view); mcview_display (view); - close_error_pipe (D_NORMAL, NULL); - view->ds_stdio_pipe = NULL; return; } } @@ -165,11 +225,10 @@ mcview_growbuf_read_until (mcview_t * view, off_t ofs) nread = mc_read (view->ds_vfs_pipe, p, bytesfree); } while (nread == -1 && errno == EINTR); - if (nread == -1 || nread == 0) + + if (nread <= 0) { - view->growbuf_finished = TRUE; - (void) mc_close (view->ds_vfs_pipe); - view->ds_vfs_pipe = -1; + mcview_growbuf_done (view); return; } } diff --git a/src/viewer/internal.h b/src/viewer/internal.h index 9562c527a..64ebb2fc7 100644 --- a/src/viewer/internal.h +++ b/src/viewer/internal.h @@ -100,7 +100,8 @@ struct mcview_struct enum view_ds datasource; /* Where the displayed data comes from */ /* stdio pipe data source */ - FILE *ds_stdio_pipe; /* Output of a shell command */ + mc_pipe_t *ds_stdio_pipe; /* Output of a shell command */ + gboolean pipe_first_err_msg; /* Show only 1st message from stderr */ /* vfs pipe data source */ int ds_vfs_pipe; /* Non-seekable vfs file descriptor */ @@ -263,6 +264,7 @@ void mcview_display_ruler (mcview_t * view); /* growbuf.c: */ void mcview_growbuf_init (mcview_t * view); +void mcview_growbuf_done (mcview_t * view); void mcview_growbuf_free (mcview_t * view); off_t mcview_growbuf_filesize (mcview_t * view); void mcview_growbuf_read_until (mcview_t * view, off_t p); diff --git a/src/viewer/lib.c b/src/viewer/lib.c index 6d51206a3..fea008dfd 100644 --- a/src/viewer/lib.c +++ b/src/viewer/lib.c @@ -304,15 +304,10 @@ mcview_select_encoding (mcview_t * view) void mcview_show_error (mcview_t * view, const char *msg) { - mcview_close_datasource (view); if (mcview_is_in_panel (view)) - { mcview_set_datasource_string (view, msg); - } else - { message (D_ERROR, MSG_ERROR, "%s", msg); - } } /* --------------------------------------------------------------------------------------------- */ diff --git a/src/viewer/mcviewer.c b/src/viewer/mcviewer.c index eb8ec73c2..2d9422087 100644 --- a/src/viewer/mcviewer.c +++ b/src/viewer/mcviewer.c @@ -325,6 +325,7 @@ mcview_load (mcview_t * view, const char *command, const char *file, int start_l { g_snprintf (tmp, sizeof (tmp), _("Cannot open \"%s\"\n%s"), file, unix_error_string (errno)); + mcview_close_datasource (view); mcview_show_error (view, tmp); vfs_path_free (view->filename_vpath); view->filename_vpath = NULL; @@ -339,6 +340,7 @@ mcview_load (mcview_t * view, const char *command, const char *file, int start_l mc_close (fd); g_snprintf (tmp, sizeof (tmp), _("Cannot stat \"%s\"\n%s"), file, unix_error_string (errno)); + mcview_close_datasource (view); mcview_show_error (view, tmp); vfs_path_free (view->filename_vpath); view->filename_vpath = NULL; @@ -350,6 +352,7 @@ mcview_load (mcview_t * view, const char *command, const char *file, int start_l if (!S_ISREG (st.st_mode)) { mc_close (fd); + mcview_close_datasource (view); mcview_show_error (view, _("Cannot view: not a regular file")); vfs_path_free (view->filename_vpath); view->filename_vpath = NULL; @@ -382,6 +385,7 @@ mcview_load (mcview_t * view, const char *command, const char *file, int start_l { g_snprintf (tmp, sizeof (tmp), _("Cannot open \"%s\" in parse mode\n%s"), file, unix_error_string (errno)); + mcview_close_datasource (view); mcview_show_error (view, tmp); } else