From ab65ab5d027e97ead372264a6ecfd3ac642299ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Andr=C3=A9=20Tanner?= Date: Fri, 25 Nov 2016 16:46:38 +0100 Subject: vis: fix I/O redirection bugs, cleanup vis_pipe The `:!` command did redirect stdout to a pipe which was used by `vis-menu` to return the selected entry. However, this breaks other interactive commands such as `:!/bin/sh` where command output was never displayed. Instead we modified `vis-menu` to re-open /dev/tty for its user interface which makes it work as a regular filter `:|` This patch also obsoletes the interactive flag previously passed to the vis_pipe function. Interactive mode is instead enabled by piping an invalid range. --- main.c | 2 +- register.c | 4 ++-- sam.c | 8 ++++---- vis-cmds.c | 4 ++-- vis.c | 17 +++++++++-------- vis.h | 10 +++++----- 6 files changed, 23 insertions(+), 22 deletions(-) diff --git a/main.c b/main.c index c558ced..1b6f14d 100644 --- a/main.c +++ b/main.c @@ -2147,7 +2147,7 @@ static char *get_completion_prefix(Vis *vis, Filerange (*text_object)(Text *, si static void insert_dialog_selection(Vis *vis, Filerange *range, const char *argv[]) { char *out = NULL, *err = NULL; - if (vis_pipe_collect(vis, range, true, argv, &out, &err) == 0) { + if (vis_pipe_collect(vis, range, argv, &out, &err) == 0) { View *view = vis_view(vis); size_t len = out ? strlen(out) : 0; for (Cursor *c = view_cursors(view); c; c = view_cursors_next(c)) { diff --git a/register.c b/register.c index a5f70ea..16a7233 100644 --- a/register.c +++ b/register.c @@ -29,7 +29,7 @@ const char *register_get(Vis *vis, Register *reg, size_t *len) { buffer_init(&buferr); buffer_clear(®->buf); - int status = vis_pipe(vis, &(Filerange){ .start = 0, .end = 0 }, false, + int status = vis_pipe(vis, &(Filerange){ .start = 0, .end = 0 }, (const char*[]){ VIS_CLIPBOARD, "--paste", NULL }, ®->buf, read_buffer, &buferr, read_buffer); @@ -70,7 +70,7 @@ bool register_put_range(Vis *vis, Register *reg, Text *txt, Filerange *range) { Buffer buferr; buffer_init(&buferr); - int status = vis_pipe(vis, range, false, (const char*[]){ VIS_CLIPBOARD, "--copy", NULL }, + int status = vis_pipe(vis, range, (const char*[]){ VIS_CLIPBOARD, "--copy", NULL }, NULL, NULL, &buferr, read_buffer); if (status != 0) diff --git a/sam.c b/sam.c index 93c41c2..594de1c 100644 --- a/sam.c +++ b/sam.c @@ -1398,7 +1398,7 @@ static bool cmd_filter(Vis *vis, Win *win, Command *cmd, const char *argv[], Cur Buffer buferr; buffer_init(&buferr); - int status = vis_pipe(vis, range, false, &argv[1], &filter, read_text, &buferr, read_buffer); + int status = vis_pipe(vis, range, &argv[1], &filter, read_text, &buferr, read_buffer); if (status == 0) { text_delete_range(txt, range); @@ -1422,8 +1422,8 @@ static bool cmd_filter(Vis *vis, Win *win, Command *cmd, const char *argv[], Cur } static bool cmd_launch(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { - Filerange empty = text_range_new(cur ? view_cursors_pos(cur) : range->start, EPOS); - return cmd_filter(vis, win, cmd, argv, cur, &empty); + Filerange invalid = text_range_new(cur ? view_cursors_pos(cur) : range->start, EPOS); + return cmd_filter(vis, win, cmd, argv, cur, &invalid); } static bool cmd_pipein(Vis *vis, Win *win, Command *cmd, const char *argv[], Cursor *cur, Filerange *range) { @@ -1446,7 +1446,7 @@ static bool cmd_pipeout(Vis *vis, Win *win, Command *cmd, const char *argv[], Cu Buffer buferr; buffer_init(&buferr); - int status = vis_pipe(vis, range, false, (const char*[]){ argv[1], NULL }, NULL, NULL, &buferr, read_buffer); + int status = vis_pipe(vis, range, (const char*[]){ argv[1], NULL }, NULL, NULL, &buferr, read_buffer); if (status == 0 && cur) view_cursors_to(cur, range->start); diff --git a/vis-cmds.c b/vis-cmds.c index 7218676..11a92dc 100644 --- a/vis-cmds.c +++ b/vis-cmds.c @@ -265,8 +265,8 @@ static const char *file_open_dialog(Vis *vis, const char *pattern) { if (!buffer_put0(&bufcmd, VIS_OPEN " ") || !buffer_append0(&bufcmd, pattern ? pattern : "")) return NULL; - Filerange empty = text_range_empty(); - int status = vis_pipe(vis, &empty, true, (const char*[]){ buffer_content0(&bufcmd), NULL }, + Filerange empty = text_range_new(0,0); + int status = vis_pipe(vis, &empty, (const char*[]){ buffer_content0(&bufcmd), NULL }, &bufout, read_buffer, &buferr, read_buffer); if (status == 0) diff --git a/vis.c b/vis.c index f8d3dbf..971af8b 100644 --- a/vis.c +++ b/vis.c @@ -1289,7 +1289,7 @@ Regex *vis_regex(Vis *vis, const char *pattern) { return regex; } -int vis_pipe(Vis *vis, Filerange *range, bool interactive, const char *argv[], +int vis_pipe(Vis *vis, Filerange *range, const char *argv[], void *stdout_context, ssize_t (*read_stdout)(void *stdout_context, char *data, size_t len), void *stderr_context, ssize_t (*read_stderr)(void *stderr_context, char *data, size_t len)) { @@ -1298,8 +1298,6 @@ int vis_pipe(Vis *vis, Filerange *range, bool interactive, const char *argv[], Text *text = vis->win->file->text; int pin[2], pout[2], perr[2], status = -1; bool valid_range = text_range_valid(range); - if (!valid_range) - interactive = true; Filerange rout = valid_range ? *range : text_range_new(0, 0); if (pipe(pin) == -1) @@ -1331,14 +1329,17 @@ int vis_pipe(Vis *vis, Filerange *range, bool interactive, const char *argv[], vis_info_show(vis, "fork failure: %s", strerror(errno)); return -1; } else if (pid == 0) { /* child i.e filter */ - if (!interactive || valid_range) + if (valid_range) dup2(pin[0], STDIN_FILENO); close(pin[0]); close(pin[1]); - dup2(pout[1], STDOUT_FILENO); + if (valid_range) + dup2(pout[1], STDOUT_FILENO); + else + dup2(STDERR_FILENO, STDOUT_FILENO); close(pout[1]); close(pout[0]); - if (!interactive) + if (valid_range) dup2(perr[1], STDERR_FILENO); close(perr[0]); close(perr[1]); @@ -1457,11 +1458,11 @@ static ssize_t read_buffer(void *context, char *data, size_t len) { return len; } -int vis_pipe_collect(Vis *vis, Filerange *range, bool interactive, const char *argv[], char **out, char **err) { +int vis_pipe_collect(Vis *vis, Filerange *range, const char *argv[], char **out, char **err) { Buffer bufout, buferr; buffer_init(&bufout); buffer_init(&buferr); - int status = vis_pipe(vis, range, interactive, argv, &bufout, read_buffer, &buferr, read_buffer); + int status = vis_pipe(vis, range, argv, &bufout, read_buffer, &buferr, read_buffer); buffer_terminate(&bufout); buffer_terminate(&buferr); if (out) diff --git a/vis.h b/vis.h index 9d6ba4d..75c3fe0 100644 --- a/vis.h +++ b/vis.h @@ -445,9 +445,9 @@ bool vis_prompt_cmd(Vis*, const char *cmd); /* pipe a given file range to an external process * - * in interactive mode stdin and stderr are not redirected, hence they - * can be used to read keyboard input and draw a user interface on top - * of vis. Attempting to pipe an invalid range enables interactive mode. + * if the range is invalid 'interactive' mode is enabled, meaning that + * stdin and stderr are passed through the underlying command, stdout + * points to vis' stderr. * * if argv contains only one non-NULL element the command is executed using * /bin/sh -c (i.e. argument expansion is performed by the shell). In contrast @@ -457,14 +457,14 @@ bool vis_prompt_cmd(Vis*, const char *cmd); * if read_std{out,err} are non-NULL they will be called when output from * the forked process is available. */ -int vis_pipe(Vis *vis, Filerange *range, bool interactive, const char *argv[], +int vis_pipe(Vis *vis, Filerange *range, const char *argv[], void *stdout_context, ssize_t (*read_stdout)(void *stdout_context, char *data, size_t len), void *stderr_context, ssize_t (*read_stderr)(void *stderr_context, char *data, size_t len)); /* pipe a range to an external application, return its exit status and store * everything that is written to stdout/stderr in the gitven char pointers * which have to be free(3)-ed by the caller */ -int vis_pipe_collect(Vis *vis, Filerange *range, bool interactive, const char *argv[], char **out, char **err); +int vis_pipe_collect(Vis *vis, Filerange *range, const char *argv[], char **out, char **err); /* given the start of a key, returns a pointer to the start of the one immediately * following as will be processed by the input system. skips over special keys -- cgit v1.2.3