diff --git a/src/proto.h b/src/proto.h
index fa6a309a429883e19a4dd50251800efbfdedb562..b7d0414b43f64a66a3621f8af04543d4891485d5 100644
--- a/src/proto.h
+++ b/src/proto.h
@@ -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
diff --git a/src/text.c b/src/text.c
index e2a038091f99dc5178e122161c88071a27a4c5aa..a4dc220aaa29a8813c546799445773e8f256d33b 100644
--- a/src/text.c
+++ b/src/text.c
@@ -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)