mcviewer: use mc_pipe_t to show command output.

Signed-off-by: Andrew Borodin <aborodin@vmail.ru>
This commit is contained in:
Andrew Borodin 2014-07-03 10:02:08 +04:00
parent a31b78003e
commit b69b8947d9
5 changed files with 101 additions and 60 deletions

View File

@ -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;
}

View File

@ -14,7 +14,7 @@
Pavel Machek, 1998
Roland Illig <roland.illig@gmx.de>, 2004, 2005
Slava Zanko <slavazanko@google.com>, 2009
Andrew Borodin <aborodin@vmail.ru>, 2009
Andrew Borodin <aborodin@vmail.ru>, 2009, 2014
Ilia Maslakov <il.smind@gmail.com>, 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;
}
}

View File

@ -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);

View File

@ -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);
}
}
/* --------------------------------------------------------------------------------------------- */

View File

@ -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