diff options
| author | Marc André Tanner <mat@brain-dump.org> | 2016-04-01 19:18:06 +0200 |
|---|---|---|
| committer | Marc André Tanner <mat@brain-dump.org> | 2016-04-03 21:38:04 +0200 |
| commit | 4238bb65e6668b7173148915add30899c7035e56 (patch) | |
| tree | 2137d0c4bd214e695ffb610a6df8d901136afe0f | |
| parent | b069d265b0b2f51544f4cbd4816897e87b8ee308 (diff) | |
| download | vis-4238bb65e6668b7173148915add30899c7035e56.tar.gz vis-4238bb65e6668b7173148915add30899c7035e56.tar.xz | |
sam: implement | (filter) command
| -rw-r--r-- | sam.c | 85 |
1 files changed, 81 insertions, 4 deletions
@@ -23,6 +23,13 @@ typedef struct Address Address; typedef struct Command Command; typedef struct CommandDef CommandDef; +typedef struct { /* used to keep context when dealing with external proceses */ + Vis *vis; /* editor instance */ + Text *txt; /* text into which received data will be inserted */ + size_t pos; /* position at which to insert new data */ + Buffer err; /* used to store everything the process writes to stderr */ +} Filter; + struct Address { char type; /* # (char) l (line) g (goto line) / ? . $ + - , ; * */ Regex *regex; /* NULL denotes default for x, y, X, and Y commands */ @@ -72,6 +79,7 @@ static bool cmd_select(Vis*, Win*, Command*, Filerange*); static bool cmd_print(Vis*, Win*, Command*, Filerange*); static bool cmd_files(Vis*, Win*, Command*, Filerange*); static bool cmd_shell(Vis*, Win*, Command*, Filerange*); +static bool cmd_filter(Vis*, Win*, Command*, Filerange*); static bool cmd_substitute(Vis*, Win*, Command*, Filerange*); static bool cmd_write(Vis*, Win*, Command*, Filerange*); static bool cmd_read(Vis*, Win*, Command*, Filerange*); @@ -90,10 +98,9 @@ static const CommandDef cmds[] = { { { "y" }, CMD_CMD|CMD_REGEX|CMD_REGEX_DEFAULT, "p", cmd_extract }, { { "X" }, CMD_CMD|CMD_REGEX|CMD_REGEX_DEFAULT, NULL, cmd_files }, { { "Y" }, CMD_CMD|CMD_REGEX|CMD_REGEX_DEFAULT, NULL, cmd_files }, - { { "!" }, CMD_SHELL|CMD_ADDRESS_NONE, NULL, cmd_shell }, { { ">" }, CMD_SHELL, NULL, cmd_shell }, { { "<" }, CMD_SHELL, NULL, cmd_shell }, - { { "|" }, CMD_SHELL, NULL, cmd_shell }, + { { "|" }, CMD_SHELL, NULL, cmd_filter }, { { "w" }, CMD_ARGV|CMD_FORCE, NULL, cmd_write }, { { "r" }, CMD_FILE, NULL, cmd_read }, { { NULL }, 0, NULL, NULL }, @@ -435,8 +442,12 @@ static Command *command_parse(Vis *vis, const char **s, int level, enum SamError cmd->argv[0] = parse_cmdname(s); - if (!cmd->argv[0] && !**s) - cmd->argv[0] = strdup("p"); + if (!cmd->argv[0]) { + char name[2] = { **s ? **s : 'p', '\0' }; + if (**s) + (*s)++; + cmd->argv[0] = strdup(name); + } const CommandDef *cmddef = NULL; if (cmd->argv[0]) { @@ -954,3 +965,69 @@ static bool cmd_write(Vis *vis, Win *win, Command *cmd, Filerange *range) { static bool cmd_read(Vis *vis, Win *win, Command *cmd, Filerange *range) { return false; } + +static ssize_t read_stdout(void *context, char *data, size_t len) { + Filter *filter = context; + text_insert(filter->txt, filter->pos, data, len); + filter->pos += len; + return len; +} + +static ssize_t read_stderr(void *context, char *data, size_t len) { + Filter *filter = context; + buffer_append(&filter->err, data, len); + return len; +} + +static bool cmd_filter(Vis *vis, Win *win, Command *cmd, Filerange *range) { + Text *txt = win->file->text; + + Filter filter = { + .vis = vis, + .txt = txt, + .pos = range->end, + }; + + buffer_init(&filter.err); + + /* The general idea is the following: + * + * 1) take a snapshot + * 2) write [range.start, range.end] to exteneral command + * 3) read the output of the external command and insert it after the range + * 4) depending on the exit status of the external command + * - on success: delete original range + * - on failure: revert to previous snapshot + * + * 2) and 3) happend in small junks + */ + + text_snapshot(txt); + + const char *argv[] = { cmd->text, NULL }; + + int status = vis_pipe(vis, &filter, range, argv, read_stdout, read_stderr); + + if (status == 0) { + text_delete_range(txt, range); + text_snapshot(txt); + range->end = filter.pos - text_range_size(range); + } else { + /* make sure we have somehting to undo */ + text_insert(txt, filter.pos, " ", 1); + text_undo(txt); + } + + if (vis->cancel_filter) + vis_info_show(vis, "Command cancelled"); + else if (status == 0) + ; //vis_info_show(vis, "Command succeded"); + else if (filter.err.len > 0) + vis_info_show(vis, "Command failed: %s", filter.err.data); + else + vis_info_show(vis, "Command failed"); + + buffer_release(&filter.err); + + return !vis->cancel_filter && status == 0; +} |
