aboutsummaryrefslogtreecommitdiff
path: root/vis.c
diff options
context:
space:
mode:
Diffstat (limited to 'vis.c')
-rw-r--r--vis.c188
1 files changed, 187 insertions, 1 deletions
diff --git a/vis.c b/vis.c
index 8dc8365..c5f0908 100644
--- a/vis.c
+++ b/vis.c
@@ -27,6 +27,7 @@
#include "text-objects.h"
#include "util.h"
#include "vis-core.h"
+#include "sam.h"
/* enable large file optimization for files larger than: */
#define LARGE_FILE (1 << 25)
@@ -587,7 +588,7 @@ void action_do(Vis *vis, Action *a) {
vis_mode_switch(vis, VIS_MODE_NORMAL);
vis_cmd(vis, a->arg.s);
} else {
- vis_prompt_show(vis, ":'<,'>!");
+ vis_prompt_show(vis, ":|");
}
} else if (vis->mode == &vis_modes[VIS_MODE_OPERATOR_PENDING]) {
mode_set(vis, vis->mode_prev);
@@ -1117,6 +1118,191 @@ Regex *vis_regex(Vis *vis, const char *pattern) {
return regex;
}
+int vis_pipe(Vis *vis, void *context, Filerange *range, const char *argv[],
+ ssize_t (*read_stdout)(void *context, char *data, size_t len),
+ ssize_t (*read_stderr)(void *context, char *data, size_t len)) {
+
+ /* if an invalid range was given, stdin (i.e. key board input) is passed
+ * through the external command. */
+ Text *text = vis->win->file->text;
+ View *view = vis->win->view;
+ int pin[2], pout[2], perr[2], status = -1;
+ bool interactive = !text_range_valid(range);
+ size_t pos = view_cursor_get(view);
+ Filerange rout = *range;
+ if (interactive)
+ rout = (Filerange){ .start = pos, .end = pos };
+
+ if (pipe(pin) == -1)
+ return -1;
+ if (pipe(pout) == -1) {
+ close(pin[0]);
+ close(pin[1]);
+ return -1;
+ }
+
+ if (pipe(perr) == -1) {
+ close(pin[0]);
+ close(pin[1]);
+ close(pout[0]);
+ close(pout[1]);
+ return -1;
+ }
+
+ vis->ui->terminal_save(vis->ui);
+ pid_t pid = fork();
+
+ if (pid == -1) {
+ close(pin[0]);
+ close(pin[1]);
+ close(pout[0]);
+ close(pout[1]);
+ close(perr[0]);
+ close(perr[1]);
+ vis_info_show(vis, "fork failure: %s", strerror(errno));
+ return -1;
+ } else if (pid == 0) { /* child i.e filter */
+ if (!interactive)
+ dup2(pin[0], STDIN_FILENO);
+ close(pin[0]);
+ close(pin[1]);
+ dup2(pout[1], STDOUT_FILENO);
+ close(pout[1]);
+ close(pout[0]);
+ if (!interactive)
+ dup2(perr[1], STDERR_FILENO);
+ close(perr[0]);
+ close(perr[1]);
+ if (!argv[1])
+ execl("/bin/sh", "sh", "-c", argv[0], NULL);
+ else
+ execvp(argv[0], (char* const*)argv);
+ vis_info_show(vis, "exec failure: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ vis->cancel_filter = false;
+
+ close(pin[0]);
+ close(pout[1]);
+ close(perr[1]);
+
+ fcntl(pout[0], F_SETFL, O_NONBLOCK);
+ fcntl(perr[0], F_SETFL, O_NONBLOCK);
+
+
+ fd_set rfds, wfds;
+
+ do {
+ if (vis->cancel_filter) {
+ kill(-pid, SIGTERM);
+ break;
+ }
+
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ if (pin[1] != -1)
+ FD_SET(pin[1], &wfds);
+ if (pout[0] != -1)
+ FD_SET(pout[0], &rfds);
+ if (perr[0] != -1)
+ FD_SET(perr[0], &rfds);
+
+ if (select(FD_SETSIZE, &rfds, &wfds, NULL, NULL) == -1) {
+ if (errno == EINTR)
+ continue;
+ vis_info_show(vis, "Select failure");
+ break;
+ }
+
+ if (pin[1] != -1 && FD_ISSET(pin[1], &wfds)) {
+ Filerange junk = rout;
+ if (junk.end > junk.start + PIPE_BUF)
+ junk.end = junk.start + PIPE_BUF;
+ ssize_t len = text_write_range(text, &junk, pin[1]);
+ if (len > 0) {
+ rout.start += len;
+ if (text_range_size(&rout) == 0) {
+ close(pout[1]);
+ pout[1] = -1;
+ }
+ } else {
+ close(pin[1]);
+ pin[1] = -1;
+ if (len == -1)
+ vis_info_show(vis, "Error writing to external command");
+ }
+ }
+
+ if (pout[0] != -1 && FD_ISSET(pout[0], &rfds)) {
+ char buf[BUFSIZ];
+ ssize_t len = read(pout[0], buf, sizeof buf);
+ if (len > 0) {
+ if (read_stdout)
+ (*read_stdout)(context, buf, len);
+ } else if (len == 0) {
+ close(pout[0]);
+ pout[0] = -1;
+ } else if (errno != EINTR && errno != EWOULDBLOCK) {
+ vis_info_show(vis, "Error reading from filter stdout");
+ close(pout[0]);
+ pout[0] = -1;
+ }
+ }
+
+ if (perr[0] != -1 && FD_ISSET(perr[0], &rfds)) {
+ char buf[BUFSIZ];
+ ssize_t len = read(perr[0], buf, sizeof buf);
+ if (len > 0) {
+ if (read_stderr)
+ (*read_stderr)(context, buf, len);
+ } else if (len == 0) {
+ close(perr[0]);
+ perr[0] = -1;
+ } else if (errno != EINTR && errno != EWOULDBLOCK) {
+ vis_info_show(vis, "Error reading from filter stderr");
+ close(perr[0]);
+ perr[0] = -1;
+ }
+ }
+
+ } while (pin[1] != -1 || pout[0] != -1 || perr[0] != -1);
+
+ if (pin[1] != -1)
+ close(pin[1]);
+ if (pout[0] != -1)
+ close(pout[0]);
+ if (perr[0] != -1)
+ close(perr[0]);
+
+ for (pid_t died; (died = waitpid(pid, &status, 0)) != -1 && pid != died;);
+
+ vis->ui->terminal_restore(vis->ui);
+
+ return status;
+}
+
+bool vis_cmd(Vis *vis, const char *cmdline) {
+ if (!cmdline)
+ return true;
+ while (*cmdline == ':')
+ cmdline++;
+ size_t len = strlen(cmdline);
+ char *line = malloc(len+2);
+ if (!line)
+ return false;
+ strncpy(line, cmdline, len+1);
+
+ for (char *end = line + len - 1; end >= line && isspace((unsigned char)*end); end--)
+ *end = '\0';
+
+ enum SamError err = sam_cmd(vis, line);
+ if (err != SAM_ERR_OK)
+ vis_info_show(vis, "%s", sam_error(err));
+ free(line);
+ return err == SAM_ERR_OK;
+}
+
Text *vis_text(Vis *vis) {
return vis->win->file->text;
}