From 01ec3aca894334821b08b9ed04926878c036e56a Mon Sep 17 00:00:00 2001
From: Benno Schulenberg <bensberg@telfort.nl>
Date: Thu, 8 Oct 2020 19:51:51 +0200
Subject: [PATCH] tentative feature: allow piping the buffer to standard output

This can be used to decode a file, edit it, and then recode it
without writing the decoded data to disk:

	gpg --decrypt secrettext.gpg | src/nano - |
			gpg --encrypt --recipient username >editedtext.gpg
	mv editedtext.gpg secrettext.gpg

(One would need to ensure that nano's memory doesn't get swapped out to
disk.  Is there a way to do that, other than disabling swap entirely?)
---
 src/files.c      | 14 +++++++++++---
 src/global.c     |  3 +++
 src/nano.c       | 20 ++++++++++++++++++++
 src/prototypes.h |  2 ++
 4 files changed, 36 insertions(+), 3 deletions(-)

diff --git a/src/files.c b/src/files.c
index 3d95002f..696f69f4 100644
--- a/src/files.c
+++ b/src/files.c
@@ -1770,14 +1770,14 @@ bool write_file(const char *name, FILE *thefile, bool normal,
 #ifdef ENABLE_OPERATINGDIR
 	/* If we're writing a temporary file, we're probably going outside
 	 * the operating directory, so skip the operating directory test. */
-	if (normal && outside_of_confinement(realname, FALSE)) {
+	if (normal && !outpipe && outside_of_confinement(realname, FALSE)) {
 		statusline(ALERT, _("Can't write outside of %s"), operating_dir);
 		goto cleanup_and_exit;
 	}
 #endif
 #ifndef NANO_TINY
 	/* Check whether the file (at the end of the symlink) exists. */
-	is_existing_file = normal && (stat(realname, &fileinfo) != -1);
+	is_existing_file = normal && !outpipe && (stat(realname, &fileinfo) != -1);
 
 	/* If we haven't statted this file before (say, the user just specified
 	 * it interactively), stat and save the value now, or else we will chase
@@ -1845,6 +1845,7 @@ bool write_file(const char *name, FILE *thefile, bool normal,
 		mode_t permissions = (normal ? RW_FOR_ALL : S_IRUSR|S_IWUSR);
 		int fd;
 
+		if (!outpipe) {
 #ifndef NANO_TINY
 		block_sigwinch(TRUE);
 		if (normal)
@@ -1873,6 +1874,8 @@ bool write_file(const char *name, FILE *thefile, bool normal,
 #endif
 			goto cleanup_and_exit;
 		}
+		} else
+			fd = outpipe;
 
 		thefile = fdopen(fd, (method == APPEND) ? "ab" : "wb");
 
@@ -1962,7 +1965,7 @@ bool write_file(const char *name, FILE *thefile, bool normal,
 
 	if (!is_existing_file || !S_ISFIFO(fileinfo.st_mode))
 		/* Ensure the data has reached the disk before reporting it as written. */
-		if (fflush(thefile) != 0 || fsync(fileno(thefile)) != 0) {
+		if (!outpipe && (fflush(thefile) != 0 || fsync(fileno(thefile)) != 0)) {
 			statusline(ALERT, _("Error writing %s: %s"), realname, strerror(errno));
 			fclose(thefile);
 			goto cleanup_and_exit;
@@ -2118,6 +2121,11 @@ int write_it_out(bool exiting, bool withprompt)
 	as_an_at = FALSE;
 
 #ifndef NANO_TINY
+	if (outpipe) {
+		write_file("", NULL, FALSE, method, TRUE);
+		finish();
+	}
+
 	given = copy_of((openfile->mark && !exiting) ? "" : openfile->filename);
 #else
 	given = copy_of(openfile->filename);
diff --git a/src/global.c b/src/global.c
index 8af8dc8d..ff729185 100644
--- a/src/global.c
+++ b/src/global.c
@@ -31,6 +31,9 @@ volatile sig_atomic_t the_window_resized = FALSE;
 		/* Set to TRUE by the handler whenever a SIGWINCH occurs. */
 #endif
 
+int outpipe = 0;
+		/* Becomes nonzero when nano's output is connected to a pipe. */
+
 bool on_a_vt = FALSE;
 		/* Whether we're running on a Linux console (a VT). */
 bool shifted_metas = FALSE;
diff --git a/src/nano.c b/src/nano.c
index eb049996..6c9f8efa 100644
--- a/src/nano.c
+++ b/src/nano.c
@@ -1807,6 +1807,24 @@ int main(int argc, char **argv)
 		{NULL, 0, NULL, 0}
 	};
 
+#ifndef NANO_TINY
+	/* When standard output is not a terminal, then clone its file descriptor
+	 * to a new one (to be used for writing the buffer) and reconnect standard
+	 * output to the terminal so that we can use the screen for editing. */
+	if (!isatty(fileno(stdout))) {
+		int thetty = open("/dev/tty", O_WRONLY);
+
+		if (thetty < 1)
+			die(_("Could not reconnect stdout to terminal\n"));
+
+		outpipe = dup(fileno(stdout));
+		SET(SAVE_ON_EXIT);
+
+		dup2(thetty, fileno(stdout));
+		close(thetty);
+	}
+#endif
+
 #ifdef __linux__
 	struct vt_stat dummy;
 
@@ -2530,6 +2548,8 @@ int main(int argc, char **argv)
 #ifdef ENABLE_MULTIBUFFER
 	else {
 		openfile = openfile->next;
+		if (outpipe && more_than_one)
+			die(_("When piping, just one input file is allowed\n"));
 		if (more_than_one)
 			mention_name_and_linecount();
 		if (ISSET(VIEW_MODE))
diff --git a/src/prototypes.h b/src/prototypes.h
index 530c4b9f..27286b10 100644
--- a/src/prototypes.h
+++ b/src/prototypes.h
@@ -26,6 +26,8 @@
 extern volatile sig_atomic_t the_window_resized;
 #endif
 
+extern int outpipe;
+
 extern bool on_a_vt;
 extern bool shifted_metas;
 
-- 
2.37.4

