[PATCH] possible new feature: cutbuffer history

Xylia Allegretta <[email protected]> Tue, 17 Mar 2026 01:43:18 +0000
Newsgroups gmane.editors.nano.devel
Message-ID <CAE7GgQ8KNkXjX8h6dMLDRqWB6TMU3U-5OCBmDWiaoq7nkUA0cw@mail.gmail.com>
When working on a project, it sometimes becomes necessary to temporarily
remove some text for testing, with the intention of adding it back later. If
multi-line comments are not available or are already being used, doing this
normally require creating a temporary file and using multi-buffer mode to
copy the text over to it, and then doing the reverse later to move the text
back, before finally deleting the temporarily file. As this is somewhat
tedious when it is repeatedly necessarily to do this (e.g. comparing behaviour
with and without a change), this patch adds an option to automatically save
and load the previous cutbuffer, so that the text can be easily added and
removed across sessions without temporary files.

Signed-off-by: Xylia Allegretta <[email protected]>
cuthistory-1.patch (application/x-patch, 6.6 KB)
From ed8060640b9725e402b306bdcd72b102071bcebc Mon Sep 17 00:00:00 2001
From: Xylia Allegretta <[email protected]>
Date: Mon, 16 Mar 2026 21:25:47 -0400
Subject: [PATCH] possible new feature: cutbuffer history

When working on a project, it sometimes becomes necessary to temporarily
remove some text for testing, with the intention of adding it back later. If
multi-line comments are not available or are already being used, doing this
normally require creating a temporary file and using multi-buffer mode to
copy the text over to it, and then doing the reverse later to move the text
back, before finally deleting the temporarily file. As this is somewhat
tedious when it is repeatedly necessarily to do this (e.g. comparing behaviour
with and without a change), this patch adds an option to automatically save
and load the previous cutbuffer, so that the text can be easily added and
removed across sessions without temporary files.

Signed-off-by: Xylia Allegretta <[email protected]>
---
 src/definitions.h |   3 +-
 src/history.c     | 111 ++++++++++++++++++++++++++++++++++++++++++++++
 src/nano.c        |  11 ++++-
 src/prototypes.h  |   2 +
 4 files changed, 125 insertions(+), 2 deletions(-)

diff --git a/src/definitions.h b/src/definitions.h
index 020f8d38..e0499e8e 100644
--- a/src/definitions.h
+++ b/src/definitions.h
@@ -380,7 +380,8 @@ enum {
 	MINIBAR,
 	ZERO,
 	MODERN_BINDINGS,
-	SOLO_SIDESCROLL
+	SOLO_SIDESCROLL,
+	CUTBUFFERLOG
 };
 
 /* Structure types. */
diff --git a/src/history.c b/src/history.c
index d4be9778..ab49fe3f 100644
--- a/src/history.c
+++ b/src/history.c
@@ -34,6 +34,16 @@
 #define POSITION_HISTORY  "filepos_history"
 #endif
 
+#ifndef CUTBUFFER_HISTORY
+#define CUTBUFFER_HISTORY  "cutbuf_history"
+#endif
+
+#ifndef CUTFILE_SIZE
+#define CUTFILE_SIZE 4096
+	/* The maximum number of lines the cutbuffer history file can have. */
+	/* Not entirely sure what this should be set to, but I doubt most people will reach this one. */
+#endif
+
 static bool history_changed = FALSE;
 		/* Whether any of the history lists has changed. */
 static char *poshistname = NULL;
@@ -597,4 +607,105 @@ void restore_cursor_position_if_any(void)
 	if (item)
 		goto_line_and_column(item->linenumber, item->columnnumber, TRUE);
 }
+
+/* Load the recorded cutbuffer. */
+void load_cuthistory(void)
+{
+	char *cuthistname = concatenate(statedir, CUTBUFFER_HISTORY);
+	FILE *cutfile = fopen(cuthistname, "rb");
+
+	if (!cutfile) {
+		if (errno != ENOENT)
+			jot_error(N_("Error reading %s: %s"), cuthistname, strerror(errno));
+		free(cuthistname);
+		return;
+	}
+
+	/* Delete the cutbuffer, if it exists. Likely a needless precaution. */
+	if (cutbuffer) {
+		free_lines(cutbuffer);
+		cutbuffer = NULL;
+	}
+	cutbottom = NULL;
+
+	int count = 0;
+	size_t dummy = 0;
+	ssize_t length;
+	char *text = NULL;
+
+	/* Read each line, and store it in the cutbuffer. */
+	while (count++ < CUTFILE_SIZE && (length = getline(&text, &dummy, cutfile)) > 0 && text) {
+		linestruct *next;
+
+		/* Remove the newline, if present. */
+		if (text[length-1] == '\n')
+			text[length-1] = '\0';
+
+		/* Add read text to a line. */
+		/* There's no point in setting multidata, has_anchor or lineno,
+		 * as they should all be reset anyway upon pasting. */
+		next = nmalloc(sizeof(linestruct));
+		next->data = text;
+
+		/* Add line to the cutbuffer. */
+		next->prev = cutbottom;
+		if (cutbottom)
+			cutbottom->next = next;
+		else
+			cutbuffer = next;
+		cutbottom = next;
+
+		/* Prevent getline from overwriting the previous text. */
+		text = NULL;
+	}
+
+	if (cutbottom)
+		cutbottom->next = NULL;
+	if (text)
+		free(text);
+
+	if (fclose(cutfile) == EOF)
+		jot_error(N_("Error reading %s: %s"), cuthistname, strerror(errno));
+
+	free(cuthistname);
+}
+
+/* Save the current cutbuffer for use in the next session. */
+void save_cuthistory(void)
+{
+	char *cuthistname = concatenate(statedir, CUTBUFFER_HISTORY);
+	FILE *cutfile = fopen(cuthistname, "w");
+	linestruct *cutcurr = cutbuffer;
+	int count = 0;
+
+	if (!cutfile) {
+		jot_error(N_("Error writing %s: %s"), cuthistname, strerror(errno));
+		free(cuthistname);
+		return;
+	}
+
+	/* Don't allow others to read or write the history file. */
+	if (chmod(cuthistname, S_IRUSR | S_IWUSR) < 0)
+		jot_error(N_("Cannot limit permissions on %s: %s"), cuthistname, strerror(errno));
+
+	/* Copy contents of cutbuffer into file. */
+	while (cutcurr && count++ < CUTFILE_SIZE - 1) {
+		if (fprintf(cutfile, "%s\n", cutcurr->data) < 0) {
+			jot_error(N_("Error writing %s: %s"), cuthistname, strerror(errno));
+			break;
+		}
+		cutcurr = cutcurr->next;
+	}
+
+	/* Add a blank line if ending early due to reaching the size limit. */
+	if (cutcurr && count == CUTFILE_SIZE) {
+		if (putc('\n', cutfile) == EOF)
+			jot_error(N_("Error writing %s: %s"), cuthistname, strerror(errno));
+	}
+
+	if (fclose(cutfile) == EOF)
+		jot_error(N_("Error writing %s: %s"), cuthistname, strerror(errno));
+
+	free(cuthistname);
+}
 #endif /* ENABLE_HISTORIES */
diff --git a/src/nano.c b/src/nano.c
index 54abaf46..9b666ea6 100644
--- a/src/nano.c
+++ b/src/nano.c
@@ -289,6 +289,8 @@ void close_and_go(void)
 #ifdef ENABLE_HISTORIES
 		if (ISSET(HISTORYLOG))
 			save_history();
+		if (ISSET(CUTBUFFERLOG))
+			save_cuthistory();
 #endif
 		finish();
 	}
@@ -2164,6 +2166,9 @@ int main(int argc, char **argv)
 		}
 	}
 
+	/* Enable for testing. */
+	SET(CUTBUFFERLOG);
+
 #ifndef NANO_TINY
 	set_up_sigwinch_handler();
 #endif
@@ -2299,6 +2304,7 @@ int main(int argc, char **argv)
 #ifdef ENABLE_NANORC
 		UNSET(HISTORYLOG);
 		UNSET(POSITIONLOG);
+		UNSET(CUTBUFFERLOG);
 #endif
 	}
 
@@ -2320,9 +2326,10 @@ int main(int argc, char **argv)
 
 	/* If we need history files, verify that we have a directory for them,
 	 * and when not, cancel the options. */
-	if ((ISSET(HISTORYLOG) || ISSET(POSITIONLOG)) && !have_statedir()) {
+	if ((ISSET(HISTORYLOG) || ISSET(POSITIONLOG) || ISSET(CUTBUFFERLOG)) && !have_statedir()) {
 		UNSET(HISTORYLOG);
 		UNSET(POSITIONLOG);
+		UNSET(CUTBUFFERLOG);
 	}
 
 	/* If the user wants history persistence, read the relevant files. */
@@ -2330,6 +2337,8 @@ int main(int argc, char **argv)
 		load_history();
 	if (ISSET(POSITIONLOG))
 		load_poshistory();
+	if (ISSET(CUTBUFFERLOG))
+		load_cuthistory();
 #endif
 
 #ifndef NANO_TINY
diff --git a/src/prototypes.h b/src/prototypes.h
index ca4c00fc..5a73ceab 100644
--- a/src/prototypes.h
+++ b/src/prototypes.h
@@ -359,6 +359,8 @@ void save_history(void);
 void load_poshistory(void);
 void update_poshistory(void);
 void restore_cursor_position_if_any(void);
+void load_cuthistory(void);
+void save_cuthistory(void);
 #endif
 
 /* Most functions in move.c. */
-- 
2.52.0