From 383da453bafc494e11f05f0555b93a4de182b23c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marco=20Diego=20Aur=C3=A9lio=20Mesquita?= Date: Mon, 21 May 2018 19:18:17 -0300 Subject: [PATCH] Implement filter with external commands in nano. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marco Diego Aurélio Mesquita --- src/files.c | 17 +++++++++ src/global.c | 15 +++++++- src/nano.h | 2 +- src/prompt.c | 2 -- src/proto.h | 2 ++ src/text.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++++++++------- 6 files changed, 137 insertions(+), 17 deletions(-) diff --git a/src/files.c b/src/files.c index c32fce1..c4d1f76 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 f5e6c0e..8abee79 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) @@ -498,6 +503,7 @@ const char *unjust_tag = N_("Unjustify"); void shortcut_init(void) { const char *readfile_tag = N_("Read File"); + const char *pipe_tag = N_("Pipe text"); const char *whereis_tag = N_("Where Is"); const char *replace_tag = N_("Replace"); const char *gotoline_tag = N_("Go To Line"); @@ -524,6 +530,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 +1042,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, + pipe_tag, WITHORSANS(pipe_gist), TOGETHER, NOVIEW); #ifdef ENABLE_BROWSER /* The file browser is only available when not in restricted mode. */ @@ -1329,8 +1340,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/nano.h b/src/nano.h index 15eadd5..1bd8cf6 100644 --- a/src/nano.h +++ b/src/nano.h @@ -178,7 +178,7 @@ typedef enum { #ifdef ENABLE_COMMENT COMMENT, UNCOMMENT, PREFLIGHT, #endif - CUT, CUT_TO_EOF, PASTE, INSERT, OTHER + CUT, CUT_TO_EOF, PASTE, INSERT, COUPLE_START, COUPLE_END, OTHER } undo_type; /* Structure types. */ diff --git a/src/prompt.c b/src/prompt.c index 47d3131..0713056 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 8cc8d14..1100a91 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 80fd7aa..6ea4c1b 100644 --- a/src/text.c +++ b/src/text.c @@ -800,6 +800,15 @@ void do_undo(void) cutbuffer = oldcutbuffer; cutbottom = oldcutbottom; break; + case COUPLE_START: + undidmsg = _("filtering"); + break; + case COUPLE_END: + openfile->current_undo = openfile->current_undo->next; + do_undo(); + do_undo(); + do_undo(); + return; case INDENT: handle_indent_action(u, TRUE, TRUE); undidmsg = _("indent"); @@ -962,6 +971,15 @@ void do_redo(void) free_filestruct(u->cutbuffer); u->cutbuffer = NULL; break; + case COUPLE_START: + openfile->current_undo = u; + do_redo(); + do_redo(); + do_redo(); + return; + case COUPLE_END: + redidmsg = _("filtering"); + break; case INDENT: handle_indent_action(u, FALSE, TRUE); redidmsg = _("indent"); @@ -1079,10 +1097,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 from_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 +1128,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(from_fd) == -1 || (should_pipe && pipe(to_fd) == -1)) { statusbar(_("Could not create pipe")); return FALSE; } @@ -1103,29 +1142,73 @@ bool execute_command(const char *command) /* Fork a child process to run the command in. */ if ((pid = fork()) == 0) { - /* Child: close the unused read end of the pipe. */ - close(fd[0]); - - /* Connect the write end of the pipe to the process' output streams. */ - dup2(fd[1], fileno(stdout)); - dup2(fd[1], fileno(stderr)); + /* Child: close the unused read end of the output pipe. */ + close(from_fd[0]); + + /* Connect the write end of the output pipe to the process' output streams. */ + dup2(from_fd[1], fileno(stdout)); + dup2(from_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); } /* Parent: close the unused write end of the pipe. */ - close(fd[1]); + close(from_fd[1]); if (pid == -1) { statusbar(_("Could not fork")); - close(fd[0]); + close(from_fd[0]); 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(); + else + add_undo(COUPLE_START); + + if (has_selection || !ISSET(MULTIBUFFER)) { + if (!has_selection) { + openfile->current = openfile->fileage; + openfile->current_x = 0; + } + if (!ISSET(MULTIBUFFER)) add_undo(CUT); + do_cut_text(ISSET(MULTIBUFFER), !has_selection); + if (!ISSET(MULTIBUFFER)) 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); + } else { + 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(); @@ -1142,12 +1225,15 @@ bool execute_command(const char *command) } } - stream = fdopen(fd[0], "rb"); + stream = fdopen(from_fd[0], "rb"); if (stream == NULL) statusline(ALERT, _("Failed to open pipe: %s"), strerror(errno)); else read_file(stream, 0, "pipe", TRUE); + if (should_pipe && !ISSET(MULTIBUFFER)) + add_undo(COUPLE_END); + if (wait(NULL) == -1) nperror("wait"); @@ -1314,6 +1400,8 @@ void add_undo(undo_type action) u->lineno += cutbottom->lineno - cutbuffer->lineno; break; case INSERT: + case COUPLE_START: + case COUPLE_END: break; case INDENT: case UNINDENT: @@ -1474,6 +1562,8 @@ fprintf(stderr, " >> Updating an undo... action = %d\n", action); case INSERT: u->mark_begin_lineno = openfile->current->lineno; u->mark_begin_x = openfile->current_x; + case COUPLE_START: + case COUPLE_END: break; default: statusline(ALERT, "Wrong undo update type -- please report a bug"); -- 2.7.4