diff --git a/ChangeLog b/ChangeLog
index 69edcebaa17741360789c13cc79fb64581ee7410..782b92e408b658e1a75f3aee1471b7005f0523cd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -93,6 +93,18 @@ CVS code -
 	  some functions that use them as a parameter to use size_t as
 	  well.  Also change some related assertions to handle them.
 	  (David Benbennick and DLR)
+	- Add code to partition a filestruct between a set of arbitrary
+	  coordinates.  Given the coordinates of the beginning and end
+	  of the mark, this allows proper and easier handling of saving
+	  marked selections, replacing text only in marked selections
+	  (suggested by Joseph Birthisel), and spell-checking marked
+	  selections using either the internal or alternate spell
+	  checker.  Do all these using a global partition structure.
+	  New functions partition_filestruct(),
+	  unpartition_filestruct(), remove_magicline(), and
+	  get_totals(); changes to write_marked(), do_int_spell_fix(),
+	  do_alt_speller(), handle_sigwinch(), and do_replace_loop().
+	  (DLR)
 - files.c:
   do_insertfile()
 	- Simplify by reusing variables whereever possible, and add a
diff --git a/TODO b/TODO
index 8ea816f6f4644d014e52b54ecf6837db769d3e92..d8169416c3d04e391bf8a73aef012c9604353d8d 100644
--- a/TODO
+++ b/TODO
@@ -10,8 +10,7 @@ For version 1.4:
 - Rebindable keys?
 - Keystroke to implement "Add next sequence as raw" like vi's ^V.
   [DONE for edit window, needs to be done for statusbar prompt]
-- Spell check selected text only. [DONE for internal spell checker,
-  partially done for external spell checker]
+- Spell check selected text only. [DONE]
 - Make "To Line" (^W^T) and "Read from Command" (^R^X) reenter their
   parent menu when their keystroke is entered a second time (^W^T^T and
   (^R^X^X) (requires figuring out when to keep cursor position and when
diff --git a/src/files.c b/src/files.c
index b144652cc8ff889dcf9c9b1aa5559eea877d30a8..40e365e7642cd2a185e6ff1210c836a4eb5ac0a8 100644
--- a/src/files.c
+++ b/src/files.c
@@ -1720,49 +1720,36 @@ int write_file(const char *name, int tmp, int append, int nonamechange)
 int write_marked(const char *name, int tmp, int append)
 {
     int retval = -1;
-    filestruct *fileagebak = fileage;
-    filestruct *filebotbak = filebot;
     bool old_modified = ISSET(MODIFIED);
 	/* write_file() unsets the MODIFIED flag. */
-    size_t topx;
-	/* The column of the beginning of the mark. */
-    char origchar;
-	/* We replace the character at the end of the mark with '\0'.
-	 * We save the original character, to restore it. */
-    char *origcharloc;
-	/* The location of the character we nulled. */
-
-    /* Set fileage as the top of the mark, and filebot as the bottom. */
-    if (current->lineno > mark_beginbuf->lineno ||
-		(current->lineno == mark_beginbuf->lineno &&
-		current_x > mark_beginx)) {
-	fileage = mark_beginbuf;
-	topx = mark_beginx;
-	filebot = current;
-	origcharloc = current->data + current_x;
-    } else {
-	fileage = current;
-	topx = current_x;
-	filebot = mark_beginbuf;
-	origcharloc = mark_beginbuf->data + mark_beginx;
-    }
-    origchar = *origcharloc;
-    *origcharloc = '\0';
-    fileage->data += topx;
+    bool added_magicline;
+	/* Whether we added a magicline after filebot. */
+    filestruct *top, *bot;
+    size_t top_x, bot_x;
+
+    /* Partition the filestruct so that it contains only the marked
+     * text. */
+    mark_order((const filestruct **)&top, &top_x,
+	(const filestruct **)&bot, &bot_x);
+    filepart = partition_filestruct(top, top_x, bot, bot_x);
 
     /* If the line at filebot is blank, treat it as the magicline and
-     * hence the end of the file.  Otherwise, treat the line after
-     * filebot as the end of the file. */
-    if (filebot->data[0] != '\0' && filebot->next != NULL)
-	filebot = filebot->next;
+     * hence the end of the file.  Otherwise, add a magicline and treat
+     * it as the end of the file. */
+    added_magicline = (filebot->data[0] != '\0');
+    if (added_magicline)
+	new_magicline();
 
     retval = write_file(name, tmp, append, TRUE);
 
-    /* Now restore everything. */
-    fileage->data -= topx;
-    *origcharloc = origchar;
-    fileage = fileagebak;
-    filebot = filebotbak;
+    /* If we added a magicline, remove it now. */
+    if (added_magicline)
+	remove_magicline();
+
+    /* Unpartition the filestruct so that it contains all the text
+     * again. */
+    unpartition_filestruct(filepart);
+
     if (old_modified)
 	set_modified();
 
diff --git a/src/global.c b/src/global.c
index 3cbce14e35ecfb77c856c98c79dbd0badf51f19d..a6ef0b6204c3b4f97af7fe51907514bf367c537c 100644
--- a/src/global.c
+++ b/src/global.c
@@ -63,6 +63,11 @@ filestruct *edittop = NULL;	/* Pointer to the top of the edit
 filestruct *filebot = NULL;	/* Last node in the file struct */
 filestruct *cutbuffer = NULL;	/* A place to store cut text */
 
+#ifndef NANO_SMALL
+partition *filepart = NULL;	/* A place to store a portion of the
+				   file struct */
+#endif
+
 #ifdef ENABLE_MULTIBUFFER
 openfilestruct *open_files = NULL;	/* The list of open files */
 #endif
diff --git a/src/nano.c b/src/nano.c
index 50d0bf194841e7bae2122c0ddce09fb2b2947158..25784f34b1120b580c798b82fc8965a0f7a047d0 100644
--- a/src/nano.c
+++ b/src/nano.c
@@ -616,6 +616,93 @@ void free_filestruct(filestruct *src)
     }
 }
 
+#ifndef NANO_SMALL
+/* Partition a filestruct so it begins at (top, top_x) and ends at (bot,
+ * bot_x). */
+partition *partition_filestruct(filestruct *top, size_t top_x,
+	filestruct *bot, size_t bot_x)
+{
+    partition *p;
+    assert(top != NULL && bot != NULL);
+
+    /* Initialize the partition. */
+    p = (partition *)nmalloc(sizeof(partition));
+
+    /* Save the top and bottom of the filestruct. */
+    p->fileage = fileage;
+    p->filebot = filebot;
+
+    /* Set the top and bottom of the partition to top and bot. */
+    fileage = top;
+    filebot = bot;
+
+    /* Save the line above the top of the partition, detach the top of
+     * the partition from it, and save the text before top_x in
+     * top_data. */
+    p->top_prev = top->prev;
+    top->prev = NULL;
+    p->top_data = mallocstrncpy(NULL, top->data, top_x + 1);
+    p->top_data[top_x] = '\0';
+
+    /* Save the line below the bottom of the partition, detach the
+     * bottom of the partition from it, and save the text after bot_x in
+     * bot_data. */
+    p->bot_next = bot->next;
+    bot->next = NULL;
+    p->bot_data = mallocstrcpy(NULL, bot->data + bot_x);
+
+    /* Remove all text after bot_x at the bottom of the partition. */
+    null_at(&bot->data, bot_x);
+
+    /* Remove all text before top_x at the top of the partition. */
+    charmove(top->data, top->data + top_x, strlen(top->data) -
+	top_x + 1);
+    align(&top->data);
+
+    /* Return the partition. */
+    return p;
+}
+
+/* Unpartition a filestruct so it begins at (fileage, 0) and ends at
+ * (filebot, strlen(filebot)) again. */
+void unpartition_filestruct(partition *p)
+{
+    char *tmp;
+    assert(p != NULL);
+
+    /* Reattach the line above the top of the partition, and restore the
+     * text before top_x from top_data.  Free top_data when we're done
+     * with it. */
+    tmp = mallocstrcpy(NULL, fileage->data);
+    fileage->prev = p->top_prev;
+    fileage->prev->next = fileage;
+    fileage->data = charealloc(fileage->data, strlen(p->top_data) +
+	strlen(fileage->data) + 1);
+    strcpy(fileage->data, p->top_data);
+    free(p->top_data);
+    strcat(fileage->data, tmp);
+    free(tmp);
+
+    /* Reattach the line below the bottom of the partition, and restore
+     * the text after bot_x from bot_data.  Free bot_data when we're
+     * done with it. */
+    filebot->next = p->bot_next;
+    filebot->next->prev = filebot;
+    filebot->data = charealloc(filebot->data, strlen(filebot->data) +
+	strlen(p->bot_data) + 1);
+    strcat(filebot->data, p->bot_data);
+    free(p->bot_data);
+
+    /* Restore the top and bottom of the filestruct. */
+    fileage = p->fileage;
+    filebot = p->filebot;
+
+    /* Uninitialize the partition. */
+    free(p);
+    p = NULL;
+}
+#endif
+
 void renumber_all(void)
 {
     filestruct *temp;
@@ -1442,6 +1529,8 @@ bool do_int_spell_fix(const char *word)
 #endif
 #ifndef NANO_SMALL
     bool old_mark_set = ISSET(MARK_ISSET);
+    filestruct *top, *bot;
+    size_t top_x, bot_x;
 #endif
 
     /* Make sure spell-check is case sensitive. */
@@ -1455,10 +1544,6 @@ bool do_int_spell_fix(const char *word)
     /* Make sure spell-check doesn't use regular expressions. */
     UNSET(USE_REGEXP);
 #endif
-#ifndef NANO_SMALL
-    /* Make sure the marking highlight is off during spell-check. */
-    UNSET(MARK_ISSET);
-#endif
 
     /* Save the current search/replace strings. */
     search_init_globals();
@@ -1469,6 +1554,16 @@ bool do_int_spell_fix(const char *word)
     last_search = mallocstrcpy(NULL, word);
     last_replace = mallocstrcpy(NULL, word);
 
+#ifndef NANO_SMALL
+    if (old_mark_set) {
+	mark_order((const filestruct **)&top, &top_x,
+	    (const filestruct **)&bot, &bot_x);
+	filepart = partition_filestruct(top, top_x, bot, bot_x);
+	edittop = fileage;
+	UNSET(MARK_ISSET);
+    }
+#endif
+
     /* Start from the top of the file. */
     edittop = fileage;
     current = fileage;
@@ -1493,9 +1588,17 @@ bool do_int_spell_fix(const char *word)
 	    do_replace_highlight(FALSE, word);
 
 	    if (!canceled && strcmp(word, answer) != 0) {
+		bool added_magicline = (filebot->data[0] != '\0');
+			/* Whether we added a magicline after
+			 * filebot. */
+
 		current_x--;
 		do_replace_loop(word, current, &current_x, TRUE,
 			&canceled);
+
+		/* If we added a magicline, remove it now. */
+		if (added_magicline)
+		    remove_magicline();
 	    }
 
 	    break;
@@ -1508,6 +1611,15 @@ bool do_int_spell_fix(const char *word)
     free(last_replace);
     last_replace = save_replace;
 
+#ifndef NANO_SMALL
+    /* If the mark was on, unpartition the filestruct so that it
+     * contains all the text again, and turn the mark back on. */
+    if (old_mark_set) {
+	unpartition_filestruct(filepart);
+	SET(MARK_ISSET);
+    }
+#endif
+
     /* Restore where we were. */
     edittop = edittop_save;
     current = current_save;
@@ -1528,11 +1640,6 @@ bool do_int_spell_fix(const char *word)
     if (regexp_set)
 	SET(USE_REGEXP);
 #endif
-#ifndef NANO_SMALL
-    /* Restore marking highlight. */
-    if (old_mark_set)
-	SET(MARK_ISSET);
-#endif
 
     return !canceled;
 }
@@ -1737,13 +1844,23 @@ const char *do_alt_speller(char *tempfile_name)
     FILE *f;
 #ifndef NANO_SMALL
     bool old_mark_set = ISSET(MARK_ISSET);
+    bool added_magicline = FALSE;
+	/* Whether we added a magicline after filebot. */
     int mbb_lineno_cur = 0;
 	/* We're going to close the current file, and open the output of
 	 * the alternate spell command.  The line that mark_beginbuf
 	 * points to will be freed, so we save the line number and
 	 * restore afterwards. */
+    int old_totlines = totlines;
+	/* Our saved value of totlines, used when we spell-check a
+	 * marked selection. */
+    long old_totsize = 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. */
 	mbb_lineno_cur = mark_beginbuf->lineno;
 	UNSET(MARK_ISSET);
     }
@@ -1798,24 +1915,77 @@ const char *do_alt_speller(char *tempfile_name)
 
 #ifndef NANO_SMALL
     if (old_mark_set) {
+	filestruct *top, *bot;
+	size_t top_x, bot_x;
+	int part_totlines;
+	long part_totsize;
+
+	/* 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);
+	filepart = partition_filestruct(top, top_x, bot, bot_x);
+	added_magicline = (filebot->data[0] != '\0');
+
+	/* Get the number of lines and the number of characters in the
+	 * marked text, and subtract them from the saved values of
+	 * totlines and totsize. */
+	get_totals(top, bot, &part_totlines, &part_totsize);
+	old_totlines -= part_totlines;
+	old_totsize -= part_totsize;
+    }
+#endif
+
+    /* Reinitialize the filestruct. */
+    free_filestruct(fileage);
+    global_init(TRUE);
+
+    /* Reload the temp file.  Do what load_buffer() would do, except for
+     * making a new buffer for the temp file if multibuffer support is
+     * available. */
+    open_file(tempfile_name, FALSE, &f);
+    read_file(f, tempfile_name);
+    current = fileage;
+
+#ifndef NANO_SMALL
+    if (old_mark_set) {
+	filestruct *old_top = fileage;
+
+	/* If we added a magicline, remove it now. */
+	if (added_magicline)
+	    remove_magicline();
+
+	/* If the mark was on, 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 add the number of lines and characters in
+	 * the spell-checked marked text to the saved values of totlines
+	 * and totsize, and then make those saved values the actual
+	 * values. */
+	renumber(old_top);
+	old_totlines += totlines;
+	old_totsize += totsize;
+	totlines = old_totlines;
+	totsize = old_totsize;
+
+	/* Assign mark_beginbuf to the line where the mark began
+	 * before. */
 	do_gotopos(mbb_lineno_cur, mark_beginx, y_cur, 0);
 	mark_beginbuf = current;
-	/* In case the line got shorter, assign mark_beginx. */
+
+	/* Assign mark_beginx to the location in mark_beginbuf where the
+	 * mark began before, adjusted for any shortening of the
+	 * line. */
 	mark_beginx = current_x;
-	SET(MARK_ISSET);
-    } else {
-#endif
-	/* Only reload the temp file if it isn't a marked selection. */
-	free_filestruct(fileage);
-	global_init(TRUE);
 
-	/* Do what load_buffer() would do, except for making a new
-	 * buffer for the temp file if multibuffer support is
-	 * available. */
-	open_file(tempfile_name, FALSE, &f);
-	read_file(f, tempfile_name);
-	current = fileage;
-#ifndef NANO_SMALL
+	/* Turn the mark back on. */
+	SET(MARK_ISSET);
     }
 #endif
 
@@ -2845,6 +3015,10 @@ void handle_sigwinch(int s)
     memset(hblank, ' ', COLS);
     hblank[COLS] = '\0';
 
+    /* If we've partitioned the filestruct, unpartition it now. */
+    if (filepart != NULL)
+	unpartition_filestruct(filepart);
+
 #ifdef USE_SLANG
     /* Slang curses emulation brain damage, part 1: If we just do what
      * curses does here, it'll only work properly if the resize made the
diff --git a/src/nano.h b/src/nano.h
index 163463a2c6e4c39a863de33f93dcdedf53eb510d..c453467fdb2237ba4a8e86aa70b456930e4230e4 100644
--- a/src/nano.h
+++ b/src/nano.h
@@ -183,6 +183,17 @@ typedef struct openfilestruct {
 } openfilestruct;
 #endif
 
+#ifndef NANO_SMALL
+typedef struct partition {
+    filestruct *fileage;
+    filestruct *top_prev;
+    char *top_data;
+    filestruct *filebot;
+    filestruct *bot_next;
+    char *bot_data;
+} partition;
+#endif
+
 typedef struct shortcut {
     /* Key values that aren't used should be set to NANO_NO_KEY. */
     int ctrlval;	/* Special sentinel key or control key we want
diff --git a/src/proto.h b/src/proto.h
index 108a9c62ade2a8840a209c65a9b065362a5e1af7..396a6dadb7a3f43209fbf72397d302ab12e3de36 100644
--- a/src/proto.h
+++ b/src/proto.h
@@ -87,6 +87,7 @@ extern struct stat fileinfo;
 extern filestruct *current, *fileage, *edittop, *filebot;
 extern filestruct *cutbuffer;
 #ifndef NANO_SMALL
+extern partition *filepart;
 extern filestruct *mark_beginbuf;
 #endif
 
@@ -301,6 +302,11 @@ void unlink_node(const filestruct *fileptr);
 void delete_node(filestruct *fileptr);
 filestruct *copy_filestruct(const filestruct *src);
 void free_filestruct(filestruct *src);
+#ifndef NANO_SMALL
+partition *partition_filestruct(filestruct *top, size_t top_x,
+	filestruct *bot, size_t bot_x);
+void unpartition_filestruct(partition *p);
+#endif
 void renumber_all(void);
 void renumber(filestruct *fileptr);
 void print1opt(const char *shortflag, const char *longflag, const char
@@ -496,6 +502,9 @@ char *mallocstrcpy(char *dest, const char *src);
 char *mallocstrassn(char *dest, char *src);
 void new_magicline(void);
 #ifndef NANO_SMALL
+void remove_magicline(void);
+void get_totals(const filestruct *begin, const filestruct *end, int
+	*lines, long *size);
 void mark_order(const filestruct **top, size_t *top_x, const filestruct
 	**bot, size_t *bot_x);
 #endif
diff --git a/src/search.c b/src/search.c
index 71bf7d051ebbdd3300a346dc97b77011daaff4fa..67ecc03d76196a5333fc5dfb43d69e036f3cdc4b 100644
--- a/src/search.c
+++ b/src/search.c
@@ -669,8 +669,22 @@ ssize_t do_replace_loop(const char *needle, const filestruct
 #endif
 #ifndef NANO_SMALL
     bool old_mark_set = ISSET(MARK_ISSET);
+    filestruct *edittop_save = edittop, *top, *bot;
+    size_t top_x, bot_x;
+    bool right_side_up = FALSE;
+	/* TRUE if (mark_beginbuf, mark_beginx) is the top of the mark,
+	 * FALSE if (current, current_x) is. */
 
     if (old_mark_set) {
+	/* If the mark is on, partition the filestruct so that it
+	 * contains only the marked text, set right_side_up properly,
+	 * set edittop to the top of the marked text, turn the mark off,
+	 * and refresh the screen. */
+	mark_order((const filestruct **)&top, &top_x,
+	    (const filestruct **)&bot, &bot_x);
+	right_side_up = (bot == current && bot_x == current_x);
+	filepart = partition_filestruct(top, top_x, bot, bot_x);
+	edittop = fileage;
 	UNSET(MARK_ISSET);
 	edit_refresh();
     }
@@ -764,18 +778,33 @@ ssize_t do_replace_loop(const char *needle, const filestruct
 	    length_change = strlen(copy) - strlen(current->data);
 
 #ifndef NANO_SMALL
+	    /* Keep mark_beginx in sync with the text changes. */
 	    if (current == mark_beginbuf && mark_beginx > current_x) {
-		if (mark_beginx < current_x + match_len)
-		    mark_beginx = current_x;
-		else
-		    mark_beginx += length_change;
+		/* If the mark was on and (mark_beginbuf, mark_begin_x)
+		 * was the top of it, don't change mark_beginx. */
+		if (!old_mark_set || !right_side_up) {
+		    if (mark_beginx < current_x + match_len)
+			mark_beginx = current_x;
+		    else
+			mark_beginx += length_change;
+		}
 	    }
 #endif
 
-	    if (current == real_current && current_x <= *real_current_x) {
-		if (*real_current_x < current_x + match_len)
-		    *real_current_x = current_x + match_len;
-		*real_current_x += length_change;
+	    /* Keep real_current_x in sync with the text changes. */
+	    if (current == real_current && current_x <=
+		*real_current_x) {
+#ifndef NANO_SMALL
+		/* If the mark was on and (current, current_x) was the
+		 * top of it, don't change real_current_x. */
+		if (!old_mark_set || right_side_up) {
+#endif
+		    if (*real_current_x < current_x + match_len)
+			*real_current_x = current_x + match_len;
+		    *real_current_x += length_change;
+#ifndef NANO_SMALL
+		}
+#endif
 	    }
 
 	    /* Set the cursor at the last character of the replacement
@@ -806,15 +835,22 @@ ssize_t do_replace_loop(const char *needle, const filestruct
 	}
     }
 
-    /* If text has been added to the magicline, make a new magicline. */
-    if (filebot->data[0] != '\0')
-	new_magicline();
-
 #ifndef NANO_SMALL
-    if (old_mark_set)
+    /* If the mark was on, unpartition the filestruct so that it
+     * contains all the text again, set edittop back to what it was
+     * before, turn the mark back on, and refresh the screen. */
+    if (old_mark_set) {
+	unpartition_filestruct(filepart);
+	edittop = edittop_save;
 	SET(MARK_ISSET);
+	edit_refresh();
+    }
 #endif
 
+    /* If text has been added to the magicline, make a new magicline. */
+    if (filebot->data[0] != '\0')
+	new_magicline();
+
     return numreplaced;
 }
 
diff --git a/src/utils.c b/src/utils.c
index 1a52e1f9b6378c8cb887b34f58209e7511273a03..5606c3f7aaa8a759284cc4d371b747eaa189d54d 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -441,6 +441,57 @@ void new_magicline(void)
 }
 
 #ifndef NANO_SMALL
+/* Remove the magicline from filebot, if there is one. */
+void remove_magicline(void)
+{
+    if (filebot->data[0] == '\0') {
+	filebot = filebot->prev;
+	free_filestruct(filebot->next);
+	filebot->next = NULL;
+	totlines--;
+	totsize--;
+    }
+}
+
+/* Calculate the number of lines and the number of characters between
+ * begin and end, and return them in lines and size, respectively. */
+void get_totals(const filestruct *begin, const filestruct *end, int
+	*lines, long *size)
+{
+    const filestruct *f;
+
+    if (lines != NULL)
+	*lines = 0;
+    if (size != NULL)
+	*size = 0;
+
+    /* Go through the lines from begin to end->prev, if we can. */
+    for (f = begin; f != NULL && f != end; f = f->next) {
+	/* Count this line. */
+	(*lines)++;
+
+	/* Count the number of characters on this line. */
+	*size += strlen(f->data);
+
+	/* Count the newline if we have one. */
+	if (f->next != NULL)
+	   (*size)++;
+    }
+
+    /* Go through the line at end, if we can. */
+    if (f != NULL) {
+	/* Count this line. */
+	(*lines)++;
+
+	/* Count the number of characters on this line. */
+	*size += strlen(f->data);
+
+	/* Count the newline if we have one. */
+	if (f->next != NULL)
+	   (*size)++;
+    }
+}
+
 /* Set top_x and bot_x to the top and bottom x-coordinates of the mark,
  * respectively, based on the locations of top and bot. */
 void mark_order(const filestruct **top, size_t *top_x, const filestruct