From 86f7bc18685732d0a7bf744e004a0c751d07ad61 Mon Sep 17 00:00:00 2001
From: David Lawrence Ramsey <pooka109@gmail.com>
Date: Thu, 9 Feb 2017 12:26:27 -0600
Subject: [PATCH] files: revamp the insertion of a file, to be more like
 pasting text

Move buffer handling and '\r' stripping from read_line() to read_file(),
so that the file gets its format determined and gets stored in its own
buffer entirely in one function.  Then use ingraft_buffer() to insert
this new buffer into the current one.

In addition to pasting the file at current[current_x], ingraft_buffer()
also deals with renumbering, the updating of totsize, and the handling
of a magicline, so read_file() doesn't need to do those anymore.

Note that all this makes read_file() depend on the position of
current[current_x] to know where to insert the file.  Accordingly,
set current_x to zero in initialize_buffer_text() instead of in
make_new_buffer(), so that replace_buffer() keeps working properly.
---
 src/files.c | 187 +++++++++++++++++++---------------------------------
 src/proto.h |   2 +-
 2 files changed, 69 insertions(+), 120 deletions(-)

diff --git a/src/files.c b/src/files.c
index dac96ac1..e9f40485 100644
--- a/src/files.c
+++ b/src/files.c
@@ -91,7 +91,6 @@ void make_new_buffer(void)
 
     initialize_buffer_text();
 
-    openfile->current_x = 0;
     openfile->placewewant = 0;
     openfile->current_y = 0;
 
@@ -129,6 +128,7 @@ void initialize_buffer_text(void)
     openfile->edittop = openfile->fileage;
     openfile->current = openfile->fileage;
 
+    openfile->current_x = 0;
     openfile->totsize = 0;
 }
 
@@ -683,44 +683,14 @@ int is_file_writable(const char *filename)
     return result;
 }
 
-/* Make a new line of text from the given buf, which is of length buf_len.
- * Then attach this line after prevnode. */
-filestruct *read_line(char *buf, size_t buf_len, filestruct *prevnode)
+/* Encode any NUL bytes in the given line of text, which is of length buf_len,
+ * and return a dynamically allocated copy of the resultant string. */
+char *encode_data(char *buf, size_t buf_len)
 {
-    filestruct *freshline = (filestruct *)nmalloc(sizeof(filestruct));
-
-    /* Convert nulls to newlines.  buf_len is the string's real length. */
     unsunder(buf, buf_len);
     buf[buf_len] = '\0';
 
-    assert(openfile->fileage != NULL && strlen(buf) == buf_len);
-
-#ifndef NANO_TINY
-    /* If file conversion isn't disabled, strip a '\r' from the line. */
-    if (buf_len > 0 && buf[buf_len - 1] == '\r' && !ISSET(NO_CONVERT))
-	buf[buf_len - 1] = '\0';
-#endif
-
-    freshline->data = mallocstrcpy(NULL, buf);
-
-#ifndef DISABLE_COLOR
-    freshline->multidata = NULL;
-#endif
-
-    freshline->prev = prevnode;
-
-    if (prevnode == NULL) {
-	/* Special case: we're inserting into the first line. */
-	freshline->next = openfile->fileage;
-	openfile->fileage = freshline;
-	freshline->lineno = 1;
-    } else {
-	prevnode->next = freshline;
-	freshline->next = NULL;
-	freshline->lineno = prevnode->lineno + 1;
-    }
-
-    return freshline;
+    return mallocstrcpy(NULL, buf);
 }
 
 /* Read an open file into the current buffer.  f should be set to the
@@ -731,6 +701,8 @@ filestruct *read_line(char *buf, size_t buf_len, filestruct *prevnode)
 void read_file(FILE *f, int fd, const char *filename, bool undoable,
 		bool checkwritable)
 {
+    ssize_t was_lineno = openfile->current->lineno;
+	/* The line number where we start the insertion. */
     size_t num_lines = 0;
 	/* The number of lines in the file. */
     size_t len = 0;
@@ -741,8 +713,10 @@ void read_file(FILE *f, int fd, const char *filename, bool undoable,
 	/* The buffer in which we assemble each line of the file. */
     size_t bufx = MAX_BUF_SIZE;
 	/* The allocated size of the line buffer; increased as needed. */
-    filestruct *fileptr = openfile->current->prev;
-	/* The line after which to start inserting. */
+    filestruct *topline;
+	/* The top of the new buffer where we store the read file. */
+    filestruct *bottomline;
+	/* The bottom of the new buffer. */
     int input_int;
 	/* The current value we read from the file, whether an input
 	 * character or EOF. */
@@ -760,7 +734,11 @@ void read_file(FILE *f, int fd, const char *filename, bool undoable,
 	add_undo(INSERT);
 #endif
 
-    /* Read the entire file into the filestruct. */
+    /* Create an empty buffer. */
+    topline = make_new_node(NULL);
+    bottomline = topline;
+
+    /* Read the entire file into the new buffer. */
     while ((input_int = getc(f)) != EOF) {
 	input = (char)input_int;
 
@@ -777,14 +755,6 @@ void read_file(FILE *f, int fd, const char *filename, bool undoable,
 		if (format == 0 || format == 2)
 		    format++;
 	    }
-#endif
-	    /* Read in the line properly. */
-	    fileptr = read_line(buf, len, fileptr);
-	    num_lines++;
-
-	    /* Reset the length in preparation for the next line. */
-	    len = 0;
-#ifndef NANO_TINY
 	/* If it's a Mac file ('\r' without '\n' on the first line if we
 	 * think it's a *nix file, or on any line otherwise), and file
 	 * conversion isn't disabled, handle it! */
@@ -795,15 +765,6 @@ void read_file(FILE *f, int fd, const char *filename, bool undoable,
 	     * set format to both DOS and Mac. */
 	    if (format == 0 || format == 1)
 		format += 2;
-
-	    /* Read in the line properly. */
-	    fileptr = read_line(buf, len, fileptr);
-	    num_lines++;
-
-	    /* Store the character after the \r as the first character
-	     * of the next line. */
-	    buf[0] = input;
-	    len = 1;
 #endif
 	} else {
 	    /* Store the character. */
@@ -819,7 +780,30 @@ void read_file(FILE *f, int fd, const char *filename, bool undoable,
 		bufx += MAX_BUF_SIZE;
 		buf = charealloc(buf, bufx);
 	    }
+	    continue;
 	}
+
+#ifndef NANO_TINY
+	/* If it's a DOS or Mac line, strip the '\r' from it. */
+	if (len > 0 && buf[len - 1] == '\r' && !ISSET(NO_CONVERT))
+	    buf[--len] = '\0';
+#endif
+
+	/* Store the data and make a new line. */
+	bottomline->data = encode_data(buf, len);
+	bottomline->next = make_new_node(bottomline);
+	bottomline = bottomline->next;
+	num_lines++;
+
+	/* Reset the length in preparation for the next line. */
+	len = 0;
+
+#ifndef NANO_TINY
+	/* If it happens to be a Mac line, store the character after the \r
+	 * as the first character of the next line. */
+	if (input != '\n')
+	    buf[len++] = input;
+#endif
     }
 
     /* Perhaps this could use some better handling. */
@@ -831,78 +815,43 @@ void read_file(FILE *f, int fd, const char *filename, bool undoable,
 	writable = is_file_writable(filename);
     }
 
-    /* Did we not get a newline and still have stuff to do? */
-    if (len > 0) {
+    /* If the file ended with newline, or it was entirely empty, make the
+     * last line blank.  Otherwise, put the last read data in. */
+    if (len == 0)
+	bottomline->data = mallocstrcpy(NULL, "");
+    else {
+	bool mac_line_needs_newline = FALSE;
+
 #ifndef NANO_TINY
-	/* If file conversion isn't disabled and the last character in
-	 * this file is '\r', set format to Mac if we currently think
-	 * the file is a *nix file, or to both DOS and Mac if we
-	 * currently think the file is a DOS file. */
-	if (buf[len - 1] == '\r' && !ISSET(NO_CONVERT) && format < 2)
-	    format += 2;
-#endif
-	/* Read in the last line properly. */
-	fileptr = read_line(buf, len, fileptr);
-	num_lines++;
-    }
+	/* If the final character is '\r', and file conversion isn't disabled,
+	 * set format to Mac if we currently think the file is a *nix file, or
+	 * to DOS-and-Mac if we currently think it is a DOS file. */
+	if (buf[len - 1] == '\r' && !ISSET(NO_CONVERT)) {
+	    if (format < 2)
+		format += 2;
 
-    free(buf);
+	    /* Strip the carriage return. */
+	    buf[--len] = '\0';
 
-    /* Attach the file we got to the filestruct.  If we got a file of
-     * zero bytes, don't do anything. */
-    if (num_lines > 0) {
-	/* If the file we got doesn't end in a newline (nor in a Mac return),
-	 * tack its last line onto the beginning of the line at current. */
-	if (len > 0 && (input != '\r' || ISSET(NO_CONVERT))) {
-	    filestruct *dropline = fileptr;
-	    size_t current_len = strlen(openfile->current->data);
-
-	    /* Adjust the current x-coordinate to compensate for the
-	     * change in the current line. */
-	    if (num_lines == 1)
-		openfile->current_x += len;
-	    else
-		openfile->current_x = len;
-
-	    /* Tack the text at fileptr onto the beginning of the text
-	     * at current. */
-	    openfile->current->data = charealloc(openfile->current->data,
-						len + current_len + 1);
-	    charmove(openfile->current->data + len, openfile->current->data,
-			current_len + 1);
-	    strncpy(openfile->current->data, fileptr->data, len);
-
-	    /* Step back one line, and blow away the unterminated line,
-	     * since its text has been copied into current. */
-	    fileptr = fileptr->prev;
-	    delete_node(dropline);
+	    /* Indicate we need to put a blank line in after this one. */
+	    mac_line_needs_newline = TRUE;
 	}
+#endif
+	/* Store the data of the final line. */
+	bottomline->data = encode_data(buf, len);
+	num_lines++;
 
-	if (fileptr == NULL)
-	    /* After inserting a single unterminated line at the top,
-	     * readjust the top-of-file pointer. */
-	    openfile->fileage = openfile->current;
-	else {
-	    /* Attach the line at current after the line at fileptr. */
-	    fileptr->next = openfile->current;
-	    openfile->current->prev = fileptr;
+	if (mac_line_needs_newline) {
+	    bottomline->next = make_new_node(bottomline);
+	    bottomline = bottomline->next;
+	    bottomline->data = mallocstrcpy(NULL, "");
 	}
-
-	/* Renumber, starting with the last line of the file we inserted. */
-	renumber(openfile->current);
     }
 
-    openfile->totsize += get_totsize(openfile->fileage, openfile->filebot);
+    free(buf);
 
-    /* If the NO_NEWLINES flag isn't set, and text has been added to
-     * the magicline (i.e. a file that doesn't end in a newline has been
-     * inserted at the end of the current buffer), add a new magicline,
-     * and move the current line down to it. */
-    if (!ISSET(NO_NEWLINES) && openfile->filebot->data[0] != '\0') {
-	new_magicline();
-	openfile->current = openfile->filebot;
-	openfile->current_x = 0;
-    }
+    /* Insert the just read buffer into the current one. */
+    ingraft_buffer(topline);
 
     /* Set the desired x position at the end of what was inserted. */
     openfile->placewewant = xplustabs();
@@ -931,7 +880,7 @@ void read_file(FILE *f, int fd, const char *filename, bool undoable,
 	statusline(HUSH, P_("Read %lu line", "Read %lu lines",
 			(unsigned long)num_lines), (unsigned long)num_lines);
 
-    if (num_lines < editwinrows)
+    if (openfile->current->lineno - was_lineno < editwinrows)
 	focusing = FALSE;
 
 #ifndef NANO_TINY
diff --git a/src/proto.h b/src/proto.h
index 45cba518..0a5dc513 100644
--- a/src/proto.h
+++ b/src/proto.h
@@ -301,7 +301,7 @@ void switch_to_prev_buffer_void(void);
 void switch_to_next_buffer_void(void);
 bool close_buffer(void);
 #endif
-filestruct *read_line(char *buf, size_t buf_len, filestruct *prevnode);
+char *encode_data(char *buf, size_t buf_len);
 void read_file(FILE *f, int fd, const char *filename, bool undoable,
 		bool checkwritable);
 int open_file(const char *filename, bool newfie, bool quiet, FILE **f);
-- 
GitLab