mirror of
https://github.com/MidnightCommander/mc
synced 2025-01-03 10:04:32 +03:00
Ticket #2966: fix view of broken manpages.
Initial commit: create MC pipe class to capture stdout and stderr of spawn processes. Signed-off-by: Andrew Borodin <aborodin@vmail.ru>
This commit is contained in:
parent
5bcc246ac2
commit
a31b78003e
42
lib/util.h
42
lib/util.h
@ -27,6 +27,13 @@
|
|||||||
#define mc_return_if_error(mcerror) do { if (mcerror != NULL && *mcerror != NULL) return; } while (0)
|
#define mc_return_if_error(mcerror) do { if (mcerror != NULL && *mcerror != NULL) return; } while (0)
|
||||||
#define mc_return_val_if_error(mcerror, mcvalue) do { if (mcerror != NULL && *mcerror != NULL) return mcvalue; } while (0)
|
#define mc_return_val_if_error(mcerror, mcvalue) do { if (mcerror != NULL && *mcerror != NULL) return mcvalue; } while (0)
|
||||||
|
|
||||||
|
#define MC_PIPE_BUFSIZE BUF_8K
|
||||||
|
#define MC_PIPE_STREAM_EOF 0
|
||||||
|
#define MC_PIPE_STREAM_UNREAD -1
|
||||||
|
#define MC_PIPE_ERROR_CREATE_PIPE -2
|
||||||
|
#define MC_PIPE_ERROR_PARSE_COMMAND -3
|
||||||
|
#define MC_PIPE_ERROR_CREATE_PIPE_STREAM -4
|
||||||
|
#define MC_PIPE_ERROR_READ -5
|
||||||
|
|
||||||
/*** enums ***************************************************************************************/
|
/*** enums ***************************************************************************************/
|
||||||
|
|
||||||
@ -51,6 +58,37 @@ enum compression_type
|
|||||||
COMPRESSION_XZ
|
COMPRESSION_XZ
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* stdout or stderr stream of child process */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
/* file descriptor */
|
||||||
|
int fd;
|
||||||
|
/* data read from fd */
|
||||||
|
char buf[MC_PIPE_BUFSIZE];
|
||||||
|
/* positive: length of data in buf as before read as after;
|
||||||
|
* zero or negative before read: do not read drom fd;
|
||||||
|
* MC_PIPE_STREAM_EOF after read: EOF of fd;
|
||||||
|
* MC_PIPE_STREAM_UNREAD after read: there was not read from fd;
|
||||||
|
* MC_PIPE_ERROR_READ after read: reading error from fd.
|
||||||
|
*/
|
||||||
|
ssize_t len;
|
||||||
|
/* whether buf is null-terminated or not */
|
||||||
|
gboolean null_term;
|
||||||
|
/* error code in case of len == MC_PIPE_ERROR_READ */
|
||||||
|
int error;
|
||||||
|
} mc_pipe_stream_t;
|
||||||
|
|
||||||
|
/* Pipe descriptor for child process */
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
/* PID of child process */
|
||||||
|
GPid child_pid;
|
||||||
|
/* stdout of child process */
|
||||||
|
mc_pipe_stream_t out;
|
||||||
|
/* stderr of child process */
|
||||||
|
mc_pipe_stream_t err;
|
||||||
|
} mc_pipe_t;
|
||||||
|
|
||||||
/*** structures declarations (and typedefs of structures)*****************************************/
|
/*** structures declarations (and typedefs of structures)*****************************************/
|
||||||
|
|
||||||
/* keys are set only during sorting */
|
/* keys are set only during sorting */
|
||||||
@ -157,6 +195,10 @@ int my_systeml (int flags, const char *shell, ...);
|
|||||||
int my_systemv (const char *command, char *const argv[]);
|
int my_systemv (const char *command, char *const argv[]);
|
||||||
int my_systemv_flags (int flags, const char *command, char *const argv[]);
|
int my_systemv_flags (int flags, const char *command, char *const argv[]);
|
||||||
|
|
||||||
|
mc_pipe_t *mc_popen (const char *command, GError ** error);
|
||||||
|
void mc_pread (mc_pipe_t * p, GError ** error);
|
||||||
|
void mc_pclose (mc_pipe_t * p, GError ** error);
|
||||||
|
|
||||||
void my_exit (int status);
|
void my_exit (int status);
|
||||||
void save_stop_handler (void);
|
void save_stop_handler (void);
|
||||||
|
|
||||||
|
212
lib/utilunix.c
212
lib/utilunix.c
@ -50,6 +50,9 @@
|
|||||||
#endif
|
#endif
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#ifdef HAVE_SYS_SELECT_H
|
||||||
|
#include <sys/select.h>
|
||||||
|
#endif
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#ifdef HAVE_SYS_IOCTL_H
|
#ifdef HAVE_SYS_IOCTL_H
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
@ -232,6 +235,50 @@ my_system_make_arg_array (int flags, const char *shell, char **execute_name)
|
|||||||
return args_array;
|
return args_array;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
static void
|
||||||
|
mc_pread_stream (mc_pipe_stream_t * ps, const fd_set * fds)
|
||||||
|
{
|
||||||
|
size_t buf_len;
|
||||||
|
ssize_t read_len;
|
||||||
|
|
||||||
|
if (!FD_ISSET (ps->fd, fds))
|
||||||
|
{
|
||||||
|
ps->len = MC_PIPE_STREAM_UNREAD;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf_len = (size_t) ps->len;
|
||||||
|
|
||||||
|
if (buf_len >= MC_PIPE_BUFSIZE)
|
||||||
|
buf_len = ps->null_term ? MC_PIPE_BUFSIZE - 1 : MC_PIPE_BUFSIZE;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
read_len = read (ps->fd, ps->buf, buf_len);
|
||||||
|
}
|
||||||
|
while (read_len < 0 && errno == EINTR);
|
||||||
|
|
||||||
|
if (read_len < 0)
|
||||||
|
{
|
||||||
|
/* reading error */
|
||||||
|
ps->len = MC_PIPE_ERROR_READ;
|
||||||
|
ps->error = errno;
|
||||||
|
}
|
||||||
|
else if (read_len == 0)
|
||||||
|
/* EOF */
|
||||||
|
ps->len = MC_PIPE_STREAM_EOF;
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* success */
|
||||||
|
ps->len = read_len;
|
||||||
|
|
||||||
|
if (ps->null_term)
|
||||||
|
ps->buf[(size_t) ps->len] = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------- */
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
/*** public functions ****************************************************************************/
|
/*** public functions ****************************************************************************/
|
||||||
/* --------------------------------------------------------------------------------------------- */
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
@ -450,6 +497,171 @@ my_systemv_flags (int flags, const char *command, char *const argv[])
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Create pipe and run child process.
|
||||||
|
*
|
||||||
|
* @parameter command command line of child process
|
||||||
|
* @paremeter error contains pointer to object to handle error code and message
|
||||||
|
*
|
||||||
|
* @return newly created object of mc_pipe_t class in success, NULL otherwise
|
||||||
|
*/
|
||||||
|
|
||||||
|
mc_pipe_t *
|
||||||
|
mc_popen (const char *command, GError ** error)
|
||||||
|
{
|
||||||
|
mc_pipe_t *p;
|
||||||
|
char **argv;
|
||||||
|
|
||||||
|
p = g_try_new (mc_pipe_t, 1);
|
||||||
|
if (p == NULL)
|
||||||
|
{
|
||||||
|
mc_replace_error (error, MC_PIPE_ERROR_CREATE_PIPE, "%s",
|
||||||
|
_("Cannot create pipe descriptor"));
|
||||||
|
goto ret_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!g_shell_parse_argv (command, NULL, &argv, error))
|
||||||
|
{
|
||||||
|
mc_replace_error (error, MC_PIPE_ERROR_PARSE_COMMAND, "%s",
|
||||||
|
_("Cannot parse command for pipe"));
|
||||||
|
goto ret_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!g_spawn_async_with_pipes (NULL, argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL,
|
||||||
|
&p->child_pid, NULL, &p->out.fd, &p->err.fd, error))
|
||||||
|
{
|
||||||
|
mc_replace_error (error, MC_PIPE_ERROR_CREATE_PIPE_STREAM, "%s",
|
||||||
|
_("Cannot create pipe streams"));
|
||||||
|
goto ret_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_strfreev (argv);
|
||||||
|
|
||||||
|
p->out.buf[0] = '\0';
|
||||||
|
p->out.len = MC_PIPE_BUFSIZE;
|
||||||
|
p->out.null_term = FALSE;
|
||||||
|
|
||||||
|
p->err.buf[0] = '\0';
|
||||||
|
p->err.len = MC_PIPE_BUFSIZE;
|
||||||
|
p->err.null_term = FALSE;
|
||||||
|
|
||||||
|
return p;
|
||||||
|
|
||||||
|
ret_err:
|
||||||
|
g_free (p);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Read stdout and stderr of pipe asynchronously.
|
||||||
|
*
|
||||||
|
* @parameter p pipe descriptor
|
||||||
|
*
|
||||||
|
* The lengths of read data contain in p->out.len and p->err.len.
|
||||||
|
* Before read, p->xxx.len is an input:
|
||||||
|
* p->xxx.len > 0: do read stream p->xxx and store data in p->xxx.buf;
|
||||||
|
* p->xxx.len <= 0: do not read stream p->xxx.
|
||||||
|
*
|
||||||
|
* After read, p->xxx.len is an output and contains the following:
|
||||||
|
* p->xxx.len > 0: an actual length of read data stored in p->xxx.buf;
|
||||||
|
* p->xxx.len == MC_PIPE_STREAM_EOF: EOF of stream p->xxx;
|
||||||
|
* p->xxx.len == MC_PIPE_STREAM_UNREAD: stream p->xxx was not read;
|
||||||
|
* p->xxx.len == MC_PIPE_ERROR_READ: reading error, and p->xxx.errno is set appropriately.
|
||||||
|
*
|
||||||
|
* @paremeter error contains pointer to object to handle error code and message
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
mc_pread (mc_pipe_t * p, GError ** error)
|
||||||
|
{
|
||||||
|
gboolean read_out, read_err;
|
||||||
|
fd_set fds;
|
||||||
|
int maxfd = 0;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
if (error != NULL)
|
||||||
|
*error = NULL;
|
||||||
|
|
||||||
|
read_out = p->out.fd >= 0 && p->out.len > 0;
|
||||||
|
read_err = p->err.fd >= 0 && p->err.len > 0;
|
||||||
|
|
||||||
|
if (!read_out && !read_err)
|
||||||
|
{
|
||||||
|
p->out.len = MC_PIPE_STREAM_UNREAD;
|
||||||
|
p->err.len = MC_PIPE_STREAM_UNREAD;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FD_ZERO (&fds);
|
||||||
|
if (read_out)
|
||||||
|
{
|
||||||
|
FD_SET (p->out.fd, &fds);
|
||||||
|
maxfd = p->out.fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read_err)
|
||||||
|
{
|
||||||
|
FD_SET (p->err.fd, &fds);
|
||||||
|
maxfd = max (maxfd, p->err.fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* no timeout */
|
||||||
|
res = select (maxfd + 1, &fds, NULL, NULL, NULL);
|
||||||
|
if (res < 0 && errno != EINTR)
|
||||||
|
{
|
||||||
|
mc_propagate_error (error, MC_PIPE_ERROR_READ,
|
||||||
|
_
|
||||||
|
("Unexpected error in select() reading data from a child process:\n%s"),
|
||||||
|
unix_error_string (errno));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read_out)
|
||||||
|
mc_pread_stream (&p->out, &fds);
|
||||||
|
else
|
||||||
|
p->out.len = MC_PIPE_STREAM_UNREAD;
|
||||||
|
|
||||||
|
if (read_err)
|
||||||
|
mc_pread_stream (&p->err, &fds);
|
||||||
|
else
|
||||||
|
p->err.len = MC_PIPE_STREAM_UNREAD;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
/**
|
||||||
|
* Close pipe and destroy pipe descriptor.
|
||||||
|
*
|
||||||
|
* @paremeter p pipe descriptor
|
||||||
|
* @paremeter error contains pointer to object to handle error code and message
|
||||||
|
*/
|
||||||
|
|
||||||
|
void
|
||||||
|
mc_pclose (mc_pipe_t * p, GError ** error)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
|
||||||
|
if (p->out.fd >= 0)
|
||||||
|
res = close (p->out.fd);
|
||||||
|
if (p->err.fd >= 0)
|
||||||
|
res = close (p->err.fd);
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
int status;
|
||||||
|
|
||||||
|
res = waitpid (p->child_pid, &status, 0);
|
||||||
|
}
|
||||||
|
while (res < 0 && errno == EINTR);
|
||||||
|
|
||||||
|
if (res < 0)
|
||||||
|
mc_replace_error (error, MC_PIPE_ERROR_READ, _("Unexpected error in waitpid():\n%s"),
|
||||||
|
unix_error_string (errno));
|
||||||
|
|
||||||
|
g_free (p);
|
||||||
|
}
|
||||||
|
|
||||||
/* --------------------------------------------------------------------------------------------- */
|
/* --------------------------------------------------------------------------------------------- */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
Reference in New Issue
Block a user