]> git.wh0rd.org Git - nano.git/commitdiff
reorder a few functions
authorDavid Lawrence Ramsey <pooka109@gmail.com>
Mon, 25 Jul 2005 02:33:45 +0000 (02:33 +0000)
committerDavid Lawrence Ramsey <pooka109@gmail.com>
Mon, 25 Jul 2005 02:33:45 +0000 (02:33 +0000)
git-svn-id: svn://svn.savannah.gnu.org/nano/trunk/nano@2922 35c25a1d-7b9e-4130-9fde-d3aeb78583b8

src/proto.h
src/text.c

index fa6a309a429883e19a4dd50251800efbfdedb562..b7d0414b43f64a66a3621f8af04543d4891485d5 100644 (file)
@@ -500,12 +500,6 @@ bool execute_command(const char *command);
 void wrap_reset(void);
 bool do_wrap(filestruct *line);
 #endif
-#ifndef DISABLE_SPELLER
-bool do_int_spell_fix(const char *word);
-const char *do_int_speller(const char *tempfile_name);
-const char *do_alt_speller(char *tempfile_name);
-void do_spell(void);
-#endif
 #if !defined(DISABLE_HELP) || !defined(DISABLE_JUSTIFY) || !defined(DISABLE_WRAPPING)
 ssize_t break_line(const char *line, ssize_t goal, bool newline);
 #endif
@@ -528,6 +522,12 @@ void do_justify(bool full_justify);
 void do_justify_void(void);
 void do_full_justify(void);
 #endif /* !DISABLE_JUSTIFY */
+#ifndef DISABLE_SPELLER
+bool do_int_spell_fix(const char *word);
+const char *do_int_speller(const char *tempfile_name);
+const char *do_alt_speller(char *tempfile_name);
+void do_spell(void);
+#endif
 #ifndef NANO_SMALL
 void do_word_count(void);
 #endif
index e2a038091f99dc5178e122161c88071a27a4c5aa..a4dc220aaa29a8813c546799445773e8f256d33b 100644 (file)
@@ -356,1499 +356,1499 @@ bool do_wrap(filestruct *line)
 }
 #endif /* !DISABLE_WRAPPING */
 
-#ifndef DISABLE_SPELLER
-/* A word is misspelled in the file.  Let the user replace it.  We
- * return FALSE if the user cancels. */
-bool do_int_spell_fix(const char *word)
+#if !defined(DISABLE_HELP) || !defined(DISABLE_JUSTIFY) || !defined(DISABLE_WRAPPING)
+/* We are trying to break a chunk off line.  We find the last blank such
+ * that the display length to there is at most goal + 1.  If there is no
+ * such blank, then we find the first blank.  We then take the last
+ * blank in that group of blanks.  The terminating '\0' counts as a
+ * blank, as does a '\n' if newline is TRUE. */
+ssize_t break_line(const char *line, ssize_t goal, bool newline)
 {
-    char *save_search, *save_replace;
-    size_t match_len, current_x_save = openfile->current_x;
-    size_t pww_save = openfile->placewewant;
-    filestruct *edittop_save = openfile->edittop;
-    filestruct *current_save = openfile->current;
-       /* Save where we are. */
-    bool canceled = FALSE;
-       /* The return value. */
-    bool case_sens_set = ISSET(CASE_SENSITIVE);
-#ifndef NANO_SMALL
-    bool backwards_search_set = ISSET(BACKWARDS_SEARCH);
-#endif
-#ifdef HAVE_REGEX_H
-    bool regexp_set = ISSET(USE_REGEXP);
-#endif
-#ifndef NANO_SMALL
-    bool old_mark_set = openfile->mark_set;
-    bool added_magicline = FALSE;
-       /* Whether we added a magicline after filebot. */
-    bool right_side_up = FALSE;
-       /* TRUE if (mark_begin, mark_begin_x) is the top of the mark,
-        * FALSE if (current, current_x) is. */
-    filestruct *top, *bot;
-    size_t top_x, bot_x;
-#endif
+    ssize_t blank_loc = -1;
+       /* Current tentative return value.  Index of the last blank we
+        * found with short enough display width.  */
+    ssize_t cur_loc = 0;
+       /* Current index in line. */
+    int line_len;
 
-    /* Make sure spell-check is case sensitive. */
-    SET(CASE_SENSITIVE);
+    assert(line != NULL);
 
-#ifndef NANO_SMALL
-    /* Make sure spell-check goes forward only. */
-    UNSET(BACKWARDS_SEARCH);
-#endif
-#ifdef HAVE_REGEX_H
-    /* Make sure spell-check doesn't use regular expressions. */
-    UNSET(USE_REGEXP);
-#endif
+    while (*line != '\0' && goal >= 0) {
+       size_t pos = 0;
 
-    /* Save the current search/replace strings. */
-    search_init_globals();
-    save_search = last_search;
-    save_replace = last_replace;
+       line_len = parse_mbchar(line, NULL, NULL, &pos);
 
-    /* Set the search/replace strings to the misspelled word. */
-    last_search = mallocstrcpy(NULL, word);
-    last_replace = mallocstrcpy(NULL, word);
+       if (is_blank_mbchar(line) || (newline && *line == '\n')) {
+           blank_loc = cur_loc;
 
-#ifndef NANO_SMALL
-    if (old_mark_set) {
-       /* If the mark is on, partition the filestruct so that it
-        * contains only the marked text, keep track of whether the text
-        * will have a magicline added when we're done correcting
-        * misspelled words, and turn the mark off. */
-       mark_order((const filestruct **)&top, &top_x,
-           (const filestruct **)&bot, &bot_x, &right_side_up);
-       filepart = partition_filestruct(top, top_x, bot, bot_x);
-       added_magicline = (openfile->filebot->data[0] != '\0');
-       openfile->mark_set = FALSE;
+           if (newline && *line == '\n')
+               break;
+       }
+
+       goal -= pos;
+       line += line_len;
+       cur_loc += line_len;
     }
-#endif
 
-    /* Start from the top of the file. */
-    openfile->edittop = openfile->fileage;
-    openfile->current = openfile->fileage;
-    openfile->current_x = (size_t)-1;
-    openfile->placewewant = 0;
+    if (goal >= 0)
+       /* In fact, the whole line displays shorter than goal. */
+       return cur_loc;
 
-    /* Find the first whole-word occurrence of word. */
-    findnextstr_wrap_reset();
-    while (findnextstr(TRUE, TRUE, FALSE, openfile->fileage, 0, word,
-       &match_len)) {
-       if (is_whole_word(openfile->current_x, openfile->current->data,
-               word)) {
-           size_t xpt = xplustabs();
-           char *exp_word = display_string(openfile->current->data,
-               xpt, strnlenpt(openfile->current->data,
-               openfile->current_x + match_len) - xpt, FALSE);
+    if (blank_loc == -1) {
+       /* No blank was found that was short enough. */
+       bool found_blank = FALSE;
 
-           edit_refresh();
+       while (*line != '\0') {
+           line_len = parse_mbchar(line, NULL, NULL, NULL);
 
-           do_replace_highlight(TRUE, exp_word);
+           if (is_blank_mbchar(line) || (newline && *line == '\n')) {
+               if (!found_blank)
+                   found_blank = TRUE;
+           } else if (found_blank)
+               return cur_loc - line_len;
 
-           /* Allow all instances of the word to be corrected. */
-           canceled = (statusq(FALSE, spell_list, word,
-#ifndef NANO_SMALL
-                       NULL,
-#endif
-                        _("Edit a replacement")) == -1);
+           line += line_len;
+           cur_loc += line_len;
+       }
 
-           do_replace_highlight(FALSE, exp_word);
+       return -1;
+    }
 
-           free(exp_word);
+    /* Move to the last blank after blank_loc, if there is one. */
+    line -= cur_loc;
+    line += blank_loc;
+    line_len = parse_mbchar(line, NULL, NULL, NULL);
+    line += line_len;
 
-           if (!canceled && strcmp(word, answer) != 0) {
-               openfile->current_x--;
-               do_replace_loop(word, openfile->current,
-                       &openfile->current_x, TRUE, &canceled);
-           }
+    while (*line != '\0' && (is_blank_mbchar(line) ||
+       (newline && *line == '\n'))) {
+       line_len = parse_mbchar(line, NULL, NULL, NULL);
 
-           break;
-       }
+       line += line_len;
+       blank_loc += line_len;
     }
 
-#ifndef NANO_SMALL
-    if (old_mark_set) {
-       /* If the mark was on and we added a magicline, remove it
-        * now. */
-       if (added_magicline)
-           remove_magicline();
+    return blank_loc;
+}
+#endif /* !DISABLE_HELP || !DISABLE_JUSTIFY || !DISABLE_WRAPPING */
 
-       /* Put the beginning and the end of the mark at the beginning
-        * and the end of the spell-checked text. */
-       if (openfile->fileage == openfile->filebot)
-           bot_x += top_x;
-       if (right_side_up) {
-           openfile->mark_begin_x = top_x;
-           current_x_save = bot_x;
-       } else {
-           current_x_save = top_x;
-           openfile->mark_begin_x = bot_x;
-       }
+#if !defined(NANO_SMALL) || !defined(DISABLE_JUSTIFY)
+/* The "indentation" of a line is the whitespace between the quote part
+ * and the non-whitespace of the line. */
+size_t indent_length(const char *line)
+{
+    size_t len = 0;
+    char *blank_mb;
+    int blank_mb_len;
 
-       /* Unpartition the filestruct so that it contains all the text
-        * again, and turn the mark back on. */
-       unpartition_filestruct(&filepart);
-       openfile->mark_set = TRUE;
-    }
-#endif
+    assert(line != NULL);
 
-    /* Restore the search/replace strings. */
-    free(last_search);
-    last_search = save_search;
-    free(last_replace);
-    last_replace = save_replace;
+    blank_mb = charalloc(mb_cur_max());
 
-    /* Restore where we were. */
-    openfile->edittop = edittop_save;
-    openfile->current = current_save;
-    openfile->current_x = current_x_save;
-    openfile->placewewant = pww_save;
+    while (*line != '\0') {
+       blank_mb_len = parse_mbchar(line, blank_mb, NULL, NULL);
 
-    /* Restore case sensitivity setting. */
-    if (!case_sens_set)
-       UNSET(CASE_SENSITIVE);
+       if (!is_blank_mbchar(blank_mb))
+           break;
 
-#ifndef NANO_SMALL
-    /* Restore search/replace direction. */
-    if (backwards_search_set)
-       SET(BACKWARDS_SEARCH);
-#endif
-#ifdef HAVE_REGEX_H
-    /* Restore regular expression usage setting. */
-    if (regexp_set)
-       SET(USE_REGEXP);
-#endif
+       line += blank_mb_len;
+       len += blank_mb_len;
+    }
 
-    return !canceled;
+    free(blank_mb);
+
+    return len;
 }
+#endif /* !NANO_SMALL || !DISABLE_JUSTIFY */
 
-/* Integrated spell checking using the spell program, filtered through
- * the sort and uniq programs.  Return NULL for normal termination,
- * and the error string otherwise. */
-const char *do_int_speller(const char *tempfile_name)
+#ifndef DISABLE_JUSTIFY
+/* justify_format() replaces blanks with spaces and multiple spaces by 1
+ * (except it maintains up to 2 after a character in punct optionally
+ * followed by a character in brackets, and removes all from the end).
+ *
+ * justify_format() might make paragraph->data shorter, and change the
+ * actual pointer with null_at().
+ *
+ * justify_format() will not look at the first skip characters of
+ * paragraph.  skip should be at most strlen(paragraph->data).  The
+ * character at paragraph[skip + 1] must not be blank. */
+void justify_format(filestruct *paragraph, size_t skip)
 {
-    char *read_buff, *read_buff_ptr, *read_buff_word;
-    size_t pipe_buff_size, read_buff_size, read_buff_read, bytesread;
-    int spell_fd[2], sort_fd[2], uniq_fd[2], tempfile_fd = -1;
-    pid_t pid_spell, pid_sort, pid_uniq;
-    int spell_status, sort_status, uniq_status;
+    char *end, *new_end, *new_paragraph_data;
+    size_t shift = 0;
+#ifndef NANO_SMALL
+    size_t mark_shift = 0;
+#endif
 
-    /* Create all three pipes up front. */
-    if (pipe(spell_fd) == -1 || pipe(sort_fd) == -1 ||
-       pipe(uniq_fd) == -1)
-       return _("Could not create pipe");
+    /* These four asserts are assumptions about the input data. */
+    assert(paragraph != NULL);
+    assert(paragraph->data != NULL);
+    assert(skip < strlen(paragraph->data));
+    assert(!is_blank_mbchar(paragraph->data + skip));
 
-    statusbar(_("Creating misspelled word list, please wait..."));
+    end = paragraph->data + skip;
+    new_paragraph_data = charalloc(strlen(paragraph->data) + 1);
+    strncpy(new_paragraph_data, paragraph->data, skip);
+    new_end = new_paragraph_data + skip;
 
-    /* A new process to run spell in. */
-    if ((pid_spell = fork()) == 0) {
-       /* Child continues (i.e, future spell process). */
-       close(spell_fd[0]);
-
-       /* Replace the standard input with the temp file. */
-       if ((tempfile_fd = open(tempfile_name, O_RDONLY)) == -1)
-           goto close_pipes_and_exit;
-
-       if (dup2(tempfile_fd, STDIN_FILENO) != STDIN_FILENO)
-           goto close_pipes_and_exit;
-
-       close(tempfile_fd);
-
-       /* Send spell's standard output to the pipe. */
-       if (dup2(spell_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
-           goto close_pipes_and_exit;
-
-       close(spell_fd[1]);
-
-       /* Start the spell program; we are using PATH. */
-       execlp("spell", "spell", NULL);
-
-       /* This should not be reached if spell is found. */
-       exit(1);
-    }
+    while (*end != '\0') {
+       int end_len;
 
-    /* Parent continues here. */
-    close(spell_fd[1]);
+       /* If this character is blank, make sure that it's a space with
+        * no blanks after it. */
+       if (is_blank_mbchar(end)) {
+           end_len = parse_mbchar(end, NULL, NULL, NULL);
 
-    /* A new process to run sort in. */
-    if ((pid_sort = fork()) == 0) {
-       /* Child continues (i.e, future spell process).  Replace the
-        * standard input with the standard output of the old pipe. */
-       if (dup2(spell_fd[0], STDIN_FILENO) != STDIN_FILENO)
-           goto close_pipes_and_exit;
+           *new_end = ' ';
+           new_end++;
+           end += end_len;
 
-       close(spell_fd[0]);
+           while (*end != '\0' && is_blank_mbchar(end)) {
+               end_len = parse_mbchar(end, NULL, NULL, NULL);
 
-       /* Send sort's standard output to the new pipe. */
-       if (dup2(sort_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
-           goto close_pipes_and_exit;
+               end += end_len;
+               shift += end_len;
 
-       close(sort_fd[1]);
+#ifndef NANO_SMALL
+               /* Keep track of the change in the current line. */
+               if (openfile->mark_set && openfile->mark_begin ==
+                       paragraph && openfile->mark_begin_x >= end -
+                       paragraph->data)
+                   mark_shift += end_len;
+#endif
+           }
+       /* If this character is punctuation optionally followed by a
+        * bracket and then followed by blanks, make sure there are no
+        * more than two blanks after it, and make sure that the blanks
+        * are spaces. */
+       } else if (mbstrchr(punct, end) != NULL) {
+           end_len = parse_mbchar(end, NULL, NULL, NULL);
 
-       /* Start the sort program.  Use -f to remove mixed case.  If
-        * this isn't portable, let me know. */
-       execlp("sort", "sort", "-f", NULL);
+           while (end_len > 0) {
+               *new_end = *end;
+               new_end++;
+               end++;
+               end_len--;
+           }
 
-       /* This should not be reached if sort is found. */
-       exit(1);
-    }
+           if (*end != '\0' && mbstrchr(brackets, end) != NULL) {
+               end_len = parse_mbchar(end, NULL, NULL, NULL);
 
-    close(spell_fd[0]);
-    close(sort_fd[1]);
+               while (end_len > 0) {
+                   *new_end = *end;
+                   new_end++;
+                   end++;
+                   end_len--;
+               }
+           }
 
-    /* A new process to run uniq in. */
-    if ((pid_uniq = fork()) == 0) {
-       /* Child continues (i.e, future uniq process).  Replace the
-        * standard input with the standard output of the old pipe. */
-       if (dup2(sort_fd[0], STDIN_FILENO) != STDIN_FILENO)
-           goto close_pipes_and_exit;
+           if (*end != '\0' && is_blank_mbchar(end)) {
+               end_len = parse_mbchar(end, NULL, NULL, NULL);
 
-       close(sort_fd[0]);
+               *new_end = ' ';
+               new_end++;
+               end += end_len;
+           }
 
-       /* Send uniq's standard output to the new pipe. */
-       if (dup2(uniq_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
-           goto close_pipes_and_exit;
+           if (*end != '\0' && is_blank_mbchar(end)) {
+               end_len = parse_mbchar(end, NULL, NULL, NULL);
 
-       close(uniq_fd[1]);
+               *new_end = ' ';
+               new_end++;
+               end += end_len;
+           }
 
-       /* Start the uniq program; we are using PATH. */
-       execlp("uniq", "uniq", NULL);
+           while (*end != '\0' && is_blank_mbchar(end)) {
+               end_len = parse_mbchar(end, NULL, NULL, NULL);
 
-       /* This should not be reached if uniq is found. */
-       exit(1);
-    }
+               end += end_len;
+               shift += end_len;
 
-    close(sort_fd[0]);
-    close(uniq_fd[1]);
+#ifndef NANO_SMALL
+               /* Keep track of the change in the current line. */
+               if (openfile->mark_set && openfile->mark_begin ==
+                       paragraph && openfile->mark_begin_x >= end -
+                       paragraph->data)
+                   mark_shift += end_len;
+#endif
+           }
+       /* If this character is neither blank nor punctuation, leave it
+        * alone. */
+       } else {
+           end_len = parse_mbchar(end, NULL, NULL, NULL);
 
-    /* The child process was not forked successfully. */
-    if (pid_spell < 0 || pid_sort < 0 || pid_uniq < 0) {
-       close(uniq_fd[0]);
-       return _("Could not fork");
+           while (end_len > 0) {
+               *new_end = *end;
+               new_end++;
+               end++;
+               end_len--;
+           }
+       }
     }
 
-    /* Get the system pipe buffer size. */
-    if ((pipe_buff_size = fpathconf(uniq_fd[0], _PC_PIPE_BUF)) < 1) {
-       close(uniq_fd[0]);
-       return _("Could not get size of pipe buffer");
-    }
+    assert(*end == '\0');
 
-    /* Read in the returned spelling errors. */
-    read_buff_read = 0;
-    read_buff_size = pipe_buff_size + 1;
-    read_buff = read_buff_ptr = charalloc(read_buff_size);
+    *new_end = *end;
 
-    while ((bytesread = read(uniq_fd[0], read_buff_ptr,
-       pipe_buff_size)) > 0) {
-       read_buff_read += bytesread;
-       read_buff_size += pipe_buff_size;
-       read_buff = read_buff_ptr = charealloc(read_buff,
-               read_buff_size);
-       read_buff_ptr += read_buff_read;
+    /* Make sure that there are no spaces at the end of the line. */
+    while (new_end > new_paragraph_data + skip &&
+       *(new_end - 1) == ' ') {
+       new_end--;
+       shift++;
     }
 
-    *read_buff_ptr = '\0';
-    close(uniq_fd[0]);
-
-    /* Process the spelling errors. */
-    read_buff_word = read_buff_ptr = read_buff;
+    if (shift > 0) {
+       openfile->totsize -= shift;
+       null_at(&new_paragraph_data, new_end - new_paragraph_data);
+       free(paragraph->data);
+       paragraph->data = new_paragraph_data;
 
-    while (*read_buff_ptr != '\0') {
-       if ((*read_buff_ptr == '\r') || (*read_buff_ptr == '\n')) {
-           *read_buff_ptr = '\0';
-           if (read_buff_word != read_buff_ptr) {
-               if (!do_int_spell_fix(read_buff_word)) {
-                   read_buff_word = read_buff_ptr;
-                   break;
-               }
-           }
-           read_buff_word = read_buff_ptr + 1;
+#ifndef NANO_SMALL
+       /* Adjust the mark coordinates to compensate for the change in
+        * the current line. */
+       if (openfile->mark_set && openfile->mark_begin == paragraph) {
+           openfile->mark_begin_x -= mark_shift;
+           if (openfile->mark_begin_x > new_end - new_paragraph_data)
+               openfile->mark_begin_x = new_end - new_paragraph_data;
        }
-       read_buff_ptr++;
-    }
-
-    /* Special case: the last word doesn't end with '\r' or '\n'. */
-    if (read_buff_word != read_buff_ptr)
-       do_int_spell_fix(read_buff_word);
-
-    free(read_buff);
-    replace_abort();
-    edit_refresh();
+#endif
+    } else
+       free(new_paragraph_data);
+}
 
-    /* Process the end of the spell process. */
-    waitpid(pid_spell, &spell_status, 0);
-    waitpid(pid_sort, &sort_status, 0);
-    waitpid(pid_uniq, &uniq_status, 0);
+/* The "quote part" of a line is the largest initial substring matching
+ * the quote string.  This function returns the length of the quote part
+ * of the given line.
+ *
+ * Note that if !HAVE_REGEX_H then we match concatenated copies of
+ * quotestr. */
+size_t quote_length(const char *line)
+{
+#ifdef HAVE_REGEX_H
+    regmatch_t matches;
+    int rc = regexec(&quotereg, line, 1, &matches, 0);
 
-    if (WIFEXITED(spell_status) == 0 || WEXITSTATUS(spell_status))
-       return _("Error invoking \"spell\"");
+    if (rc == REG_NOMATCH || matches.rm_so == (regoff_t)-1)
+       return 0;
+    /* matches.rm_so should be 0, since the quote string should start
+     * with the caret ^. */
+    return matches.rm_eo;
+#else  /* !HAVE_REGEX_H */
+    size_t qdepth = 0;
 
-    if (WIFEXITED(sort_status)  == 0 || WEXITSTATUS(sort_status))
-       return _("Error invoking \"sort -f\"");
+    /* Compute quote depth level. */
+    while (strncmp(line + qdepth, quotestr, quotelen) == 0)
+       qdepth += quotelen;
+    return qdepth;
+#endif /* !HAVE_REGEX_H */
+}
 
-    if (WIFEXITED(uniq_status) == 0 || WEXITSTATUS(uniq_status))
-       return _("Error invoking \"uniq\"");
+/* a_line and b_line are lines of text.  The quotation part of a_line is
+ * the first a_quote characters.  Check that the quotation part of
+ * b_line is the same. */
+bool quotes_match(const char *a_line, size_t a_quote, const char
+       *b_line)
+{
+    /* Here is the assumption about a_quote. */
+    assert(a_quote == quote_length(a_line));
 
-    /* Otherwise... */
-    return NULL;
+    return (a_quote == quote_length(b_line) &&
+       strncmp(a_line, b_line, a_quote) == 0);
+}
 
-  close_pipes_and_exit:
   /* Don't leak any handles. */
-    close(tempfile_fd);
-    close(spell_fd[0]);
-    close(spell_fd[1]);
-    close(sort_fd[0]);
-    close(sort_fd[1]);
-    close(uniq_fd[0]);
-    close(uniq_fd[1]);
-    exit(1);
+/* We assume a_line and b_line have no quote part.  Then, we return
* whether b_line could follow a_line in a paragraph. */
+bool indents_match(const char *a_line, size_t a_indent, const char
+       *b_line, size_t b_indent)
+{
+    assert(a_indent == indent_length(a_line));
+    assert(b_indent == indent_length(b_line));
+
+    return (b_indent <= a_indent &&
+       strncmp(a_line, b_line, b_indent) == 0);
 }
 
-/* External spell checking.  Return value: NULL for normal termination,
- * otherwise the error string. */
-const char *do_alt_speller(char *tempfile_name)
+/* Is foo the beginning of a paragraph?
+ *
+ *   A line of text consists of a "quote part", followed by an
+ *   "indentation part", followed by text.  The functions quote_length()
+ *   and indent_length() calculate these parts.
+ *
+ *   A line is "part of a paragraph" if it has a part not in the quote
+ *   part or the indentation.
+ *
+ *   A line is "the beginning of a paragraph" if it is part of a
+ *   paragraph and
+ *     1) it is the top line of the file, or
+ *     2) the line above it is not part of a paragraph, or
+ *     3) the line above it does not have precisely the same quote
+ *        part, or
+ *     4) the indentation of this line is not an initial substring of
+ *        the indentation of the previous line, or
+ *     5) this line has no quote part and some indentation, and
+ *        autoindent isn't turned on.
+ *   The reason for number 5) is that if autoindent isn't turned on,
+ *   then an indented line is expected to start a paragraph, as in
+ *   books.  Thus, nano can justify an indented paragraph only if
+ *   autoindent is turned on. */
+bool begpar(const filestruct *const foo)
 {
-    int alt_spell_status;
-    size_t current_x_save = openfile->current_x;
-    size_t pww_save = openfile->placewewant;
-    ssize_t current_y_save = openfile->current_y;
-    ssize_t lineno_save = openfile->current->lineno;
-    pid_t pid_spell;
-    char *ptr;
-    static int arglen = 3;
-    static char **spellargs = NULL;
-    FILE *f;
-#ifndef NANO_SMALL
-    bool old_mark_set = openfile->mark_set;
-    bool added_magicline = FALSE;
-       /* Whether we added a magicline after filebot. */
-    bool right_side_up = FALSE;
-       /* TRUE if (mark_begin, mark_begin_x) is the top of the mark,
-        * FALSE if (current, current_x) is. */
-    filestruct *top, *bot;
-    size_t top_x, bot_x;
-    ssize_t mb_lineno_save = 0;
-       /* We're going to close the current file, and open the output of
-        * the alternate spell command.  The line that mark_begin points
-        * to will be freed, so we save the line number and restore it
-        * afterwards. */
-    size_t totsize_save = openfile->totsize;
-       /* Our saved value of totsize, used when we spell-check a marked
-        * selection. */
-
-    if (old_mark_set) {
-       /* If the mark is on, save the number of the line it starts on,
-        * and then turn the mark off. */
-       mb_lineno_save = openfile->mark_begin->lineno;
-       openfile->mark_set = FALSE;
-    }
-#endif
+    size_t quote_len;
+    size_t indent_len;
+    size_t temp_id_len;
 
-    endwin();
+    /* Case 1). */
+    if (foo->prev == NULL)
+       return TRUE;
 
-    /* Set up an argument list to pass execvp(). */
-    if (spellargs == NULL) {
-       spellargs = (char **)nmalloc(arglen * sizeof(char *));
+    quote_len = quote_length(foo->data);
+    indent_len = indent_length(foo->data + quote_len);
 
-       spellargs[0] = strtok(alt_speller, " ");
-       while ((ptr = strtok(NULL, " ")) != NULL) {
-           arglen++;
-           spellargs = (char **)nrealloc(spellargs, arglen *
-               sizeof(char *));
-           spellargs[arglen - 3] = ptr;
-       }
-       spellargs[arglen - 1] = NULL;
-    }
-    spellargs[arglen - 2] = tempfile_name;
+    /* Not part of a paragraph. */
+    if (foo->data[quote_len + indent_len] == '\0')
+       return FALSE;
 
-    /* Start a new process for the alternate speller. */
-    if ((pid_spell = fork()) == 0) {
-       /* Start alternate spell program; we are using PATH. */
-       execvp(spellargs[0], spellargs);
+    /* Case 3). */
+    if (!quotes_match(foo->data, quote_len, foo->prev->data))
+       return TRUE;
 
-       /* Should not be reached, if alternate speller is found!!! */
-       exit(1);
-    }
+    temp_id_len = indent_length(foo->prev->data + quote_len);
 
-    /* If we couldn't fork, get out. */
-    if (pid_spell < 0)
-       return _("Could not fork");
+    /* Case 2) or 5) or 4). */
+    if (foo->prev->data[quote_len + temp_id_len] == '\0' ||
+       (quote_len == 0 && indent_len > 0
+#ifndef NANO_SMALL
+       && !ISSET(AUTOINDENT)
+#endif
+       ) || !indents_match(foo->prev->data + quote_len, temp_id_len,
+       foo->data + quote_len, indent_len))
+       return TRUE;
 
-    /* Wait for alternate speller to complete. */
-    wait(&alt_spell_status);
+    return FALSE;
+}
 
-    refresh();
+/* Is foo inside a paragraph? */
+bool inpar(const filestruct *const foo)
+{
+    size_t quote_len;
 
-    /* Restore the terminal to its previous state. */
-    terminal_init();
+    if (foo == NULL)
+       return FALSE;
 
-    /* Turn the cursor back on for sure. */
-    curs_set(1);
+    quote_len = quote_length(foo->data);
 
-    if (!WIFEXITED(alt_spell_status) ||
-               WEXITSTATUS(alt_spell_status) != 0) {
-       char *altspell_error;
-       char *invoke_error = _("Error invoking \"%s\"");
+    return foo->data[quote_len + indent_length(foo->data +
+       quote_len)] != '\0';
+}
 
+/* Put the next par_len lines, starting with first_line, into the
+ * justify buffer, leaving copies of those lines in place.  Assume there
+ * are enough lines after first_line.  Return the new copy of
+ * first_line. */
+filestruct *backup_lines(filestruct *first_line, size_t par_len, size_t
+       quote_len)
+{
+    filestruct *top = first_line;
+       /* The top of the paragraph we're backing up. */
+    filestruct *bot = first_line;
+       /* The bottom of the paragraph we're backing up. */
+    size_t i;
+       /* Generic loop variable. */
+    size_t current_x_save = openfile->current_x;
+    ssize_t fl_lineno_save = first_line->lineno;
+    ssize_t edittop_lineno_save = openfile->edittop->lineno;
+    ssize_t current_lineno_save = openfile->current->lineno;
 #ifndef NANO_SMALL
-       /* Turn the mark back on if it was on before. */
-       openfile->mark_set = old_mark_set;
-#endif
-
-       altspell_error =
-               charalloc(strlen(invoke_error) +
-               strlen(alt_speller) + 1);
-       sprintf(altspell_error, invoke_error, alt_speller);
-       return altspell_error;
-    }
+    bool old_mark_set = openfile->mark_set;
+    ssize_t mb_lineno_save = 0;
+    size_t mark_begin_x_save = 0;
 
-#ifndef NANO_SMALL
     if (old_mark_set) {
-       /* If the mark was on, partition the filestruct so that it
-        * contains only the marked text, and keep track of whether the
-        * temp file (which should contain the spell-checked marked
-        * text) will have a magicline added when it's reloaded. */
-       mark_order((const filestruct **)&top, &top_x,
-               (const filestruct **)&bot, &bot_x, &right_side_up);
-       filepart = partition_filestruct(top, top_x, bot, bot_x);
-       added_magicline = (openfile->filebot->data[0] != '\0');
-
-       /* Get the number of characters in the marked text, and subtract
-        * it from the saved value of totsize. */
-       totsize_save -= get_totsize(top, bot);
+       mb_lineno_save = openfile->mark_begin->lineno;
+       mark_begin_x_save = openfile->mark_begin_x;
     }
 #endif
 
-    /* Set up the window size. */
-    window_size_init();
+    /* Move bot down par_len lines to the newline after the last line of
+     * the paragraph. */
+    for (i = par_len; i > 0; i--)
+       bot = bot->next;
 
-    /* Reinitialize the text of the current buffer. */
-    free_filestruct(openfile->fileage);
-    initialize_buffer_text();
+    /* Move the paragraph from the current buffer's filestruct to the
+     * justify buffer. */
+    move_to_filestruct(&jusbuffer, &jusbottom, top, 0, bot, 0);
 
-    /* Reload the temp file.  Open it, read it into the current buffer,
-     * and move back to the first line of the buffer. */
-    open_file(tempfile_name, FALSE, &f);
-    read_file(f, tempfile_name);
-    openfile->current = openfile->fileage;
+    /* Copy the paragraph back to the current buffer's filestruct from
+     * the justify buffer. */
+    copy_from_filestruct(jusbuffer, jusbottom);
 
+    /* Move upward from the last line of the paragraph to the first
+     * line, putting first_line, edittop, current, and mark_begin at the
+     * same lines in the copied paragraph that they had in the original
+     * paragraph. */
+    top = openfile->current->prev;
+    for (i = par_len; i > 0; i--) {
+       if (top->lineno == fl_lineno_save)
+           first_line = top;
+       if (top->lineno == edittop_lineno_save)
+           openfile->edittop = top;
+       if (top->lineno == current_lineno_save)
+           openfile->current = top;
 #ifndef NANO_SMALL
-    if (old_mark_set) {
-       filestruct *top_save = openfile->fileage;
-
-       /* If the mark was on and we added a magicline, remove it
-        * now. */
-       if (added_magicline)
-           remove_magicline();
-
-       /* Put the beginning and the end of the mark at the beginning
-        * and the end of the spell-checked text. */
-       if (openfile->fileage == openfile->filebot)
-           bot_x += top_x;
-       if (right_side_up) {
-           openfile->mark_begin_x = top_x;
-           current_x_save = bot_x;
-       } else {
-           current_x_save = top_x;
-           openfile->mark_begin_x = bot_x;
+       if (old_mark_set && top->lineno == mb_lineno_save) {
+           openfile->mark_begin = top;
+           openfile->mark_begin_x = mark_begin_x_save;
        }
+#endif
+       top = top->prev;
+    }
 
-       /* Unpartition the filestruct so that it contains all the text
-        * again.  Note that we've replaced the marked text originally
-        * in the partition with the spell-checked marked text in the
-        * temp file. */
-       unpartition_filestruct(&filepart);
+    /* Put current_x at the same place in the copied paragraph that it
+     * had in the original paragraph. */
+    openfile->current_x = current_x_save;
 
-       /* Renumber starting with the beginning line of the old
-        * partition.  Also set totlines to the new number of lines in
-        * the file, add the number of characters in the spell-checked
-        * marked text to the saved value of totsize, and then make that
-        * saved value the actual value. */
-       renumber(top_save);
-       openfile->totlines = openfile->filebot->lineno;
-       totsize_save += openfile->totsize;
-       openfile->totsize = totsize_save;
+    set_modified();
 
-       /* Assign mark_begin to the line where the mark began before. */
-       do_gotopos(mb_lineno_save, openfile->mark_begin_x,
-               current_y_save, 0);
-       openfile->mark_begin = openfile->current;
+    return first_line;
+}
 
-       /* Assign mark_begin_x to the location in mark_begin where the
-        * mark began before, adjusted for any shortening of the
-        * line. */
-       openfile->mark_begin_x = openfile->current_x;
+/* Find the beginning of the current paragraph if we're in one, or the
+ * beginning of the next paragraph if we're not.  Afterwards, save the
+ * quote length and paragraph length in *quote and *par.  Return TRUE if
+ * we found a paragraph, or FALSE if there was an error or we didn't
+ * find a paragraph.
+ *
+ * See the comment at begpar() for more about when a line is the
+ * beginning of a paragraph. */
+bool find_paragraph(size_t *const quote, size_t *const par)
+{
+    size_t quote_len;
+       /* Length of the initial quotation of the paragraph we search
+        * for. */
+    size_t par_len;
+       /* Number of lines in the paragraph we search for. */
+    filestruct *current_save;
+       /* The line at the beginning of the paragraph we search for. */
+    ssize_t current_y_save;
+       /* The y-coordinate at the beginning of the paragraph we search
+        * for. */
 
-       /* Turn the mark back on. */
-       openfile->mark_set = TRUE;
+#ifdef HAVE_REGEX_H
+    if (quoterc != 0) {
+       statusbar(_("Bad quote string %s: %s"), quotestr, quoteerr);
+       return FALSE;
     }
 #endif
 
-    /* Go back to the old position, and mark the file as modified. */
-    do_gotopos(lineno_save, current_x_save, current_y_save, pww_save);
-    set_modified();
+    assert(openfile->current != NULL);
 
-    return NULL;
-}
+    /* Move back to the beginning of the current line. */
+    openfile->current_x = 0;
+    openfile->placewewant = 0;
 
-void do_spell(void)
-{
-    int i;
-    FILE *temp_file;
-    char *temp = safe_tempfile(&temp_file);
-    const char *spell_msg;
+    /* Find the first line of the current or next paragraph.  First, if
+     * the current line isn't in a paragraph, move forward to the line
+     * after the last line of the next paragraph.  If we end up on the
+     * same line, or the line before that isn't in a paragraph, it means
+     * that there aren't any paragraphs left, so get out.  Otherwise,
+     * move back to the last line of the paragraph.  If the current line
+     * is in a paragraph and it isn't the first line of that paragraph,
+     * move back to the first line. */
+    if (!inpar(openfile->current)) {
+       current_save = openfile->current;
 
-    if (temp == NULL) {
-       statusbar(_("Could not create temp file: %s"), strerror(errno));
-       return;
+       do_para_end(FALSE);
+       if (openfile->current == current_save ||
+               !inpar(openfile->current->prev))
+           return FALSE;
+       if (openfile->current->prev != NULL)
+           openfile->current = openfile->current->prev;
     }
+    if (!begpar(openfile->current))
+       do_para_begin(FALSE);
 
-#ifndef NANO_SMALL
-    if (openfile->mark_set)
-       i = write_marked_file(temp, temp_file, TRUE, FALSE);
-    else
-#endif
-       i = write_file(temp, temp_file, TRUE, FALSE, FALSE);
-
-    if (i == -1) {
-       statusbar(_("Error writing temp file: %s"), strerror(errno));
-       free(temp);
-       return;
-    }
+    /* Now current is the first line of the paragraph.  Set quote_len to
+     * the quotation length of that line, and set par_len to the number
+     * of lines in this paragraph. */
+    quote_len = quote_length(openfile->current->data);
+    current_save = openfile->current;
+    current_y_save = openfile->current_y;
+    do_para_end(FALSE);
+    par_len = openfile->current->lineno - current_save->lineno;
+    openfile->current = current_save;
+    openfile->current_y = current_y_save;
 
-    spell_msg = (alt_speller != NULL) ? do_alt_speller(temp) :
-       do_int_speller(temp);
-    unlink(temp);
-    free(temp);
+    /* Save the values of quote_len and par_len. */
+    assert(quote != NULL && par != NULL);
 
-    /* If the spell-checker printed any error messages onscreen, make
-     * sure that they're cleared off. */
-    total_refresh();
+    *quote = quote_len;
+    *par = par_len;
 
-    if (spell_msg != NULL) {
-       if (errno == 0)
-           /* Don't display an error message of "Success". */
-           statusbar(_("Spell checking failed: %s"), spell_msg);
-       else
-           statusbar(_("Spell checking failed: %s: %s"), spell_msg,
-               strerror(errno));
-    } else
-       statusbar(_("Finished checking spelling"));
+    return TRUE;
 }
-#endif /* !DISABLE_SPELLER */
 
-#if !defined(DISABLE_HELP) || !defined(DISABLE_JUSTIFY) || !defined(DISABLE_WRAPPING)
-/* We are trying to break a chunk off line.  We find the last blank such
- * that the display length to there is at most goal + 1.  If there is no
- * such blank, then we find the first blank.  We then take the last
- * blank in that group of blanks.  The terminating '\0' counts as a
- * blank, as does a '\n' if newline is TRUE. */
-ssize_t break_line(const char *line, ssize_t goal, bool newline)
+/* If full_justify is TRUE, justify the entire file.  Otherwise, justify
+ * the current paragraph. */
+void do_justify(bool full_justify)
 {
-    ssize_t blank_loc = -1;
-       /* Current tentative return value.  Index of the last blank we
-        * found with short enough display width.  */
-    ssize_t cur_loc = 0;
-       /* Current index in line. */
-    int line_len;
+    filestruct *first_par_line = NULL;
+       /* Will be the first line of the resulting justified paragraph.
+        * For restoring after unjustify. */
+    filestruct *last_par_line;
+       /* Will be the line containing the newline after the last line
+        * of the result.  Also for restoring after unjustify. */
 
-    assert(line != NULL);
+    /* We save these variables to be restored if the user unjustifies.
+     * Note that we don't need to save totlines. */
+    size_t current_x_save = openfile->current_x;
+    size_t pww_save = openfile->placewewant;
+    ssize_t current_y_save = openfile->current_y;
+    bool modified_save = openfile->modified;
+    size_t totsize_save = openfile->totsize;
+    filestruct *edittop_save = openfile->edittop;
+    filestruct *current_save = openfile->current;
+#ifndef NANO_SMALL
+    filestruct *mark_begin_save = openfile->mark_begin;
+    size_t mark_begin_x_save = openfile->mark_begin_x;
+#endif
+    int kbinput;
+    bool meta_key, func_key, s_or_t, ran_func, finished;
 
-    while (*line != '\0' && goal >= 0) {
-       size_t pos = 0;
+    /* If we're justifying the entire file, start at the beginning. */
+    if (full_justify)
+       openfile->current = openfile->fileage;
 
-       line_len = parse_mbchar(line, NULL, NULL, &pos);
+    last_par_line = openfile->current;
 
-       if (is_blank_mbchar(line) || (newline && *line == '\n')) {
-           blank_loc = cur_loc;
+    while (TRUE) {
+       size_t i;
+           /* Generic loop variable. */
+       size_t quote_len;
+           /* Length of the initial quotation of the paragraph we
+            * justify. */
+       size_t indent_len;
+           /* Length of the initial indentation of the paragraph we
+            * justify. */
+       size_t par_len;
+           /* Number of lines in the paragraph we justify. */
+       ssize_t break_pos;
+           /* Where we will break lines. */
+       char *indent_string;
+           /* The first indentation that doesn't match the initial
+            * indentation of the paragraph we justify.  This is put at
+            * the beginning of every line broken off the first
+            * justified line of the paragraph.  (Note that this works
+            * because a paragraph can only contain two indentations at
+            * most: the initial one, and a different one starting on a
+            * line after the first.  See the comment at begpar() for
+            * more about when a line is part of a paragraph.) */
 
-           if (newline && *line == '\n')
+       /* Find the first line of the paragraph to be justified.  That
+        * is the start of this paragraph if we're in one, or the start
+        * of the next otherwise.  Save the quote length and paragraph
+        * length (number of lines).  Don't refresh the screen yet,
+        * since we'll do that after we justify.
+        *
+        * If the search failed, we do one of two things.  If we're
+        * justifying the whole file, we've found at least one
+        * paragraph, and the search didn't leave us on the last line of
+        * the file, it means that we should justify all the way to the
+        * last line of the file, so set the last line of the text to be
+        * justified to the last line of the file and break out of the
+        * loop.  Otherwise, it means that there are no paragraph(s) to
+        * justify, so refresh the screen and get out. */
+       if (!find_paragraph(&quote_len, &par_len)) {
+           if (full_justify && first_par_line != NULL &&
+               first_par_line != openfile->filebot) {
+               last_par_line = openfile->filebot;
                break;
+           } else {
+               edit_refresh();
+               return;
+           }
        }
 
-       goal -= pos;
-       line += line_len;
-       cur_loc += line_len;
-    }
+       /* If we haven't already done it, copy the original paragraph(s)
+        * to the justify buffer. */
+       if (first_par_line == NULL)
+           first_par_line = backup_lines(openfile->current,
+               full_justify ? openfile->filebot->lineno -
+               openfile->current->lineno : par_len, quote_len);
 
-    if (goal >= 0)
-       /* In fact, the whole line displays shorter than goal. */
-       return cur_loc;
+       /* Initialize indent_string to a blank string. */
+       indent_string = mallocstrcpy(NULL, "");
 
-    if (blank_loc == -1) {
-       /* No blank was found that was short enough. */
-       bool found_blank = FALSE;
+       /* Find the first indentation in the paragraph that doesn't
+        * match the indentation of the first line, and save it in
+        * indent_string.  If all the indentations are the same, save
+        * the indentation of the first line in indent_string. */
+       {
+           const filestruct *indent_line = openfile->current;
+           bool past_first_line = FALSE;
 
-       while (*line != '\0') {
-           line_len = parse_mbchar(line, NULL, NULL, NULL);
+           for (i = 0; i < par_len; i++) {
+               indent_len = quote_len +
+                       indent_length(indent_line->data + quote_len);
 
-           if (is_blank_mbchar(line) || (newline && *line == '\n')) {
-               if (!found_blank)
-                   found_blank = TRUE;
-           } else if (found_blank)
-               return cur_loc - line_len;
-
-           line += line_len;
-           cur_loc += line_len;
-       }
+               if (indent_len != strlen(indent_string)) {
+                   indent_string = mallocstrncpy(indent_string,
+                       indent_line->data, indent_len + 1);
+                   indent_string[indent_len] = '\0';
 
-       return -1;
-    }
+                   if (past_first_line)
+                       break;
+               }
 
-    /* Move to the last blank after blank_loc, if there is one. */
-    line -= cur_loc;
-    line += blank_loc;
-    line_len = parse_mbchar(line, NULL, NULL, NULL);
-    line += line_len;
+               if (indent_line == openfile->current)
+                   past_first_line = TRUE;
 
-    while (*line != '\0' && (is_blank_mbchar(line) ||
-       (newline && *line == '\n'))) {
-       line_len = parse_mbchar(line, NULL, NULL, NULL);
+               indent_line = indent_line->next;
+           }
+       }
 
-       line += line_len;
-       blank_loc += line_len;
-    }
+       /* Now tack all the lines of the paragraph together, skipping
+        * the quoting and indentation on all lines after the first. */
+       for (i = 0; i < par_len - 1; i++) {
+           filestruct *next_line = openfile->current->next;
+           size_t line_len = strlen(openfile->current->data);
+           size_t next_line_len =
+               strlen(openfile->current->next->data);
 
-    return blank_loc;
-}
-#endif /* !DISABLE_HELP || !DISABLE_JUSTIFY || !DISABLE_WRAPPING */
+           indent_len = quote_len +
+               indent_length(openfile->current->next->data +
+               quote_len);
 
-#if !defined(NANO_SMALL) || !defined(DISABLE_JUSTIFY)
-/* The "indentation" of a line is the whitespace between the quote part
- * and the non-whitespace of the line. */
-size_t indent_length(const char *line)
-{
-    size_t len = 0;
-    char *blank_mb;
-    int blank_mb_len;
+           next_line_len -= indent_len;
+           openfile->totsize -= indent_len;
 
-    assert(line != NULL);
+           /* We're just about to tack the next line onto this one.  If
+            * this line isn't empty, make sure it ends in a space. */
+           if (line_len > 0 &&
+               openfile->current->data[line_len - 1] != ' ') {
+               line_len++;
+               openfile->current->data =
+                       charealloc(openfile->current->data,
+                       line_len + 1);
+               openfile->current->data[line_len - 1] = ' ';
+               openfile->current->data[line_len] = '\0';
+               openfile->totsize++;
+           }
 
-    blank_mb = charalloc(mb_cur_max());
+           openfile->current->data =
+               charealloc(openfile->current->data, line_len +
+               next_line_len + 1);
+           strcat(openfile->current->data, next_line->data +
+               indent_len);
 
-    while (*line != '\0') {
-       blank_mb_len = parse_mbchar(line, blank_mb, NULL, NULL);
+           /* Don't destroy edittop! */
+           if (openfile->edittop == next_line)
+               openfile->edittop = openfile->current;
 
-       if (!is_blank_mbchar(blank_mb))
-           break;
+#ifndef NANO_SMALL
+           /* Adjust the mark coordinates to compensate for the change
+            * in the next line. */
+           if (openfile->mark_set && openfile->mark_begin ==
+               next_line) {
+               openfile->mark_begin = openfile->current;
+               openfile->mark_begin_x += line_len - indent_len;
+           }
+#endif
 
-       line += blank_mb_len;
-       len += blank_mb_len;
-    }
+           unlink_node(next_line);
+           delete_node(next_line);
 
-    free(blank_mb);
+           /* If we've removed the next line, we need to go through
+            * this line again. */
+           i--;
 
-    return len;
-}
-#endif /* !NANO_SMALL || !DISABLE_JUSTIFY */
+           par_len--;
+           openfile->totlines--;
+           openfile->totsize--;
+       }
 
-#ifndef DISABLE_JUSTIFY
-/* justify_format() replaces blanks with spaces and multiple spaces by 1
- * (except it maintains up to 2 after a character in punct optionally
- * followed by a character in brackets, and removes all from the end).
- *
- * justify_format() might make paragraph->data shorter, and change the
- * actual pointer with null_at().
- *
- * justify_format() will not look at the first skip characters of
- * paragraph.  skip should be at most strlen(paragraph->data).  The
- * character at paragraph[skip + 1] must not be blank. */
-void justify_format(filestruct *paragraph, size_t skip)
-{
-    char *end, *new_end, *new_paragraph_data;
-    size_t shift = 0;
-#ifndef NANO_SMALL
-    size_t mark_shift = 0;
-#endif
+       /* Call justify_format() on the paragraph, which will remove
+        * excess spaces from it and change all blank characters to
+        * spaces. */
+       justify_format(openfile->current, quote_len +
+               indent_length(openfile->current->data + quote_len));
 
-    /* These four asserts are assumptions about the input data. */
-    assert(paragraph != NULL);
-    assert(paragraph->data != NULL);
-    assert(skip < strlen(paragraph->data));
-    assert(!is_blank_mbchar(paragraph->data + skip));
+       while (par_len > 0 &&
+               strlenpt(openfile->current->data) > fill) {
+           size_t line_len = strlen(openfile->current->data);
 
-    end = paragraph->data + skip;
-    new_paragraph_data = charalloc(strlen(paragraph->data) + 1);
-    strncpy(new_paragraph_data, paragraph->data, skip);
-    new_end = new_paragraph_data + skip;
+           indent_len = strlen(indent_string);
 
-    while (*end != '\0') {
-       int end_len;
+           /* If this line is too long, try to wrap it to the next line
+            * to make it short enough. */
+           break_pos =
+               break_line(openfile->current->data + indent_len, fill -
+               strnlenpt(openfile->current->data, indent_len), FALSE);
 
-       /* If this character is blank, make sure that it's a space with
-        * no blanks after it. */
-       if (is_blank_mbchar(end)) {
-           end_len = parse_mbchar(end, NULL, NULL, NULL);
+           /* We can't break the line, or don't need to, so get out. */
+           if (break_pos == -1 || break_pos + indent_len == line_len)
+               break;
 
-           *new_end = ' ';
-           new_end++;
-           end += end_len;
+           /* Move forward to the character after the indentation and
+            * just after the space. */
+           break_pos += indent_len + 1;
 
-           while (*end != '\0' && is_blank_mbchar(end)) {
-               end_len = parse_mbchar(end, NULL, NULL, NULL);
+           assert(break_pos <= line_len);
 
-               end += end_len;
-               shift += end_len;
+           /* Make a new line, and copy the text after where we're
+            * going to break this line to the beginning of the new
+            * line. */
+           splice_node(openfile->current,
+               make_new_node(openfile->current),
+               openfile->current->next);
 
+           /* If this paragraph is non-quoted, and autoindent isn't
+            * turned on, set the indentation length to zero so that the
+            * indentation is treated as part of the line. */
+           if (quote_len == 0
 #ifndef NANO_SMALL
-               /* Keep track of the change in the current line. */
-               if (openfile->mark_set && openfile->mark_begin ==
-                       paragraph && openfile->mark_begin_x >= end -
-                       paragraph->data)
-                   mark_shift += end_len;
+               && !ISSET(AUTOINDENT)
 #endif
-           }
-       /* If this character is punctuation optionally followed by a
-        * bracket and then followed by blanks, make sure there are no
-        * more than two blanks after it, and make sure that the blanks
-        * are spaces. */
-       } else if (mbstrchr(punct, end) != NULL) {
-           end_len = parse_mbchar(end, NULL, NULL, NULL);
+               )
+               indent_len = 0;
 
-           while (end_len > 0) {
-               *new_end = *end;
-               new_end++;
-               end++;
-               end_len--;
-           }
+           /* Copy the text after where we're going to break the
+            * current line to the next line. */
+           openfile->current->next->data = charalloc(indent_len + 1 +
+               line_len - break_pos);
+           strncpy(openfile->current->next->data, indent_string,
+               indent_len);
+           strcpy(openfile->current->next->data + indent_len,
+               openfile->current->data + break_pos);
 
-           if (*end != '\0' && mbstrchr(brackets, end) != NULL) {
-               end_len = parse_mbchar(end, NULL, NULL, NULL);
+           par_len++;
+           openfile->totlines++;
+           openfile->totsize += indent_len + 1;
 
-               while (end_len > 0) {
-                   *new_end = *end;
-                   new_end++;
-                   end++;
-                   end_len--;
-               }
+#ifndef NANO_SMALL
+           /* Adjust the mark coordinates to compensate for the change
+            * in the current line. */
+           if (openfile->mark_set && openfile->mark_begin ==
+               openfile->current && openfile->mark_begin_x >
+               break_pos) {
+               openfile->mark_begin = openfile->current->next;
+               openfile->mark_begin_x -= break_pos - indent_len;
            }
+#endif
 
-           if (*end != '\0' && is_blank_mbchar(end)) {
-               end_len = parse_mbchar(end, NULL, NULL, NULL);
-
-               *new_end = ' ';
-               new_end++;
-               end += end_len;
-           }
+           /* Break the current line. */
+           null_at(&openfile->current->data, break_pos);
 
-           if (*end != '\0' && is_blank_mbchar(end)) {
-               end_len = parse_mbchar(end, NULL, NULL, NULL);
+           /* Go to the next line. */
+           par_len--;
+           openfile->current_y++;
+           openfile->current = openfile->current->next;
+       }
 
-               *new_end = ' ';
-               new_end++;
-               end += end_len;
-           }
+       /* We're done breaking lines, so we don't need indent_string
+        * anymore. */
+       free(indent_string);
 
-           while (*end != '\0' && is_blank_mbchar(end)) {
-               end_len = parse_mbchar(end, NULL, NULL, NULL);
+       /* Go to the next line, the line after the last line of the
+        * paragraph. */
+       openfile->current_y++;
+       openfile->current = openfile->current->next;
 
-               end += end_len;
-               shift += end_len;
-
-#ifndef NANO_SMALL
-               /* Keep track of the change in the current line. */
-               if (openfile->mark_set && openfile->mark_begin ==
-                       paragraph && openfile->mark_begin_x >= end -
-                       paragraph->data)
-                   mark_shift += end_len;
-#endif
-           }
-       /* If this character is neither blank nor punctuation, leave it
-        * alone. */
-       } else {
-           end_len = parse_mbchar(end, NULL, NULL, NULL);
-
-           while (end_len > 0) {
-               *new_end = *end;
-               new_end++;
-               end++;
-               end_len--;
-           }
-       }
+       /* We've just justified a paragraph. If we're not justifying the
+        * entire file, break out of the loop.  Otherwise, continue the
+        * loop so that we justify all the paragraphs in the file. */
+       if (!full_justify)
+           break;
     }
 
-    assert(*end == '\0');
-
-    *new_end = *end;
-
-    /* Make sure that there are no spaces at the end of the line. */
-    while (new_end > new_paragraph_data + skip &&
-       *(new_end - 1) == ' ') {
-       new_end--;
-       shift++;
+    /* We are now done justifying the paragraph or the file, so clean
+     * up.  totlines, totsize, and current_y have been maintained above.
+     * Set last_par_line to the new end of the paragraph, update
+     * fileage, and renumber since edit_refresh() needs the line numbers
+     * to be right (but only do the last two if we actually justified
+     * something). */
+    last_par_line = openfile->current;
+    if (first_par_line != NULL) {
+       if (first_par_line->prev == NULL)
+           openfile->fileage = first_par_line;
+       renumber(first_par_line);
     }
 
-    if (shift > 0) {
-       openfile->totsize -= shift;
-       null_at(&new_paragraph_data, new_end - new_paragraph_data);
-       free(paragraph->data);
-       paragraph->data = new_paragraph_data;
+    edit_refresh();
 
-#ifndef NANO_SMALL
-       /* Adjust the mark coordinates to compensate for the change in
-        * the current line. */
-       if (openfile->mark_set && openfile->mark_begin == paragraph) {
-           openfile->mark_begin_x -= mark_shift;
-           if (openfile->mark_begin_x > new_end - new_paragraph_data)
-               openfile->mark_begin_x = new_end - new_paragraph_data;
-       }
-#endif
-    } else
-       free(new_paragraph_data);
-}
+    statusbar(_("Can now UnJustify!"));
 
-/* The "quote part" of a line is the largest initial substring matching
- * the quote string.  This function returns the length of the quote part
- * of the given line.
- *
- * Note that if !HAVE_REGEX_H then we match concatenated copies of
- * quotestr. */
-size_t quote_length(const char *line)
-{
-#ifdef HAVE_REGEX_H
-    regmatch_t matches;
-    int rc = regexec(&quotereg, line, 1, &matches, 0);
+    /* If constant cursor position display is on, make sure the current
+     * cursor position will be properly displayed on the statusbar. */
+    if (ISSET(CONST_UPDATE))
+       do_cursorpos(TRUE);
 
-    if (rc == REG_NOMATCH || matches.rm_so == (regoff_t)-1)
-       return 0;
-    /* matches.rm_so should be 0, since the quote string should start
-     * with the caret ^. */
-    return matches.rm_eo;
-#else  /* !HAVE_REGEX_H */
-    size_t qdepth = 0;
+    /* Display the shortcut list with UnJustify. */
+    shortcut_init(TRUE);
+    display_main_list();
 
-    /* Compute quote depth level. */
-    while (strncmp(line + qdepth, quotestr, quotelen) == 0)
-       qdepth += quotelen;
-    return qdepth;
-#endif /* !HAVE_REGEX_H */
-}
+    /* Now get a keystroke and see if it's unjustify.  If not, put back
+     * the keystroke and return. */
+    kbinput = do_input(&meta_key, &func_key, &s_or_t, &ran_func,
+       &finished, FALSE);
 
-/* a_line and b_line are lines of text.  The quotation part of a_line is
- * the first a_quote characters.  Check that the quotation part of
* b_line is the same. */
-bool quotes_match(const char *a_line, size_t a_quote, const char
-       *b_line)
-{
-    /* Here is the assumption about a_quote. */
-    assert(a_quote == quote_length(a_line));
+    if (!meta_key && !func_key && s_or_t &&
+       kbinput == NANO_UNJUSTIFY_KEY) {
      /* Restore the justify we just did (ungrateful user!). */
+       openfile->current = current_save;
+       openfile->current_x = current_x_save;
+       openfile->placewewant = pww_save;
+       openfile->current_y = current_y_save;
+       openfile->edittop = edittop_save;
 
-    return (a_quote == quote_length(b_line) &&
-       strncmp(a_line, b_line, a_quote) == 0);
-}
+       /* Splice the justify buffer back into the file, but only if we
+        * actually justified something. */
+       if (first_par_line != NULL) {
+           filestruct *top_save;
 
-/* We assume a_line and b_line have no quote part.  Then, we return
- * whether b_line could follow a_line in a paragraph. */
-bool indents_match(const char *a_line, size_t a_indent, const char
-       *b_line, size_t b_indent)
-{
-    assert(a_indent == indent_length(a_line));
-    assert(b_indent == indent_length(b_line));
+           /* Partition the filestruct so that it contains only the
+            * text of the justified paragraph. */
+           filepart = partition_filestruct(first_par_line, 0,
+               last_par_line, 0);
 
-    return (b_indent <= a_indent &&
-       strncmp(a_line, b_line, b_indent) == 0);
-}
+           /* Remove the text of the justified paragraph, and
+            * put the text in the justify buffer in its place. */
+           free_filestruct(openfile->fileage);
+           openfile->fileage = jusbuffer;
+           openfile->filebot = jusbottom;
 
-/* Is foo the beginning of a paragraph?
- *
- *   A line of text consists of a "quote part", followed by an
- *   "indentation part", followed by text.  The functions quote_length()
- *   and indent_length() calculate these parts.
- *
- *   A line is "part of a paragraph" if it has a part not in the quote
- *   part or the indentation.
- *
- *   A line is "the beginning of a paragraph" if it is part of a
- *   paragraph and
- *     1) it is the top line of the file, or
- *     2) the line above it is not part of a paragraph, or
- *     3) the line above it does not have precisely the same quote
- *        part, or
- *     4) the indentation of this line is not an initial substring of
- *        the indentation of the previous line, or
- *     5) this line has no quote part and some indentation, and
- *        autoindent isn't turned on.
- *   The reason for number 5) is that if autoindent isn't turned on,
- *   then an indented line is expected to start a paragraph, as in
- *   books.  Thus, nano can justify an indented paragraph only if
- *   autoindent is turned on. */
-bool begpar(const filestruct *const foo)
-{
-    size_t quote_len;
-    size_t indent_len;
-    size_t temp_id_len;
+           top_save = openfile->fileage;
 
-    /* Case 1). */
-    if (foo->prev == NULL)
-       return TRUE;
+           /* Unpartition the filestruct so that it contains all the
+            * text again.  Note that the justified paragraph has been
+            * replaced with the unjustified paragraph. */
+           unpartition_filestruct(&filepart);
 
-    quote_len = quote_length(foo->data);
-    indent_len = indent_length(foo->data + quote_len);
+            /* Renumber starting with the beginning line of the old
+             * partition. */
+           renumber(top_save);
 
-    /* Not part of a paragraph. */
-    if (foo->data[quote_len + indent_len] == '\0')
-       return FALSE;
+           /* Restore variables from before the justify. */
+           openfile->totsize = totsize_save;
+           openfile->totlines = openfile->filebot->lineno;
+#ifndef NANO_SMALL
+           if (openfile->mark_set) {
+               openfile->mark_begin = mark_begin_save;
+               openfile->mark_begin_x = mark_begin_x_save;
+           }
+#endif
+           openfile->modified = modified_save;
 
-    /* Case 3). */
-    if (!quotes_match(foo->data, quote_len, foo->prev->data))
-       return TRUE;
+           /* Clear the justify buffer. */
+           jusbuffer = NULL;
 
-    temp_id_len = indent_length(foo->prev->data + quote_len);
+           if (!openfile->modified)
+               titlebar(NULL);
+           edit_refresh();
+       }
+    } else {
+       unget_kbinput(kbinput, meta_key, func_key);
 
-    /* Case 2) or 5) or 4). */
-    if (foo->prev->data[quote_len + temp_id_len] == '\0' ||
-       (quote_len == 0 && indent_len > 0
-#ifndef NANO_SMALL
-       && !ISSET(AUTOINDENT)
-#endif
-       ) || !indents_match(foo->prev->data + quote_len, temp_id_len,
-       foo->data + quote_len, indent_len))
-       return TRUE;
+       /* Blow away the text in the justify buffer. */
+       free_filestruct(jusbuffer);
+       jusbuffer = NULL;
+    }
 
-    return FALSE;
+    blank_statusbar();
+
+    /* Display the shortcut list with UnCut. */
+    shortcut_init(FALSE);
+    display_main_list();
 }
 
-/* Is foo inside a paragraph? */
-bool inpar(const filestruct *const foo)
+void do_justify_void(void)
 {
-    size_t quote_len;
-
-    if (foo == NULL)
-       return FALSE;
-
-    quote_len = quote_length(foo->data);
+    do_justify(FALSE);
+}
 
-    return foo->data[quote_len + indent_length(foo->data +
-       quote_len)] != '\0';
+void do_full_justify(void)
+{
+    do_justify(TRUE);
 }
+#endif /* !DISABLE_JUSTIFY */
 
-/* Put the next par_len lines, starting with first_line, into the
- * justify buffer, leaving copies of those lines in place.  Assume there
- * are enough lines after first_line.  Return the new copy of
- * first_line. */
-filestruct *backup_lines(filestruct *first_line, size_t par_len, size_t
-       quote_len)
+#ifndef DISABLE_SPELLER
+/* A word is misspelled in the file.  Let the user replace it.  We
+ * return FALSE if the user cancels. */
+bool do_int_spell_fix(const char *word)
 {
-    filestruct *top = first_line;
-       /* The top of the paragraph we're backing up. */
-    filestruct *bot = first_line;
-       /* The bottom of the paragraph we're backing up. */
-    size_t i;
-       /* Generic loop variable. */
-    size_t current_x_save = openfile->current_x;
-    ssize_t fl_lineno_save = first_line->lineno;
-    ssize_t edittop_lineno_save = openfile->edittop->lineno;
-    ssize_t current_lineno_save = openfile->current->lineno;
+    char *save_search, *save_replace;
+    size_t match_len, current_x_save = openfile->current_x;
+    size_t pww_save = openfile->placewewant;
+    filestruct *edittop_save = openfile->edittop;
+    filestruct *current_save = openfile->current;
+       /* Save where we are. */
+    bool canceled = FALSE;
+       /* The return value. */
+    bool case_sens_set = ISSET(CASE_SENSITIVE);
+#ifndef NANO_SMALL
+    bool backwards_search_set = ISSET(BACKWARDS_SEARCH);
+#endif
+#ifdef HAVE_REGEX_H
+    bool regexp_set = ISSET(USE_REGEXP);
+#endif
 #ifndef NANO_SMALL
     bool old_mark_set = openfile->mark_set;
-    ssize_t mb_lineno_save = 0;
-    size_t mark_begin_x_save = 0;
+    bool added_magicline = FALSE;
+       /* Whether we added a magicline after filebot. */
+    bool right_side_up = FALSE;
+       /* TRUE if (mark_begin, mark_begin_x) is the top of the mark,
+        * FALSE if (current, current_x) is. */
+    filestruct *top, *bot;
+    size_t top_x, bot_x;
+#endif
+
+    /* Make sure spell-check is case sensitive. */
+    SET(CASE_SENSITIVE);
 
+#ifndef NANO_SMALL
+    /* Make sure spell-check goes forward only. */
+    UNSET(BACKWARDS_SEARCH);
+#endif
+#ifdef HAVE_REGEX_H
+    /* Make sure spell-check doesn't use regular expressions. */
+    UNSET(USE_REGEXP);
+#endif
+
+    /* Save the current search/replace strings. */
+    search_init_globals();
+    save_search = last_search;
+    save_replace = last_replace;
+
+    /* Set the search/replace strings to the misspelled word. */
+    last_search = mallocstrcpy(NULL, word);
+    last_replace = mallocstrcpy(NULL, word);
+
+#ifndef NANO_SMALL
     if (old_mark_set) {
-       mb_lineno_save = openfile->mark_begin->lineno;
-       mark_begin_x_save = openfile->mark_begin_x;
+       /* If the mark is on, partition the filestruct so that it
+        * contains only the marked text, keep track of whether the text
+        * will have a magicline added when we're done correcting
+        * misspelled words, and turn the mark off. */
+       mark_order((const filestruct **)&top, &top_x,
+           (const filestruct **)&bot, &bot_x, &right_side_up);
+       filepart = partition_filestruct(top, top_x, bot, bot_x);
+       added_magicline = (openfile->filebot->data[0] != '\0');
+       openfile->mark_set = FALSE;
     }
 #endif
 
-    /* Move bot down par_len lines to the newline after the last line of
-     * the paragraph. */
-    for (i = par_len; i > 0; i--)
-       bot = bot->next;
+    /* Start from the top of the file. */
+    openfile->edittop = openfile->fileage;
+    openfile->current = openfile->fileage;
+    openfile->current_x = (size_t)-1;
+    openfile->placewewant = 0;
 
-    /* Move the paragraph from the current buffer's filestruct to the
-     * justify buffer. */
-    move_to_filestruct(&jusbuffer, &jusbottom, top, 0, bot, 0);
+    /* Find the first whole-word occurrence of word. */
+    findnextstr_wrap_reset();
+    while (findnextstr(TRUE, TRUE, FALSE, openfile->fileage, 0, word,
+       &match_len)) {
+       if (is_whole_word(openfile->current_x, openfile->current->data,
+               word)) {
+           size_t xpt = xplustabs();
+           char *exp_word = display_string(openfile->current->data,
+               xpt, strnlenpt(openfile->current->data,
+               openfile->current_x + match_len) - xpt, FALSE);
 
-    /* Copy the paragraph back to the current buffer's filestruct from
-     * the justify buffer. */
-    copy_from_filestruct(jusbuffer, jusbottom);
+           edit_refresh();
 
-    /* Move upward from the last line of the paragraph to the first
-     * line, putting first_line, edittop, current, and mark_begin at the
-     * same lines in the copied paragraph that they had in the original
-     * paragraph. */
-    top = openfile->current->prev;
-    for (i = par_len; i > 0; i--) {
-       if (top->lineno == fl_lineno_save)
-           first_line = top;
-       if (top->lineno == edittop_lineno_save)
-           openfile->edittop = top;
-       if (top->lineno == current_lineno_save)
-           openfile->current = top;
+           do_replace_highlight(TRUE, exp_word);
+
+           /* Allow all instances of the word to be corrected. */
+           canceled = (statusq(FALSE, spell_list, word,
 #ifndef NANO_SMALL
-       if (old_mark_set && top->lineno == mb_lineno_save) {
-           openfile->mark_begin = top;
-           openfile->mark_begin_x = mark_begin_x_save;
-       }
+                       NULL,
 #endif
-       top = top->prev;
-    }
-
-    /* Put current_x at the same place in the copied paragraph that it
-     * had in the original paragraph. */
-    openfile->current_x = current_x_save;
+                        _("Edit a replacement")) == -1);
 
-    set_modified();
+           do_replace_highlight(FALSE, exp_word);
 
-    return first_line;
-}
+           free(exp_word);
 
-/* Find the beginning of the current paragraph if we're in one, or the
- * beginning of the next paragraph if we're not.  Afterwards, save the
- * quote length and paragraph length in *quote and *par.  Return TRUE if
- * we found a paragraph, or FALSE if there was an error or we didn't
- * find a paragraph.
- *
- * See the comment at begpar() for more about when a line is the
- * beginning of a paragraph. */
-bool find_paragraph(size_t *const quote, size_t *const par)
-{
-    size_t quote_len;
-       /* Length of the initial quotation of the paragraph we search
-        * for. */
-    size_t par_len;
-       /* Number of lines in the paragraph we search for. */
-    filestruct *current_save;
-       /* The line at the beginning of the paragraph we search for. */
-    ssize_t current_y_save;
-       /* The y-coordinate at the beginning of the paragraph we search
-        * for. */
+           if (!canceled && strcmp(word, answer) != 0) {
+               openfile->current_x--;
+               do_replace_loop(word, openfile->current,
+                       &openfile->current_x, TRUE, &canceled);
+           }
 
-#ifdef HAVE_REGEX_H
-    if (quoterc != 0) {
-       statusbar(_("Bad quote string %s: %s"), quotestr, quoteerr);
-       return FALSE;
+           break;
+       }
     }
-#endif
 
-    assert(openfile->current != NULL);
-
-    /* Move back to the beginning of the current line. */
-    openfile->current_x = 0;
-    openfile->placewewant = 0;
+#ifndef NANO_SMALL
+    if (old_mark_set) {
+       /* If the mark was on and we added a magicline, remove it
+        * now. */
+       if (added_magicline)
+           remove_magicline();
 
-    /* Find the first line of the current or next paragraph.  First, if
-     * the current line isn't in a paragraph, move forward to the line
-     * after the last line of the next paragraph.  If we end up on the
-     * same line, or the line before that isn't in a paragraph, it means
-     * that there aren't any paragraphs left, so get out.  Otherwise,
-     * move back to the last line of the paragraph.  If the current line
-     * is in a paragraph and it isn't the first line of that paragraph,
-     * move back to the first line. */
-    if (!inpar(openfile->current)) {
-       current_save = openfile->current;
+       /* Put the beginning and the end of the mark at the beginning
+        * and the end of the spell-checked text. */
+       if (openfile->fileage == openfile->filebot)
+           bot_x += top_x;
+       if (right_side_up) {
+           openfile->mark_begin_x = top_x;
+           current_x_save = bot_x;
+       } else {
+           current_x_save = top_x;
+           openfile->mark_begin_x = bot_x;
+       }
 
-       do_para_end(FALSE);
-       if (openfile->current == current_save ||
-               !inpar(openfile->current->prev))
-           return FALSE;
-       if (openfile->current->prev != NULL)
-           openfile->current = openfile->current->prev;
+       /* Unpartition the filestruct so that it contains all the text
+        * again, and turn the mark back on. */
+       unpartition_filestruct(&filepart);
+       openfile->mark_set = TRUE;
     }
-    if (!begpar(openfile->current))
-       do_para_begin(FALSE);
+#endif
 
-    /* Now current is the first line of the paragraph.  Set quote_len to
-     * the quotation length of that line, and set par_len to the number
-     * of lines in this paragraph. */
-    quote_len = quote_length(openfile->current->data);
-    current_save = openfile->current;
-    current_y_save = openfile->current_y;
-    do_para_end(FALSE);
-    par_len = openfile->current->lineno - current_save->lineno;
+    /* Restore the search/replace strings. */
+    free(last_search);
+    last_search = save_search;
+    free(last_replace);
+    last_replace = save_replace;
+
+    /* Restore where we were. */
+    openfile->edittop = edittop_save;
     openfile->current = current_save;
-    openfile->current_y = current_y_save;
+    openfile->current_x = current_x_save;
+    openfile->placewewant = pww_save;
 
-    /* Save the values of quote_len and par_len. */
-    assert(quote != NULL && par != NULL);
+    /* Restore case sensitivity setting. */
+    if (!case_sens_set)
+       UNSET(CASE_SENSITIVE);
 
-    *quote = quote_len;
-    *par = par_len;
+#ifndef NANO_SMALL
+    /* Restore search/replace direction. */
+    if (backwards_search_set)
+       SET(BACKWARDS_SEARCH);
+#endif
+#ifdef HAVE_REGEX_H
+    /* Restore regular expression usage setting. */
+    if (regexp_set)
+       SET(USE_REGEXP);
+#endif
 
-    return TRUE;
+    return !canceled;
 }
 
-/* If full_justify is TRUE, justify the entire file.  Otherwise, justify
- * the current paragraph. */
-void do_justify(bool full_justify)
+/* Integrated spell checking using the spell program, filtered through
+ * the sort and uniq programs.  Return NULL for normal termination,
+ * and the error string otherwise. */
+const char *do_int_speller(const char *tempfile_name)
 {
-    filestruct *first_par_line = NULL;
-       /* Will be the first line of the resulting justified paragraph.
-        * For restoring after unjustify. */
-    filestruct *last_par_line;
-       /* Will be the line containing the newline after the last line
-        * of the result.  Also for restoring after unjustify. */
+    char *read_buff, *read_buff_ptr, *read_buff_word;
+    size_t pipe_buff_size, read_buff_size, read_buff_read, bytesread;
+    int spell_fd[2], sort_fd[2], uniq_fd[2], tempfile_fd = -1;
+    pid_t pid_spell, pid_sort, pid_uniq;
+    int spell_status, sort_status, uniq_status;
 
-    /* We save these variables to be restored if the user unjustifies.
-     * Note that we don't need to save totlines. */
-    size_t current_x_save = openfile->current_x;
-    size_t pww_save = openfile->placewewant;
-    ssize_t current_y_save = openfile->current_y;
-    bool modified_save = openfile->modified;
-    size_t totsize_save = openfile->totsize;
-    filestruct *edittop_save = openfile->edittop;
-    filestruct *current_save = openfile->current;
-#ifndef NANO_SMALL
-    filestruct *mark_begin_save = openfile->mark_begin;
-    size_t mark_begin_x_save = openfile->mark_begin_x;
-#endif
-    int kbinput;
-    bool meta_key, func_key, s_or_t, ran_func, finished;
+    /* Create all three pipes up front. */
+    if (pipe(spell_fd) == -1 || pipe(sort_fd) == -1 ||
+       pipe(uniq_fd) == -1)
+       return _("Could not create pipe");
 
-    /* If we're justifying the entire file, start at the beginning. */
-    if (full_justify)
-       openfile->current = openfile->fileage;
+    statusbar(_("Creating misspelled word list, please wait..."));
 
-    last_par_line = openfile->current;
+    /* A new process to run spell in. */
+    if ((pid_spell = fork()) == 0) {
+       /* Child continues (i.e, future spell process). */
+       close(spell_fd[0]);
 
-    while (TRUE) {
-       size_t i;
-           /* Generic loop variable. */
-       size_t quote_len;
-           /* Length of the initial quotation of the paragraph we
-            * justify. */
-       size_t indent_len;
-           /* Length of the initial indentation of the paragraph we
-            * justify. */
-       size_t par_len;
-           /* Number of lines in the paragraph we justify. */
-       ssize_t break_pos;
-           /* Where we will break lines. */
-       char *indent_string;
-           /* The first indentation that doesn't match the initial
-            * indentation of the paragraph we justify.  This is put at
-            * the beginning of every line broken off the first
-            * justified line of the paragraph.  (Note that this works
-            * because a paragraph can only contain two indentations at
-            * most: the initial one, and a different one starting on a
-            * line after the first.  See the comment at begpar() for
-            * more about when a line is part of a paragraph.) */
+       /* Replace the standard input with the temp file. */
+       if ((tempfile_fd = open(tempfile_name, O_RDONLY)) == -1)
+           goto close_pipes_and_exit;
 
-       /* Find the first line of the paragraph to be justified.  That
-        * is the start of this paragraph if we're in one, or the start
-        * of the next otherwise.  Save the quote length and paragraph
-        * length (number of lines).  Don't refresh the screen yet,
-        * since we'll do that after we justify.
-        *
-        * If the search failed, we do one of two things.  If we're
-        * justifying the whole file, we've found at least one
-        * paragraph, and the search didn't leave us on the last line of
-        * the file, it means that we should justify all the way to the
-        * last line of the file, so set the last line of the text to be
-        * justified to the last line of the file and break out of the
-        * loop.  Otherwise, it means that there are no paragraph(s) to
-        * justify, so refresh the screen and get out. */
-       if (!find_paragraph(&quote_len, &par_len)) {
-           if (full_justify && first_par_line != NULL &&
-               first_par_line != openfile->filebot) {
-               last_par_line = openfile->filebot;
-               break;
-           } else {
-               edit_refresh();
-               return;
-           }
-       }
+       if (dup2(tempfile_fd, STDIN_FILENO) != STDIN_FILENO)
+           goto close_pipes_and_exit;
 
-       /* If we haven't already done it, copy the original paragraph(s)
-        * to the justify buffer. */
-       if (first_par_line == NULL)
-           first_par_line = backup_lines(openfile->current,
-               full_justify ? openfile->filebot->lineno -
-               openfile->current->lineno : par_len, quote_len);
+       close(tempfile_fd);
 
-       /* Initialize indent_string to a blank string. */
-       indent_string = mallocstrcpy(NULL, "");
+       /* Send spell's standard output to the pipe. */
+       if (dup2(spell_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
+           goto close_pipes_and_exit;
 
-       /* Find the first indentation in the paragraph that doesn't
-        * match the indentation of the first line, and save it in
-        * indent_string.  If all the indentations are the same, save
-        * the indentation of the first line in indent_string. */
-       {
-           const filestruct *indent_line = openfile->current;
-           bool past_first_line = FALSE;
+       close(spell_fd[1]);
+
+       /* Start the spell program; we are using PATH. */
+       execlp("spell", "spell", NULL);
+
+       /* This should not be reached if spell is found. */
+       exit(1);
+    }
+
+    /* Parent continues here. */
+    close(spell_fd[1]);
+
+    /* A new process to run sort in. */
+    if ((pid_sort = fork()) == 0) {
+       /* Child continues (i.e, future spell process).  Replace the
+        * standard input with the standard output of the old pipe. */
+       if (dup2(spell_fd[0], STDIN_FILENO) != STDIN_FILENO)
+           goto close_pipes_and_exit;
+
+       close(spell_fd[0]);
+
+       /* Send sort's standard output to the new pipe. */
+       if (dup2(sort_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
+           goto close_pipes_and_exit;
+
+       close(sort_fd[1]);
+
+       /* Start the sort program.  Use -f to remove mixed case.  If
+        * this isn't portable, let me know. */
+       execlp("sort", "sort", "-f", NULL);
+
+       /* This should not be reached if sort is found. */
+       exit(1);
+    }
 
-           for (i = 0; i < par_len; i++) {
-               indent_len = quote_len +
-                       indent_length(indent_line->data + quote_len);
+    close(spell_fd[0]);
+    close(sort_fd[1]);
 
-               if (indent_len != strlen(indent_string)) {
-                   indent_string = mallocstrncpy(indent_string,
-                       indent_line->data, indent_len + 1);
-                   indent_string[indent_len] = '\0';
+    /* A new process to run uniq in. */
+    if ((pid_uniq = fork()) == 0) {
+       /* Child continues (i.e, future uniq process).  Replace the
+        * standard input with the standard output of the old pipe. */
+       if (dup2(sort_fd[0], STDIN_FILENO) != STDIN_FILENO)
+           goto close_pipes_and_exit;
 
-                   if (past_first_line)
-                       break;
-               }
+       close(sort_fd[0]);
 
-               if (indent_line == openfile->current)
-                   past_first_line = TRUE;
+       /* Send uniq's standard output to the new pipe. */
+       if (dup2(uniq_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
+           goto close_pipes_and_exit;
 
-               indent_line = indent_line->next;
-           }
-       }
+       close(uniq_fd[1]);
 
-       /* Now tack all the lines of the paragraph together, skipping
-        * the quoting and indentation on all lines after the first. */
-       for (i = 0; i < par_len - 1; i++) {
-           filestruct *next_line = openfile->current->next;
-           size_t line_len = strlen(openfile->current->data);
-           size_t next_line_len =
-               strlen(openfile->current->next->data);
+       /* Start the uniq program; we are using PATH. */
+       execlp("uniq", "uniq", NULL);
 
-           indent_len = quote_len +
-               indent_length(openfile->current->next->data +
-               quote_len);
+       /* This should not be reached if uniq is found. */
+       exit(1);
+    }
 
-           next_line_len -= indent_len;
-           openfile->totsize -= indent_len;
+    close(sort_fd[0]);
+    close(uniq_fd[1]);
 
-           /* We're just about to tack the next line onto this one.  If
-            * this line isn't empty, make sure it ends in a space. */
-           if (line_len > 0 &&
-               openfile->current->data[line_len - 1] != ' ') {
-               line_len++;
-               openfile->current->data =
-                       charealloc(openfile->current->data,
-                       line_len + 1);
-               openfile->current->data[line_len - 1] = ' ';
-               openfile->current->data[line_len] = '\0';
-               openfile->totsize++;
-           }
+    /* The child process was not forked successfully. */
+    if (pid_spell < 0 || pid_sort < 0 || pid_uniq < 0) {
+       close(uniq_fd[0]);
+       return _("Could not fork");
+    }
 
-           openfile->current->data =
-               charealloc(openfile->current->data, line_len +
-               next_line_len + 1);
-           strcat(openfile->current->data, next_line->data +
-               indent_len);
+    /* Get the system pipe buffer size. */
+    if ((pipe_buff_size = fpathconf(uniq_fd[0], _PC_PIPE_BUF)) < 1) {
+       close(uniq_fd[0]);
+       return _("Could not get size of pipe buffer");
+    }
 
-           /* Don't destroy edittop! */
-           if (openfile->edittop == next_line)
-               openfile->edittop = openfile->current;
+    /* Read in the returned spelling errors. */
+    read_buff_read = 0;
+    read_buff_size = pipe_buff_size + 1;
+    read_buff = read_buff_ptr = charalloc(read_buff_size);
 
-#ifndef NANO_SMALL
-           /* Adjust the mark coordinates to compensate for the change
-            * in the next line. */
-           if (openfile->mark_set && openfile->mark_begin ==
-               next_line) {
-               openfile->mark_begin = openfile->current;
-               openfile->mark_begin_x += line_len - indent_len;
-           }
-#endif
+    while ((bytesread = read(uniq_fd[0], read_buff_ptr,
+       pipe_buff_size)) > 0) {
+       read_buff_read += bytesread;
+       read_buff_size += pipe_buff_size;
+       read_buff = read_buff_ptr = charealloc(read_buff,
+               read_buff_size);
+       read_buff_ptr += read_buff_read;
+    }
 
-           unlink_node(next_line);
-           delete_node(next_line);
+    *read_buff_ptr = '\0';
+    close(uniq_fd[0]);
 
-           /* If we've removed the next line, we need to go through
-            * this line again. */
-           i--;
+    /* Process the spelling errors. */
+    read_buff_word = read_buff_ptr = read_buff;
 
-           par_len--;
-           openfile->totlines--;
-           openfile->totsize--;
+    while (*read_buff_ptr != '\0') {
+       if ((*read_buff_ptr == '\r') || (*read_buff_ptr == '\n')) {
+           *read_buff_ptr = '\0';
+           if (read_buff_word != read_buff_ptr) {
+               if (!do_int_spell_fix(read_buff_word)) {
+                   read_buff_word = read_buff_ptr;
+                   break;
+               }
+           }
+           read_buff_word = read_buff_ptr + 1;
        }
+       read_buff_ptr++;
+    }
 
-       /* Call justify_format() on the paragraph, which will remove
-        * excess spaces from it and change all blank characters to
-        * spaces. */
-       justify_format(openfile->current, quote_len +
-               indent_length(openfile->current->data + quote_len));
+    /* Special case: the last word doesn't end with '\r' or '\n'. */
+    if (read_buff_word != read_buff_ptr)
+       do_int_spell_fix(read_buff_word);
 
-       while (par_len > 0 &&
-               strlenpt(openfile->current->data) > fill) {
-           size_t line_len = strlen(openfile->current->data);
+    free(read_buff);
+    replace_abort();
+    edit_refresh();
 
-           indent_len = strlen(indent_string);
+    /* Process the end of the spell process. */
+    waitpid(pid_spell, &spell_status, 0);
+    waitpid(pid_sort, &sort_status, 0);
+    waitpid(pid_uniq, &uniq_status, 0);
 
-           /* If this line is too long, try to wrap it to the next line
-            * to make it short enough. */
-           break_pos =
-               break_line(openfile->current->data + indent_len, fill -
-               strnlenpt(openfile->current->data, indent_len), FALSE);
+    if (WIFEXITED(spell_status) == 0 || WEXITSTATUS(spell_status))
+       return _("Error invoking \"spell\"");
 
-           /* We can't break the line, or don't need to, so get out. */
-           if (break_pos == -1 || break_pos + indent_len == line_len)
-               break;
+    if (WIFEXITED(sort_status)  == 0 || WEXITSTATUS(sort_status))
+       return _("Error invoking \"sort -f\"");
 
-           /* Move forward to the character after the indentation and
-            * just after the space. */
-           break_pos += indent_len + 1;
+    if (WIFEXITED(uniq_status) == 0 || WEXITSTATUS(uniq_status))
+       return _("Error invoking \"uniq\"");
 
-           assert(break_pos <= line_len);
+    /* Otherwise... */
+    return NULL;
 
-           /* Make a new line, and copy the text after where we're
-            * going to break this line to the beginning of the new
-            * line. */
-           splice_node(openfile->current,
-               make_new_node(openfile->current),
-               openfile->current->next);
+  close_pipes_and_exit:
+    /* Don't leak any handles. */
+    close(tempfile_fd);
+    close(spell_fd[0]);
+    close(spell_fd[1]);
+    close(sort_fd[0]);
+    close(sort_fd[1]);
+    close(uniq_fd[0]);
+    close(uniq_fd[1]);
+    exit(1);
+}
 
-           /* If this paragraph is non-quoted, and autoindent isn't
-            * turned on, set the indentation length to zero so that the
-            * indentation is treated as part of the line. */
-           if (quote_len == 0
+/* External spell checking.  Return value: NULL for normal termination,
+ * otherwise the error string. */
+const char *do_alt_speller(char *tempfile_name)
+{
+    int alt_spell_status;
+    size_t current_x_save = openfile->current_x;
+    size_t pww_save = openfile->placewewant;
+    ssize_t current_y_save = openfile->current_y;
+    ssize_t lineno_save = openfile->current->lineno;
+    pid_t pid_spell;
+    char *ptr;
+    static int arglen = 3;
+    static char **spellargs = NULL;
+    FILE *f;
 #ifndef NANO_SMALL
-               && !ISSET(AUTOINDENT)
-#endif
-               )
-               indent_len = 0;
-
-           /* Copy the text after where we're going to break the
-            * current line to the next line. */
-           openfile->current->next->data = charalloc(indent_len + 1 +
-               line_len - break_pos);
-           strncpy(openfile->current->next->data, indent_string,
-               indent_len);
-           strcpy(openfile->current->next->data + indent_len,
-               openfile->current->data + break_pos);
-
-           par_len++;
-           openfile->totlines++;
-           openfile->totsize += indent_len + 1;
+    bool old_mark_set = openfile->mark_set;
+    bool added_magicline = FALSE;
+       /* Whether we added a magicline after filebot. */
+    bool right_side_up = FALSE;
+       /* TRUE if (mark_begin, mark_begin_x) is the top of the mark,
+        * FALSE if (current, current_x) is. */
+    filestruct *top, *bot;
+    size_t top_x, bot_x;
+    ssize_t mb_lineno_save = 0;
+       /* We're going to close the current file, and open the output of
+        * the alternate spell command.  The line that mark_begin points
+        * to will be freed, so we save the line number and restore it
+        * afterwards. */
+    size_t totsize_save = openfile->totsize;
+       /* Our saved value of totsize, used when we spell-check a marked
+        * selection. */
 
-#ifndef NANO_SMALL
-           /* Adjust the mark coordinates to compensate for the change
-            * in the current line. */
-           if (openfile->mark_set && openfile->mark_begin ==
-               openfile->current && openfile->mark_begin_x >
-               break_pos) {
-               openfile->mark_begin = openfile->current->next;
-               openfile->mark_begin_x -= break_pos - indent_len;
-           }
+    if (old_mark_set) {
+       /* If the mark is on, save the number of the line it starts on,
+        * and then turn the mark off. */
+       mb_lineno_save = openfile->mark_begin->lineno;
+       openfile->mark_set = FALSE;
+    }
 #endif
 
-           /* Break the current line. */
-           null_at(&openfile->current->data, break_pos);
-
-           /* Go to the next line. */
-           par_len--;
-           openfile->current_y++;
-           openfile->current = openfile->current->next;
-       }
-
-       /* We're done breaking lines, so we don't need indent_string
-        * anymore. */
-       free(indent_string);
+    endwin();
 
-       /* Go to the next line, the line after the last line of the
-        * paragraph. */
-       openfile->current_y++;
-       openfile->current = openfile->current->next;
+    /* Set up an argument list to pass execvp(). */
+    if (spellargs == NULL) {
+       spellargs = (char **)nmalloc(arglen * sizeof(char *));
 
-       /* We've just justified a paragraph. If we're not justifying the
-        * entire file, break out of the loop.  Otherwise, continue the
-        * loop so that we justify all the paragraphs in the file. */
-       if (!full_justify)
-           break;
+       spellargs[0] = strtok(alt_speller, " ");
+       while ((ptr = strtok(NULL, " ")) != NULL) {
+           arglen++;
+           spellargs = (char **)nrealloc(spellargs, arglen *
+               sizeof(char *));
+           spellargs[arglen - 3] = ptr;
+       }
+       spellargs[arglen - 1] = NULL;
     }
+    spellargs[arglen - 2] = tempfile_name;
 
-    /* We are now done justifying the paragraph or the file, so clean
-     * up.  totlines, totsize, and current_y have been maintained above.
-     * Set last_par_line to the new end of the paragraph, update
-     * fileage, and renumber since edit_refresh() needs the line numbers
-     * to be right (but only do the last two if we actually justified
-     * something). */
-    last_par_line = openfile->current;
-    if (first_par_line != NULL) {
-       if (first_par_line->prev == NULL)
-           openfile->fileage = first_par_line;
-       renumber(first_par_line);
+    /* Start a new process for the alternate speller. */
+    if ((pid_spell = fork()) == 0) {
+       /* Start alternate spell program; we are using PATH. */
+       execvp(spellargs[0], spellargs);
+
+       /* Should not be reached, if alternate speller is found!!! */
+       exit(1);
     }
 
-    edit_refresh();
+    /* If we couldn't fork, get out. */
+    if (pid_spell < 0)
+       return _("Could not fork");
 
-    statusbar(_("Can now UnJustify!"));
+    /* Wait for alternate speller to complete. */
+    wait(&alt_spell_status);
 
-    /* If constant cursor position display is on, make sure the current
-     * cursor position will be properly displayed on the statusbar. */
-    if (ISSET(CONST_UPDATE))
-       do_cursorpos(TRUE);
+    refresh();
 
-    /* Display the shortcut list with UnJustify. */
-    shortcut_init(TRUE);
-    display_main_list();
+    /* Restore the terminal to its previous state. */
+    terminal_init();
 
-    /* Now get a keystroke and see if it's unjustify.  If not, put back
-     * the keystroke and return. */
-    kbinput = do_input(&meta_key, &func_key, &s_or_t, &ran_func,
-       &finished, FALSE);
+    /* Turn the cursor back on for sure. */
+    curs_set(1);
 
-    if (!meta_key && !func_key && s_or_t &&
-       kbinput == NANO_UNJUSTIFY_KEY) {
-       /* Restore the justify we just did (ungrateful user!). */
-       openfile->current = current_save;
-       openfile->current_x = current_x_save;
-       openfile->placewewant = pww_save;
-       openfile->current_y = current_y_save;
-       openfile->edittop = edittop_save;
+    if (!WIFEXITED(alt_spell_status) ||
+               WEXITSTATUS(alt_spell_status) != 0) {
+       char *altspell_error;
+       char *invoke_error = _("Error invoking \"%s\"");
 
-       /* Splice the justify buffer back into the file, but only if we
-        * actually justified something. */
-       if (first_par_line != NULL) {
-           filestruct *top_save;
+#ifndef NANO_SMALL
+       /* Turn the mark back on if it was on before. */
+       openfile->mark_set = old_mark_set;
+#endif
 
-           /* Partition the filestruct so that it contains only the
-            * text of the justified paragraph. */
-           filepart = partition_filestruct(first_par_line, 0,
-               last_par_line, 0);
+       altspell_error =
+               charalloc(strlen(invoke_error) +
+               strlen(alt_speller) + 1);
+       sprintf(altspell_error, invoke_error, alt_speller);
+       return altspell_error;
+    }
 
-           /* Remove the text of the justified paragraph, and
-            * put the text in the justify buffer in its place. */
-           free_filestruct(openfile->fileage);
-           openfile->fileage = jusbuffer;
-           openfile->filebot = jusbottom;
+#ifndef NANO_SMALL
+    if (old_mark_set) {
+       /* If the mark was on, partition the filestruct so that it
+        * contains only the marked text, and keep track of whether the
+        * temp file (which should contain the spell-checked marked
+        * text) will have a magicline added when it's reloaded. */
+       mark_order((const filestruct **)&top, &top_x,
+               (const filestruct **)&bot, &bot_x, &right_side_up);
+       filepart = partition_filestruct(top, top_x, bot, bot_x);
+       added_magicline = (openfile->filebot->data[0] != '\0');
 
-           top_save = openfile->fileage;
+       /* Get the number of characters in the marked text, and subtract
+        * it from the saved value of totsize. */
+       totsize_save -= get_totsize(top, bot);
+    }
+#endif
 
-           /* Unpartition the filestruct so that it contains all the
-            * text again.  Note that the justified paragraph has been
-            * replaced with the unjustified paragraph. */
-           unpartition_filestruct(&filepart);
+    /* Set up the window size. */
+    window_size_init();
 
-            /* Renumber starting with the beginning line of the old
-             * partition. */
-           renumber(top_save);
+    /* Reinitialize the text of the current buffer. */
+    free_filestruct(openfile->fileage);
+    initialize_buffer_text();
+
+    /* Reload the temp file.  Open it, read it into the current buffer,
+     * and move back to the first line of the buffer. */
+    open_file(tempfile_name, FALSE, &f);
+    read_file(f, tempfile_name);
+    openfile->current = openfile->fileage;
 
-           /* Restore variables from before the justify. */
-           openfile->totsize = totsize_save;
-           openfile->totlines = openfile->filebot->lineno;
 #ifndef NANO_SMALL
-           if (openfile->mark_set) {
-               openfile->mark_begin = mark_begin_save;
-               openfile->mark_begin_x = mark_begin_x_save;
-           }
-#endif
-           openfile->modified = modified_save;
+    if (old_mark_set) {
+       filestruct *top_save = openfile->fileage;
 
-           /* Clear the justify buffer. */
-           jusbuffer = NULL;
+       /* If the mark was on and we added a magicline, remove it
+        * now. */
+       if (added_magicline)
+           remove_magicline();
 
-           if (!openfile->modified)
-               titlebar(NULL);
-           edit_refresh();
+       /* Put the beginning and the end of the mark at the beginning
+        * and the end of the spell-checked text. */
+       if (openfile->fileage == openfile->filebot)
+           bot_x += top_x;
+       if (right_side_up) {
+           openfile->mark_begin_x = top_x;
+           current_x_save = bot_x;
+       } else {
+           current_x_save = top_x;
+           openfile->mark_begin_x = bot_x;
        }
-    } else {
-       unget_kbinput(kbinput, meta_key, func_key);
 
-       /* Blow away the text in the justify buffer. */
-       free_filestruct(jusbuffer);
-       jusbuffer = NULL;
+       /* Unpartition the filestruct so that it contains all the text
+        * again.  Note that we've replaced the marked text originally
+        * in the partition with the spell-checked marked text in the
+        * temp file. */
+       unpartition_filestruct(&filepart);
+
+       /* Renumber starting with the beginning line of the old
+        * partition.  Also set totlines to the new number of lines in
+        * the file, add the number of characters in the spell-checked
+        * marked text to the saved value of totsize, and then make that
+        * saved value the actual value. */
+       renumber(top_save);
+       openfile->totlines = openfile->filebot->lineno;
+       totsize_save += openfile->totsize;
+       openfile->totsize = totsize_save;
+
+       /* Assign mark_begin to the line where the mark began before. */
+       do_gotopos(mb_lineno_save, openfile->mark_begin_x,
+               current_y_save, 0);
+       openfile->mark_begin = openfile->current;
+
+       /* Assign mark_begin_x to the location in mark_begin where the
+        * mark began before, adjusted for any shortening of the
+        * line. */
+       openfile->mark_begin_x = openfile->current_x;
+
+       /* Turn the mark back on. */
+       openfile->mark_set = TRUE;
     }
+#endif
 
-    blank_statusbar();
+    /* Go back to the old position, and mark the file as modified. */
+    do_gotopos(lineno_save, current_x_save, current_y_save, pww_save);
+    set_modified();
 
-    /* Display the shortcut list with UnCut. */
-    shortcut_init(FALSE);
-    display_main_list();
+    return NULL;
 }
 
-void do_justify_void(void)
+void do_spell(void)
 {
-    do_justify(FALSE);
-}
+    int i;
+    FILE *temp_file;
+    char *temp = safe_tempfile(&temp_file);
+    const char *spell_msg;
 
-void do_full_justify(void)
-{
-    do_justify(TRUE);
+    if (temp == NULL) {
+       statusbar(_("Could not create temp file: %s"), strerror(errno));
+       return;
+    }
+
+#ifndef NANO_SMALL
+    if (openfile->mark_set)
+       i = write_marked_file(temp, temp_file, TRUE, FALSE);
+    else
+#endif
+       i = write_file(temp, temp_file, TRUE, FALSE, FALSE);
+
+    if (i == -1) {
+       statusbar(_("Error writing temp file: %s"), strerror(errno));
+       free(temp);
+       return;
+    }
+
+    spell_msg = (alt_speller != NULL) ? do_alt_speller(temp) :
+       do_int_speller(temp);
+    unlink(temp);
+    free(temp);
+
+    /* If the spell-checker printed any error messages onscreen, make
+     * sure that they're cleared off. */
+    total_refresh();
+
+    if (spell_msg != NULL) {
+       if (errno == 0)
+           /* Don't display an error message of "Success". */
+           statusbar(_("Spell checking failed: %s"), spell_msg);
+       else
+           statusbar(_("Spell checking failed: %s: %s"), spell_msg,
+               strerror(errno));
+    } else
+       statusbar(_("Finished checking spelling"));
 }
-#endif /* !DISABLE_JUSTIFY */
+#endif /* !DISABLE_SPELLER */
 
 #ifndef NANO_SMALL
 void do_word_count(void)