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.
master
David Lawrence Ramsey 2017-02-09 12:26:27 -06:00 committed by Benno Schulenberg
parent 3d9a4d6257
commit 86f7bc1868
2 changed files with 71 additions and 122 deletions

View File

@ -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;
/* 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;
/* Strip the carriage return. */
buf[--len] = '\0';
/* Indicate we need to put a blank line in after this one. */
mac_line_needs_newline = TRUE;
}
#endif
/* Read in the last line properly. */
fileptr = read_line(buf, len, fileptr);
/* Store the data of the final line. */
bottomline->data = encode_data(buf, len);
num_lines++;
if (mac_line_needs_newline) {
bottomline->next = make_new_node(bottomline);
bottomline = bottomline->next;
bottomline->data = mallocstrcpy(NULL, "");
}
}
free(buf);
/* 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);
}
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;
}
/* Renumber, starting with the last line of the file we inserted. */
renumber(openfile->current);
}
openfile->totsize += get_totsize(openfile->fileage, openfile->filebot);
/* 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

View File

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