diff --git a/src/files.c b/src/files.c index c32fce1b..c4d1f764 100644 --- a/src/files.c +++ b/src/files.c @@ -1110,6 +1110,23 @@ void do_insertfile(void) i = 0; } #endif + if (func == flip_pipe) { + /* Remove or add the pipe character at the answer's head. */ + if (answer[0] == '|') { + charmove(answer, answer + 1, strlen(answer) + 1); + if (statusbar_x > 0) + statusbar_x--; + } else { + answer = charealloc(answer, strlen(answer) + 2); + charmove(answer + 1, answer, strlen(answer) + 1); + answer[0] = '|'; + statusbar_x++; + } + + given = mallocstrcpy(given, answer); + continue; + } + /* If we don't have a file yet, go back to the prompt. */ if (i != 0 && (!ISSET(MULTIBUFFER) || i != -2)) continue; diff --git a/src/global.c b/src/global.c index f5e6c0e7..4befbc4b 100644 --- a/src/global.c +++ b/src/global.c @@ -152,6 +152,8 @@ char *word_chars = NULL; char *answer = NULL; /* The answer string used by the statusbar prompt. */ +size_t statusbar_x = HIGHEST_POSITIVE; + /* The cursor position in answer. */ ssize_t tabsize = -1; /* The width of a tab in spaces. The default is set in main(). */ @@ -311,6 +313,9 @@ void backup_file_void(void) void flip_execute(void) { } +void flip_pipe(void) +{ +} #endif #ifdef ENABLE_MULTIBUFFER void flip_newbuffer(void) @@ -524,6 +529,8 @@ void shortcut_init(void) N_("Write the current buffer (or the marked region) to disk"); const char *readfile_gist = N_("Insert another file into current buffer (or into new buffer)"); + const char *pipe_gist = + N_("Pipe the current buffer (or marked region) to the command"); const char *whereis_gist = N_("Search forward for a string or a regular expression"); const char *wherewas_gist = @@ -1034,6 +1041,9 @@ void shortcut_init(void) add_to_funcs(flip_newbuffer, MINSERTFILE|MEXTCMD, N_("New Buffer"), WITHORSANS(newbuffer_gist), TOGETHER, NOVIEW); #endif + if (!ISSET(RESTRICTED)) + add_to_funcs(flip_pipe, MEXTCMD, + N_("Pipe Text"), WITHORSANS(pipe_gist), TOGETHER, NOVIEW); #ifdef ENABLE_BROWSER /* The file browser is only available when not in restricted mode. */ @@ -1329,8 +1339,10 @@ void shortcut_init(void) #endif #ifdef ENABLE_MULTIBUFFER /* Only when not in restricted mode, allow multiple buffers. */ - if (!ISSET(RESTRICTED)) + if (!ISSET(RESTRICTED)) { add_to_sclist(MINSERTFILE|MEXTCMD, "M-F", 0, flip_newbuffer, 0); + add_to_sclist(MEXTCMD, "M-\\", 0, flip_pipe, 0); + } #endif #ifdef ENABLE_BROWSER /* Only when not in restricted mode, allow entering the file browser. */ diff --git a/src/prompt.c b/src/prompt.c index 47d3131b..07130568 100644 --- a/src/prompt.c +++ b/src/prompt.c @@ -25,8 +25,6 @@ static char *prompt = NULL; /* The prompt string used for statusbar questions. */ -static size_t statusbar_x = HIGHEST_POSITIVE; - /* The cursor position in answer. */ #ifdef ENABLE_MOUSE /* Handle a mouse click on the statusbar prompt or the shortcut list. */ diff --git a/src/proto.h b/src/proto.h index 8cc8d14f..1100a91a 100644 --- a/src/proto.h +++ b/src/proto.h @@ -124,6 +124,7 @@ extern char *quoteerr; extern char *word_chars; extern char *answer; +extern size_t statusbar_x; extern ssize_t tabsize; @@ -709,6 +710,7 @@ void append_void(void); void prepend_void(void); void backup_file_void(void); void flip_execute(void); +void flip_pipe(void); #endif #ifdef ENABLE_MULTIBUFFER void flip_newbuffer(void); diff --git a/src/text.c b/src/text.c index 80fd7aad..86857446 100644 --- a/src/text.c +++ b/src/text.c @@ -1079,10 +1079,30 @@ RETSIGTYPE cancel_command(int signal) nperror("kill"); } +/* Send the text that starts at the given line to file descriptor fd. */ +void send_data(const filestruct *line, int fd) +{ + FILE *tube = fdopen(fd, "w"); + + if (tube == NULL) + return; + + /* Send each line, except a final empty line. */ + while (line != NULL && (line->next != NULL || line->data[0] != '\0')) { + fprintf(tube, "%s%s", line->data, line->next == NULL ? "" : "\n"); + line = line->next; + } + + fclose(tube); +} + /* Execute command in a shell. Return TRUE on success. */ bool execute_command(const char *command) { - int fd[2]; + int fd[2], to_fd[2]; + /* The pipes through which text will written and read. */ + const bool has_selection = ISSET(MULTIBUFFER) ? openfile->prev->mark : openfile->mark; + const bool should_pipe = (command[0] == '|'); FILE *stream; const char *shellenv; struct sigaction oldaction, newaction; @@ -1090,8 +1110,9 @@ bool execute_command(const char *command) bool setup_failed = FALSE; /* Whether setting up the temporary SIGINT handler failed. */ - /* Create a pipe to read the command's output from. */ - if (pipe(fd) == -1) { + /* Create a pipe to read the command's output from, and, if needed, + * a pipe to feed the command's input through. */ + if (pipe(fd) == -1 || (should_pipe && pipe(to_fd) == -1)) { statusbar(_("Could not create pipe")); return FALSE; } @@ -1110,8 +1131,15 @@ bool execute_command(const char *command) dup2(fd[1], fileno(stdout)); dup2(fd[1], fileno(stderr)); + /* If the parent sends text, connect the read end of the + * feeding pipe to the child's input stream. */ + if (should_pipe) { + dup2(to_fd[0], fileno(stdin)); + close(to_fd[1]); + } + /* Run the given command inside the preferred shell. */ - execl(shellenv, tail(shellenv), "-c", command, NULL); + execl(shellenv, tail(shellenv), "-c", should_pipe ? &command[1] : command, NULL); /* If the exec call returns, there was an error. */ exit(1); @@ -1126,6 +1154,41 @@ bool execute_command(const char *command) return FALSE; } + /* If the command starts with "|", pipe buffer or region to the command. */ + if (should_pipe) { + filestruct *was_cutbuffer = cutbuffer; + cutbuffer = NULL; + + if (ISSET(MULTIBUFFER)) + switch_to_prev_buffer(); + + if (has_selection || !ISSET(MULTIBUFFER)) { + if (!has_selection) { + openfile->current = openfile->fileage; + openfile->current_x = 0; + } + add_undo(CUT); + do_cut_text(ISSET(MULTIBUFFER), !has_selection); + update_undo(CUT); + } + + if (fork() == 0) { + close(to_fd[0]); + send_data(cutbuffer != NULL ? cutbuffer : openfile->fileage, to_fd[1]); + close(to_fd[1]); + exit(0); + } + + close(to_fd[0]); + close(to_fd[1]); + + if (ISSET(MULTIBUFFER)) + switch_to_next_buffer(); + + free_filestruct(cutbuffer); + cutbuffer = was_cutbuffer; + } + /* Re-enable interpretation of the special control keys so that we get * SIGINT when Ctrl-C is pressed. */ enable_signals();