--- v2 Changes: - Made the cmd_substitute function work when called by either 's' or 'substitute'. The rest of the patch is unchanged from v1. Please note that the error handling in this RFC patch is still very rudimentary. If we go down this route we would have to make it at least somewhat more robust. vis.c | 179 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 178 insertions(+), 1 deletion(-) diff --git a/vis.c b/vis.c index cd2fb5b..4e116e4 100644 --- a/vis.c +++ b/vis.c _AT_@ -30,6 +30,7 @@ #include <sys/stat.h> #include <sys/ioctl.h> #include <sys/mman.h> +#include <sys/wait.h> #include "ui-curses.h" #include "editor.h" _AT_@ -413,6 +414,14 @@ static bool cmd_write(Filerange*, enum CmdOpt, const char *argv[]); * associate the new name with the buffer. further :w commands * without arguments will write to the new filename */ static bool cmd_saveas(Filerange*, enum CmdOpt, const char *argv[]); +/* Run external commands by forking a child in which to start them. The + * bytes from range will be sent to the stdin of the external program and + * the stdout will replace the bytes in the range after the program has + * finished */ +static bool exec_external_cmd(Filerange *range, enum CmdOpt opt, char * const argv[]); +/* Run external commands by forking a child in which to start them. Vis + * will wait for the program to return before continuing */ +static bool exec_external_cmd_without_range(enum CmdOpt opt, char * const argv[]); static void action_reset(Action *a); static void switchmode_to(Mode *new_mode); _AT_@ -1561,8 +1570,176 @@ static bool cmd_read(Filerange *range, enum CmdOpt opt, const char *argv[]) { return true; } +static bool exec_external_cmd(Filerange *range, enum CmdOpt opt, char * const argv[]) { + pid_t pid = 0; + int outputpipe[2], inputpipe[2], pret, readcount; + char *inbuf; + char tmpbuf[BUFSIZ]; + size_t totalread, inputlen, curpos; + + if (!range) + return exec_external_cmd_without_range(opt, argv); + + Buffer *outbuf = calloc(1, sizeof(Buffer)); + + pret = pipe(outputpipe); + if (pret < 0) { + editor_info_show(vis, "Could not make the output pipe for the external command."); + return false; + } + pret = pipe(inputpipe); + if (pret < 0) { + editor_info_show(vis, "Could not make the input pipe for the external command."); + return false; + } + + pid = fork(); + if (pid < 0) { + editor_info_show(vis, "Could not start the external command."); + return false; + } + + if (pid == 0) { + close(outputpipe[0]); + dup2(outputpipe[1], STDOUT_FILENO); + + close(inputpipe[1]); + dup2(inputpipe[0], STDIN_FILENO); + + execvp(argv[0], argv); + } + + close(inputpipe[0]); + + Text *text = vis->win->file->text; + inputlen = range->end-range->start; + + inbuf = malloc(inputlen); + if (!inbuf) { + editor_info_show(vis, "Could not allocate input buffer for external program."); + return false; + } + + // Save cursor position in order to restore it after replacing + // the filtered text. + View *view = vis->win->view; + CursorPos curspos = view_cursor_getpos(view); + text_bytes_get(text, range->start, inputlen, inbuf); + + int wrote = 0; + int totalwritten = 0; + int towrite = inputlen; + + readcount = totalread = 0; + + close(outputpipe[1]); + if (!buffer_alloc(outbuf, BUFSIZ)) { + editor_info_show(vis, "Could not allocate output buffer for external program."); + return false; + } + + int fret = fcntl(outputpipe[0], F_SETFL, O_NONBLOCK); + if (fret < 0) { + editor_info_show(vis, "Could not set output pipe to nonblocking."); + close(inputpipe[1]); + close(outputpipe[0]); + return false; + } + + fret = fcntl(inputpipe[1], F_SETFL, O_NONBLOCK); + if (fret < 0) { + editor_info_show(vis, "Could not set input pipe to nonblocking."); + close(inputpipe[1]); + close(outputpipe[0]); + return false; + } + + // TODO: This is fugly and should be replaced with a better + // version... + // In order not to starve the external command or getting stuck when + // reading from its stdout we write and read from it in parallel and + // asynchronously. As soon as we have written all the text data to + // it, we restart another reading loop until the output pipe has + // seen an EOF (when read returns zero). + while (towrite > totalwritten) { + wrote = write(inputpipe[1], inbuf+totalwritten, towrite-totalwritten); + if (wrote > 0) + totalwritten += wrote; + + readcount = read(outputpipe[0], tmpbuf, sizeof(tmpbuf)); + if (readcount < 0) + continue; + totalread += readcount; + buffer_append(outbuf, tmpbuf, readcount); + } + close(inputpipe[1]); + free(inbuf); + + while (readcount != 0) { + readcount = read(outputpipe[0], tmpbuf, sizeof(tmpbuf)); + if (readcount < 0) + continue; + + totalread += readcount; + buffer_append(outbuf, tmpbuf, readcount); + } + close(outputpipe[0]); + + + editor_delete(vis, range->start, inputlen); + editor_insert(vis, range->start, outbuf->data, totalread); + + buffer_free(outbuf); + free(outbuf); + + curpos = text_pos_by_lineno(text, curspos.line); + view_cursor_to(view, curpos); + + return true; +} + +static bool exec_external_cmd_without_range(enum CmdOpt opt, char * const argv[]) { + pid_t pid = 0; + int status; + + pid = fork(); + if (pid < 0) { + editor_info_show(vis, "Could not fork to start the external command."); + return false; + } + + if (pid == 0) { + int null = open("/dev/null", O_WRONLY); + if (null < 0) + return false; + dup2(null, STDOUT_FILENO); + execvp(argv[0], argv); + } + + // Wait for the child to return. + waitpid(pid, &status, 0); + return status; +} + static bool cmd_substitute(Filerange *range, enum CmdOpt opt, const char *argv[]) { - // TODO + char *arg = (char *)argv[0]; + char *termchk = arg; + + // Insert the s for sed to recognize the argument. + while (*arg) arg++; + *arg = 's'; + char *sep = arg+1; + + while (*termchk) termchk++; + char *end = termchk-1; + if(*end != *sep && *end != 'i' && *end != 'g') { + editor_info_show(vis, "Sed command not properly terminated."); + return false; + } + + char * const sedcmd[] = {"sed", arg, (char *)NULL}; + exec_external_cmd(range, opt, sedcmd); + return true; } -- 2.4.0Received on Sat May 09 2015 - 18:12:54 CEST
This archive was generated by hypermail 2.3.0 : Sat May 09 2015 - 18:24:13 CEST