new feature: allow piping (selected) text to an external command

When executing a command, it is now possible to pipe the entire buffer
(or the marked region, if anything is marked) to the external command.
The output from the command replaces the buffer (or the marked region),
or goes to a new buffer.

This fulfills https://savannah.gnu.org/bugs/?28993,
and fulfills https://savannah.gnu.org/bugs/?53041.

Signed-off-by: Marco Diego Aurélio Mesquita <marcodiegomesquita@gmail.com>
Signed-off-by: Benno Schulenberg <bensberg@telfort.nl>
master
Marco Diego Aurélio Mesquita 2018-05-15 22:20:11 -03:00 committed by Benno Schulenberg
parent 299e710845
commit f304b9aee1
5 changed files with 99 additions and 7 deletions

View File

@ -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;

View File

@ -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. */

View File

@ -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. */

View File

@ -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);

View File

@ -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();