]> git.wh0rd.org Git - nano.git/commitdiff
reorder some functions for consistency
authorDavid Lawrence Ramsey <pooka109@gmail.com>
Fri, 8 Jul 2005 02:47:05 +0000 (02:47 +0000)
committerDavid Lawrence Ramsey <pooka109@gmail.com>
Fri, 8 Jul 2005 02:47:05 +0000 (02:47 +0000)
git-svn-id: svn://svn.savannah.gnu.org/nano/trunk/nano@2830 35c25a1d-7b9e-4130-9fde-d3aeb78583b8

ChangeLog
src/files.c
src/nano.c
src/proto.h

index 7eac5d27d78dbc70626e088d456bf7b311e1c0cd..dc6b155960d5609253f0fb02d74ceb616a868ca8 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -5,6 +5,7 @@ CVS code -
          saved as an int instead of a ssize_t.  Changes to
          renumber_all(), renumber(), do_alt_speller(), and
          backup_lines(). (DLR)
+       - Reorder some functions for consistency. (DLR)
 - global.c:
   shortcut_init()
        - Simplify wording of nano_gotoline_msg. (Jordi)
index c4bd32656c9cf44ca2db27321d1c044a41f69d8c..2c5a79d753d9ca720b1374d0e21107fdc2a0c1f5 100644 (file)
 static file_format fmt = NIX_FILE;
        /* The format of the current file. */
 
-/* What happens when there is no file to open? aiee! */
-void new_file(void)
+#ifdef ENABLE_MULTIBUFFER
+/* Create a new openfilestruct node. */
+openfilestruct *make_new_opennode(void)
 {
-    fileage = make_new_node(NULL);
-    fileage->data = mallocstrcpy(NULL, "");
-    filebot = fileage;
-    edittop = fileage;
-    current = fileage;
-    current_x = 0;
-    totlines = 1;
-    totsize = 0;
+    openfilestruct *newnode =
+       (openfilestruct *)nmalloc(sizeof(openfilestruct));
+    newnode->filename = NULL;
 
-#ifdef ENABLE_COLOR
-    update_color();
-    if (!ISSET(NO_COLOR_SYNTAX))
-       edit_refresh();
-#endif
+    return newnode;
 }
 
-/* We make a new line of text from buf.  buf is length buf_len.  If
- * first_line_ins is TRUE, then we put the new line at the top of the
- * file.  Otherwise, we assume prevnode is the last line of the file,
- * and put our line after prevnode. */
-filestruct *read_line(char *buf, filestruct *prevnode, bool
-       *first_line_ins, size_t buf_len)
+/* Splice a node into an existing openfilestruct. */
+void splice_opennode(openfilestruct *begin, openfilestruct *newnode,
+       openfilestruct *end)
 {
-    filestruct *fileptr = (filestruct *)nmalloc(sizeof(filestruct));
-
-    /* Convert nulls to newlines.  buf_len is the string's real length
-     * here. */
-    unsunder(buf, buf_len);
-
-    assert(strlen(buf) == buf_len);
-
-    fileptr->data = mallocstrcpy(NULL, buf);
-
-#ifndef NANO_SMALL
-    /* If it's a DOS file ("\r\n"), and file conversion isn't disabled,
-     * strip the '\r' part from fileptr->data. */
-    if (!ISSET(NO_CONVERT) && buf_len > 0 && buf[buf_len - 1] == '\r')
-       fileptr->data[buf_len - 1] = '\0';
-#endif
+    assert(newnode != NULL && begin != NULL);
 
-    if (*first_line_ins == TRUE || fileage == NULL) {
-       /* Special case: We're inserting with the cursor on the first
-        * line. */
-       fileptr->prev = NULL;
-       fileptr->next = fileage;
-       fileptr->lineno = 1;
-       if (*first_line_ins == TRUE) {
-           *first_line_ins = FALSE;
-           /* If we're inserting into the first line of the file, then
-            * we want to make sure that our edit buffer stays on the
-            * first line and that fileage stays up to date. */
-           edittop = fileptr;
-       } else
-           filebot = fileptr;
-       fileage = fileptr;
-    } else {
-       assert(prevnode != NULL);
+    newnode->next = end;
+    newnode->prev = begin;
+    begin->next = newnode;
+    if (end != NULL)
+       end->prev = newnode;
+}
 
-       fileptr->prev = prevnode;
-       fileptr->next = NULL;
-       fileptr->lineno = prevnode->lineno + 1;
-       prevnode->next = fileptr;
-    }
+/* Unlink a node from the rest of the openfilestruct, and delete it. */
+void unlink_opennode(openfilestruct *fileptr)
+{
+    assert(fileptr != NULL && fileptr->prev != NULL && fileptr->next != NULL && fileptr != fileptr->prev && fileptr != fileptr->next);
 
-    return fileptr;
+    fileptr->prev->next = fileptr->next;
+    fileptr->next->prev = fileptr->prev;
+    delete_opennode(fileptr);
 }
 
-/* Load a file into the edit buffer.  This takes data from the file
- * struct. */
-void load_file(void)
+/* Delete a node from the openfilestruct. */
+void delete_opennode(openfilestruct *fileptr)
 {
-    current = fileage;
-
-#ifdef ENABLE_MULTIBUFFER
-    /* Add a new entry to the open_files structure. */
-    add_open_file(FALSE);
+    assert(fileptr != NULL && fileptr->filename != NULL && fileptr->fileage != NULL);
 
-    /* Reinitialize the shortcut list. */
-    shortcut_init(FALSE);
-#endif
+    free(fileptr->filename);
+    free_filestruct(fileptr->fileage);
+    free(fileptr);
 }
 
-void read_file(FILE *f, const char *filename)
+#ifdef DEBUG
+/* Deallocate all memory associated with this and later files, including
+ * the lines of text. */
+void free_openfilestruct(openfilestruct *src)
 {
-    size_t num_lines = 0;
-       /* The number of lines in the file. */
-    size_t num_chars;
-       /* The number of characters in the file. */
-    size_t len = 0;
-       /* The length of the current line of the file. */
-    size_t i = 0;
-       /* The position in the current line of the file. */
-    size_t bufx = MAX_BUF_SIZE;
-       /* The size of each chunk of the file that we read. */
-    char input = '\0';
-       /* The current input character. */
-    char *buf;
-       /* The buffer where we store chunks of the file. */
-    filestruct *fileptr = current;
-       /* The current line of the file. */
-    bool first_line_ins = FALSE;
-       /* Whether we're inserting with the cursor on the first line. */
-    int input_int;
-       /* The current value we read from the file, whether an input
-        * character or EOF. */
-#ifndef NANO_SMALL
-    int format = 0;
-       /* 0 = *nix, 1 = DOS, 2 = Mac, 3 = both DOS and Mac. */
+    assert(src != NULL);
+
+    while (src != src->next) {
+       src = src->next;
+       delete_opennode(src->prev);
+    }
+    delete_opennode(src);
+}
 #endif
 
-    buf = charalloc(bufx);
-    buf[0] = '\0';
+/* Add/update an entry to the open_files openfilestruct.  If update is
+ * FALSE, a new entry is created; otherwise, the current entry is
+ * updated. */
+void add_open_file(bool update)
+{
+    if (update && open_files == NULL)
+       return;
 
-    if (current != NULL) {
-       if (current == fileage)
-           first_line_ins = TRUE;
-       else
-           fileptr = current->prev;
+    /* If there are no entries in open_files, make the first one. */
+    if (open_files == NULL) {
+       open_files = make_new_opennode();
+       splice_opennode(open_files, open_files, open_files);
+    /* Otherwise, if we're not updating, make a new entry for
+     * open_files and splice it in after the current entry. */
+    } else if (!update) {
+       splice_opennode(open_files, make_new_opennode(),
+               open_files->next);
+       open_files = open_files->next;
     }
 
-    /* For the assertion in read_line(), it must be true that if current
-     * is NULL, then so is fileage. */
-    assert(current != NULL || fileage == NULL);
+    /* Save the current filename. */
+    open_files->filename = mallocstrcpy(open_files->filename, filename);
 
 #ifndef NANO_SMALL
-    /* We don't know which file format we have yet, so assume it's a
-     * *nix file for now. */
-    fmt = NIX_FILE;
+    /* Save the current file's stat. */
+    open_files->originalfilestat = originalfilestat;
 #endif
 
-    /* Read the entire file into the file struct. */
-    while ((input_int = getc(f)) != EOF) {
-       input = (char)input_int;
+    /* Save the current file buffer. */
+    open_files->fileage = fileage;
+    open_files->filebot = filebot;
 
-       /* If it's a *nix file ("\n") or a DOS file ("\r\n"), and file
-        * conversion isn't disabled, handle it! */
-       if (input == '\n') {
-#ifndef NANO_SMALL
-           /* If there's a '\r' before the '\n', set format to DOS if
-            * we currently think this is a *nix file, or to both if we
-            * currently think it's a Mac file. */
-           if (!ISSET(NO_CONVERT) && i > 0 && buf[i - 1] == '\r' &&
-               (format == 0 || format == 2))
-               format++;
-#endif
+    /* Save the current top of the edit window. */
+    open_files->edittop = edittop;
 
-           /* Read in the line properly. */
-           fileptr = read_line(buf, fileptr, &first_line_ins, len);
+    /* Save the current line. */
+    open_files->current = current;
 
-           /* Reset the line length in preparation for the next
-            * line. */
-           len = 0;
+    /* Save the current cursor position. */
+    open_files->current_x = current_x;
 
-           num_lines++;
-           buf[0] = '\0';
-           i = 0;
-#ifndef NANO_SMALL
-       /* If it's a Mac file ('\r' without '\n'), and file conversion
-        * isn't disabled, handle it! */
-       } else if (!ISSET(NO_CONVERT) && i > 0 && buf[i - 1] == '\r') {
+    /* Save the current place we want. */
+    open_files->placewewant = placewewant;
 
-           /* If we currently think the file is a *nix file, set format
-            * to Mac.  If we currently think the file is a DOS file,
-            * set format to both DOS and Mac. */
-           if (format == 0 || format == 1)
-               format += 2;
+    /* Save the current total number of lines. */
+    open_files->totlines = totlines;
 
-           /* Read in the line properly. */
-           fileptr = read_line(buf, fileptr, &first_line_ins, len);
+    /* Save the current total size. */
+    open_files->totsize = totsize;
 
-           /* Reset the line length in preparation for the next line.
-            * Since we've already read in the next character, reset it
-            * to 1 instead of 0. */
-           len = 1;
+    /* Start with no flags saved. */
+    open_files->flags = 0;
 
-           num_lines++;
-           buf[0] = input;
-           buf[1] = '\0';
-           i = 1;
-#endif
-       } else {
-           /* Calculate the total length of the line.  It might have
-            * nulls in it, so we can't just use strlen() here. */
-           len++;
+    /* Save the current modification status. */
+    if (ISSET(MODIFIED))
+       open_files->flags |= MODIFIED;
 
-           /* Now we allocate a bigger buffer MAX_BUF_SIZE characters
-            * at a time.  If we allocate a lot of space for one line,
-            * we may indeed have to use a buffer this big later on, so
-            * we don't decrease it at all.  We do free it at the end,
-            * though. */
-           if (i >= bufx - 1) {
-               bufx += MAX_BUF_SIZE;
-               buf = charealloc(buf, bufx);
-           }
+#ifndef NANO_SMALL
+    /* Save the current marking status and mark, if applicable. */
+    if (ISSET(MARK_ISSET)) {
+       open_files->flags |= MARK_ISSET;
+       open_files->mark_beginbuf = mark_beginbuf;
+       open_files->mark_beginx = mark_beginx;
+    }
 
-           buf[i] = input;
-           buf[i + 1] = '\0';
-           i++;
-       }
-    }
+    /* Save the current file format. */
+    open_files->fmt = fmt;
+#endif
 
-    /* Perhaps this could use some better handling. */
-    if (ferror(f))
-       nperror(filename);
-    fclose(f);
+#ifdef DEBUG
+    fprintf(stderr, "filename is %s\n", open_files->filename);
+#endif
+}
 
-#ifndef NANO_SMALL
-    /* If file conversion isn't disabled and the last character in this
-     * file is '\r', read it in properly as a Mac format line. */
-    if (len == 0 && !ISSET(NO_CONVERT) && input == '\r') {
-       len = 1;
+/* Read the current entry in the open_files structure and set up the
+ * currently open file buffer using that entry's information. */
+void load_open_file(void)
+{
+    assert(open_files != NULL);
 
-       buf[0] = input;
-       buf[1] = '\0';
-    }
-#endif
+    /* Restore the current filename. */
+    filename = mallocstrcpy(filename, open_files->filename);
 
-    /* Did we not get a newline and still have stuff to do? */
-    if (len > 0) {
 #ifndef NANO_SMALL
-       /* 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 (!ISSET(NO_CONVERT) && buf[len - 1] == '\r' &&
-               (format == 0 || format == 1))
-           format += 2;
+    /* Restore the current file's stat. */
+    originalfilestat = open_files->originalfilestat;
 #endif
 
-       /* Read in the last line properly. */
-       fileptr = read_line(buf, fileptr, &first_line_ins, len);
-       num_lines++;
-    }
+    /* Restore the current file buffer. */
+    fileage = open_files->fileage;
+    filebot = open_files->filebot;
 
-    free(buf);
+    /* Restore the current top of the edit window. */
+    edittop = open_files->edittop;
 
-    /* If we didn't get a file and we don't already have one, make a new
-     * file. */
-    if (fileptr == NULL)
-       new_file();
+    /* Restore the current line. */
+    current = open_files->current;
 
-    /* Did we try to insert a file of 0 bytes? */
-    if (num_lines != 0) {
-       if (current != NULL) {
-           fileptr->next = current;
-           current->prev = fileptr;
-           renumber(current);
-           current_x = 0;
-           placewewant = 0;
-       } else if (fileptr->next == NULL) {
-           filebot = fileptr;
-           new_magicline();
-           totsize--;
-       }
-    }
+    /* Restore the current cursor position. */
+    current_x = open_files->current_x;
 
-    get_totals(fileage, filebot, NULL, &num_chars);
-    totsize += num_chars;
+    /* Restore the current place we want. */
+    placewewant = open_files->placewewant;
+
+    /* Restore the current total number of lines. */
+    totlines = open_files->totlines;
+
+    /* Restore the current total size. */
+    totsize = open_files->totsize;
+
+    /* Restore the current modification status. */
+    if (open_files->flags & MODIFIED)
+       SET(MODIFIED);
+    else
+       UNSET(MODIFIED);
 
 #ifndef NANO_SMALL
-    if (format == 3)
-       statusbar(
-               P_("Read %lu line (Converted from DOS and Mac format)",
-               "Read %lu lines (Converted from DOS and Mac format)",
-               (unsigned long)num_lines), (unsigned long)num_lines);
-    else if (format == 2) {
-       fmt = MAC_FILE;
-       statusbar(P_("Read %lu line (Converted from Mac format)",
-               "Read %lu lines (Converted from Mac format)",
-               (unsigned long)num_lines), (unsigned long)num_lines);
-    } else if (format == 1) {
-       fmt = DOS_FILE;
-       statusbar(P_("Read %lu line (Converted from DOS format)",
-               "Read %lu lines (Converted from DOS format)",
-               (unsigned long)num_lines), (unsigned long)num_lines);
+    /* Restore the current marking status and mark, if applicable. */
+    if (open_files->flags & MARK_ISSET) {
+       mark_beginbuf = open_files->mark_beginbuf;
+       mark_beginx = open_files->mark_beginx;
+       SET(MARK_ISSET);
     } else
+       UNSET(MARK_ISSET);
+
+    /* Restore the current file format. */
+    fmt = open_files->fmt;
 #endif
-       statusbar(P_("Read %lu line", "Read %lu lines",
-               (unsigned long)num_lines), (unsigned long)num_lines);
 
-    totlines += num_lines;
+#ifdef ENABLE_COLOR
+    update_color();
+#endif
+    edit_refresh();
+
+    /* Update the titlebar. */
+    titlebar(NULL);
 }
 
-/* Open the file (and decide if it exists).  If newfie is TRUE, display
- * "New File" if the file is missing.  Otherwise, say "[filename] not
- * found".
- *
- * Return -2 if we say "New File".  Otherwise, -1 if the file isn't
- * opened, 0 otherwise.  The file might still have an error while
- * reading with a 0 return value.  *f is set to the opened file. */
-int open_file(const char *filename, bool newfie, FILE **f)
+/* Open either the next or previous file buffer. */
+void open_prevnext_file(bool next_file)
 {
-    int fd;
-    struct stat fileinfo;
-
-    assert(f != NULL);
+    assert(open_files != NULL);
 
-    if (filename == NULL || filename[0] == '\0' ||
-           stat(filename, &fileinfo) == -1) {
-       if (newfie) {
-           statusbar(_("New File"));
-           return -2;
-       }
-       statusbar(_("\"%s\" not found"), filename);
-       return -1;
-    } else if (S_ISDIR(fileinfo.st_mode) || S_ISCHR(fileinfo.st_mode) ||
-               S_ISBLK(fileinfo.st_mode)) {
-       /* Don't open character or block files.  Sorry, /dev/sndstat! */
-       statusbar(S_ISDIR(fileinfo.st_mode) ? _("\"%s\" is a directory")
-               : _("File \"%s\" is a device file"), filename);
-       return -1;
-    } else if ((fd = open(filename, O_RDONLY)) == -1) {
-       statusbar(_("Error reading %s: %s"), filename, strerror(errno));
-       return -1;
-     } else {
-       /* File is A-OK.  Open it in binary mode for our own end-of-line
-        * character munging. */
-       *f = fdopen(fd, "rb");
+    add_open_file(TRUE);
 
-       if (*f == NULL) {
-           statusbar(_("Error reading %s: %s"), filename,
-               strerror(errno));
-           close(fd);
-       } else
-           statusbar(_("Reading File"));
+    /* If only one file buffer is open, indicate it on the statusbar and
+     * get out. */
+    if (open_files == open_files->next) {
+       statusbar(_("No more open file buffers"));
+       return;
     }
-    return 0;
+
+    /* Switch to the next or previous file, depending on the value of
+     * next. */
+    open_files = next_file ? open_files->next : open_files->prev;
+
+#ifdef DEBUG
+    fprintf(stderr, "filename is %s\n", open_files->filename);
+#endif
+
+    /* Load the file we switched to. */
+    load_open_file();
+
+    /* And indicate the switch on the statusbar. */
+    statusbar(_("Switched to %s"),
+      ((open_files->filename[0] == '\0') ? _("New Buffer") :
+       open_files->filename));
+
+#ifdef DEBUG
+    dump_buffer(current);
+#endif
 }
 
-/* This function will return the name of the first available extension
- * of a filename (starting with [name][suffix], then [name][suffix].1,
- * etc.).  Memory is allocated for the return value.  If no writable
- * extension exists, we return "". */
-char *get_next_filename(const char *name, const char *suffix)
+/* Open the previous entry in the open_files structure.  This function
+ * is used by the shortcut list. */
+void open_prevfile_void(void)
 {
-    unsigned long i = 0;
-    char *buf;
-    size_t namelen, suffixlen;
-
-    assert(name != NULL && suffix != NULL);
+    open_prevnext_file(FALSE);
+}
 
-    namelen = strlen(name);
-    suffixlen = strlen(suffix);
+/* Open the next entry in the open_files structure.  This function is
+ * used by the shortcut list. */
+void open_nextfile_void(void)
+{
+    open_prevnext_file(TRUE);
+}
 
-    buf = charalloc(namelen + suffixlen + digits(ULONG_MAX) + 2);
-    sprintf(buf, "%s%s", name, suffix);
+/* Delete an entry from the open_files filestruct.  After deletion of an
+ * entry, the next entry is opened.  Return TRUE on success or FALSE if
+ * there are no more open file buffers. */
+bool close_open_file(void)
+{
+    assert(open_files != NULL);
 
-    while (TRUE) {
-       struct stat fs;
+    /* If only one file is open, get out. */
+    if (open_files == open_files->next)
+       return FALSE;
 
-       if (stat(buf, &fs) == -1)
-           return buf;
-       if (i == ULONG_MAX)
-           break;
+    /* Open the next file. */
+    open_nextfile_void();
 
-       i++;
-       sprintf(buf + namelen + suffixlen, ".%lu", i);
-    }
+    /* Close the file we had open before. */
+    unlink_opennode(open_files->prev);
 
-    /* We get here only if there is no possible save file.  Blank out
-     * the filename to indicate this. */
-    null_at(&buf, 0);
+    /* Reinitialize the shortcut list. */
+    shortcut_init(FALSE);
+    display_main_list();
 
-    return buf;
+    return TRUE;
 }
+#endif /* ENABLE_MULTIBUFFER */
 
-#ifndef NANO_SMALL
-void execute_command(const char *command)
+/* What happens when there is no file to open? aiee! */
+void new_file(void)
 {
-#ifdef ENABLE_MULTIBUFFER
-    if (ISSET(MULTIBUFFER)) {
-       /* Update the current entry in the open_files structure. */
-       add_open_file(TRUE);
-       new_file();
-       UNSET(MODIFIED);
-       UNSET(MARK_ISSET);
-    }
-#endif /* ENABLE_MULTIBUFFER */
-    open_pipe(command);
-#ifdef ENABLE_MULTIBUFFER
-    /* Add this new entry to the open_files structure. */
-    if (ISSET(MULTIBUFFER))
-       load_file();
-#endif /* ENABLE_MULTIBUFFER */
+    fileage = make_new_node(NULL);
+    fileage->data = mallocstrcpy(NULL, "");
+    filebot = fileage;
+    edittop = fileage;
+    current = fileage;
+    current_x = 0;
+    totlines = 1;
+    totsize = 0;
+
+#ifdef ENABLE_COLOR
+    update_color();
+    if (!ISSET(NO_COLOR_SYNTAX))
+       edit_refresh();
+#endif
 }
-#endif /* !NANO_SMALL */
 
-/* name is a file name to open.  We make a new buffer if necessary, then
- * open and read the file. */
-void load_buffer(const char *name)
+/* We make a new line of text from buf.  buf is length buf_len.  If
+ * first_line_ins is TRUE, then we put the new line at the top of the
+ * file.  Otherwise, we assume prevnode is the last line of the file,
+ * and put our line after prevnode. */
+filestruct *read_line(char *buf, filestruct *prevnode, bool
+       *first_line_ins, size_t buf_len)
 {
-    bool new_buffer = (fileage == NULL
-#ifdef ENABLE_MULTIBUFFER
-        || ISSET(MULTIBUFFER)
-#endif
-       );
-       /* new_buffer says whether we load into this buffer or a new
-        * one.  If new_buffer is TRUE, we display "New File" if the
-        * file is not found, and if it is found we set filename and add
-        * a new open_files entry. */
-    FILE *f;
-    int rc;
-       /* rc == -2 means that the statusbar displayed "New File".  -1
-        * means that the open failed.  0 means success. */
+    filestruct *fileptr = (filestruct *)nmalloc(sizeof(filestruct));
 
-#ifndef DISABLE_OPERATINGDIR
-    if (check_operating_dir(name, FALSE)) {
-       statusbar(_("Can't insert file from outside of %s"),
-               operating_dir);
-       return;
-    }
-#endif
+    /* Convert nulls to newlines.  buf_len is the string's real length
+     * here. */
+    unsunder(buf, buf_len);
 
-#ifdef ENABLE_MULTIBUFFER
-    /* Update the current entry in the open_files structure. */
-    add_open_file(TRUE);
-#endif
+    assert(strlen(buf) == buf_len);
 
-    rc = open_file(name, new_buffer, &f);
+    fileptr->data = mallocstrcpy(NULL, buf);
 
-#ifdef ENABLE_MULTIBUFFER
-    if (rc != -1 && ISSET(MULTIBUFFER)) {
-       UNSET(MODIFIED);
 #ifndef NANO_SMALL
-       UNSET(MARK_ISSET);
-#endif
-    }
+    /* If it's a DOS file ("\r\n"), and file conversion isn't disabled,
+     * strip the '\r' part from fileptr->data. */
+    if (!ISSET(NO_CONVERT) && buf_len > 0 && buf[buf_len - 1] == '\r')
+       fileptr->data[buf_len - 1] = '\0';
 #endif
 
-    if (rc != -1 && new_buffer) {
-       filename = mallocstrcpy(filename, name);
-       new_file();
+    if (*first_line_ins == TRUE || fileage == NULL) {
+       /* Special case: We're inserting with the cursor on the first
+        * line. */
+       fileptr->prev = NULL;
+       fileptr->next = fileage;
+       fileptr->lineno = 1;
+       if (*first_line_ins == TRUE) {
+           *first_line_ins = FALSE;
+           /* If we're inserting into the first line of the file, then
+            * we want to make sure that our edit buffer stays on the
+            * first line and that fileage stays up to date. */
+           edittop = fileptr;
+       } else
+           filebot = fileptr;
+       fileage = fileptr;
+    } else {
+       assert(prevnode != NULL);
+
+       fileptr->prev = prevnode;
+       fileptr->next = NULL;
+       fileptr->lineno = prevnode->lineno + 1;
+       prevnode->next = fileptr;
     }
 
-    if (rc == 0) {
-       file_format fmt_save = fmt;
+    return fileptr;
+}
 
-       read_file(f, filename);
+/* Load a file into the edit buffer.  This takes data from the file
+ * struct. */
+void load_file(void)
+{
+    current = fileage;
 
-       /* If we're not loading into a new buffer, preserve the file
-        * format. */
-       if (!new_buffer)
-           fmt = fmt_save;
+#ifdef ENABLE_MULTIBUFFER
+    /* Add a new entry to the open_files structure. */
+    add_open_file(FALSE);
 
-#ifndef NANO_SMALL
-       stat(filename, &originalfilestat);
+    /* Reinitialize the shortcut list. */
+    shortcut_init(FALSE);
 #endif
-    }
-
-    /* Add this new entry to the open_files structure if we have
-     * multibuffer support, or to the main filestruct if we don't. */
-    if (rc != -1 && new_buffer)
-       load_file();
 }
 
-void do_insertfile(
+void read_file(FILE *f, const char *filename)
+{
+    size_t num_lines = 0;
+       /* The number of lines in the file. */
+    size_t num_chars;
+       /* The number of characters in the file. */
+    size_t len = 0;
+       /* The length of the current line of the file. */
+    size_t i = 0;
+       /* The position in the current line of the file. */
+    size_t bufx = MAX_BUF_SIZE;
+       /* The size of each chunk of the file that we read. */
+    char input = '\0';
+       /* The current input character. */
+    char *buf;
+       /* The buffer where we store chunks of the file. */
+    filestruct *fileptr = current;
+       /* The current line of the file. */
+    bool first_line_ins = FALSE;
+       /* Whether we're inserting with the cursor on the first line. */
+    int input_int;
+       /* The current value we read from the file, whether an input
+        * character or EOF. */
 #ifndef NANO_SMALL
-       bool execute
-#else
-       void
+    int format = 0;
+       /* 0 = *nix, 1 = DOS, 2 = Mac, 3 = both DOS and Mac. */
 #endif
-       )
-{
-    int i;
-    const char *msg;
-    char *ans = mallocstrcpy(NULL, "");
-       /* The last answer the user typed on the statusbar. */
-    filestruct *edittop_save = edittop;
-    ssize_t current_y_save = current_y;
-    bool at_edittop = FALSE;
-       /* Whether we're at the top of the edit window. */
 
-#ifndef DISABLE_WRAPPING
-    wrap_reset();
-#endif
+    buf = charalloc(bufx);
+    buf[0] = '\0';
 
-    while (TRUE) {
-#ifndef NANO_SMALL
-       if (execute) {
-#ifdef ENABLE_MULTIBUFFER
-           if (ISSET(MULTIBUFFER))
-               msg = N_("Command to execute in new buffer [from %s] ");
-           else
-#endif
-               msg = N_("Command to execute [from %s] ");
-       } else {
-#endif
-#ifdef ENABLE_MULTIBUFFER
-           if (ISSET(MULTIBUFFER)) {
-               msg = N_("File to insert into new buffer [from %s] ");
-           } else
-#endif
-               msg = N_("File to insert [from %s] ");
-#ifndef NANO_SMALL
-       }
-#endif
+    if (current != NULL) {
+       if (current == fileage)
+           first_line_ins = TRUE;
+       else
+           fileptr = current->prev;
+    }
+
+    /* For the assertion in read_line(), it must be true that if current
+     * is NULL, then so is fileage. */
+    assert(current != NULL || fileage == NULL);
 
-       i = statusq(TRUE,
 #ifndef NANO_SMALL
-               execute ? extcmd_list :
+    /* We don't know which file format we have yet, so assume it's a
+     * *nix file for now. */
+    fmt = NIX_FILE;
 #endif
-               insertfile_list, ans,
+
+    /* Read the entire file into the file struct. */
+    while ((input_int = getc(f)) != EOF) {
+       input = (char)input_int;
+
+       /* If it's a *nix file ("\n") or a DOS file ("\r\n"), and file
+        * conversion isn't disabled, handle it! */
+       if (input == '\n') {
 #ifndef NANO_SMALL
-               NULL,
-#endif
-               _(msg),
-#ifndef DISABLE_OPERATINGDIR
-               operating_dir != NULL && strcmp(operating_dir, ".") != 0 ?
-               operating_dir :
+           /* If there's a '\r' before the '\n', set format to DOS if
+            * we currently think this is a *nix file, or to both if we
+            * currently think it's a Mac file. */
+           if (!ISSET(NO_CONVERT) && i > 0 && buf[i - 1] == '\r' &&
+               (format == 0 || format == 2))
+               format++;
 #endif
-               "./");
 
-       /* If we're in multibuffer mode and the filename or command is
-        * blank, open a new buffer instead of canceling. */
-       if (i == -1 || (i == -2
-#ifdef ENABLE_MULTIBUFFER
-               && !ISSET(MULTIBUFFER)
-#endif
-               )) {
-           statusbar(_("Cancelled"));
-           break;
-       } else {
-           size_t pww_save = placewewant;
+           /* Read in the line properly. */
+           fileptr = read_line(buf, fileptr, &first_line_ins, len);
 
-           ans = mallocstrcpy(ans, answer);
+           /* Reset the line length in preparation for the next
+            * line. */
+           len = 0;
 
+           num_lines++;
+           buf[0] = '\0';
+           i = 0;
 #ifndef NANO_SMALL
-#ifdef ENABLE_MULTIBUFFER
-           if (i == TOGGLE_MULTIBUFFER_KEY) {
-               /* Don't allow toggling if we're in view mode. */
-               if (!ISSET(VIEW_MODE))
-                   TOGGLE(MULTIBUFFER);
-               continue;
-           } else
-#endif
-           if (i == NANO_TOOTHERINSERT_KEY) {
-               execute = !execute;
-               continue;
-           }
-#ifndef DISABLE_BROWSER
-           else
-#endif
-#endif /* !NANO_SMALL */
-
-#ifndef DISABLE_BROWSER
-           if (i == NANO_TOFILES_KEY) {
-               char *tmp = do_browse_from(answer);
+       /* If it's a Mac file ('\r' without '\n'), and file conversion
+        * isn't disabled, handle it! */
+       } else if (!ISSET(NO_CONVERT) && i > 0 && buf[i - 1] == '\r') {
 
-               if (tmp == NULL)
-                   continue;
+           /* If we currently think the file is a *nix file, set format
+            * to Mac.  If we currently think the file is a DOS file,
+            * set format to both DOS and Mac. */
+           if (format == 0 || format == 1)
+               format += 2;
 
-               free(answer);
-               answer = tmp;
+           /* Read in the line properly. */
+           fileptr = read_line(buf, fileptr, &first_line_ins, len);
 
-               /* We have a file now.  Indicate this and get out of the
-                * statusbar prompt cleanly. */
-               i = 0;
-               statusq_abort();
-           }
-#endif
+           /* Reset the line length in preparation for the next line.
+            * Since we've already read in the next character, reset it
+            * to 1 instead of 0. */
+           len = 1;
 
-           /* If we don't have a file yet, go back to the statusbar
-            * prompt. */
-           if (i != 0
-#ifdef ENABLE_MULTIBUFFER
-               && (i != -2 || !ISSET(MULTIBUFFER))
+           num_lines++;
+           buf[0] = input;
+           buf[1] = '\0';
+           i = 1;
 #endif
-               )
-               continue;
+       } else {
+           /* Calculate the total length of the line.  It might have
+            * nulls in it, so we can't just use strlen() here. */
+           len++;
 
-#ifdef ENABLE_MULTIBUFFER
-           if (!ISSET(MULTIBUFFER)) {
-#endif
-               /* If we're not inserting into a new buffer, partition
-                * the filestruct so that it contains no text and hence
-                * looks like a new buffer, and keep track of whether
-                * the top of the partition is the top of the edit
-                * window. */
-               filepart = partition_filestruct(current, current_x,
-                       current, current_x);
-               at_edittop = (fileage == edittop);
-#ifdef ENABLE_MULTIBUFFER
+           /* Now we allocate a bigger buffer MAX_BUF_SIZE characters
+            * at a time.  If we allocate a lot of space for one line,
+            * we may indeed have to use a buffer this big later on, so
+            * we don't decrease it at all.  We do free it at the end,
+            * though. */
+           if (i >= bufx - 1) {
+               bufx += MAX_BUF_SIZE;
+               buf = charealloc(buf, bufx);
            }
-#endif
+
+           buf[i] = input;
+           buf[i + 1] = '\0';
+           i++;
+       }
+    }
+
+    /* Perhaps this could use some better handling. */
+    if (ferror(f))
+       nperror(filename);
+    fclose(f);
 
 #ifndef NANO_SMALL
-           if (execute)
-               execute_command(answer);
-           else {
-#endif
-               answer = mallocstrassn(answer,
-                       real_dir_from_tilde(answer));
-               load_buffer(answer);
-#ifndef NANO_SMALL
-           }
+    /* If file conversion isn't disabled and the last character in this
+     * file is '\r', read it in properly as a Mac format line. */
+    if (len == 0 && !ISSET(NO_CONVERT) && input == '\r') {
+       len = 1;
+
+       buf[0] = input;
+       buf[1] = '\0';
+    }
 #endif
 
-#ifdef ENABLE_MULTIBUFFER
-           if (!ISSET(MULTIBUFFER))
+    /* Did we not get a newline and still have stuff to do? */
+    if (len > 0) {
+#ifndef NANO_SMALL
+       /* 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 (!ISSET(NO_CONVERT) && buf[len - 1] == '\r' &&
+               (format == 0 || format == 1))
+           format += 2;
 #endif
-           {
-               filestruct *top_save = fileage;
 
-               /* If we didn't insert into a new buffer, and we were at
-                * the top of the edit window before, set the saved
-                * value of edittop to the new top of the edit window,
-                * and update the current y-coordinate to account for
-                * the number of lines inserted. */
-               if (at_edittop)
-                   edittop_save = fileage;
-               current_y += current_y_save;
+       /* Read in the last line properly. */
+       fileptr = read_line(buf, fileptr, &first_line_ins, len);
+       num_lines++;
+    }
 
-               /* If we didn't insert into a new buffer, unpartition
-                * the filestruct so that it contains all the text
-                * again.  Note that we've replaced the non-text
-                * originally in the partition with the text in the
-                * inserted file/executed command output. */
-               unpartition_filestruct(&filepart);
+    free(buf);
 
-               /* Renumber starting with the beginning line of the old
-                * partition. */
-               renumber(top_save);
+    /* If we didn't get a file and we don't already have one, make a new
+     * file. */
+    if (fileptr == NULL)
+       new_file();
 
-               /* Set edittop back to what it was before. */
-               edittop = edittop_save;
-           }
+    /* Did we try to insert a file of 0 bytes? */
+    if (num_lines != 0) {
+       if (current != NULL) {
+           fileptr->next = current;
+           current->prev = fileptr;
+           renumber(current);
+           current_x = 0;
+           placewewant = 0;
+       } else if (fileptr->next == NULL) {
+           filebot = fileptr;
+           new_magicline();
+           totsize--;
+       }
+    }
 
-#ifdef ENABLE_MULTIBUFFER
-           if (ISSET(MULTIBUFFER)) {
-               /* Update the titlebar. */
-               titlebar(NULL);
+    get_totals(fileage, filebot, NULL, &num_chars);
+    totsize += num_chars;
 
-               /* Reinitialize the shortcut list. */
-               shortcut_init(FALSE);
-           } else {
+#ifndef NANO_SMALL
+    if (format == 3)
+       statusbar(
+               P_("Read %lu line (Converted from DOS and Mac format)",
+               "Read %lu lines (Converted from DOS and Mac format)",
+               (unsigned long)num_lines), (unsigned long)num_lines);
+    else if (format == 2) {
+       fmt = MAC_FILE;
+       statusbar(P_("Read %lu line (Converted from Mac format)",
+               "Read %lu lines (Converted from Mac format)",
+               (unsigned long)num_lines), (unsigned long)num_lines);
+    } else if (format == 1) {
+       fmt = DOS_FILE;
+       statusbar(P_("Read %lu line (Converted from DOS format)",
+               "Read %lu lines (Converted from DOS format)",
+               (unsigned long)num_lines), (unsigned long)num_lines);
+    } else
 #endif
-               /* Mark the file as modified. */
-               set_modified();
+       statusbar(P_("Read %lu line", "Read %lu lines",
+               (unsigned long)num_lines), (unsigned long)num_lines);
 
-               /* Restore the old place we want. */
-               placewewant = pww_save;
-#ifdef ENABLE_MULTIBUFFER
-           }
-#endif
+    totlines += num_lines;
+}
 
-           /* Refresh the screen. */
-           edit_refresh();
+/* Open the file (and decide if it exists).  If newfie is TRUE, display
+ * "New File" if the file is missing.  Otherwise, say "[filename] not
+ * found".
+ *
+ * Return -2 if we say "New File".  Otherwise, -1 if the file isn't
+ * opened, 0 otherwise.  The file might still have an error while
+ * reading with a 0 return value.  *f is set to the opened file. */
+int open_file(const char *filename, bool newfie, FILE **f)
+{
+    int fd;
+    struct stat fileinfo;
+
+    assert(f != NULL);
+
+    if (filename == NULL || filename[0] == '\0' ||
+           stat(filename, &fileinfo) == -1) {
+       if (newfie) {
+           statusbar(_("New File"));
+           return -2;
+       }
+       statusbar(_("\"%s\" not found"), filename);
+       return -1;
+    } else if (S_ISDIR(fileinfo.st_mode) || S_ISCHR(fileinfo.st_mode) ||
+               S_ISBLK(fileinfo.st_mode)) {
+       /* Don't open character or block files.  Sorry, /dev/sndstat! */
+       statusbar(S_ISDIR(fileinfo.st_mode) ? _("\"%s\" is a directory")
+               : _("File \"%s\" is a device file"), filename);
+       return -1;
+    } else if ((fd = open(filename, O_RDONLY)) == -1) {
+       statusbar(_("Error reading %s: %s"), filename, strerror(errno));
+       return -1;
+     } else {
+       /* File is A-OK.  Open it in binary mode for our own end-of-line
+        * character munging. */
+       *f = fdopen(fd, "rb");
 
-           break;
-       }
+       if (*f == NULL) {
+           statusbar(_("Error reading %s: %s"), filename,
+               strerror(errno));
+           close(fd);
+       } else
+           statusbar(_("Reading File"));
     }
-
-    free(ans);
+    return 0;
 }
 
-void do_insertfile_void(void)
+/* This function will return the name of the first available extension
+ * of a filename (starting with [name][suffix], then [name][suffix].1,
+ * etc.).  Memory is allocated for the return value.  If no writable
+ * extension exists, we return "". */
+char *get_next_filename(const char *name, const char *suffix)
 {
-#ifdef ENABLE_MULTIBUFFER
-    if (ISSET(VIEW_MODE) && !ISSET(MULTIBUFFER))
-       statusbar(_("Key illegal in non-multibuffer mode"));
-    else
-#endif
-       do_insertfile(
-#ifndef NANO_SMALL
-               FALSE
-#endif
-               );
-
-    display_main_list();
-}
+    unsigned long i = 0;
+    char *buf;
+    size_t namelen, suffixlen;
 
-#ifdef ENABLE_MULTIBUFFER
-/* Create a new openfilestruct node. */
-openfilestruct *make_new_opennode(void)
-{
-    openfilestruct *newnode =
-       (openfilestruct *)nmalloc(sizeof(openfilestruct));
-    newnode->filename = NULL;
+    assert(name != NULL && suffix != NULL);
 
-    return newnode;
-}
+    namelen = strlen(name);
+    suffixlen = strlen(suffix);
 
-/* Splice a node into an existing openfilestruct. */
-void splice_opennode(openfilestruct *begin, openfilestruct *newnode,
-       openfilestruct *end)
-{
-    assert(newnode != NULL && begin != NULL);
+    buf = charalloc(namelen + suffixlen + digits(ULONG_MAX) + 2);
+    sprintf(buf, "%s%s", name, suffix);
 
-    newnode->next = end;
-    newnode->prev = begin;
-    begin->next = newnode;
-    if (end != NULL)
-       end->prev = newnode;
-}
+    while (TRUE) {
+       struct stat fs;
 
-/* Unlink a node from the rest of the openfilestruct, and delete it. */
-void unlink_opennode(openfilestruct *fileptr)
-{
-    assert(fileptr != NULL && fileptr->prev != NULL && fileptr->next != NULL && fileptr != fileptr->prev && fileptr != fileptr->next);
+       if (stat(buf, &fs) == -1)
+           return buf;
+       if (i == ULONG_MAX)
+           break;
 
-    fileptr->prev->next = fileptr->next;
-    fileptr->next->prev = fileptr->prev;
-    delete_opennode(fileptr);
-}
+       i++;
+       sprintf(buf + namelen + suffixlen, ".%lu", i);
+    }
 
-/* Delete a node from the openfilestruct. */
-void delete_opennode(openfilestruct *fileptr)
-{
-    assert(fileptr != NULL && fileptr->filename != NULL && fileptr->fileage != NULL);
+    /* We get here only if there is no possible save file.  Blank out
+     * the filename to indicate this. */
+    null_at(&buf, 0);
 
-    free(fileptr->filename);
-    free_filestruct(fileptr->fileage);
-    free(fileptr);
+    return buf;
 }
 
-#ifdef DEBUG
-/* Deallocate all memory associated with this and later files, including
- * the lines of text. */
-void free_openfilestruct(openfilestruct *src)
+#ifndef NANO_SMALL
+void execute_command(const char *command)
 {
-    assert(src != NULL);
-
-    while (src != src->next) {
-       src = src->next;
-       delete_opennode(src->prev);
+#ifdef ENABLE_MULTIBUFFER
+    if (ISSET(MULTIBUFFER)) {
+       /* Update the current entry in the open_files structure. */
+       add_open_file(TRUE);
+       new_file();
+       UNSET(MODIFIED);
+       UNSET(MARK_ISSET);
     }
-    delete_opennode(src);
+#endif /* ENABLE_MULTIBUFFER */
+    open_pipe(command);
+#ifdef ENABLE_MULTIBUFFER
+    /* Add this new entry to the open_files structure. */
+    if (ISSET(MULTIBUFFER))
+       load_file();
+#endif /* ENABLE_MULTIBUFFER */
 }
-#endif
+#endif /* !NANO_SMALL */
 
-/* Add/update an entry to the open_files openfilestruct.  If update is
- * FALSE, a new entry is created; otherwise, the current entry is
- * updated. */
-void add_open_file(bool update)
+/* name is a file name to open.  We make a new buffer if necessary, then
+ * open and read the file. */
+void load_buffer(const char *name)
 {
-    if (update && open_files == NULL)
-       return;
+    bool new_buffer = (fileage == NULL
+#ifdef ENABLE_MULTIBUFFER
+        || ISSET(MULTIBUFFER)
+#endif
+       );
+       /* new_buffer says whether we load into this buffer or a new
+        * one.  If new_buffer is TRUE, we display "New File" if the
+        * file is not found, and if it is found we set filename and add
+        * a new open_files entry. */
+    FILE *f;
+    int rc;
+       /* rc == -2 means that the statusbar displayed "New File".  -1
+        * means that the open failed.  0 means success. */
 
-    /* If there are no entries in open_files, make the first one. */
-    if (open_files == NULL) {
-       open_files = make_new_opennode();
-       splice_opennode(open_files, open_files, open_files);
-    /* Otherwise, if we're not updating, make a new entry for
-     * open_files and splice it in after the current entry. */
-    } else if (!update) {
-       splice_opennode(open_files, make_new_opennode(),
-               open_files->next);
-       open_files = open_files->next;
+#ifndef DISABLE_OPERATINGDIR
+    if (check_operating_dir(name, FALSE)) {
+       statusbar(_("Can't insert file from outside of %s"),
+               operating_dir);
+       return;
     }
+#endif
 
-    /* Save the current filename. */
-    open_files->filename = mallocstrcpy(open_files->filename, filename);
+#ifdef ENABLE_MULTIBUFFER
+    /* Update the current entry in the open_files structure. */
+    add_open_file(TRUE);
+#endif
 
+    rc = open_file(name, new_buffer, &f);
+
+#ifdef ENABLE_MULTIBUFFER
+    if (rc != -1 && ISSET(MULTIBUFFER)) {
+       UNSET(MODIFIED);
 #ifndef NANO_SMALL
-    /* Save the current file's stat. */
-    open_files->originalfilestat = originalfilestat;
+       UNSET(MARK_ISSET);
+#endif
+    }
 #endif
 
-    /* Save the current file buffer. */
-    open_files->fileage = fileage;
-    open_files->filebot = filebot;
+    if (rc != -1 && new_buffer) {
+       filename = mallocstrcpy(filename, name);
+       new_file();
+    }
 
-    /* Save the current top of the edit window. */
-    open_files->edittop = edittop;
+    if (rc == 0) {
+       file_format fmt_save = fmt;
 
-    /* Save the current line. */
-    open_files->current = current;
+       read_file(f, filename);
 
-    /* Save the current cursor position. */
-    open_files->current_x = current_x;
+       /* If we're not loading into a new buffer, preserve the file
+        * format. */
+       if (!new_buffer)
+           fmt = fmt_save;
 
-    /* Save the current place we want. */
-    open_files->placewewant = placewewant;
+#ifndef NANO_SMALL
+       stat(filename, &originalfilestat);
+#endif
+    }
 
-    /* Save the current total number of lines. */
-    open_files->totlines = totlines;
+    /* Add this new entry to the open_files structure if we have
+     * multibuffer support, or to the main filestruct if we don't. */
+    if (rc != -1 && new_buffer)
+       load_file();
+}
 
-    /* Save the current total size. */
-    open_files->totsize = totsize;
+void do_insertfile(
+#ifndef NANO_SMALL
+       bool execute
+#else
+       void
+#endif
+       )
+{
+    int i;
+    const char *msg;
+    char *ans = mallocstrcpy(NULL, "");
+       /* The last answer the user typed on the statusbar. */
+    filestruct *edittop_save = edittop;
+    ssize_t current_y_save = current_y;
+    bool at_edittop = FALSE;
+       /* Whether we're at the top of the edit window. */
 
-    /* Start with no flags saved. */
-    open_files->flags = 0;
+#ifndef DISABLE_WRAPPING
+    wrap_reset();
+#endif
 
-    /* Save the current modification status. */
-    if (ISSET(MODIFIED))
-       open_files->flags |= MODIFIED;
+    while (TRUE) {
+#ifndef NANO_SMALL
+       if (execute) {
+#ifdef ENABLE_MULTIBUFFER
+           if (ISSET(MULTIBUFFER))
+               msg = N_("Command to execute in new buffer [from %s] ");
+           else
+#endif
+               msg = N_("Command to execute [from %s] ");
+       } else {
+#endif
+#ifdef ENABLE_MULTIBUFFER
+           if (ISSET(MULTIBUFFER)) {
+               msg = N_("File to insert into new buffer [from %s] ");
+           } else
+#endif
+               msg = N_("File to insert [from %s] ");
+#ifndef NANO_SMALL
+       }
+#endif
 
+       i = statusq(TRUE,
 #ifndef NANO_SMALL
-    /* Save the current marking status and mark, if applicable. */
-    if (ISSET(MARK_ISSET)) {
-       open_files->flags |= MARK_ISSET;
-       open_files->mark_beginbuf = mark_beginbuf;
-       open_files->mark_beginx = mark_beginx;
-    }
-
-    /* Save the current file format. */
-    open_files->fmt = fmt;
+               execute ? extcmd_list :
 #endif
-
-#ifdef DEBUG
-    fprintf(stderr, "filename is %s\n", open_files->filename);
+               insertfile_list, ans,
+#ifndef NANO_SMALL
+               NULL,
 #endif
-}
+               _(msg),
+#ifndef DISABLE_OPERATINGDIR
+               operating_dir != NULL && strcmp(operating_dir, ".") != 0 ?
+               operating_dir :
+#endif
+               "./");
 
-/* Read the current entry in the open_files structure and set up the
- * currently open file buffer using that entry's information. */
-void load_open_file(void)
-{
-    assert(open_files != NULL);
+       /* If we're in multibuffer mode and the filename or command is
+        * blank, open a new buffer instead of canceling. */
+       if (i == -1 || (i == -2
+#ifdef ENABLE_MULTIBUFFER
+               && !ISSET(MULTIBUFFER)
+#endif
+               )) {
+           statusbar(_("Cancelled"));
+           break;
+       } else {
+           size_t pww_save = placewewant;
 
-    /* Restore the current filename. */
-    filename = mallocstrcpy(filename, open_files->filename);
+           ans = mallocstrcpy(ans, answer);
 
 #ifndef NANO_SMALL
-    /* Restore the current file's stat. */
-    originalfilestat = open_files->originalfilestat;
+#ifdef ENABLE_MULTIBUFFER
+           if (i == TOGGLE_MULTIBUFFER_KEY) {
+               /* Don't allow toggling if we're in view mode. */
+               if (!ISSET(VIEW_MODE))
+                   TOGGLE(MULTIBUFFER);
+               continue;
+           } else
 #endif
+           if (i == NANO_TOOTHERINSERT_KEY) {
+               execute = !execute;
+               continue;
+           }
+#ifndef DISABLE_BROWSER
+           else
+#endif
+#endif /* !NANO_SMALL */
 
-    /* Restore the current file buffer. */
-    fileage = open_files->fileage;
-    filebot = open_files->filebot;
-
-    /* Restore the current top of the edit window. */
-    edittop = open_files->edittop;
-
-    /* Restore the current line. */
-    current = open_files->current;
+#ifndef DISABLE_BROWSER
+           if (i == NANO_TOFILES_KEY) {
+               char *tmp = do_browse_from(answer);
 
-    /* Restore the current cursor position. */
-    current_x = open_files->current_x;
+               if (tmp == NULL)
+                   continue;
 
-    /* Restore the current place we want. */
-    placewewant = open_files->placewewant;
+               free(answer);
+               answer = tmp;
 
-    /* Restore the current total number of lines. */
-    totlines = open_files->totlines;
+               /* We have a file now.  Indicate this and get out of the
+                * statusbar prompt cleanly. */
+               i = 0;
+               statusq_abort();
+           }
+#endif
 
-    /* Restore the current total size. */
-    totsize = open_files->totsize;
+           /* If we don't have a file yet, go back to the statusbar
+            * prompt. */
+           if (i != 0
+#ifdef ENABLE_MULTIBUFFER
+               && (i != -2 || !ISSET(MULTIBUFFER))
+#endif
+               )
+               continue;
 
-    /* Restore the current modification status. */
-    if (open_files->flags & MODIFIED)
-       SET(MODIFIED);
-    else
-       UNSET(MODIFIED);
+#ifdef ENABLE_MULTIBUFFER
+           if (!ISSET(MULTIBUFFER)) {
+#endif
+               /* If we're not inserting into a new buffer, partition
+                * the filestruct so that it contains no text and hence
+                * looks like a new buffer, and keep track of whether
+                * the top of the partition is the top of the edit
+                * window. */
+               filepart = partition_filestruct(current, current_x,
+                       current, current_x);
+               at_edittop = (fileage == edittop);
+#ifdef ENABLE_MULTIBUFFER
+           }
+#endif
 
 #ifndef NANO_SMALL
-    /* Restore the current marking status and mark, if applicable. */
-    if (open_files->flags & MARK_ISSET) {
-       mark_beginbuf = open_files->mark_beginbuf;
-       mark_beginx = open_files->mark_beginx;
-       SET(MARK_ISSET);
-    } else
-       UNSET(MARK_ISSET);
-
-    /* Restore the current file format. */
-    fmt = open_files->fmt;
+           if (execute)
+               execute_command(answer);
+           else {
+#endif
+               answer = mallocstrassn(answer,
+                       real_dir_from_tilde(answer));
+               load_buffer(answer);
+#ifndef NANO_SMALL
+           }
 #endif
 
-#ifdef ENABLE_COLOR
-    update_color();
+#ifdef ENABLE_MULTIBUFFER
+           if (!ISSET(MULTIBUFFER))
 #endif
-    edit_refresh();
+           {
+               filestruct *top_save = fileage;
 
-    /* Update the titlebar. */
-    titlebar(NULL);
-}
+               /* If we didn't insert into a new buffer, and we were at
+                * the top of the edit window before, set the saved
+                * value of edittop to the new top of the edit window,
+                * and update the current y-coordinate to account for
+                * the number of lines inserted. */
+               if (at_edittop)
+                   edittop_save = fileage;
+               current_y += current_y_save;
 
-/* Open either the next or previous file buffer. */
-void open_prevnext_file(bool next_file)
-{
-    assert(open_files != NULL);
+               /* If we didn't insert into a new buffer, unpartition
+                * the filestruct so that it contains all the text
+                * again.  Note that we've replaced the non-text
+                * originally in the partition with the text in the
+                * inserted file/executed command output. */
+               unpartition_filestruct(&filepart);
 
-    add_open_file(TRUE);
+               /* Renumber starting with the beginning line of the old
+                * partition. */
+               renumber(top_save);
 
-    /* If only one file buffer is open, indicate it on the statusbar and
-     * get out. */
-    if (open_files == open_files->next) {
-       statusbar(_("No more open file buffers"));
-       return;
-    }
+               /* Set edittop back to what it was before. */
+               edittop = edittop_save;
+           }
 
-    /* Switch to the next or previous file, depending on the value of
-     * next. */
-    open_files = next_file ? open_files->next : open_files->prev;
+#ifdef ENABLE_MULTIBUFFER
+           if (ISSET(MULTIBUFFER)) {
+               /* Update the titlebar. */
+               titlebar(NULL);
 
-#ifdef DEBUG
-    fprintf(stderr, "filename is %s\n", open_files->filename);
+               /* Reinitialize the shortcut list. */
+               shortcut_init(FALSE);
+           } else {
 #endif
+               /* Mark the file as modified. */
+               set_modified();
 
-    /* Load the file we switched to. */
-    load_open_file();
-
-    /* And indicate the switch on the statusbar. */
-    statusbar(_("Switched to %s"),
-      ((open_files->filename[0] == '\0') ? _("New Buffer") :
-       open_files->filename));
-
-#ifdef DEBUG
-    dump_buffer(current);
+               /* Restore the old place we want. */
+               placewewant = pww_save;
+#ifdef ENABLE_MULTIBUFFER
+           }
 #endif
-}
 
-/* Open the previous entry in the open_files structure.  This function
- * is used by the shortcut list. */
-void open_prevfile_void(void)
-{
-    open_prevnext_file(FALSE);
-}
+           /* Refresh the screen. */
+           edit_refresh();
 
-/* Open the next entry in the open_files structure.  This function is
- * used by the shortcut list. */
-void open_nextfile_void(void)
-{
-    open_prevnext_file(TRUE);
+           break;
+       }
+    }
+
+    free(ans);
 }
 
-/* Delete an entry from the open_files filestruct.  After deletion of an
- * entry, the next entry is opened.  Return TRUE on success or FALSE if
- * there are no more open file buffers. */
-bool close_open_file(void)
+void do_insertfile_void(void)
 {
-    assert(open_files != NULL);
-
-    /* If only one file is open, get out. */
-    if (open_files == open_files->next)
-       return FALSE;
-
-    /* Open the next file. */
-    open_nextfile_void();
-
-    /* Close the file we had open before. */
-    unlink_opennode(open_files->prev);
+#ifdef ENABLE_MULTIBUFFER
+    if (ISSET(VIEW_MODE) && !ISSET(MULTIBUFFER))
+       statusbar(_("Key illegal in non-multibuffer mode"));
+    else
+#endif
+       do_insertfile(
+#ifndef NANO_SMALL
+               FALSE
+#endif
+               );
 
-    /* Reinitialize the shortcut list. */
-    shortcut_init(FALSE);
     display_main_list();
-
-    return TRUE;
 }
-#endif /* ENABLE_MULTIBUFFER */
 
 /* When passed "[relative path]" or "[relative path][filename]" in
  * origpath, return "[full path]" or "[full path][filename]" on success,
index e20c18adb36bb6ad800f52439925d7d82d901209..c6257e8480e242b757a86157f8d881b1afad9d1a 100644 (file)
@@ -78,903 +78,903 @@ static filestruct *jusbottom = NULL;
        /* Pointer to end of justify buffer. */
 #endif
 
-void print_view_warning(void)
+/* Create a new filestruct node.  Note that we specifically do not set
+ * prevnode->next equal to the new line. */
+filestruct *make_new_node(filestruct *prevnode)
 {
-    statusbar(_("Key illegal in VIEW mode"));
+    filestruct *newnode = (filestruct *)nmalloc(sizeof(filestruct));
+
+    newnode->data = NULL;
+    newnode->prev = prevnode;
+    newnode->next = NULL;
+    newnode->lineno = (prevnode != NULL) ? prevnode->lineno + 1 : 1;
+
+    return newnode;
 }
 
-/* What we do when we're all set to exit. */
-void finish(void)
+/* Make a copy of a filestruct node. */
+filestruct *copy_node(const filestruct *src)
 {
-    if (!ISSET(NO_HELP))
-       blank_bottombars();
-    else
-       blank_statusbar();
-
-    wrefresh(bottomwin);
-    endwin();
+    filestruct *dst;
 
-    /* Restore the old terminal settings. */
-    tcsetattr(0, TCSANOW, &oldterm);
+    assert(src != NULL);
 
-#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
-    if (!ISSET(NO_RCFILE) && ISSET(HISTORYLOG))
-       save_history();
-#endif
+    dst = (filestruct *)nmalloc(sizeof(filestruct));
 
-#ifdef DEBUG
-    thanks_for_all_the_fish();
-#endif
+    dst->data = mallocstrcpy(NULL, src->data);
+    dst->next = src->next;
+    dst->prev = src->prev;
+    dst->lineno = src->lineno;
 
-    exit(0);
+    return dst;
 }
 
-/* Die (gracefully?). */
-void die(const char *msg, ...)
+/* Splice a node into an existing filestruct. */
+void splice_node(filestruct *begin, filestruct *newnode, filestruct
+       *end)
 {
-    va_list ap;
-
-    endwin();
-    curses_ended = TRUE;
-
-    /* Restore the old terminal settings. */
-    tcsetattr(0, TCSANOW, &oldterm);
-
-    va_start(ap, msg);
-    vfprintf(stderr, msg, ap);
-    va_end(ap);
-
-    /* Save the current file buffer if it's been modified. */
-    if (ISSET(MODIFIED)) {
-       /* If we've partitioned the filestruct, unpartition it now. */
-       if (filepart != NULL)
-           unpartition_filestruct(&filepart);
+    assert(newnode != NULL && begin != NULL);
 
-       die_save_file(filename);
-    }
+    newnode->next = end;
+    newnode->prev = begin;
+    begin->next = newnode;
+    if (end != NULL)
+       end->prev = newnode;
+}
 
-#ifdef ENABLE_MULTIBUFFER
-    /* Save all of the other modified file buffers, if any. */
-    if (open_files != NULL) {
-       openfilestruct *tmp = open_files;
+/* Unlink a node from the rest of the filestruct. */
+void unlink_node(const filestruct *fileptr)
+{
+    assert(fileptr != NULL);
 
-       while (tmp != open_files->next) {
-           open_files = open_files->next;
+    if (fileptr->prev != NULL)
+       fileptr->prev->next = fileptr->next;
+    if (fileptr->next != NULL)
+       fileptr->next->prev = fileptr->prev;
+}
 
-           /* Save the current file buffer if it's been modified. */
-           if (open_files->flags & MODIFIED) {
-               /* Set fileage and filebot to match the current file
-                * buffer, and then write it to disk. */
-               fileage = open_files->fileage;
-               filebot = open_files->filebot;
-               die_save_file(open_files->filename);
-           }
-       }
-    }
-#endif
+/* Delete a node from the filestruct. */
+void delete_node(filestruct *fileptr)
+{
+    assert(fileptr != NULL && fileptr->data != NULL);
 
-    /* Get out. */
-    exit(1);
+    if (fileptr->data != NULL)
+       free(fileptr->data);
+    free(fileptr);
 }
 
-void die_save_file(const char *die_filename)
+/* Duplicate a whole filestruct. */
+filestruct *copy_filestruct(const filestruct *src)
 {
-    char *retval;
-    bool failed = TRUE;
+    filestruct *head, *copy;
 
-    /* If we're using restricted mode, don't write any emergency backup
-     * files, since that would allow reading from or writing to files
-     * not specified on the command line. */
-    if (ISSET(RESTRICTED))
-       return;
+    assert(src != NULL);
 
-    /* If we can't save, we have REAL bad problems, but we might as well
-     * TRY. */
-    if (die_filename[0] == '\0')
-       die_filename = "nano";
+    copy = copy_node(src);
+    copy->prev = NULL;
+    head = copy;
+    src = src->next;
 
-    retval = get_next_filename(die_filename, ".save");
-    if (retval[0] != '\0')
-       failed = (write_file(retval, NULL, TRUE, FALSE, TRUE) == -1);
+    while (src != NULL) {
+       copy->next = copy_node(src);
+       copy->next->prev = copy;
+       copy = copy->next;
 
-    if (!failed)
-       fprintf(stderr, _("\nBuffer written to %s\n"), retval);
-    else if (retval[0] != '\0')
-       fprintf(stderr, _("\nBuffer not written to %s: %s\n"), retval,
-               strerror(errno));
-    else
-       fprintf(stderr, _("\nBuffer not written: %s\n"),
-               _("Too many backup files?"));
+       src = src->next;
+    }
+    copy->next = NULL;
 
-    free(retval);
+    return head;
 }
 
-/* Die with an error message that the screen was too small if, well, the
- * screen is too small. */
-void check_die_too_small(void)
+/* Frees a filestruct. */
+void free_filestruct(filestruct *src)
 {
-    editwinrows = LINES - 5 + no_more_space() + no_help();
-    if (editwinrows < MIN_EDITOR_ROWS)
-       die(_("Window size is too small for nano...\n"));
+    assert(src != NULL);
+
+    while (src->next != NULL) {
+       src = src->next;
+       delete_node(src->prev);
+    }
+    delete_node(src);
 }
 
-/* Reinitialize the variables that depend on the window size.  That is,
- * fill and hblank. */
-void resize_variables(void)
+void renumber_all(void)
 {
-#ifndef DISABLE_WRAPJUSTIFY
-    fill = wrap_at;
-    if (fill <= 0)
-       fill += COLS;
-    if (fill < 0)
-       fill = 0;
-#endif
+    filestruct *temp;
+    ssize_t line = 1;
 
-    hblank = charealloc(hblank, COLS + 1);
-    charset(hblank, ' ', COLS);
-    hblank[COLS] = '\0';
+    assert(fileage == NULL || fileage != fileage->next);
+
+    for (temp = fileage; temp != NULL; temp = temp->next)
+       temp->lineno = line++;
 }
 
-/* Initialize global variables -- no better way for now.  If
- * save_cutbuffer is TRUE, don't set cutbuffer to NULL. */
-void global_init(bool save_cutbuffer)
+void renumber(filestruct *fileptr)
 {
-    check_die_too_small();
-    resize_variables();
+    if (fileptr == NULL || fileptr->prev == NULL || fileptr == fileage)
+       renumber_all();
+    else {
+       ssize_t line = fileptr->prev->lineno;
 
-    fileage = NULL;
-    edittop = NULL;
-    current = NULL;
-    if (!save_cutbuffer)
-       cutbuffer = NULL;
-    current_x = 0;
-    placewewant = 0;
-    current_y = 0;
-    totlines = 0;
-    totsize = 0;
+       assert(fileptr != fileptr->next);
+
+       for (; fileptr != NULL; fileptr = fileptr->next)
+           fileptr->lineno = ++line;
+    }
 }
 
-void window_init(void)
+/* Partition a filestruct so it begins at (top, top_x) and ends at (bot,
+ * bot_x). */
+partition *partition_filestruct(filestruct *top, size_t top_x,
+       filestruct *bot, size_t bot_x)
 {
-    check_die_too_small();
-
-    if (topwin != NULL)
-       delwin(topwin);
-    if (edit != NULL)
-       delwin(edit);
-    if (bottomwin != NULL)
-       delwin(bottomwin);
+    partition *p;
 
-    /* Set up the windows. */
-    topwin = newwin(2 - no_more_space(), COLS, 0, 0);
-    edit = newwin(editwinrows, COLS, 2 - no_more_space(), 0);
-    bottomwin = newwin(3 - no_help(), COLS, editwinrows +
-       (2 - no_more_space()), 0);
+    assert(top != NULL && bot != NULL && fileage != NULL && filebot != NULL);
 
-    /* Turn the keypad back on. */
-    keypad(edit, TRUE);
-    keypad(bottomwin, TRUE);
-}
+    /* Initialize the partition. */
+    p = (partition *)nmalloc(sizeof(partition));
 
-#ifndef DISABLE_MOUSE
-void mouse_init(void)
-{
-    if (ISSET(USE_MOUSE)) {
-       mousemask(BUTTON1_RELEASED, NULL);
-       mouseinterval(50);
+    /* If the top and bottom of the partition are different from the top
+     * and bottom of the filestruct, save the latter and then set them
+     * to top and bot. */
+    if (top != fileage) {
+       p->fileage = fileage;
+       fileage = top;
     } else
-       mousemask(0, NULL);
-}
-#endif
+       p->fileage = NULL;
+    if (bot != filebot) {
+       p->filebot = filebot;
+       filebot = bot;
+    } else
+       p->filebot = NULL;
 
-#ifndef DISABLE_HELP
-/* This function allocates help_text, and stores the help string in it. 
- * help_text should be NULL initially. */
-void help_init(void)
-{
-    size_t allocsize = 0;      /* Space needed for help_text. */
-    const char *htx[3];                /* Untranslated help message.  We break
-                                * it up into three chunks in case the
-                                * full string is too long for the
-                                * compiler to handle. */
-    char *ptr;
-    const shortcut *s;
-#ifndef NANO_SMALL
-    const toggle *t;
-#ifdef ENABLE_NANORC
-    bool old_whitespace = ISSET(WHITESPACE_DISPLAY);
-
-    UNSET(WHITESPACE_DISPLAY);
-#endif
-#endif
-
-    /* First, set up the initial help text for the current function. */
-    if (currshortcut == whereis_list || currshortcut == replace_list
-            || currshortcut == replace_list_2) {
-       htx[0] = N_("Search Command Help Text\n\n "
-               "Enter the words or characters you would like to "
-               "search for, and then press Enter.  If there is a "
-               "match for the text you entered, the screen will be "
-               "updated to the location of the nearest match for the "
-               "search string.\n\n The previous search string will be "
-               "shown in brackets after the search prompt.  Hitting "
-               "Enter without entering any text will perform the "
-               "previous search.  ");
-       htx[1] = N_("If you have selected text with the mark and then "
-               "search to replace, only matches in the selected text "
-               "will be replaced.\n\n The following function keys are "
-               "available in Search mode:\n\n");
-       htx[2] = NULL;
-    } else if (currshortcut == gotoline_list) {
-       htx[0] = N_("Go To Line Help Text\n\n "
-               "Enter the line number that you wish to go to and hit "
-               "Enter.  If there are fewer lines of text than the "
-               "number you entered, you will be brought to the last "
-               "line of the file.\n\n The following function keys are "
-               "available in Go To Line mode:\n\n");
-       htx[1] = NULL;
-       htx[2] = NULL;
-    } else if (currshortcut == insertfile_list) {
-       htx[0] = N_("Insert File Help Text\n\n "
-               "Type in the name of a file to be inserted into the "
-               "current file buffer at the current cursor "
-               "location.\n\n If you have compiled nano with multiple "
-               "file buffer support, and enable multiple file buffers "
-               "with the -F or --multibuffer command line flags, the "
-               "Meta-F toggle, or a nanorc file, inserting a file "
-               "will cause it to be loaded into a separate buffer "
-               "(use Meta-< and > to switch between file buffers).  ");
-       htx[1] = N_("If you need another blank buffer, do not enter "
-               "any filename, or type in a nonexistent filename at "
-               "the prompt and press Enter.\n\n The following "
-               "function keys are available in Insert File mode:\n\n");
-       htx[2] = NULL;
-    } else if (currshortcut == writefile_list) {
-       htx[0] = N_("Write File Help Text\n\n "
-               "Type the name that you wish to save the current file "
-               "as and press Enter to save the file.\n\n If you have "
-               "selected text with the mark, you will be prompted to "
-               "save only the selected portion to a separate file.  To "
-               "reduce the chance of overwriting the current file with "
-               "just a portion of it, the current filename is not the "
-               "default in this mode.\n\n The following function keys "
-               "are available in Write File mode:\n\n");
-       htx[1] = NULL;
-       htx[2] = NULL;
-    }
-#ifndef DISABLE_BROWSER
-    else if (currshortcut == browser_list) {
-       htx[0] = N_("File Browser Help Text\n\n "
-               "The file browser is used to visually browse the "
-               "directory structure to select a file for reading "
-               "or writing.  You may use the arrow keys or Page Up/"
-               "Down to browse through the files, and S or Enter to "
-               "choose the selected file or enter the selected "
-               "directory.  To move up one level, select the "
-               "directory called \"..\" at the top of the file "
-               "list.\n\n The following function keys are available "
-               "in the file browser:\n\n");
-       htx[1] = NULL;
-       htx[2] = NULL;
-    } else if (currshortcut == gotodir_list) {
-       htx[0] = N_("Browser Go To Directory Help Text\n\n "
-               "Enter the name of the directory you would like to "
-               "browse to.\n\n If tab completion has not been "
-               "disabled, you can use the Tab key to (attempt to) "
-               "automatically complete the directory name.\n\n The "
-               "following function keys are available in Browser Go "
-               "To Directory mode:\n\n");
-       htx[1] = NULL;
-       htx[2] = NULL;
-    }
-#endif
-#ifndef DISABLE_SPELLER
-    else if (currshortcut == spell_list) {
-       htx[0] = N_("Spell Check Help Text\n\n "
-               "The spell checker checks the spelling of all text in "
-               "the current file.  When an unknown word is "
-               "encountered, it is highlighted and a replacement can "
-               "be edited.  It will then prompt to replace every "
-               "instance of the given misspelled word in the current "
-               "file, or, if you have selected text with the mark, in "
-               "the selected text.\n\n The following other functions "
-               "are available in Spell Check mode:\n\n");
-       htx[1] = NULL;
-       htx[2] = NULL;
-    }
-#endif
-#ifndef NANO_SMALL
-    else if (currshortcut == extcmd_list) {
-       htx[0] = N_("Execute Command Help Text\n\n "
-               "This menu allows you to insert the output of a "
-               "command run by the shell into the current buffer (or "
-               "a new buffer in multiple file buffer mode). If you "
-               "need another blank buffer, do not enter any "
-               "command.\n\n The following keys are available in "
-               "Execute Command mode:\n\n");
-       htx[1] = NULL;
-       htx[2] = NULL;
-    }
-#endif
-    else {
-       /* Default to the main help list. */
-       htx[0] = N_(" nano help text\n\n "
-               "The nano editor is designed to emulate the "
-               "functionality and ease-of-use of the UW Pico text "
-               "editor.  There are four main sections of the editor.  "
-               "The top line shows the program version, the current "
-               "filename being edited, and whether or not the file "
-               "has been modified.  Next is the main editor window "
-               "showing the file being edited.  The status line is "
-               "the third line from the bottom and shows important "
-               "messages.  The bottom two lines show the most "
-               "commonly used shortcuts in the editor.\n\n ");
-       htx[1] = N_("The notation for shortcuts is as follows: "
-               "Control-key sequences are notated with a caret (^) "
-               "symbol and can be entered either by using the Control "
-               "(Ctrl) key or pressing the Escape (Esc) key twice.  "
-               "Escape-key sequences are notated with the Meta (M) "
-               "symbol and can be entered using either the Esc, Alt, "
-               "or Meta key depending on your keyboard setup.  ");
-       htx[2] = N_("Also, pressing Esc twice and then typing a "
-               "three-digit decimal number from 000 to 255 will enter "
-               "the character with the corresponding value.  The "
-               "following keystrokes are available in the main editor "
-               "window.  Alternative keys are shown in "
-               "parentheses:\n\n");
-    }
-
-    htx[0] = _(htx[0]);
-    if (htx[1] != NULL)
-       htx[1] = _(htx[1]);
-    if (htx[2] != NULL)
-       htx[2] = _(htx[2]);
+    /* Save the line above the top of the partition, detach the top of
+     * the partition from it, and save the text before top_x in
+     * top_data. */
+    p->top_prev = top->prev;
+    top->prev = NULL;
+    p->top_data = mallocstrncpy(NULL, top->data, top_x + 1);
+    p->top_data[top_x] = '\0';
 
-    allocsize += strlen(htx[0]);
-    if (htx[1] != NULL)
-       allocsize += strlen(htx[1]);
-    if (htx[2] != NULL)
-       allocsize += strlen(htx[2]);
+    /* Save the line below the bottom of the partition, detach the
+     * bottom of the partition from it, and save the text after bot_x in
+     * bot_data. */
+    p->bot_next = bot->next;
+    bot->next = NULL;
+    p->bot_data = mallocstrcpy(NULL, bot->data + bot_x);
 
-    /* The space needed for the shortcut lists, at most COLS characters,
-     * plus '\n'. */
-    allocsize += (COLS < 24 ? (24 * mb_cur_max()) :
-       ((COLS + 1) * mb_cur_max())) * length_of_list(currshortcut);
+    /* Remove all text after bot_x at the bottom of the partition. */
+    null_at(&bot->data, bot_x);
 
-#ifndef NANO_SMALL
-    /* If we're on the main list, we also count the toggle help text.
-     * Each line has "M-%c\t\t\t", which fills 24 columns, plus a space,
-     * plus translated text, plus '\n'. */
-    if (currshortcut == main_list) {
-       size_t endis_len = strlen(_("enable/disable"));
+    /* Remove all text before top_x at the top of the partition. */
+    charmove(top->data, top->data + top_x, strlen(top->data) -
+       top_x + 1);
+    align(&top->data);
 
-       for (t = toggles; t != NULL; t = t->next)
-           allocsize += 8 + strlen(t->desc) + endis_len;
-    }
-#endif
+    /* Return the partition. */
+    return p;
+}
 
-    /* help_text has been freed and set to NULL unless the user resized
-     * while in the help screen. */
-    free(help_text);
+/* Unpartition a filestruct so it begins at (fileage, 0) and ends at
+ * (filebot, strlen(filebot)) again. */
+void unpartition_filestruct(partition **p)
+{
+    char *tmp;
 
-    /* Allocate space for the help text. */
-    help_text = charalloc(allocsize + 1);
+    assert(p != NULL && fileage != NULL && filebot != NULL);
 
-    /* Now add the text we want. */
-    strcpy(help_text, htx[0]);
-    if (htx[1] != NULL)
-       strcat(help_text, htx[1]);
-    if (htx[2] != NULL)
-       strcat(help_text, htx[2]);
+    /* Reattach the line above the top of the partition, and restore the
+     * text before top_x from top_data.  Free top_data when we're done
+     * with it. */
+    tmp = mallocstrcpy(NULL, fileage->data);
+    fileage->prev = (*p)->top_prev;
+    if (fileage->prev != NULL)
+       fileage->prev->next = fileage;
+    fileage->data = charealloc(fileage->data, strlen((*p)->top_data) +
+       strlen(fileage->data) + 1);
+    strcpy(fileage->data, (*p)->top_data);
+    free((*p)->top_data);
+    strcat(fileage->data, tmp);
+    free(tmp);
 
-    ptr = help_text + strlen(help_text);
+    /* Reattach the line below the bottom of the partition, and restore
+     * the text after bot_x from bot_data.  Free bot_data when we're
+     * done with it. */
+    filebot->next = (*p)->bot_next;
+    if (filebot->next != NULL)
+       filebot->next->prev = filebot;
+    filebot->data = charealloc(filebot->data, strlen(filebot->data) +
+       strlen((*p)->bot_data) + 1);
+    strcat(filebot->data, (*p)->bot_data);
+    free((*p)->bot_data);
 
-    /* Now add our shortcut info.  Assume that each shortcut has, at the
-     * very least, an equivalent control key, an equivalent primary meta
-     * key sequence, or both.  Also assume that the meta key values are
-     * not control characters.  We can display a maximum of 3 shortcut
-     * entries. */
-    for (s = currshortcut; s != NULL; s = s->next) {
-       int entries = 0;
+    /* Restore the top and bottom of the filestruct, if they were
+     * different from the top and bottom of the partition. */
+    if ((*p)->fileage != NULL)
+       fileage = (*p)->fileage;
+    if ((*p)->filebot != NULL)
+       filebot = (*p)->filebot;
 
-       /* Control key. */
-       if (s->ctrlval != NANO_NO_KEY) {
-           entries++;
-           /* Yucky sentinel values that we can't handle a better
-            * way. */
-           if (s->ctrlval == NANO_CONTROL_SPACE) {
-               char *space_ptr = display_string(_("Space"), 0, 6,
-                       FALSE);
+    /* Uninitialize the partition. */
+    free(*p);
+    *p = NULL;
+}
 
-               ptr += sprintf(ptr, "^%s", space_ptr);
+/* Move all the text between (top, top_x) and (bot, bot_x) in the
+ * current filestruct to a filestruct beginning with file_top and ending
+ * with file_bot.  If no text is between (top, top_x) and (bot, bot_x),
+ * don't do anything. */
+void move_to_filestruct(filestruct **file_top, filestruct **file_bot,
+       filestruct *top, size_t top_x, filestruct *bot, size_t bot_x)
+{
+    filestruct *top_save;
+    size_t part_totsize;
+    bool at_edittop;
+#ifndef NANO_SMALL
+    bool mark_inside = FALSE;
+#endif
 
-               free(space_ptr);
-           } else if (s->ctrlval == NANO_CONTROL_8)
-               ptr += sprintf(ptr, "^?");
-           /* Normal values. */
-           else
-               ptr += sprintf(ptr, "^%c", s->ctrlval + 64);
-           *(ptr++) = '\t';
-       }
+    assert(file_top != NULL && file_bot != NULL && top != NULL && bot != NULL);
 
-       /* Function key. */
-       if (s->funcval != NANO_NO_KEY) {
-           entries++;
-           /* If this is the first entry, put it in the middle. */
-           if (entries == 1) {
-               entries++;
-               *(ptr++) = '\t';
-           }
-           ptr += sprintf(ptr, "(F%d)", s->funcval - KEY_F0);
-           *(ptr++) = '\t';
-       }
+    /* If (top, top_x)-(bot, bot_x) doesn't cover any text, get out. */
+    if (top == bot && top_x == bot_x)
+       return;
 
-       /* Primary meta key sequence.  If it's the first entry, don't
-        * put parentheses around it. */
-       if (s->metaval != NANO_NO_KEY) {
-           entries++;
-           /* If this is the last entry, put it at the end. */
-           if (entries == 2 && s->miscval == NANO_NO_KEY) {
-               entries++;
-               *(ptr++) = '\t';
-           }
-           /* Yucky sentinel values that we can't handle a better
-            * way. */
-           if (s->metaval == NANO_ALT_SPACE && entries == 1) {
-               char *space_ptr = display_string(_("Space"), 0, 5,
-                       FALSE);
+    /* Partition the filestruct so that it contains only the text from
+     * (top, top_x) to (bot, bot_x), keep track of whether the top of
+     * the partition is the top of the edit window, and keep track of
+     * whether the mark begins inside the partition. */
+    filepart = partition_filestruct(top, top_x, bot, bot_x);
+    at_edittop = (fileage == edittop);
+#ifndef NANO_SMALL
+    if (ISSET(MARK_ISSET))
+       mark_inside = (mark_beginbuf->lineno >= fileage->lineno &&
+               mark_beginbuf->lineno <= filebot->lineno &&
+               (mark_beginbuf != fileage || mark_beginx >= top_x) &&
+               (mark_beginbuf != filebot || mark_beginx <= bot_x));
+#endif
 
-               ptr += sprintf(ptr, "M-%s", space_ptr);
+    /* Get the number of characters in the text, and subtract it from
+     * totsize. */
+    get_totals(top, bot, NULL, &part_totsize);
+    totsize -= part_totsize;
 
-               free(space_ptr);
-           } else
-               /* Normal values. */
-               ptr += sprintf(ptr, (entries == 1) ? "M-%c" : "(M-%c)",
-                       toupper(s->metaval));
-           *(ptr++) = '\t';
-       }
+    if (*file_top == NULL) {
+       /* If file_top is empty, just move all the text directly into
+        * it.  This is equivalent to tacking the text in top onto the
+        * (lack of) text at the end of file_top. */
+       *file_top = fileage;
+       *file_bot = filebot;
+    } else {
+       /* Otherwise, tack the text in top onto the text at the end of
+        * file_bot. */
+       (*file_bot)->data = charealloc((*file_bot)->data,
+               strlen((*file_bot)->data) + strlen(fileage->data) + 1);
+       strcat((*file_bot)->data, fileage->data);
 
-       /* Miscellaneous meta key sequence. */
-       if (entries < 3 && s->miscval != NANO_NO_KEY) {
-           entries++;
-           /* If this is the last entry, put it at the end. */
-           if (entries == 2) {
-               entries++;
-               *(ptr++) = '\t';
-           }
-           ptr += sprintf(ptr, "(M-%c)", toupper(s->miscval));
-           *(ptr++) = '\t';
+       /* Attach the line after top to the line after file_bot.  Then,
+        * if there's more than one line after top, move file_bot down
+        * to bot. */
+       (*file_bot)->next = fileage->next;
+       if ((*file_bot)->next != NULL) {
+           (*file_bot)->next->prev = *file_bot;
+           *file_bot = filebot;
        }
+    }
 
-       /* Make sure all the help text starts at the same place. */
-       while (entries < 3) {
-           entries++;
-           *(ptr++) = '\t';
-       }
+    /* Since the text has now been saved, remove it from the filestruct.
+     * If the top of the partition was the top of the edit window, set
+     * edittop to where the text used to start.  If the mark began
+     * inside the partition, set the beginning of the mark to where the
+     * text used to start. */
+    fileage = (filestruct *)nmalloc(sizeof(filestruct));
+    fileage->data = mallocstrcpy(NULL, "");
+    filebot = fileage;
+    if (at_edittop)
+       edittop = fileage;
+#ifndef NANO_SMALL
+    if (mark_inside) {
+       mark_beginbuf = fileage;
+       mark_beginx = top_x;
+    }
+#endif
 
-       assert(s->help != NULL);
+    /* Restore the current line and cursor position. */
+    current = fileage;
+    current_x = top_x;
 
-       if (COLS > 24) {
-           char *help_ptr = display_string(s->help, 0, COLS - 24,
-               FALSE);
+    top_save = fileage;
 
-           ptr += sprintf(ptr, help_ptr);
+    /* Unpartition the filestruct so that it contains all the text
+     * again, minus the saved text. */
+    unpartition_filestruct(&filepart);
 
-           free(help_ptr);
-       }
+    /* Renumber starting with the beginning line of the old
+     * partition. */
+    renumber(top_save);
 
-       ptr += sprintf(ptr, "\n");
-    }
+    if (filebot->data[0] != '\0')
+       new_magicline();
 
-#ifndef NANO_SMALL
-    /* And the toggles... */
-    if (currshortcut == main_list) {
-       for (t = toggles; t != NULL; t = t->next) {
+    /* Set totlines to the new number of lines in the file. */
+    totlines = filebot->lineno;
+}
 
-           assert(t->desc != NULL);
+/* Copy all the text from the filestruct beginning with file_top and
+ * ending with file_bot to the current filestruct at the current cursor
+ * position. */
+void copy_from_filestruct(filestruct *file_top, filestruct *file_bot)
+{
+    filestruct *top_save;
+    size_t part_totlines, part_totsize;
+    bool at_edittop;
 
-           ptr += sprintf(ptr, "M-%c\t\t\t%s %s\n", toupper(t->val),
-               t->desc, _("enable/disable"));
-       }
-    }
+    assert(file_top != NULL && file_bot != NULL);
 
-#ifdef ENABLE_NANORC
-    if (old_whitespace)
-       SET(WHITESPACE_DISPLAY);
-#endif
-#endif
+    /* Partition the filestruct so that it contains no text, and keep
+     * track of whether the top of the partition is the top of the edit
+     * window. */
+    filepart = partition_filestruct(current, current_x, current,
+       current_x);
+    at_edittop = (fileage == edittop);
 
-    /* If all went well, we didn't overwrite the allocated space for
-     * help_text. */
-    assert(strlen(help_text) <= allocsize + 1);
-}
-#endif
+    /* Put the top and bottom of the filestruct at copies of file_top
+     * and file_bot. */
+    fileage = copy_filestruct(file_top);
+    filebot = fileage;
+    while (filebot->next != NULL)
+       filebot = filebot->next;
 
-/* Create a new filestruct node.  Note that we specifically do not set
- * prevnode->next equal to the new line. */
-filestruct *make_new_node(filestruct *prevnode)
-{
-    filestruct *newnode = (filestruct *)nmalloc(sizeof(filestruct));
+    /* Restore the current line and cursor position. */
+    current = filebot;
+    current_x = strlen(filebot->data);
+    if (fileage == filebot)
+       current_x += strlen(filepart->top_data);
 
-    newnode->data = NULL;
-    newnode->prev = prevnode;
-    newnode->next = NULL;
-    newnode->lineno = (prevnode != NULL) ? prevnode->lineno + 1 : 1;
+    /* Get the number of lines and the number of characters in the saved
+     * text, and add the latter to totsize. */
+    get_totals(fileage, filebot, &part_totlines, &part_totsize);
+    totsize += part_totsize;
 
-    return newnode;
-}
+    /* If the top of the partition was the top of the edit window, set
+     * edittop to where the saved text now starts, and update the
+     * current y-coordinate to account for the number of lines it
+     * has, less one since the first line will be tacked onto the
+     * current line. */
+    if (at_edittop)
+       edittop = fileage;
+    current_y += part_totlines - 1;
 
-/* Make a copy of a filestruct node. */
-filestruct *copy_node(const filestruct *src)
-{
-    filestruct *dst;
+    top_save = fileage;
 
-    assert(src != NULL);
+    /* Unpartition the filestruct so that it contains all the text
+     * again, minus the saved text. */
+    unpartition_filestruct(&filepart);
 
-    dst = (filestruct *)nmalloc(sizeof(filestruct));
+    /* Renumber starting with the beginning line of the old
+     * partition. */
+    renumber(top_save);
 
-    dst->data = mallocstrcpy(NULL, src->data);
-    dst->next = src->next;
-    dst->prev = src->prev;
-    dst->lineno = src->lineno;
+    if (filebot->data[0] != '\0')
+       new_magicline();
 
-    return dst;
+    /* Set totlines to the new number of lines in the file. */
+    totlines = filebot->lineno;
 }
 
-/* Splice a node into an existing filestruct. */
-void splice_node(filestruct *begin, filestruct *newnode, filestruct
-       *end)
+void print_view_warning(void)
 {
-    assert(newnode != NULL && begin != NULL);
-
-    newnode->next = end;
-    newnode->prev = begin;
-    begin->next = newnode;
-    if (end != NULL)
-       end->prev = newnode;
+    statusbar(_("Key illegal in VIEW mode"));
 }
 
-/* Unlink a node from the rest of the filestruct. */
-void unlink_node(const filestruct *fileptr)
+/* What we do when we're all set to exit. */
+void finish(void)
 {
-    assert(fileptr != NULL);
+    if (!ISSET(NO_HELP))
+       blank_bottombars();
+    else
+       blank_statusbar();
 
-    if (fileptr->prev != NULL)
-       fileptr->prev->next = fileptr->next;
-    if (fileptr->next != NULL)
-       fileptr->next->prev = fileptr->prev;
-}
+    wrefresh(bottomwin);
+    endwin();
 
-/* Delete a node from the filestruct. */
-void delete_node(filestruct *fileptr)
-{
-    assert(fileptr != NULL && fileptr->data != NULL);
+    /* Restore the old terminal settings. */
+    tcsetattr(0, TCSANOW, &oldterm);
 
-    if (fileptr->data != NULL)
-       free(fileptr->data);
-    free(fileptr);
+#if !defined(NANO_SMALL) && defined(ENABLE_NANORC)
+    if (!ISSET(NO_RCFILE) && ISSET(HISTORYLOG))
+       save_history();
+#endif
+
+#ifdef DEBUG
+    thanks_for_all_the_fish();
+#endif
+
+    exit(0);
 }
 
-/* Duplicate a whole filestruct. */
-filestruct *copy_filestruct(const filestruct *src)
+/* Die (gracefully?). */
+void die(const char *msg, ...)
 {
-    filestruct *head, *copy;
+    va_list ap;
 
-    assert(src != NULL);
+    endwin();
+    curses_ended = TRUE;
 
-    copy = copy_node(src);
-    copy->prev = NULL;
-    head = copy;
-    src = src->next;
+    /* Restore the old terminal settings. */
+    tcsetattr(0, TCSANOW, &oldterm);
 
-    while (src != NULL) {
-       copy->next = copy_node(src);
-       copy->next->prev = copy;
-       copy = copy->next;
+    va_start(ap, msg);
+    vfprintf(stderr, msg, ap);
+    va_end(ap);
 
-       src = src->next;
+    /* Save the current file buffer if it's been modified. */
+    if (ISSET(MODIFIED)) {
+       /* If we've partitioned the filestruct, unpartition it now. */
+       if (filepart != NULL)
+           unpartition_filestruct(&filepart);
+
+       die_save_file(filename);
     }
-    copy->next = NULL;
 
-    return head;
-}
+#ifdef ENABLE_MULTIBUFFER
+    /* Save all of the other modified file buffers, if any. */
+    if (open_files != NULL) {
+       openfilestruct *tmp = open_files;
 
-/* Frees a filestruct. */
-void free_filestruct(filestruct *src)
-{
-    assert(src != NULL);
+       while (tmp != open_files->next) {
+           open_files = open_files->next;
 
-    while (src->next != NULL) {
-       src = src->next;
-       delete_node(src->prev);
+           /* Save the current file buffer if it's been modified. */
+           if (open_files->flags & MODIFIED) {
+               /* Set fileage and filebot to match the current file
+                * buffer, and then write it to disk. */
+               fileage = open_files->fileage;
+               filebot = open_files->filebot;
+               die_save_file(open_files->filename);
+           }
+       }
     }
-    delete_node(src);
+#endif
+
+    /* Get out. */
+    exit(1);
 }
 
-/* Partition a filestruct so it begins at (top, top_x) and ends at (bot,
- * bot_x). */
-partition *partition_filestruct(filestruct *top, size_t top_x,
-       filestruct *bot, size_t bot_x)
+void die_save_file(const char *die_filename)
 {
-    partition *p;
+    char *retval;
+    bool failed = TRUE;
 
-    assert(top != NULL && bot != NULL && fileage != NULL && filebot != NULL);
+    /* If we're using restricted mode, don't write any emergency backup
+     * files, since that would allow reading from or writing to files
+     * not specified on the command line. */
+    if (ISSET(RESTRICTED))
+       return;
 
-    /* Initialize the partition. */
-    p = (partition *)nmalloc(sizeof(partition));
+    /* If we can't save, we have REAL bad problems, but we might as well
+     * TRY. */
+    if (die_filename[0] == '\0')
+       die_filename = "nano";
 
-    /* If the top and bottom of the partition are different from the top
-     * and bottom of the filestruct, save the latter and then set them
-     * to top and bot. */
-    if (top != fileage) {
-       p->fileage = fileage;
-       fileage = top;
-    } else
-       p->fileage = NULL;
-    if (bot != filebot) {
-       p->filebot = filebot;
-       filebot = bot;
-    } else
-       p->filebot = NULL;
+    retval = get_next_filename(die_filename, ".save");
+    if (retval[0] != '\0')
+       failed = (write_file(retval, NULL, TRUE, FALSE, TRUE) == -1);
 
-    /* Save the line above the top of the partition, detach the top of
-     * the partition from it, and save the text before top_x in
-     * top_data. */
-    p->top_prev = top->prev;
-    top->prev = NULL;
-    p->top_data = mallocstrncpy(NULL, top->data, top_x + 1);
-    p->top_data[top_x] = '\0';
+    if (!failed)
+       fprintf(stderr, _("\nBuffer written to %s\n"), retval);
+    else if (retval[0] != '\0')
+       fprintf(stderr, _("\nBuffer not written to %s: %s\n"), retval,
+               strerror(errno));
+    else
+       fprintf(stderr, _("\nBuffer not written: %s\n"),
+               _("Too many backup files?"));
 
-    /* Save the line below the bottom of the partition, detach the
-     * bottom of the partition from it, and save the text after bot_x in
-     * bot_data. */
-    p->bot_next = bot->next;
-    bot->next = NULL;
-    p->bot_data = mallocstrcpy(NULL, bot->data + bot_x);
+    free(retval);
+}
 
-    /* Remove all text after bot_x at the bottom of the partition. */
-    null_at(&bot->data, bot_x);
+/* Die with an error message that the screen was too small if, well, the
+ * screen is too small. */
+void check_die_too_small(void)
+{
+    editwinrows = LINES - 5 + no_more_space() + no_help();
+    if (editwinrows < MIN_EDITOR_ROWS)
+       die(_("Window size is too small for nano...\n"));
+}
 
-    /* Remove all text before top_x at the top of the partition. */
-    charmove(top->data, top->data + top_x, strlen(top->data) -
-       top_x + 1);
-    align(&top->data);
+/* Reinitialize the variables that depend on the window size.  That is,
+ * fill and hblank. */
+void resize_variables(void)
+{
+#ifndef DISABLE_WRAPJUSTIFY
+    fill = wrap_at;
+    if (fill <= 0)
+       fill += COLS;
+    if (fill < 0)
+       fill = 0;
+#endif
 
-    /* Return the partition. */
-    return p;
+    hblank = charealloc(hblank, COLS + 1);
+    charset(hblank, ' ', COLS);
+    hblank[COLS] = '\0';
 }
 
-/* Unpartition a filestruct so it begins at (fileage, 0) and ends at
- * (filebot, strlen(filebot)) again. */
-void unpartition_filestruct(partition **p)
+/* Initialize global variables -- no better way for now.  If
+ * save_cutbuffer is TRUE, don't set cutbuffer to NULL. */
+void global_init(bool save_cutbuffer)
 {
-    char *tmp;
+    check_die_too_small();
+    resize_variables();
 
-    assert(p != NULL && fileage != NULL && filebot != NULL);
+    fileage = NULL;
+    edittop = NULL;
+    current = NULL;
+    if (!save_cutbuffer)
+       cutbuffer = NULL;
+    current_x = 0;
+    placewewant = 0;
+    current_y = 0;
+    totlines = 0;
+    totsize = 0;
+}
 
-    /* Reattach the line above the top of the partition, and restore the
-     * text before top_x from top_data.  Free top_data when we're done
-     * with it. */
-    tmp = mallocstrcpy(NULL, fileage->data);
-    fileage->prev = (*p)->top_prev;
-    if (fileage->prev != NULL)
-       fileage->prev->next = fileage;
-    fileage->data = charealloc(fileage->data, strlen((*p)->top_data) +
-       strlen(fileage->data) + 1);
-    strcpy(fileage->data, (*p)->top_data);
-    free((*p)->top_data);
-    strcat(fileage->data, tmp);
-    free(tmp);
+void window_init(void)
+{
+    check_die_too_small();
 
-    /* Reattach the line below the bottom of the partition, and restore
-     * the text after bot_x from bot_data.  Free bot_data when we're
-     * done with it. */
-    filebot->next = (*p)->bot_next;
-    if (filebot->next != NULL)
-       filebot->next->prev = filebot;
-    filebot->data = charealloc(filebot->data, strlen(filebot->data) +
-       strlen((*p)->bot_data) + 1);
-    strcat(filebot->data, (*p)->bot_data);
-    free((*p)->bot_data);
+    if (topwin != NULL)
+       delwin(topwin);
+    if (edit != NULL)
+       delwin(edit);
+    if (bottomwin != NULL)
+       delwin(bottomwin);
 
-    /* Restore the top and bottom of the filestruct, if they were
-     * different from the top and bottom of the partition. */
-    if ((*p)->fileage != NULL)
-       fileage = (*p)->fileage;
-    if ((*p)->filebot != NULL)
-       filebot = (*p)->filebot;
+    /* Set up the windows. */
+    topwin = newwin(2 - no_more_space(), COLS, 0, 0);
+    edit = newwin(editwinrows, COLS, 2 - no_more_space(), 0);
+    bottomwin = newwin(3 - no_help(), COLS, editwinrows +
+       (2 - no_more_space()), 0);
 
-    /* Uninitialize the partition. */
-    free(*p);
-    *p = NULL;
+    /* Turn the keypad back on. */
+    keypad(edit, TRUE);
+    keypad(bottomwin, TRUE);
 }
 
-/* Move all the text between (top, top_x) and (bot, bot_x) in the
- * current filestruct to a filestruct beginning with file_top and ending
- * with file_bot.  If no text is between (top, top_x) and (bot, bot_x),
- * don't do anything. */
-void move_to_filestruct(filestruct **file_top, filestruct **file_bot,
-       filestruct *top, size_t top_x, filestruct *bot, size_t bot_x)
+#ifndef DISABLE_MOUSE
+void mouse_init(void)
 {
-    filestruct *top_save;
-    size_t part_totsize;
-    bool at_edittop;
-#ifndef NANO_SMALL
-    bool mark_inside = FALSE;
+    if (ISSET(USE_MOUSE)) {
+       mousemask(BUTTON1_RELEASED, NULL);
+       mouseinterval(50);
+    } else
+       mousemask(0, NULL);
+}
 #endif
 
-    assert(file_top != NULL && file_bot != NULL && top != NULL && bot != NULL);
+#ifndef DISABLE_HELP
+/* This function allocates help_text, and stores the help string in it. 
+ * help_text should be NULL initially. */
+void help_init(void)
+{
+    size_t allocsize = 0;      /* Space needed for help_text. */
+    const char *htx[3];                /* Untranslated help message.  We break
+                                * it up into three chunks in case the
+                                * full string is too long for the
+                                * compiler to handle. */
+    char *ptr;
+    const shortcut *s;
+#ifndef NANO_SMALL
+    const toggle *t;
+#ifdef ENABLE_NANORC
+    bool old_whitespace = ISSET(WHITESPACE_DISPLAY);
 
-    /* If (top, top_x)-(bot, bot_x) doesn't cover any text, get out. */
-    if (top == bot && top_x == bot_x)
-       return;
+    UNSET(WHITESPACE_DISPLAY);
+#endif
+#endif
 
-    /* Partition the filestruct so that it contains only the text from
-     * (top, top_x) to (bot, bot_x), keep track of whether the top of
-     * the partition is the top of the edit window, and keep track of
-     * whether the mark begins inside the partition. */
-    filepart = partition_filestruct(top, top_x, bot, bot_x);
-    at_edittop = (fileage == edittop);
+    /* First, set up the initial help text for the current function. */
+    if (currshortcut == whereis_list || currshortcut == replace_list
+            || currshortcut == replace_list_2) {
+       htx[0] = N_("Search Command Help Text\n\n "
+               "Enter the words or characters you would like to "
+               "search for, and then press Enter.  If there is a "
+               "match for the text you entered, the screen will be "
+               "updated to the location of the nearest match for the "
+               "search string.\n\n The previous search string will be "
+               "shown in brackets after the search prompt.  Hitting "
+               "Enter without entering any text will perform the "
+               "previous search.  ");
+       htx[1] = N_("If you have selected text with the mark and then "
+               "search to replace, only matches in the selected text "
+               "will be replaced.\n\n The following function keys are "
+               "available in Search mode:\n\n");
+       htx[2] = NULL;
+    } else if (currshortcut == gotoline_list) {
+       htx[0] = N_("Go To Line Help Text\n\n "
+               "Enter the line number that you wish to go to and hit "
+               "Enter.  If there are fewer lines of text than the "
+               "number you entered, you will be brought to the last "
+               "line of the file.\n\n The following function keys are "
+               "available in Go To Line mode:\n\n");
+       htx[1] = NULL;
+       htx[2] = NULL;
+    } else if (currshortcut == insertfile_list) {
+       htx[0] = N_("Insert File Help Text\n\n "
+               "Type in the name of a file to be inserted into the "
+               "current file buffer at the current cursor "
+               "location.\n\n If you have compiled nano with multiple "
+               "file buffer support, and enable multiple file buffers "
+               "with the -F or --multibuffer command line flags, the "
+               "Meta-F toggle, or a nanorc file, inserting a file "
+               "will cause it to be loaded into a separate buffer "
+               "(use Meta-< and > to switch between file buffers).  ");
+       htx[1] = N_("If you need another blank buffer, do not enter "
+               "any filename, or type in a nonexistent filename at "
+               "the prompt and press Enter.\n\n The following "
+               "function keys are available in Insert File mode:\n\n");
+       htx[2] = NULL;
+    } else if (currshortcut == writefile_list) {
+       htx[0] = N_("Write File Help Text\n\n "
+               "Type the name that you wish to save the current file "
+               "as and press Enter to save the file.\n\n If you have "
+               "selected text with the mark, you will be prompted to "
+               "save only the selected portion to a separate file.  To "
+               "reduce the chance of overwriting the current file with "
+               "just a portion of it, the current filename is not the "
+               "default in this mode.\n\n The following function keys "
+               "are available in Write File mode:\n\n");
+       htx[1] = NULL;
+       htx[2] = NULL;
+    }
+#ifndef DISABLE_BROWSER
+    else if (currshortcut == browser_list) {
+       htx[0] = N_("File Browser Help Text\n\n "
+               "The file browser is used to visually browse the "
+               "directory structure to select a file for reading "
+               "or writing.  You may use the arrow keys or Page Up/"
+               "Down to browse through the files, and S or Enter to "
+               "choose the selected file or enter the selected "
+               "directory.  To move up one level, select the "
+               "directory called \"..\" at the top of the file "
+               "list.\n\n The following function keys are available "
+               "in the file browser:\n\n");
+       htx[1] = NULL;
+       htx[2] = NULL;
+    } else if (currshortcut == gotodir_list) {
+       htx[0] = N_("Browser Go To Directory Help Text\n\n "
+               "Enter the name of the directory you would like to "
+               "browse to.\n\n If tab completion has not been "
+               "disabled, you can use the Tab key to (attempt to) "
+               "automatically complete the directory name.\n\n The "
+               "following function keys are available in Browser Go "
+               "To Directory mode:\n\n");
+       htx[1] = NULL;
+       htx[2] = NULL;
+    }
+#endif
+#ifndef DISABLE_SPELLER
+    else if (currshortcut == spell_list) {
+       htx[0] = N_("Spell Check Help Text\n\n "
+               "The spell checker checks the spelling of all text in "
+               "the current file.  When an unknown word is "
+               "encountered, it is highlighted and a replacement can "
+               "be edited.  It will then prompt to replace every "
+               "instance of the given misspelled word in the current "
+               "file, or, if you have selected text with the mark, in "
+               "the selected text.\n\n The following other functions "
+               "are available in Spell Check mode:\n\n");
+       htx[1] = NULL;
+       htx[2] = NULL;
+    }
+#endif
 #ifndef NANO_SMALL
-    if (ISSET(MARK_ISSET))
-       mark_inside = (mark_beginbuf->lineno >= fileage->lineno &&
-               mark_beginbuf->lineno <= filebot->lineno &&
-               (mark_beginbuf != fileage || mark_beginx >= top_x) &&
-               (mark_beginbuf != filebot || mark_beginx <= bot_x));
+    else if (currshortcut == extcmd_list) {
+       htx[0] = N_("Execute Command Help Text\n\n "
+               "This menu allows you to insert the output of a "
+               "command run by the shell into the current buffer (or "
+               "a new buffer in multiple file buffer mode). If you "
+               "need another blank buffer, do not enter any "
+               "command.\n\n The following keys are available in "
+               "Execute Command mode:\n\n");
+       htx[1] = NULL;
+       htx[2] = NULL;
+    }
 #endif
+    else {
+       /* Default to the main help list. */
+       htx[0] = N_(" nano help text\n\n "
+               "The nano editor is designed to emulate the "
+               "functionality and ease-of-use of the UW Pico text "
+               "editor.  There are four main sections of the editor.  "
+               "The top line shows the program version, the current "
+               "filename being edited, and whether or not the file "
+               "has been modified.  Next is the main editor window "
+               "showing the file being edited.  The status line is "
+               "the third line from the bottom and shows important "
+               "messages.  The bottom two lines show the most "
+               "commonly used shortcuts in the editor.\n\n ");
+       htx[1] = N_("The notation for shortcuts is as follows: "
+               "Control-key sequences are notated with a caret (^) "
+               "symbol and can be entered either by using the Control "
+               "(Ctrl) key or pressing the Escape (Esc) key twice.  "
+               "Escape-key sequences are notated with the Meta (M) "
+               "symbol and can be entered using either the Esc, Alt, "
+               "or Meta key depending on your keyboard setup.  ");
+       htx[2] = N_("Also, pressing Esc twice and then typing a "
+               "three-digit decimal number from 000 to 255 will enter "
+               "the character with the corresponding value.  The "
+               "following keystrokes are available in the main editor "
+               "window.  Alternative keys are shown in "
+               "parentheses:\n\n");
+    }
 
-    /* Get the number of characters in the text, and subtract it from
-     * totsize. */
-    get_totals(top, bot, NULL, &part_totsize);
-    totsize -= part_totsize;
+    htx[0] = _(htx[0]);
+    if (htx[1] != NULL)
+       htx[1] = _(htx[1]);
+    if (htx[2] != NULL)
+       htx[2] = _(htx[2]);
 
-    if (*file_top == NULL) {
-       /* If file_top is empty, just move all the text directly into
-        * it.  This is equivalent to tacking the text in top onto the
-        * (lack of) text at the end of file_top. */
-       *file_top = fileage;
-       *file_bot = filebot;
-    } else {
-       /* Otherwise, tack the text in top onto the text at the end of
-        * file_bot. */
-       (*file_bot)->data = charealloc((*file_bot)->data,
-               strlen((*file_bot)->data) + strlen(fileage->data) + 1);
-       strcat((*file_bot)->data, fileage->data);
+    allocsize += strlen(htx[0]);
+    if (htx[1] != NULL)
+       allocsize += strlen(htx[1]);
+    if (htx[2] != NULL)
+       allocsize += strlen(htx[2]);
 
-       /* Attach the line after top to the line after file_bot.  Then,
-        * if there's more than one line after top, move file_bot down
-        * to bot. */
-       (*file_bot)->next = fileage->next;
-       if ((*file_bot)->next != NULL) {
-           (*file_bot)->next->prev = *file_bot;
-           *file_bot = filebot;
-       }
-    }
+    /* The space needed for the shortcut lists, at most COLS characters,
+     * plus '\n'. */
+    allocsize += (COLS < 24 ? (24 * mb_cur_max()) :
+       ((COLS + 1) * mb_cur_max())) * length_of_list(currshortcut);
 
-    /* Since the text has now been saved, remove it from the filestruct.
-     * If the top of the partition was the top of the edit window, set
-     * edittop to where the text used to start.  If the mark began
-     * inside the partition, set the beginning of the mark to where the
-     * text used to start. */
-    fileage = (filestruct *)nmalloc(sizeof(filestruct));
-    fileage->data = mallocstrcpy(NULL, "");
-    filebot = fileage;
-    if (at_edittop)
-       edittop = fileage;
 #ifndef NANO_SMALL
-    if (mark_inside) {
-       mark_beginbuf = fileage;
-       mark_beginx = top_x;
+    /* If we're on the main list, we also count the toggle help text.
+     * Each line has "M-%c\t\t\t", which fills 24 columns, plus a space,
+     * plus translated text, plus '\n'. */
+    if (currshortcut == main_list) {
+       size_t endis_len = strlen(_("enable/disable"));
+
+       for (t = toggles; t != NULL; t = t->next)
+           allocsize += 8 + strlen(t->desc) + endis_len;
     }
 #endif
 
-    /* Restore the current line and cursor position. */
-    current = fileage;
-    current_x = top_x;
+    /* help_text has been freed and set to NULL unless the user resized
+     * while in the help screen. */
+    free(help_text);
 
-    top_save = fileage;
+    /* Allocate space for the help text. */
+    help_text = charalloc(allocsize + 1);
 
-    /* Unpartition the filestruct so that it contains all the text
-     * again, minus the saved text. */
-    unpartition_filestruct(&filepart);
+    /* Now add the text we want. */
+    strcpy(help_text, htx[0]);
+    if (htx[1] != NULL)
+       strcat(help_text, htx[1]);
+    if (htx[2] != NULL)
+       strcat(help_text, htx[2]);
 
-    /* Renumber starting with the beginning line of the old
-     * partition. */
-    renumber(top_save);
+    ptr = help_text + strlen(help_text);
 
-    if (filebot->data[0] != '\0')
-       new_magicline();
+    /* Now add our shortcut info.  Assume that each shortcut has, at the
+     * very least, an equivalent control key, an equivalent primary meta
+     * key sequence, or both.  Also assume that the meta key values are
+     * not control characters.  We can display a maximum of 3 shortcut
+     * entries. */
+    for (s = currshortcut; s != NULL; s = s->next) {
+       int entries = 0;
 
-    /* Set totlines to the new number of lines in the file. */
-    totlines = filebot->lineno;
-}
+       /* Control key. */
+       if (s->ctrlval != NANO_NO_KEY) {
+           entries++;
+           /* Yucky sentinel values that we can't handle a better
+            * way. */
+           if (s->ctrlval == NANO_CONTROL_SPACE) {
+               char *space_ptr = display_string(_("Space"), 0, 6,
+                       FALSE);
 
-/* Copy all the text from the filestruct beginning with file_top and
- * ending with file_bot to the current filestruct at the current cursor
- * position. */
-void copy_from_filestruct(filestruct *file_top, filestruct *file_bot)
-{
-    filestruct *top_save;
-    size_t part_totlines, part_totsize;
-    bool at_edittop;
+               ptr += sprintf(ptr, "^%s", space_ptr);
 
-    assert(file_top != NULL && file_bot != NULL);
+               free(space_ptr);
+           } else if (s->ctrlval == NANO_CONTROL_8)
+               ptr += sprintf(ptr, "^?");
+           /* Normal values. */
+           else
+               ptr += sprintf(ptr, "^%c", s->ctrlval + 64);
+           *(ptr++) = '\t';
+       }
 
-    /* Partition the filestruct so that it contains no text, and keep
-     * track of whether the top of the partition is the top of the edit
-     * window. */
-    filepart = partition_filestruct(current, current_x, current,
-       current_x);
-    at_edittop = (fileage == edittop);
+       /* Function key. */
+       if (s->funcval != NANO_NO_KEY) {
+           entries++;
+           /* If this is the first entry, put it in the middle. */
+           if (entries == 1) {
+               entries++;
+               *(ptr++) = '\t';
+           }
+           ptr += sprintf(ptr, "(F%d)", s->funcval - KEY_F0);
+           *(ptr++) = '\t';
+       }
 
-    /* Put the top and bottom of the filestruct at copies of file_top
-     * and file_bot. */
-    fileage = copy_filestruct(file_top);
-    filebot = fileage;
-    while (filebot->next != NULL)
-       filebot = filebot->next;
+       /* Primary meta key sequence.  If it's the first entry, don't
+        * put parentheses around it. */
+       if (s->metaval != NANO_NO_KEY) {
+           entries++;
+           /* If this is the last entry, put it at the end. */
+           if (entries == 2 && s->miscval == NANO_NO_KEY) {
+               entries++;
+               *(ptr++) = '\t';
+           }
+           /* Yucky sentinel values that we can't handle a better
+            * way. */
+           if (s->metaval == NANO_ALT_SPACE && entries == 1) {
+               char *space_ptr = display_string(_("Space"), 0, 5,
+                       FALSE);
 
-    /* Restore the current line and cursor position. */
-    current = filebot;
-    current_x = strlen(filebot->data);
-    if (fileage == filebot)
-       current_x += strlen(filepart->top_data);
+               ptr += sprintf(ptr, "M-%s", space_ptr);
 
-    /* Get the number of lines and the number of characters in the saved
-     * text, and add the latter to totsize. */
-    get_totals(fileage, filebot, &part_totlines, &part_totsize);
-    totsize += part_totsize;
+               free(space_ptr);
+           } else
+               /* Normal values. */
+               ptr += sprintf(ptr, (entries == 1) ? "M-%c" : "(M-%c)",
+                       toupper(s->metaval));
+           *(ptr++) = '\t';
+       }
 
-    /* If the top of the partition was the top of the edit window, set
-     * edittop to where the saved text now starts, and update the
-     * current y-coordinate to account for the number of lines it
-     * has, less one since the first line will be tacked onto the
-     * current line. */
-    if (at_edittop)
-       edittop = fileage;
-    current_y += part_totlines - 1;
+       /* Miscellaneous meta key sequence. */
+       if (entries < 3 && s->miscval != NANO_NO_KEY) {
+           entries++;
+           /* If this is the last entry, put it at the end. */
+           if (entries == 2) {
+               entries++;
+               *(ptr++) = '\t';
+           }
+           ptr += sprintf(ptr, "(M-%c)", toupper(s->miscval));
+           *(ptr++) = '\t';
+       }
 
-    top_save = fileage;
+       /* Make sure all the help text starts at the same place. */
+       while (entries < 3) {
+           entries++;
+           *(ptr++) = '\t';
+       }
 
-    /* Unpartition the filestruct so that it contains all the text
-     * again, minus the saved text. */
-    unpartition_filestruct(&filepart);
+       assert(s->help != NULL);
 
-    /* Renumber starting with the beginning line of the old
-     * partition. */
-    renumber(top_save);
+       if (COLS > 24) {
+           char *help_ptr = display_string(s->help, 0, COLS - 24,
+               FALSE);
 
-    if (filebot->data[0] != '\0')
-       new_magicline();
+           ptr += sprintf(ptr, help_ptr);
 
-    /* Set totlines to the new number of lines in the file. */
-    totlines = filebot->lineno;
-}
+           free(help_ptr);
+       }
 
-void renumber_all(void)
-{
-    filestruct *temp;
-    ssize_t line = 1;
+       ptr += sprintf(ptr, "\n");
+    }
 
-    assert(fileage == NULL || fileage != fileage->next);
+#ifndef NANO_SMALL
+    /* And the toggles... */
+    if (currshortcut == main_list) {
+       for (t = toggles; t != NULL; t = t->next) {
 
-    for (temp = fileage; temp != NULL; temp = temp->next)
-       temp->lineno = line++;
-}
+           assert(t->desc != NULL);
 
-void renumber(filestruct *fileptr)
-{
-    if (fileptr == NULL || fileptr->prev == NULL || fileptr == fileage)
-       renumber_all();
-    else {
-       ssize_t line = fileptr->prev->lineno;
+           ptr += sprintf(ptr, "M-%c\t\t\t%s %s\n", toupper(t->val),
+               t->desc, _("enable/disable"));
+       }
+    }
 
-       assert(fileptr != fileptr->next);
+#ifdef ENABLE_NANORC
+    if (old_whitespace)
+       SET(WHITESPACE_DISPLAY);
+#endif
+#endif
 
-       for (; fileptr != NULL; fileptr = fileptr->next)
-           fileptr->lineno = ++line;
-    }
+    /* If all went well, we didn't overwrite the allocated space for
+     * help_text. */
+    assert(strlen(help_text) <= allocsize + 1);
 }
+#endif
 
 #ifdef HAVE_GETOPT_LONG
 #define print1opt(shortflag, longflag, desc) print1opt_full(shortflag, longflag, desc)
index 33c26f4bb8709b44281f1a7cfb8bf16fde49c67d..53d7ab503105bf8e5590f39d66b9ea048d90b72a 100644 (file)
@@ -246,6 +246,22 @@ void do_cut_till_end(void);
 void do_uncut_text(void);
 
 /* Public functions in files.c. */
+#ifdef ENABLE_MULTIBUFFER
+openfilestruct *make_new_opennode(void);
+void splice_opennode(openfilestruct *begin, openfilestruct *newnode,
+       openfilestruct *end);
+void unlink_opennode(openfilestruct *fileptr);
+void delete_opennode(openfilestruct *fileptr);
+#ifdef DEBUG
+void free_openfilestruct(openfilestruct *src);
+#endif
+void add_open_file(bool update);
+void load_open_file(void);
+void open_prevnext_file(bool next_file);
+void open_prevfile_void(void);
+void open_nextfile_void(void);
+bool close_open_file(void);
+#endif
 void new_file(void);
 filestruct *read_line(char *buf, filestruct *prevnode, bool
        *first_line_ins, size_t buf_len);
@@ -265,22 +281,6 @@ void do_insertfile(
 #endif
        );
 void do_insertfile_void(void);
-#ifdef ENABLE_MULTIBUFFER
-openfilestruct *make_new_opennode(void);
-void splice_opennode(openfilestruct *begin, openfilestruct *newnode,
-       openfilestruct *end);
-void unlink_opennode(openfilestruct *fileptr);
-void delete_opennode(openfilestruct *fileptr);
-#ifdef DEBUG
-void free_openfilestruct(openfilestruct *src);
-#endif
-void add_open_file(bool update);
-void load_open_file(void);
-void open_prevnext_file(bool next_file);
-void open_prevfile_void(void);
-void open_nextfile_void(void);
-bool close_open_file(void);
-#endif
 char *get_full_path(const char *origpath);
 char *check_writable_directory(const char *path);
 char *safe_tempfile(FILE **f);
@@ -359,20 +359,6 @@ void do_right(bool allow_update);
 void do_right_void(void);
 
 /* Public functions in nano.c. */
-void print_view_warning(void);
-void finish(void);
-void die(const char *msg, ...);
-void die_save_file(const char *die_filename);
-void check_die_too_small(void);
-void resize_variables(void);
-void global_init(bool save_cutbuffer);
-void window_init(void);
-#ifndef DISABLE_MOUSE
-void mouse_init(void);
-#endif
-#ifndef DISABLE_HELP
-void help_init(void);
-#endif
 filestruct *make_new_node(filestruct *prevnode);
 filestruct *copy_node(const filestruct *src);
 void splice_node(filestruct *begin, filestruct *newnode, filestruct
@@ -381,14 +367,28 @@ void unlink_node(const filestruct *fileptr);
 void delete_node(filestruct *fileptr);
 filestruct *copy_filestruct(const filestruct *src);
 void free_filestruct(filestruct *src);
+void renumber_all(void);
+void renumber(filestruct *fileptr);
 partition *partition_filestruct(filestruct *top, size_t top_x,
        filestruct *bot, size_t bot_x);
 void unpartition_filestruct(partition **p);
 void move_to_filestruct(filestruct **file_top, filestruct **file_bot,
        filestruct *top, size_t top_x, filestruct *bot, size_t bot_x);
 void copy_from_filestruct(filestruct *file_top, filestruct *file_bot);
-void renumber_all(void);
-void renumber(filestruct *fileptr);
+void print_view_warning(void);
+void finish(void);
+void die(const char *msg, ...);
+void die_save_file(const char *die_filename);
+void check_die_too_small(void);
+void resize_variables(void);
+void global_init(bool save_cutbuffer);
+void window_init(void);
+#ifndef DISABLE_MOUSE
+void mouse_init(void);
+#endif
+#ifndef DISABLE_HELP
+void help_init(void);
+#endif
 void print1opt_full(const char *shortflag
 #ifdef HAVE_GETOPT_LONG
        , const char *longflag