]> git.wh0rd.org Git - nano.git/commitdiff
move advanced text operations (command execution in a buffer, wrapping,
authorDavid Lawrence Ramsey <pooka109@gmail.com>
Sun, 24 Jul 2005 19:57:51 +0000 (19:57 +0000)
committerDavid Lawrence Ramsey <pooka109@gmail.com>
Sun, 24 Jul 2005 19:57:51 +0000 (19:57 +0000)
spell checking, justifying, and word counting) to their own source file,
text.c

git-svn-id: svn://svn.savannah.gnu.org/nano/trunk/nano@2919 35c25a1d-7b9e-4130-9fde-d3aeb78583b8

18 files changed:
ChangeLog
po/ChangeLog
po/POTFILES.in
src/Makefile.am
src/chars.c
src/color.c
src/cut.c
src/files.c
src/global.c
src/move.c
src/nano.c
src/nano.h
src/proto.h
src/rcfile.c
src/search.c
src/text.c [new file with mode: 0644]
src/utils.c
src/winio.c

index 31b52306f576ce898fa17fa72d0a31aff7c483e6..471f3c42c0fcbe62581d5093c0b01ae81fff92a8 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -88,6 +88,19 @@ CVS code -
          mbwidth(), make_mbchar(), parse_mbchar(), mbstrncasecmp(),
          mbstrcasestr(), mbrevstrcasestr(), mbstrchr(), and
          display_string(). (DLR)
+       - Move advanced text operations (command execution in a buffer,
+         wrapping, spell checking, justifying, and word counting) to
+         their own source file, and adjust related variables
+         accordingly.  New file text.c; changes to cancel_command(),
+         execute_command(), wrap_reset(), do_wrap(),
+         do_int_spell_fix(), do_int_speller(), do_alt_speller(),
+         do_spell(), break_line(), indent_length(), justify_format(),
+         quote_length(), quotes_match(), indents_match(), begpar(),
+         inpar(), backup_lines(), find_paragraph(), do_justify(),
+         do_justify_void(), do_full_justify(), and do_word_count() (all
+         moved to text.c). (DLR)
+- color.c:
+       - Remove unneeded string.h and fcntl.h includes. (DLR)
 - chars.c:
   mbstrchr()
        - Don't count matches between valid and invalid multibyte
@@ -157,6 +170,8 @@ CVS code -
          HAVE_SNPRINTF. (DLR)
        - Remove TOP from the topmidnone enum, and rename it centernone.
          (DLR)
+       - Move stdlib.h, dirent.h, regex.h, and assert.h includes here,
+         as every source file needs them. (DLR)
   proto.h:
        - Add declarations for bad_mbchar and bad_mbchar_len, so that we
          can use them in display_string() as well as chars.c. (DLR)
@@ -219,6 +234,8 @@ CVS code -
          tweaks by DLR)
 - doc/man/fr/nano.1, doc/man/fr/nanorc.1:
        - Updated translation by Jean-Philippe Gérard.
+- src/Makefile.am:
+       - Add text.c to nano_SOURCES. (DLR)
 
 GNU nano 1.3.8 - 2005.06.30
 - General:
index 7b866efe7ee03d0ba98bb6607e703cd83902d10e..49d853acbb2bb2126239fd72ffa975a36429d624 100644 (file)
@@ -1,3 +1,7 @@
+2005-07-24  David Lawrence Ramsey  <pooka_regent@sluggy.net>
+
+       * POTFILES.in: Add text.c.
+
 2005-07-19  Jordi Mallach  <jordi@gnu.org>
 
        * ms.po: Updated Malay translation by
index d7a9162ced01f87eb59783c9ac8b76f296fcf2ee..5659c7f51514caea96f658ad05241b9ce92aa487 100644 (file)
@@ -6,5 +6,6 @@ src/global.c
 src/nano.c
 src/rcfile.c
 src/search.c
+src/text.c
 src/utils.c
 src/winio.c
index 3aa1ff5061565e0a17a0dcfe48c5ddd6aef83485..f9621c26e2016ec637a5b5a611607c810b97892d 100644 (file)
@@ -15,6 +15,7 @@ nano_SOURCES =        chars.c \
                proto.h \
                rcfile.c \
                search.c \
+               text.c \
                utils.c \
                winio.c 
 
index 1e1dce57fd80aa357c3aa6d167f760f207e382c0..c62d7b427c5099f07397abd9f2a321416a55f136 100644 (file)
 #include <config.h>
 #endif
 
-#include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
-#include <assert.h>
 #include "proto.h"
 
 #ifdef ENABLE_UTF8
index 34fcab9403b3c10b16b1cd7b103ecc7294b4cdb3..811f64a910f359489605662c5ea08753f1db88cb 100644 (file)
 #include <config.h>
 #endif
 
-#include <stdlib.h>
-#include <string.h>
 #include <stdio.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <assert.h>
 #include "proto.h"
 
 #ifdef ENABLE_COLOR
index 130432810de035b646372b32a777dfb281d30985..d8cb01beb11d163b01eabe618dcd76f23e0822b6 100644 (file)
--- a/src/cut.c
+++ b/src/cut.c
 #include <config.h>
 #endif
 
-#include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
-#include <assert.h>
 #include "proto.h"
 
 static bool keep_cutbuffer = FALSE;
index da39f6feefcd8b0a5fa49a3d708fcf296637ff98..51c3251cc088ba9accd9bb4156e33b6ec2d3a99a 100644 (file)
@@ -24,7 +24,6 @@
 #include <config.h>
 #endif
 
-#include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
 #include <unistd.h>
@@ -34,7 +33,6 @@
 #include <errno.h>
 #include <ctype.h>
 #include <pwd.h>
-#include <assert.h>
 #include "proto.h"
 
 /* Add an entry to the openfile openfilestruct.  This should only be
index 26c9e1e9dd4be469404484b1015fb16d525184d8..1e7a1ec63af7bc0315651e8676ea70006bd29101 100644 (file)
 #include <config.h>
 #endif
 
-#include <stdlib.h>
-#include <assert.h>
 #include "proto.h"
 
 /* Global variables */
-
 #ifndef DISABLE_WRAPJUSTIFY
-/* wrap_at might be set in rcfile.c or nano.c. */
-ssize_t wrap_at = -CHARS_FROM_EOL;     /* Right justified fill value,
-                                          allows resize */
+ssize_t fill = 0;              /* Where we will wrap lines. */
+ssize_t wrap_at = -CHARS_FROM_EOL;
+                               /* The position that corresponds to
+                                * fill.  If it's greater than zero,
+                                * fill is equal to it.  Otherwise, fill
+                                * is equal to the number of screen
+                                * columns less it.  This allows
+                                * dynamic wrapping based on the current
+                                * screen width. */
 #endif
 
 char *last_search = NULL;      /* Last string we searched for */
 char *last_replace = NULL;     /* Last replacement string */
 
 long flags = 0;                        /* Our flag containing many options */
-WINDOW *topwin;                        /* Top buffer */
+WINDOW *topwin;                        /* Top subwindow */
 WINDOW *edit;                  /* The file portion of the editor */
-WINDOW *bottomwin;             /* Bottom buffer */
+WINDOW *bottomwin;             /* Bottom subwindow */
 
 int editwinrows = 0;           /* How many rows long is the edit
                                   window? */
index 38dedc3af939447bbf0dfc005711975a8cc586ea..21a8175d5498d0f5594559496e1d4bd513ce48c7 100644 (file)
 #include <config.h>
 #endif
 
-#include <stdlib.h>
 #include <string.h>
 #include <ctype.h>
-#include <assert.h>
 #include "proto.h"
 
 void do_first_line(void)
index 1a8f9d6155c22628ef90fbca2d944a705b528f3e..45750e70b54229784202a061f894aca5a856ef31 100644 (file)
@@ -25,7 +25,6 @@
 #endif
 
 #include <stdio.h>
-#include <stdlib.h>
 #include <stdarg.h>
 #include <signal.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
 #include <sys/param.h>
-#include <sys/wait.h>
 #include <errno.h>
 #include <ctype.h>
 #include <locale.h>
-#include <assert.h>
 #include "proto.h"
 
 #ifdef HAVE_TERMIOS_H
 #ifndef DISABLE_WRAPJUSTIFY
 static ssize_t fill = 0;       /* Fill - where to wrap lines */
 #endif
-#ifndef DISABLE_WRAPPING
-static bool same_line_wrap = FALSE;    /* Whether wrapped text should
-                                          be prepended to the next
-                                          line */
-#endif
 
 static struct termios oldterm; /* The user's original term settings */
 static struct sigaction act;   /* For all our fun signal handlers */
@@ -67,15 +59,6 @@ static struct sigaction act; /* For all our fun signal handlers */
 #ifndef NANO_SMALL
 static sigjmp_buf jmpbuf;      /* Used to return to main() after a
                                   SIGWINCH. */
-static pid_t pid;              /* The PID of the newly forked process
-                                * in execute_command().  It must be
-                                * global because the signal handler
-                                * needs it. */
-#endif
-
-#ifndef DISABLE_JUSTIFY
-static filestruct *jusbottom = NULL;
-       /* Pointer to end of justify buffer. */
 #endif
 
 /* Create a new filestruct node.  Note that we specifically do not set
@@ -1239,94 +1222,6 @@ void nano_disabled_msg(void)
     statusbar(_("Sorry, support for this function has been disabled"));
 }
 
-#ifndef NANO_SMALL
-void cancel_command(int signal)
-{
-    if (kill(pid, SIGKILL) == -1)
-       nperror("kill");
-}
-
-/* Return TRUE on success. */
-bool execute_command(const char *command)
-{
-    int fd[2];
-    FILE *f;
-    struct sigaction oldaction, newaction;
-       /* Original and temporary handlers for SIGINT. */
-    bool sig_failed = FALSE;
-       /* Did sigaction() fail without changing the signal handlers? */
-
-    /* Make our pipes. */
-    if (pipe(fd) == -1) {
-       statusbar(_("Could not pipe"));
-       return FALSE;
-    }
-
-    /* Fork a child. */
-    if ((pid = fork()) == 0) {
-       close(fd[0]);
-       dup2(fd[1], fileno(stdout));
-       dup2(fd[1], fileno(stderr));
-
-       /* If execl() returns at all, there was an error. */
-       execl("/bin/sh", "sh", "-c", command, NULL);
-       exit(0);
-    }
-
-    /* Else continue as parent. */
-    close(fd[1]);
-
-    if (pid == -1) {
-       close(fd[0]);
-       statusbar(_("Could not fork"));
-       return FALSE;
-    }
-
-    /* Before we start reading the forked command's output, we set
-     * things up so that Ctrl-C will cancel the new process. */
-
-    /* Enable interpretation of the special control keys so that we get
-     * SIGINT when Ctrl-C is pressed. */
-    enable_signals();
-
-    if (sigaction(SIGINT, NULL, &newaction) == -1) {
-       sig_failed = TRUE;
-       nperror("sigaction");
-    } else {
-       newaction.sa_handler = cancel_command;
-       if (sigaction(SIGINT, &newaction, &oldaction) == -1) {
-           sig_failed = TRUE;
-           nperror("sigaction");
-       }
-    }
-    /* Note that now oldaction is the previous SIGINT signal handler,
-     * to be restored later. */
-
-    f = fdopen(fd[0], "rb");
-    if (f == NULL)
-       nperror("fdopen");
-
-    read_file(f, "stdin");
-
-    /* If multibuffer mode is on, we could be here in view mode.  If so,
-     * don't set the modification flag. */
-    if (!ISSET(VIEW_MODE))
-       set_modified();
-
-    if (wait(NULL) == -1)
-       nperror("wait");
-
-    if (!sig_failed && sigaction(SIGINT, &oldaction, NULL) == -1)
-       nperror("sigaction");
-
-    /* Disable interpretation of the special control keys so that we can
-     * use Ctrl-C for other things. */
-    disable_signals();
-
-    return TRUE;
-}
-#endif /* !NANO_SMALL */
-
 void do_verbatim_input(void)
 {
     int *kbinput;
@@ -1536,68 +1431,6 @@ void do_enter(void)
 }
 
 #ifndef NANO_SMALL
-void do_word_count(void)
-{
-    size_t words = 0, current_x_save = openfile->current_x;
-    size_t pww_save = openfile->placewewant;
-    filestruct *current_save = openfile->current;
-    bool old_mark_set = openfile->mark_set;
-    bool added_magicline = FALSE;
-       /* Whether we added a magicline after filebot. */
-    filestruct *top, *bot;
-    size_t top_x, bot_x;
-
-    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 need a magicline added while we're counting words, add
-        * the magicline if necessary, and turn the mark off. */
-       mark_order((const filestruct **)&top, &top_x,
-           (const filestruct **)&bot, &bot_x, NULL);
-       filepart = partition_filestruct(top, top_x, bot, bot_x);
-       if ((added_magicline = (openfile->filebot->data[0] != '\0')))
-           new_magicline();
-       openfile->mark_set = FALSE;
-    }
-
-    /* Start at the top of the file. */
-    openfile->current = openfile->fileage;
-    openfile->current_x = 0;
-    openfile->placewewant = 0;
-
-    /* Keep moving to the next word (counting punctuation characters as
-     * part of a word so that we match the output of "wc -w"), without
-     * updating the screen, until we reach the end of the file,
-     * incrementing the total word count whenever we're on a word just
-     * before moving. */
-    while (openfile->current != openfile->filebot ||
-       openfile->current_x != 0) {
-       if (do_next_word(TRUE, FALSE))
-           words++;
-    }
-
-    if (old_mark_set) {
-       /* If the mark was on and we added a magicline, remove it
-        * now. */
-       if (added_magicline)
-           remove_magicline();
-
-       /* Unpartition the filestruct so that it contains all the text
-        * again, and turn the mark back on. */
-       unpartition_filestruct(&filepart);
-       openfile->mark_set = TRUE;
-    }
-
-    /* Restore where we were. */
-    openfile->current = current_save;
-    openfile->current_x = current_x_save;
-    openfile->placewewant = pww_save;
-
-    /* Display the total word count on the statusbar. */
-    statusbar("%s: %lu", old_mark_set ? _("Word Count in Selection") :
-       _("Word Count"), (unsigned long)words);
-}
-
 void do_mark(void)
 {
     openfile->mark_set = !openfile->mark_set;
@@ -1614,1722 +1447,6 @@ void do_mark(void)
 }
 #endif /* !NANO_SMALL */
 
-#ifndef DISABLE_WRAPPING
-void wrap_reset(void)
-{
-    same_line_wrap = FALSE;
-}
-#endif
-
-#ifndef DISABLE_WRAPPING
-/* We wrap the given line.  Precondition: we assume the cursor has been
- * moved forward since the last typed character.  Return value: whether
- * we wrapped. */
-bool do_wrap(filestruct *line)
-{
-    size_t line_len;
-       /* Length of the line we wrap. */
-    ssize_t wrap_loc;
-       /* Index of line->data where we wrap. */
-#ifndef NANO_SMALL
-    const char *indent_string = NULL;
-       /* Indentation to prepend to the new line. */
-    size_t indent_len = 0;     /* The length of indent_string. */
-#endif
-    const char *after_break;   /* The text after the wrap point. */
-    size_t after_break_len;    /* The length of after_break. */
-    bool wrapping = FALSE;     /* Do we prepend to the next line? */
-    const char *next_line = NULL;
-       /* The next line, minus indentation. */
-    size_t next_line_len = 0;  /* The length of next_line. */
-    char *new_line = NULL;     /* The line we create. */
-    size_t new_line_len = 0;   /* The eventual length of new_line. */
-
-    /* There are three steps.  First, we decide where to wrap.  Then, we
-     * create the new wrap line.  Finally, we clean up. */
-
-    /* Step 1, finding where to wrap.  We are going to add a new line
-     * after a blank character.  In this step, we call break_line() to
-     * get the location of the last blank we can break the line at, and
-     * and set wrap_loc to the location of the character after it, so
-     * that the blank is preserved at the end of the line.
-     *
-     * If there is no legal wrap point, or we reach the last character
-     * of the line while trying to find one, we should return without
-     * wrapping.  Note that if autoindent is turned on, we don't break
-     * at the end of it! */
-
-    assert(line != NULL && line->data != NULL);
-
-    /* Save the length of the line. */
-    line_len = strlen(line->data);
-
-    /* Find the last blank where we can break the line. */
-    wrap_loc = break_line(line->data, fill, FALSE);
-
-    /* If we couldn't break the line, or we've reached the end of it, we
-     * don't wrap. */
-    if (wrap_loc == -1 || line->data[wrap_loc] == '\0')
-       return FALSE;
-
-    /* Otherwise, move forward to the character just after the blank. */
-    wrap_loc += move_mbright(line->data + wrap_loc, 0);
-
-    /* If we've reached the end of the line, we don't wrap. */
-    if (line->data[wrap_loc] == '\0')
-       return FALSE;
-
-#ifndef NANO_SMALL
-    /* If autoindent is turned on, and we're on the character just after
-     * the indentation, we don't wrap. */
-    if (ISSET(AUTOINDENT)) {
-       /* Get the indentation of this line. */
-       indent_string = line->data;
-       indent_len = indent_length(indent_string);
-
-       if (wrap_loc == indent_len)
-           return FALSE;
-    }
-#endif
-
-    /* Step 2, making the new wrap line.  It will consist of indentation
-     * followed by the text after the wrap point, optionally followed by
-     * a space (if the text after the wrap point doesn't end in a blank)
-     * and the text of the next line, if they can fit without
-     * wrapping, the next line exists, and the same_line_wrap flag is
-     * set. */
-
-    /* after_break is the text that will be wrapped to the next line. */
-    after_break = line->data + wrap_loc;
-    after_break_len = line_len - wrap_loc;
-
-    assert(strlen(after_break) == after_break_len);
-
-    /* We prepend the wrapped text to the next line, if the
-     * same_line_wrap flag is set, there is a next line, and prepending
-     * would not make the line too long. */
-    if (same_line_wrap && line->next != NULL) {
-       const char *end = after_break + move_mbleft(after_break,
-               after_break_len);
-
-       /* If after_break doesn't end in a blank, make sure it ends in a
-        * space. */
-       if (!is_blank_mbchar(end)) {
-           line_len++;
-           line->data = charealloc(line->data, line_len + 1);
-           line->data[line_len - 1] = ' ';
-           line->data[line_len] = '\0';
-           after_break = line->data + wrap_loc;
-           after_break_len++;
-           openfile->totsize++;
-       }
-
-       next_line = line->next->data;
-       next_line_len = strlen(next_line);
-
-       if (after_break_len + next_line_len <= fill) {
-           wrapping = TRUE;
-           new_line_len += next_line_len;
-       }
-    }
-
-    /* new_line_len is now the length of the text that will be wrapped
-     * to the next line, plus (if we're prepending to it) the length of
-     * the text of the next line. */
-    new_line_len += after_break_len;
-
-#ifndef NANO_SMALL
-    if (ISSET(AUTOINDENT)) {
-       if (wrapping) {
-           /* If we're wrapping, the indentation will come from the
-            * next line. */
-           indent_string = next_line;
-           indent_len = indent_length(indent_string);
-           next_line += indent_len;
-       } else {
-           /* Otherwise, it will come from this line, in which case
-            * we should increase new_line_len to make room for it. */
-           new_line_len += indent_len;
-           openfile->totsize += mbstrnlen(indent_string, indent_len);
-       }
-    }
-#endif
-
-    /* Now we allocate the new line and copy the text into it. */
-    new_line = charalloc(new_line_len + 1);
-    new_line[0] = '\0';
-
-#ifndef NANO_SMALL
-    if (ISSET(AUTOINDENT)) {
-       /* Copy the indentation. */
-       strncpy(new_line, indent_string, indent_len);
-       new_line[indent_len] = '\0';
-       new_line_len += indent_len;
-    }
-#endif
-
-    /* Copy all the text after the wrap point of the current line. */
-    strcat(new_line, after_break);
-
-    /* Break the current line at the wrap point. */
-    null_at(&line->data, wrap_loc);
-
-    if (wrapping) {
-       /* If we're wrapping, copy the text from the next line, minus
-        * the indentation that we already copied above. */
-       strcat(new_line, next_line);
-
-       free(line->next->data);
-       line->next->data = new_line;
-    } else {
-       /* Otherwise, make a new line and copy the text after where we
-        * broke this line to the beginning of the new line. */
-       splice_node(openfile->current, make_new_node(openfile->current),
-               openfile->current->next);
-
-       openfile->current->next->data = new_line;
-
-       openfile->totlines++;
-       openfile->totsize++;
-    }
-
-    /* Step 3, clean up.  Reposition the cursor and mark, and do some
-     * other sundry things. */
-
-    /* Set the same_line_wrap flag, so that later wraps of this line
-     * will be prepended to the next line. */
-    same_line_wrap = TRUE;
-
-    /* Each line knows its line number.  We recalculate these if we
-     * inserted a new line. */
-    if (!wrapping)
-       renumber(line);
-
-    /* If the cursor was after the break point, we must move it.  We
-     * also clear the same_line_wrap flag in this case. */
-    if (openfile->current_x > wrap_loc) {
-       same_line_wrap = FALSE;
-       openfile->current = openfile->current->next;
-       openfile->current_x -= wrap_loc
-#ifndef NANO_SMALL
-               - indent_len
-#endif
-               ;
-       openfile->placewewant = xplustabs();
-    }
-
-#ifndef NANO_SMALL
-    /* If the mark was on this line after the wrap point, we move it
-     * down.  If it was on the next line and we wrapped onto that line,
-     * we move it right. */
-    if (openfile->mark_set) {
-       if (openfile->mark_begin == line && openfile->mark_begin_x >
-               wrap_loc) {
-           openfile->mark_begin = line->next;
-           openfile->mark_begin_x -= wrap_loc - indent_len + 1;
-       } else if (wrapping && openfile->mark_begin == line->next)
-           openfile->mark_begin_x += after_break_len;
-    }
-#endif
-
-    return TRUE;
-}
-#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)
-{
-    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
-
-    /* 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) {
-       /* 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
-
-    /* Start from the top of the file. */
-    openfile->edittop = openfile->fileage;
-    openfile->current = openfile->fileage;
-    openfile->current_x = (size_t)-1;
-    openfile->placewewant = 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);
-
-           edit_refresh();
-
-           do_replace_highlight(TRUE, exp_word);
-
-           /* Allow all instances of the word to be corrected. */
-           canceled = (statusq(FALSE, spell_list, word,
-#ifndef NANO_SMALL
-                       NULL,
-#endif
-                        _("Edit a replacement")) == -1);
-
-           do_replace_highlight(FALSE, exp_word);
-
-           free(exp_word);
-
-           if (!canceled && strcmp(word, answer) != 0) {
-               openfile->current_x--;
-               do_replace_loop(word, openfile->current,
-                       &openfile->current_x, TRUE, &canceled);
-           }
-
-           break;
-       }
-    }
-
-#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();
-
-       /* 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;
-       }
-
-       /* 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
-
-    /* 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_x = current_x_save;
-    openfile->placewewant = pww_save;
-
-    /* Restore case sensitivity setting. */
-    if (!case_sens_set)
-       UNSET(CASE_SENSITIVE);
-
-#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 !canceled;
-}
-
-/* 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)
-{
-    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;
-
-    /* Create all three pipes up front. */
-    if (pipe(spell_fd) == -1 || pipe(sort_fd) == -1 ||
-       pipe(uniq_fd) == -1)
-       return _("Could not create pipe");
-
-    statusbar(_("Creating misspelled word list, please wait..."));
-
-    /* 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);
-    }
-
-    /* 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);
-    }
-
-    close(spell_fd[0]);
-    close(sort_fd[1]);
-
-    /* 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;
-
-       close(sort_fd[0]);
-
-       /* Send uniq's standard output to the new pipe. */
-       if (dup2(uniq_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
-           goto close_pipes_and_exit;
-
-       close(uniq_fd[1]);
-
-       /* Start the uniq program; we are using PATH. */
-       execlp("uniq", "uniq", NULL);
-
-       /* This should not be reached if uniq is found. */
-       exit(1);
-    }
-
-    close(sort_fd[0]);
-    close(uniq_fd[1]);
-
-    /* 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");
-    }
-
-    /* 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");
-    }
-
-    /* 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);
-
-    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;
-    }
-
-    *read_buff_ptr = '\0';
-    close(uniq_fd[0]);
-
-    /* Process the spelling errors. */
-    read_buff_word = read_buff_ptr = read_buff;
-
-    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++;
-    }
-
-    /* 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();
-
-    /* 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 (WIFEXITED(spell_status) == 0 || WEXITSTATUS(spell_status))
-       return _("Error invoking \"spell\"");
-
-    if (WIFEXITED(sort_status)  == 0 || WEXITSTATUS(sort_status))
-       return _("Error invoking \"sort -f\"");
-
-    if (WIFEXITED(uniq_status) == 0 || WEXITSTATUS(uniq_status))
-       return _("Error invoking \"uniq\"");
-
-    /* Otherwise... */
-    return NULL;
-
-  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);
-}
-
-/* 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
-    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
-
-    endwin();
-
-    /* Set up an argument list to pass execvp(). */
-    if (spellargs == NULL) {
-       spellargs = (char **)nmalloc(arglen * sizeof(char *));
-
-       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;
-
-    /* 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);
-    }
-
-    /* Could not fork?? */
-    if (pid_spell < 0)
-       return _("Could not fork");
-
-    /* Wait for alternate speller to complete. */
-    wait(&alt_spell_status);
-
-    refresh();
-
-    /* Restore the terminal to its previous state. */
-    terminal_init();
-
-    /* Turn the cursor back on for sure. */
-    curs_set(1);
-
-    if (!WIFEXITED(alt_spell_status) ||
-               WEXITSTATUS(alt_spell_status) != 0) {
-       char *altspell_error;
-       char *invoke_error = _("Error invoking \"%s\"");
-
-#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;
-    }
-
-#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);
-    }
-#endif
-
-    /* Set up the window size. */
-    window_size_init();
-
-    /* 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;
-
-#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;
-       }
-
-       /* 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
-
-    /* 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();
-
-    return NULL;
-}
-
-void do_spell(void)
-{
-    int i;
-    FILE *temp_file;
-    char *temp = safe_tempfile(&temp_file);
-    const char *spell_msg;
-
-    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_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)
-{
-    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;
-
-    assert(line != NULL);
-
-    while (*line != '\0' && goal >= 0) {
-       size_t pos = 0;
-
-       line_len = parse_mbchar(line, NULL, NULL, &pos);
-
-       if (is_blank_mbchar(line) || (newline && *line == '\n')) {
-           blank_loc = cur_loc;
-
-           if (newline && *line == '\n')
-               break;
-       }
-
-       goal -= pos;
-       line += line_len;
-       cur_loc += line_len;
-    }
-
-    if (goal >= 0)
-       /* In fact, the whole line displays shorter than goal. */
-       return cur_loc;
-
-    if (blank_loc == -1) {
-       /* No blank was found that was short enough. */
-       bool found_blank = FALSE;
-
-       while (*line != '\0') {
-           line_len = parse_mbchar(line, NULL, NULL, NULL);
-
-           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;
-       }
-
-       return -1;
-    }
-
-    /* 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;
-
-    while (*line != '\0' && (is_blank_mbchar(line) ||
-       (newline && *line == '\n'))) {
-       line_len = parse_mbchar(line, NULL, NULL, NULL);
-
-       line += line_len;
-       blank_loc += line_len;
-    }
-
-    return blank_loc;
-}
-#endif /* !DISABLE_HELP || !DISABLE_JUSTIFY || !DISABLE_WRAPPING */
-
-#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;
-
-    assert(line != NULL);
-
-    blank_mb = charalloc(mb_cur_max());
-
-    while (*line != '\0') {
-       blank_mb_len = parse_mbchar(line, blank_mb, NULL, NULL);
-
-       if (!is_blank_mbchar(blank_mb))
-           break;
-
-       line += blank_mb_len;
-       len += blank_mb_len;
-    }
-
-    free(blank_mb);
-
-    return len;
-}
-#endif /* !NANO_SMALL || !DISABLE_JUSTIFY */
-
-#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
-
-    /* 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));
-
-    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;
-
-    while (*end != '\0') {
-       int end_len;
-
-       /* 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);
-
-           *new_end = ' ';
-           new_end++;
-           end += end_len;
-
-           while (*end != '\0' && is_blank_mbchar(end)) {
-               end_len = parse_mbchar(end, NULL, NULL, NULL);
-
-               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 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);
-
-           while (end_len > 0) {
-               *new_end = *end;
-               new_end++;
-               end++;
-               end_len--;
-           }
-
-           if (*end != '\0' && mbstrchr(brackets, end) != NULL) {
-               end_len = parse_mbchar(end, NULL, NULL, NULL);
-
-               while (end_len > 0) {
-                   *new_end = *end;
-                   new_end++;
-                   end++;
-                   end_len--;
-               }
-           }
-
-           if (*end != '\0' && is_blank_mbchar(end)) {
-               end_len = parse_mbchar(end, NULL, NULL, NULL);
-
-               *new_end = ' ';
-               new_end++;
-               end += end_len;
-           }
-
-           if (*end != '\0' && is_blank_mbchar(end)) {
-               end_len = parse_mbchar(end, NULL, NULL, NULL);
-
-               *new_end = ' ';
-               new_end++;
-               end += end_len;
-           }
-
-           while (*end != '\0' && is_blank_mbchar(end)) {
-               end_len = parse_mbchar(end, NULL, NULL, NULL);
-
-               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--;
-           }
-       }
-    }
-
-    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++;
-    }
-
-    if (shift > 0) {
-       openfile->totsize -= shift;
-       null_at(&new_paragraph_data, new_end - new_paragraph_data);
-       free(paragraph->data);
-       paragraph->data = new_paragraph_data;
-
-#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);
-}
-
-/* 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 (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;
-
-    /* Compute quote depth level. */
-    while (strncmp(line + qdepth, quotestr, quotelen) == 0)
-       qdepth += quotelen;
-    return qdepth;
-#endif /* !HAVE_REGEX_H */
-}
-
-/* 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));
-
-    return (a_quote == quote_length(b_line) &&
-       strncmp(a_line, b_line, a_quote) == 0);
-}
-
-/* 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);
-}
-
-/* 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;
-
-    /* Case 1). */
-    if (foo->prev == NULL)
-       return TRUE;
-
-    quote_len = quote_length(foo->data);
-    indent_len = indent_length(foo->data + quote_len);
-
-    /* Not part of a paragraph. */
-    if (foo->data[quote_len + indent_len] == '\0')
-       return FALSE;
-
-    /* Case 3). */
-    if (!quotes_match(foo->data, quote_len, foo->prev->data))
-       return TRUE;
-
-    temp_id_len = indent_length(foo->prev->data + quote_len);
-
-    /* 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;
-
-    return FALSE;
-}
-
-/* Is foo inside a paragraph? */
-bool inpar(const filestruct *const foo)
-{
-    size_t quote_len;
-
-    if (foo == NULL)
-       return FALSE;
-
-    quote_len = quote_length(foo->data);
-
-    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
-    bool old_mark_set = openfile->mark_set;
-    ssize_t mb_lineno_save = 0;
-    size_t mark_begin_x_save = 0;
-
-    if (old_mark_set) {
-       mb_lineno_save = openfile->mark_begin->lineno;
-       mark_begin_x_save = openfile->mark_begin_x;
-    }
-#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;
-
-    /* Move the paragraph from the current buffer's filestruct to the
-     * justify buffer. */
-    move_to_filestruct(&jusbuffer, &jusbottom, top, 0, bot, 0);
-
-    /* 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 && top->lineno == mb_lineno_save) {
-           openfile->mark_begin = top;
-           openfile->mark_begin_x = mark_begin_x_save;
-       }
-#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;
-
-    set_modified();
-
-    return first_line;
-}
-
-/* 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. */
-
-#ifdef HAVE_REGEX_H
-    if (quoterc != 0) {
-       statusbar(_("Bad quote string %s: %s"), quotestr, quoteerr);
-       return FALSE;
-    }
-#endif
-
-    assert(openfile->current != NULL);
-
-    /* Move back to the beginning of the current line. */
-    openfile->current_x = 0;
-    openfile->placewewant = 0;
-
-    /* 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;
-
-       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);
-
-    /* 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;
-
-    /* Save the values of quote_len and par_len. */
-    assert(quote != NULL && par != NULL);
-
-    *quote = quote_len;
-    *par = par_len;
-
-    return TRUE;
-}
-
-/* If full_justify is TRUE, justify the entire file.  Otherwise, justify
- * the current paragraph. */
-void do_justify(bool full_justify)
-{
-    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. */
-
-    /* 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;
-
-    /* If we're justifying the entire file, start at the beginning. */
-    if (full_justify)
-       openfile->current = openfile->fileage;
-
-    last_par_line = openfile->current;
-
-    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.) */
-
-       /* 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 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);
-
-       /* Initialize indent_string to a blank string. */
-       indent_string = mallocstrcpy(NULL, "");
-
-       /* 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;
-
-           for (i = 0; i < par_len; i++) {
-               indent_len = quote_len +
-                       indent_length(indent_line->data + quote_len);
-
-               if (indent_len != strlen(indent_string)) {
-                   indent_string = mallocstrncpy(indent_string,
-                       indent_line->data, indent_len + 1);
-                   indent_string[indent_len] = '\0';
-
-                   if (past_first_line)
-                       break;
-               }
-
-               if (indent_line == openfile->current)
-                   past_first_line = TRUE;
-
-               indent_line = indent_line->next;
-           }
-       }
-
-       /* 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);
-
-           indent_len = quote_len +
-               indent_length(openfile->current->next->data +
-               quote_len);
-
-           next_line_len -= indent_len;
-           openfile->totsize -= indent_len;
-
-           /* 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++;
-           }
-
-           openfile->current->data =
-               charealloc(openfile->current->data, line_len +
-               next_line_len + 1);
-           strcat(openfile->current->data, next_line->data +
-               indent_len);
-
-           /* Don't destroy edittop! */
-           if (openfile->edittop == next_line)
-               openfile->edittop = openfile->current;
-
-#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
-
-           unlink_node(next_line);
-           delete_node(next_line);
-
-           /* If we've removed the next line, we need to go through
-            * this line again. */
-           i--;
-
-           par_len--;
-           openfile->totlines--;
-           openfile->totsize--;
-       }
-
-       /* 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));
-
-       while (par_len > 0 &&
-               strlenpt(openfile->current->data) > fill) {
-           size_t line_len = strlen(openfile->current->data);
-
-           indent_len = strlen(indent_string);
-
-           /* 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);
-
-           /* 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;
-
-           /* Move forward to the character after the indentation and
-            * just after the space. */
-           break_pos += indent_len + 1;
-
-           assert(break_pos <= line_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
-               && !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;
-
-#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
-
-           /* 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);
-
-       /* Go to the next line, the line after the last line of the
-        * paragraph. */
-       openfile->current_y++;
-       openfile->current = openfile->current->next;
-
-       /* 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;
-    }
-
-    /* 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);
-    }
-
-    edit_refresh();
-
-    statusbar(_("Can now UnJustify!"));
-
-    /* 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);
-
-    /* Display the shortcut list with UnJustify. */
-    shortcut_init(TRUE);
-    display_main_list();
-
-    /* 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);
-
-    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;
-
-       /* Splice the justify buffer back into the file, but only if we
-        * actually justified something. */
-       if (first_par_line != NULL) {
-           filestruct *top_save;
-
-           /* 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);
-
-           /* 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;
-
-           top_save = openfile->fileage;
-
-           /* 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);
-
-            /* Renumber starting with the beginning line of the old
-             * partition. */
-           renumber(top_save);
-
-           /* 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;
-
-           /* Clear the justify buffer. */
-           jusbuffer = NULL;
-
-           if (!openfile->modified)
-               titlebar(NULL);
-           edit_refresh();
-       }
-    } else {
-       unget_kbinput(kbinput, meta_key, func_key);
-
-       /* Blow away the text in the justify buffer. */
-       free_filestruct(jusbuffer);
-       jusbuffer = NULL;
-    }
-
-    blank_statusbar();
-
-    /* Display the shortcut list with UnCut. */
-    shortcut_init(FALSE);
-    display_main_list();
-}
-
-void do_justify_void(void)
-{
-    do_justify(FALSE);
-}
-
-void do_full_justify(void)
-{
-    do_justify(TRUE);
-}
-#endif /* !DISABLE_JUSTIFY */
-
 void do_exit(void)
 {
     int i;
index a131a9e76204ce171af10c297e9b393219aa5fc3..bc0ad56c27a2ecf526935a817b082ed7fbe88f9e 100644 (file)
        /* Mark a string that will be sent to gettext() later. */
 
 #include <stddef.h>
+#include <stdlib.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <dirent.h>
+#ifdef HAVE_REGEX_H
+#include <regex.h>
+#endif
+#include <assert.h>
 
 /* If no vsnprintf(), use the version from glib 2.x. */
 #ifndef HAVE_VSNPRINTF
index 6e34971db1ef4e0e84e51b0d26771f6afb1ab183..8bee99c1993b596ee58e1a6314948d32dabd4f0e 100644 (file)
 #define PROTO_H 1
 
 /* Externs. */
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <dirent.h>
-#ifdef HAVE_REGEX_H
-#include <regex.h>
-#endif
 #include "nano.h"
 
 #ifndef DISABLE_WRAPJUSTIFY
+extern ssize_t fill;
 extern ssize_t wrap_at;
 #endif
 extern int editwinrows;
@@ -394,51 +388,14 @@ void version(void);
 int no_more_space(void);
 int no_help(void);
 void nano_disabled_msg(void);
-#ifndef NANO_SMALL
-void cancel_command(int signal);
-bool execute_command(const char *command);
-#endif
 void do_verbatim_input(void);
 void do_backspace(void);
 void do_delete(void);
 void do_tab(void);
 void do_enter(void);
 #ifndef NANO_SMALL
-void do_word_count(void);
 void do_mark(void);
 #endif
-#ifndef DISABLE_WRAPPING
-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
-#if !defined(NANO_SMALL) || !defined(DISABLE_JUSTIFY)
-size_t indent_length(const char *line);
-#endif
-#ifndef DISABLE_JUSTIFY
-void justify_format(filestruct *paragraph, size_t skip);
-size_t quote_length(const char *line);
-bool quotes_match(const char *a_line, size_t a_quote, const char
-       *b_line);
-bool indents_match(const char *a_line, size_t a_indent, const char
-       *b_line, size_t b_indent);
-bool begpar(const filestruct *const foo);
-bool inpar(const filestruct *const foo);
-filestruct *backup_lines(filestruct *first_line, size_t par_len, size_t
-       quote_len);
-bool find_paragraph(size_t *const quote, size_t *const par);
-void do_justify(bool full_justify);
-void do_justify_void(void);
-void do_full_justify(void);
-#endif /* !DISABLE_JUSTIFY */
 void do_exit(void);
 void signal_init(void);
 void handle_hupterm(int signal);
@@ -491,7 +448,6 @@ void not_found_msg(const char *str);
 void search_abort(void);
 void search_init_globals(void);
 int search_init(bool replacing, bool use_answer);
-bool is_whole_word(size_t pos, const char *buf, const char *word);
 bool findnextstr(bool can_display_wrap, bool wholeword, bool
        no_sameline, const filestruct *begin, size_t begin_x, const char
        *needle, size_t *needle_len);
@@ -535,14 +491,48 @@ char *get_history_completion(filestruct **h, const char *s, size_t len);
 #endif
 #endif /* !NANO_SMALL */
 
-/* Public functions in utils.c. */
-#ifdef HAVE_REGEX_H
-#ifdef BROKEN_REGEXEC
-int safe_regexec(const regex_t *preg, const char *string, size_t nmatch,
-       regmatch_t pmatch[], int eflags);
+/* Public functions in text.c. */
+#ifndef NANO_SMALL
+void cancel_command(int signal);
+bool execute_command(const char *command);
 #endif
-int regexp_bol_or_eol(const regex_t *preg, const char *string);
+#ifndef DISABLE_WRAPPING
+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
+#if !defined(NANO_SMALL) || !defined(DISABLE_JUSTIFY)
+size_t indent_length(const char *line);
+#endif
+#ifndef DISABLE_JUSTIFY
+void justify_format(filestruct *paragraph, size_t skip);
+size_t quote_length(const char *line);
+bool quotes_match(const char *a_line, size_t a_quote, const char
+       *b_line);
+bool indents_match(const char *a_line, size_t a_indent, const char
+       *b_line, size_t b_indent);
+bool begpar(const filestruct *const foo);
+bool inpar(const filestruct *const foo);
+filestruct *backup_lines(filestruct *first_line, size_t par_len, size_t
+       quote_len);
+bool find_paragraph(size_t *const quote, size_t *const par);
+void do_justify(bool full_justify);
+void do_justify_void(void);
+void do_full_justify(void);
+#endif /* !DISABLE_JUSTIFY */
+#ifndef NANO_SMALL
+void do_word_count(void);
+#endif
+
+/* Public functions in utils.c. */
 int digits(size_t n);
 void get_homedir(void);
 bool parse_num(const char *str, ssize_t *val);
@@ -559,6 +549,14 @@ ssize_t ngetline(char **lineptr, size_t *n, FILE *stream);
 ssize_t ngetdelim(char **lineptr, size_t *n, int delim, FILE *stream);
 #endif
 #endif /* !NANO_SMALL && ENABLE_NANORC */
+#ifdef HAVE_REGEX_H
+#ifdef BROKEN_REGEXEC
+int safe_regexec(const regex_t *preg, const char *string, size_t nmatch,
+       regmatch_t pmatch[], int eflags);
+#endif
+int regexp_bol_or_eol(const regex_t *preg, const char *string);
+#endif
+bool is_whole_word(size_t pos, const char *buf, const char *word);
 const char *strstrwrapper(const char *haystack, const char *needle,
        const char *start);
 void nperror(const char *s);
index ac3559e826e05fb4a55cea52455a5e31fd39d645..eb39876716ab074984c5e8ae5ea0db51774a4b18 100644 (file)
 #include <config.h>
 #endif
 
-#include <stdlib.h>
 #include <stdarg.h>
 #include <string.h>
 #include <stdio.h>
 #include <errno.h>
 #include <unistd.h>
 #include <ctype.h>
-#include <assert.h>
 #include "proto.h"
 
 #ifdef ENABLE_NANORC
index fcd62b938901039925605e79e7b36a53bfa78fd2..53d3ac35a4ef5e62f82abafaa851ce760603d9c1 100644 (file)
 #include <config.h>
 #endif
 
-#include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <ctype.h>
 #include <errno.h>
-#include <assert.h>
 #include "proto.h"
 
 static bool search_last_line = FALSE;
@@ -256,30 +254,6 @@ int search_init(bool replacing, bool use_answer)
     return 0;
 }
 
-bool is_whole_word(size_t pos, const char *buf, const char *word)
-{
-    char *p = charalloc(mb_cur_max()), *r = charalloc(mb_cur_max());
-    size_t word_end = pos + strlen(word);
-    bool retval;
-
-    assert(buf != NULL && pos <= strlen(buf) && word != NULL);
-
-    parse_mbchar(buf + move_mbleft(buf, pos), p, NULL, NULL);
-    parse_mbchar(buf + word_end, r, NULL, NULL);
-
-    /* If we're at the beginning of the line or the character before the
-     * word isn't a non-punctuation "word" character, and if we're at
-     * the end of the line or the character after the word isn't a
-     * non-punctuation "word" character, we have a whole word. */
-    retval = (pos == 0 || !is_word_mbchar(p, FALSE)) &&
-       (word_end == strlen(buf) || !is_word_mbchar(r, FALSE));
-
-    free(p);
-    free(r);
-
-    return retval;
-}
-
 /* Look for needle, starting at (current, current_x).  If no_sameline is
  * TRUE, skip over begin when looking for needle.  begin is the line
  * where we first started searching, at column begin_x.  If
diff --git a/src/text.c b/src/text.c
new file mode 100644 (file)
index 0000000..e2a0380
--- /dev/null
@@ -0,0 +1,1915 @@
+/* $Id$ */
+/**************************************************************************
+ *   text.c                                                              *
+ *                                                                        *
+ *   Copyright (C) 2005 Chris Allegretta                                  *
+ *   This program is free software; you can redistribute it and/or modify *
+ *   it under the terms of the GNU General Public License as published by *
+ *   the Free Software Foundation; either version 2, or (at your option)  *
+ *   any later version.                                                   *
+ *                                                                        *
+ *   This program is distributed in the hope that it will be useful, but  *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of           *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    *
+ *   General Public License for more details.                             *
+ *                                                                        *
+ *   You should have received a copy of the GNU General Public License    *
+ *   along with this program; if not, write to the Free Software          *
+ *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA            *
+ *   02110-1301, USA.                                                     *
+ *                                                                        *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include "proto.h"
+
+#ifndef NANO_SMALL
+static pid_t pid;              /* The PID of the newly forked process
+                                * in execute_command().  It must be
+                                * global because the cancel_command()
+                                * signal handler needs it. */
+#endif
+#ifndef DISABLE_WRAPPING
+static bool same_line_wrap = FALSE;
+       /* Should we prepend wrapped text to the next line? */
+#endif
+#ifndef DISABLE_JUSTIFY
+static filestruct *jusbottom = NULL;
+       /* Pointer to end of justify buffer. */
+#endif
+
+#ifndef NANO_SMALL
+void cancel_command(int signal)
+{
+    if (kill(pid, SIGKILL) == -1)
+       nperror("kill");
+}
+
+/* Return TRUE on success. */
+bool execute_command(const char *command)
+{
+    int fd[2];
+    FILE *f;
+    struct sigaction oldaction, newaction;
+       /* Original and temporary handlers for SIGINT. */
+    bool sig_failed = FALSE;
+       /* Did sigaction() fail without changing the signal handlers? */
+
+    /* Make our pipes. */
+    if (pipe(fd) == -1) {
+       statusbar(_("Could not pipe"));
+       return FALSE;
+    }
+
+    /* Fork a child. */
+    if ((pid = fork()) == 0) {
+       close(fd[0]);
+       dup2(fd[1], fileno(stdout));
+       dup2(fd[1], fileno(stderr));
+
+       /* If execl() returns at all, there was an error. */
+       execl("/bin/sh", "sh", "-c", command, NULL);
+       exit(0);
+    }
+
+    /* Else continue as parent. */
+    close(fd[1]);
+
+    if (pid == -1) {
+       close(fd[0]);
+       statusbar(_("Could not fork"));
+       return FALSE;
+    }
+
+    /* Before we start reading the forked command's output, we set
+     * things up so that Ctrl-C will cancel the new process. */
+
+    /* Enable interpretation of the special control keys so that we get
+     * SIGINT when Ctrl-C is pressed. */
+    enable_signals();
+
+    if (sigaction(SIGINT, NULL, &newaction) == -1) {
+       sig_failed = TRUE;
+       nperror("sigaction");
+    } else {
+       newaction.sa_handler = cancel_command;
+       if (sigaction(SIGINT, &newaction, &oldaction) == -1) {
+           sig_failed = TRUE;
+           nperror("sigaction");
+       }
+    }
+
+    /* Note that now oldaction is the previous SIGINT signal handler,
+     * to be restored later. */
+
+    f = fdopen(fd[0], "rb");
+    if (f == NULL)
+       nperror("fdopen");
+
+    read_file(f, "stdin");
+
+    /* If multibuffer mode is on, we could be here in view mode.  If so,
+     * don't set the modification flag. */
+    if (!ISSET(VIEW_MODE))
+       set_modified();
+
+    if (wait(NULL) == -1)
+       nperror("wait");
+
+    if (!sig_failed && sigaction(SIGINT, &oldaction, NULL) == -1)
+       nperror("sigaction");
+
+    /* Disable interpretation of the special control keys so that we can
+     * use Ctrl-C for other things. */
+    disable_signals();
+
+    return TRUE;
+}
+#endif /* !NANO_SMALL */
+
+#ifndef DISABLE_WRAPPING
+void wrap_reset(void)
+{
+    same_line_wrap = FALSE;
+}
+
+/* We wrap the given line.  Precondition: we assume the cursor has been
+ * moved forward since the last typed character.  Return value: whether
+ * we wrapped. */
+bool do_wrap(filestruct *line)
+{
+    size_t line_len;
+       /* Length of the line we wrap. */
+    ssize_t wrap_loc;
+       /* Index of line->data where we wrap. */
+#ifndef NANO_SMALL
+    const char *indent_string = NULL;
+       /* Indentation to prepend to the new line. */
+    size_t indent_len = 0;     /* The length of indent_string. */
+#endif
+    const char *after_break;   /* The text after the wrap point. */
+    size_t after_break_len;    /* The length of after_break. */
+    bool wrapping = FALSE;     /* Do we prepend to the next line? */
+    const char *next_line = NULL;
+       /* The next line, minus indentation. */
+    size_t next_line_len = 0;  /* The length of next_line. */
+    char *new_line = NULL;     /* The line we create. */
+    size_t new_line_len = 0;   /* The eventual length of new_line. */
+
+    /* There are three steps.  First, we decide where to wrap.  Then, we
+     * create the new wrap line.  Finally, we clean up. */
+
+    /* Step 1, finding where to wrap.  We are going to add a new line
+     * after a blank character.  In this step, we call break_line() to
+     * get the location of the last blank we can break the line at, and
+     * and set wrap_loc to the location of the character after it, so
+     * that the blank is preserved at the end of the line.
+     *
+     * If there is no legal wrap point, or we reach the last character
+     * of the line while trying to find one, we should return without
+     * wrapping.  Note that if autoindent is turned on, we don't break
+     * at the end of it! */
+
+    assert(line != NULL && line->data != NULL);
+
+    /* Save the length of the line. */
+    line_len = strlen(line->data);
+
+    /* Find the last blank where we can break the line. */
+    wrap_loc = break_line(line->data, fill, FALSE);
+
+    /* If we couldn't break the line, or we've reached the end of it, we
+     * don't wrap. */
+    if (wrap_loc == -1 || line->data[wrap_loc] == '\0')
+       return FALSE;
+
+    /* Otherwise, move forward to the character just after the blank. */
+    wrap_loc += move_mbright(line->data + wrap_loc, 0);
+
+    /* If we've reached the end of the line, we don't wrap. */
+    if (line->data[wrap_loc] == '\0')
+       return FALSE;
+
+#ifndef NANO_SMALL
+    /* If autoindent is turned on, and we're on the character just after
+     * the indentation, we don't wrap. */
+    if (ISSET(AUTOINDENT)) {
+       /* Get the indentation of this line. */
+       indent_string = line->data;
+       indent_len = indent_length(indent_string);
+
+       if (wrap_loc == indent_len)
+           return FALSE;
+    }
+#endif
+
+    /* Step 2, making the new wrap line.  It will consist of indentation
+     * followed by the text after the wrap point, optionally followed by
+     * a space (if the text after the wrap point doesn't end in a blank)
+     * and the text of the next line, if they can fit without
+     * wrapping, the next line exists, and the same_line_wrap flag is
+     * set. */
+
+    /* after_break is the text that will be wrapped to the next line. */
+    after_break = line->data + wrap_loc;
+    after_break_len = line_len - wrap_loc;
+
+    assert(strlen(after_break) == after_break_len);
+
+    /* We prepend the wrapped text to the next line, if the
+     * same_line_wrap flag is set, there is a next line, and prepending
+     * would not make the line too long. */
+    if (same_line_wrap && line->next != NULL) {
+       const char *end = after_break + move_mbleft(after_break,
+               after_break_len);
+
+       /* If after_break doesn't end in a blank, make sure it ends in a
+        * space. */
+       if (!is_blank_mbchar(end)) {
+           line_len++;
+           line->data = charealloc(line->data, line_len + 1);
+           line->data[line_len - 1] = ' ';
+           line->data[line_len] = '\0';
+           after_break = line->data + wrap_loc;
+           after_break_len++;
+           openfile->totsize++;
+       }
+
+       next_line = line->next->data;
+       next_line_len = strlen(next_line);
+
+       if (after_break_len + next_line_len <= fill) {
+           wrapping = TRUE;
+           new_line_len += next_line_len;
+       }
+    }
+
+    /* new_line_len is now the length of the text that will be wrapped
+     * to the next line, plus (if we're prepending to it) the length of
+     * the text of the next line. */
+    new_line_len += after_break_len;
+
+#ifndef NANO_SMALL
+    if (ISSET(AUTOINDENT)) {
+       if (wrapping) {
+           /* If we're wrapping, the indentation will come from the
+            * next line. */
+           indent_string = next_line;
+           indent_len = indent_length(indent_string);
+           next_line += indent_len;
+       } else {
+           /* Otherwise, it will come from this line, in which case
+            * we should increase new_line_len to make room for it. */
+           new_line_len += indent_len;
+           openfile->totsize += mbstrnlen(indent_string, indent_len);
+       }
+    }
+#endif
+
+    /* Now we allocate the new line and copy the text into it. */
+    new_line = charalloc(new_line_len + 1);
+    new_line[0] = '\0';
+
+#ifndef NANO_SMALL
+    if (ISSET(AUTOINDENT)) {
+       /* Copy the indentation. */
+       strncpy(new_line, indent_string, indent_len);
+       new_line[indent_len] = '\0';
+       new_line_len += indent_len;
+    }
+#endif
+
+    /* Copy all the text after the wrap point of the current line. */
+    strcat(new_line, after_break);
+
+    /* Break the current line at the wrap point. */
+    null_at(&line->data, wrap_loc);
+
+    if (wrapping) {
+       /* If we're wrapping, copy the text from the next line, minus
+        * the indentation that we already copied above. */
+       strcat(new_line, next_line);
+
+       free(line->next->data);
+       line->next->data = new_line;
+    } else {
+       /* Otherwise, make a new line and copy the text after where we
+        * broke this line to the beginning of the new line. */
+       splice_node(openfile->current, make_new_node(openfile->current),
+               openfile->current->next);
+
+       openfile->current->next->data = new_line;
+
+       openfile->totlines++;
+       openfile->totsize++;
+    }
+
+    /* Step 3, clean up.  Reposition the cursor and mark, and do some
+     * other sundry things. */
+
+    /* Set the same_line_wrap flag, so that later wraps of this line
+     * will be prepended to the next line. */
+    same_line_wrap = TRUE;
+
+    /* Each line knows its line number.  We recalculate these if we
+     * inserted a new line. */
+    if (!wrapping)
+       renumber(line);
+
+    /* If the cursor was after the break point, we must move it.  We
+     * also clear the same_line_wrap flag in this case. */
+    if (openfile->current_x > wrap_loc) {
+       same_line_wrap = FALSE;
+       openfile->current = openfile->current->next;
+       openfile->current_x -= wrap_loc
+#ifndef NANO_SMALL
+               - indent_len
+#endif
+               ;
+       openfile->placewewant = xplustabs();
+    }
+
+#ifndef NANO_SMALL
+    /* If the mark was on this line after the wrap point, we move it
+     * down.  If it was on the next line and we wrapped onto that line,
+     * we move it right. */
+    if (openfile->mark_set) {
+       if (openfile->mark_begin == line && openfile->mark_begin_x >
+               wrap_loc) {
+           openfile->mark_begin = line->next;
+           openfile->mark_begin_x -= wrap_loc - indent_len + 1;
+       } else if (wrapping && openfile->mark_begin == line->next)
+           openfile->mark_begin_x += after_break_len;
+    }
+#endif
+
+    return TRUE;
+}
+#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)
+{
+    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
+
+    /* 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) {
+       /* 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
+
+    /* Start from the top of the file. */
+    openfile->edittop = openfile->fileage;
+    openfile->current = openfile->fileage;
+    openfile->current_x = (size_t)-1;
+    openfile->placewewant = 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);
+
+           edit_refresh();
+
+           do_replace_highlight(TRUE, exp_word);
+
+           /* Allow all instances of the word to be corrected. */
+           canceled = (statusq(FALSE, spell_list, word,
+#ifndef NANO_SMALL
+                       NULL,
+#endif
+                        _("Edit a replacement")) == -1);
+
+           do_replace_highlight(FALSE, exp_word);
+
+           free(exp_word);
+
+           if (!canceled && strcmp(word, answer) != 0) {
+               openfile->current_x--;
+               do_replace_loop(word, openfile->current,
+                       &openfile->current_x, TRUE, &canceled);
+           }
+
+           break;
+       }
+    }
+
+#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();
+
+       /* 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;
+       }
+
+       /* 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
+
+    /* 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_x = current_x_save;
+    openfile->placewewant = pww_save;
+
+    /* Restore case sensitivity setting. */
+    if (!case_sens_set)
+       UNSET(CASE_SENSITIVE);
+
+#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 !canceled;
+}
+
+/* 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)
+{
+    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;
+
+    /* Create all three pipes up front. */
+    if (pipe(spell_fd) == -1 || pipe(sort_fd) == -1 ||
+       pipe(uniq_fd) == -1)
+       return _("Could not create pipe");
+
+    statusbar(_("Creating misspelled word list, please wait..."));
+
+    /* 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);
+    }
+
+    /* 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);
+    }
+
+    close(spell_fd[0]);
+    close(sort_fd[1]);
+
+    /* 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;
+
+       close(sort_fd[0]);
+
+       /* Send uniq's standard output to the new pipe. */
+       if (dup2(uniq_fd[1], STDOUT_FILENO) != STDOUT_FILENO)
+           goto close_pipes_and_exit;
+
+       close(uniq_fd[1]);
+
+       /* Start the uniq program; we are using PATH. */
+       execlp("uniq", "uniq", NULL);
+
+       /* This should not be reached if uniq is found. */
+       exit(1);
+    }
+
+    close(sort_fd[0]);
+    close(uniq_fd[1]);
+
+    /* 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");
+    }
+
+    /* 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");
+    }
+
+    /* 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);
+
+    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;
+    }
+
+    *read_buff_ptr = '\0';
+    close(uniq_fd[0]);
+
+    /* Process the spelling errors. */
+    read_buff_word = read_buff_ptr = read_buff;
+
+    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++;
+    }
+
+    /* 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();
+
+    /* 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 (WIFEXITED(spell_status) == 0 || WEXITSTATUS(spell_status))
+       return _("Error invoking \"spell\"");
+
+    if (WIFEXITED(sort_status)  == 0 || WEXITSTATUS(sort_status))
+       return _("Error invoking \"sort -f\"");
+
+    if (WIFEXITED(uniq_status) == 0 || WEXITSTATUS(uniq_status))
+       return _("Error invoking \"uniq\"");
+
+    /* Otherwise... */
+    return NULL;
+
+  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);
+}
+
+/* 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
+    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
+
+    endwin();
+
+    /* Set up an argument list to pass execvp(). */
+    if (spellargs == NULL) {
+       spellargs = (char **)nmalloc(arglen * sizeof(char *));
+
+       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;
+
+    /* 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);
+    }
+
+    /* If we couldn't fork, get out. */
+    if (pid_spell < 0)
+       return _("Could not fork");
+
+    /* Wait for alternate speller to complete. */
+    wait(&alt_spell_status);
+
+    refresh();
+
+    /* Restore the terminal to its previous state. */
+    terminal_init();
+
+    /* Turn the cursor back on for sure. */
+    curs_set(1);
+
+    if (!WIFEXITED(alt_spell_status) ||
+               WEXITSTATUS(alt_spell_status) != 0) {
+       char *altspell_error;
+       char *invoke_error = _("Error invoking \"%s\"");
+
+#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;
+    }
+
+#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);
+    }
+#endif
+
+    /* Set up the window size. */
+    window_size_init();
+
+    /* 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;
+
+#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;
+       }
+
+       /* 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
+
+    /* 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();
+
+    return NULL;
+}
+
+void do_spell(void)
+{
+    int i;
+    FILE *temp_file;
+    char *temp = safe_tempfile(&temp_file);
+    const char *spell_msg;
+
+    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_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)
+{
+    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;
+
+    assert(line != NULL);
+
+    while (*line != '\0' && goal >= 0) {
+       size_t pos = 0;
+
+       line_len = parse_mbchar(line, NULL, NULL, &pos);
+
+       if (is_blank_mbchar(line) || (newline && *line == '\n')) {
+           blank_loc = cur_loc;
+
+           if (newline && *line == '\n')
+               break;
+       }
+
+       goal -= pos;
+       line += line_len;
+       cur_loc += line_len;
+    }
+
+    if (goal >= 0)
+       /* In fact, the whole line displays shorter than goal. */
+       return cur_loc;
+
+    if (blank_loc == -1) {
+       /* No blank was found that was short enough. */
+       bool found_blank = FALSE;
+
+       while (*line != '\0') {
+           line_len = parse_mbchar(line, NULL, NULL, NULL);
+
+           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;
+       }
+
+       return -1;
+    }
+
+    /* 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;
+
+    while (*line != '\0' && (is_blank_mbchar(line) ||
+       (newline && *line == '\n'))) {
+       line_len = parse_mbchar(line, NULL, NULL, NULL);
+
+       line += line_len;
+       blank_loc += line_len;
+    }
+
+    return blank_loc;
+}
+#endif /* !DISABLE_HELP || !DISABLE_JUSTIFY || !DISABLE_WRAPPING */
+
+#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;
+
+    assert(line != NULL);
+
+    blank_mb = charalloc(mb_cur_max());
+
+    while (*line != '\0') {
+       blank_mb_len = parse_mbchar(line, blank_mb, NULL, NULL);
+
+       if (!is_blank_mbchar(blank_mb))
+           break;
+
+       line += blank_mb_len;
+       len += blank_mb_len;
+    }
+
+    free(blank_mb);
+
+    return len;
+}
+#endif /* !NANO_SMALL || !DISABLE_JUSTIFY */
+
+#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
+
+    /* 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));
+
+    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;
+
+    while (*end != '\0') {
+       int end_len;
+
+       /* 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);
+
+           *new_end = ' ';
+           new_end++;
+           end += end_len;
+
+           while (*end != '\0' && is_blank_mbchar(end)) {
+               end_len = parse_mbchar(end, NULL, NULL, NULL);
+
+               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 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);
+
+           while (end_len > 0) {
+               *new_end = *end;
+               new_end++;
+               end++;
+               end_len--;
+           }
+
+           if (*end != '\0' && mbstrchr(brackets, end) != NULL) {
+               end_len = parse_mbchar(end, NULL, NULL, NULL);
+
+               while (end_len > 0) {
+                   *new_end = *end;
+                   new_end++;
+                   end++;
+                   end_len--;
+               }
+           }
+
+           if (*end != '\0' && is_blank_mbchar(end)) {
+               end_len = parse_mbchar(end, NULL, NULL, NULL);
+
+               *new_end = ' ';
+               new_end++;
+               end += end_len;
+           }
+
+           if (*end != '\0' && is_blank_mbchar(end)) {
+               end_len = parse_mbchar(end, NULL, NULL, NULL);
+
+               *new_end = ' ';
+               new_end++;
+               end += end_len;
+           }
+
+           while (*end != '\0' && is_blank_mbchar(end)) {
+               end_len = parse_mbchar(end, NULL, NULL, NULL);
+
+               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--;
+           }
+       }
+    }
+
+    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++;
+    }
+
+    if (shift > 0) {
+       openfile->totsize -= shift;
+       null_at(&new_paragraph_data, new_end - new_paragraph_data);
+       free(paragraph->data);
+       paragraph->data = new_paragraph_data;
+
+#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);
+}
+
+/* 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 (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;
+
+    /* Compute quote depth level. */
+    while (strncmp(line + qdepth, quotestr, quotelen) == 0)
+       qdepth += quotelen;
+    return qdepth;
+#endif /* !HAVE_REGEX_H */
+}
+
+/* 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));
+
+    return (a_quote == quote_length(b_line) &&
+       strncmp(a_line, b_line, a_quote) == 0);
+}
+
+/* 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);
+}
+
+/* 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;
+
+    /* Case 1). */
+    if (foo->prev == NULL)
+       return TRUE;
+
+    quote_len = quote_length(foo->data);
+    indent_len = indent_length(foo->data + quote_len);
+
+    /* Not part of a paragraph. */
+    if (foo->data[quote_len + indent_len] == '\0')
+       return FALSE;
+
+    /* Case 3). */
+    if (!quotes_match(foo->data, quote_len, foo->prev->data))
+       return TRUE;
+
+    temp_id_len = indent_length(foo->prev->data + quote_len);
+
+    /* 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;
+
+    return FALSE;
+}
+
+/* Is foo inside a paragraph? */
+bool inpar(const filestruct *const foo)
+{
+    size_t quote_len;
+
+    if (foo == NULL)
+       return FALSE;
+
+    quote_len = quote_length(foo->data);
+
+    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
+    bool old_mark_set = openfile->mark_set;
+    ssize_t mb_lineno_save = 0;
+    size_t mark_begin_x_save = 0;
+
+    if (old_mark_set) {
+       mb_lineno_save = openfile->mark_begin->lineno;
+       mark_begin_x_save = openfile->mark_begin_x;
+    }
+#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;
+
+    /* Move the paragraph from the current buffer's filestruct to the
+     * justify buffer. */
+    move_to_filestruct(&jusbuffer, &jusbottom, top, 0, bot, 0);
+
+    /* 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 && top->lineno == mb_lineno_save) {
+           openfile->mark_begin = top;
+           openfile->mark_begin_x = mark_begin_x_save;
+       }
+#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;
+
+    set_modified();
+
+    return first_line;
+}
+
+/* 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. */
+
+#ifdef HAVE_REGEX_H
+    if (quoterc != 0) {
+       statusbar(_("Bad quote string %s: %s"), quotestr, quoteerr);
+       return FALSE;
+    }
+#endif
+
+    assert(openfile->current != NULL);
+
+    /* Move back to the beginning of the current line. */
+    openfile->current_x = 0;
+    openfile->placewewant = 0;
+
+    /* 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;
+
+       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);
+
+    /* 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;
+
+    /* Save the values of quote_len and par_len. */
+    assert(quote != NULL && par != NULL);
+
+    *quote = quote_len;
+    *par = par_len;
+
+    return TRUE;
+}
+
+/* If full_justify is TRUE, justify the entire file.  Otherwise, justify
+ * the current paragraph. */
+void do_justify(bool full_justify)
+{
+    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. */
+
+    /* 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;
+
+    /* If we're justifying the entire file, start at the beginning. */
+    if (full_justify)
+       openfile->current = openfile->fileage;
+
+    last_par_line = openfile->current;
+
+    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.) */
+
+       /* 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 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);
+
+       /* Initialize indent_string to a blank string. */
+       indent_string = mallocstrcpy(NULL, "");
+
+       /* 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;
+
+           for (i = 0; i < par_len; i++) {
+               indent_len = quote_len +
+                       indent_length(indent_line->data + quote_len);
+
+               if (indent_len != strlen(indent_string)) {
+                   indent_string = mallocstrncpy(indent_string,
+                       indent_line->data, indent_len + 1);
+                   indent_string[indent_len] = '\0';
+
+                   if (past_first_line)
+                       break;
+               }
+
+               if (indent_line == openfile->current)
+                   past_first_line = TRUE;
+
+               indent_line = indent_line->next;
+           }
+       }
+
+       /* 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);
+
+           indent_len = quote_len +
+               indent_length(openfile->current->next->data +
+               quote_len);
+
+           next_line_len -= indent_len;
+           openfile->totsize -= indent_len;
+
+           /* 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++;
+           }
+
+           openfile->current->data =
+               charealloc(openfile->current->data, line_len +
+               next_line_len + 1);
+           strcat(openfile->current->data, next_line->data +
+               indent_len);
+
+           /* Don't destroy edittop! */
+           if (openfile->edittop == next_line)
+               openfile->edittop = openfile->current;
+
+#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
+
+           unlink_node(next_line);
+           delete_node(next_line);
+
+           /* If we've removed the next line, we need to go through
+            * this line again. */
+           i--;
+
+           par_len--;
+           openfile->totlines--;
+           openfile->totsize--;
+       }
+
+       /* 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));
+
+       while (par_len > 0 &&
+               strlenpt(openfile->current->data) > fill) {
+           size_t line_len = strlen(openfile->current->data);
+
+           indent_len = strlen(indent_string);
+
+           /* 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);
+
+           /* 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;
+
+           /* Move forward to the character after the indentation and
+            * just after the space. */
+           break_pos += indent_len + 1;
+
+           assert(break_pos <= line_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
+               && !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;
+
+#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
+
+           /* 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);
+
+       /* Go to the next line, the line after the last line of the
+        * paragraph. */
+       openfile->current_y++;
+       openfile->current = openfile->current->next;
+
+       /* 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;
+    }
+
+    /* 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);
+    }
+
+    edit_refresh();
+
+    statusbar(_("Can now UnJustify!"));
+
+    /* 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);
+
+    /* Display the shortcut list with UnJustify. */
+    shortcut_init(TRUE);
+    display_main_list();
+
+    /* 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);
+
+    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;
+
+       /* Splice the justify buffer back into the file, but only if we
+        * actually justified something. */
+       if (first_par_line != NULL) {
+           filestruct *top_save;
+
+           /* 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);
+
+           /* 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;
+
+           top_save = openfile->fileage;
+
+           /* 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);
+
+            /* Renumber starting with the beginning line of the old
+             * partition. */
+           renumber(top_save);
+
+           /* 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;
+
+           /* Clear the justify buffer. */
+           jusbuffer = NULL;
+
+           if (!openfile->modified)
+               titlebar(NULL);
+           edit_refresh();
+       }
+    } else {
+       unget_kbinput(kbinput, meta_key, func_key);
+
+       /* Blow away the text in the justify buffer. */
+       free_filestruct(jusbuffer);
+       jusbuffer = NULL;
+    }
+
+    blank_statusbar();
+
+    /* Display the shortcut list with UnCut. */
+    shortcut_init(FALSE);
+    display_main_list();
+}
+
+void do_justify_void(void)
+{
+    do_justify(FALSE);
+}
+
+void do_full_justify(void)
+{
+    do_justify(TRUE);
+}
+#endif /* !DISABLE_JUSTIFY */
+
+#ifndef NANO_SMALL
+void do_word_count(void)
+{
+    size_t words = 0, current_x_save = openfile->current_x;
+    size_t pww_save = openfile->placewewant;
+    filestruct *current_save = openfile->current;
+    bool old_mark_set = openfile->mark_set;
+    bool added_magicline = FALSE;
+       /* Whether we added a magicline after filebot. */
+    filestruct *top, *bot;
+    size_t top_x, bot_x;
+
+    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 need a magicline added while we're counting words, add
+        * the magicline if necessary, and turn the mark off. */
+       mark_order((const filestruct **)&top, &top_x,
+           (const filestruct **)&bot, &bot_x, NULL);
+       filepart = partition_filestruct(top, top_x, bot, bot_x);
+       if ((added_magicline = (openfile->filebot->data[0] != '\0')))
+           new_magicline();
+       openfile->mark_set = FALSE;
+    }
+
+    /* Start at the top of the file. */
+    openfile->current = openfile->fileage;
+    openfile->current_x = 0;
+    openfile->placewewant = 0;
+
+    /* Keep moving to the next word (counting punctuation characters as
+     * part of a word so that we match the output of "wc -w"), without
+     * updating the screen, until we reach the end of the file,
+     * incrementing the total word count whenever we're on a word just
+     * before moving. */
+    while (openfile->current != openfile->filebot ||
+       openfile->current_x != 0) {
+       if (do_next_word(TRUE, FALSE))
+           words++;
+    }
+
+    if (old_mark_set) {
+       /* If the mark was on and we added a magicline, remove it
+        * now. */
+       if (added_magicline)
+           remove_magicline();
+
+       /* Unpartition the filestruct so that it contains all the text
+        * again, and turn the mark back on. */
+       unpartition_filestruct(&filepart);
+       openfile->mark_set = TRUE;
+    }
+
+    /* Restore where we were. */
+    openfile->current = current_save;
+    openfile->current_x = current_x_save;
+    openfile->placewewant = pww_save;
+
+    /* Display the total word count on the statusbar. */
+    statusbar("%s: %lu", old_mark_set ? _("Word Count in Selection") :
+       _("Word Count"), (unsigned long)words);
+}
+#endif /* !NANO_SMALL */
index 026c1c4f6eef7eb01bf7671818e14052f44f0958..3313b53279700dcd8ec0bd64fa44865daf12fac0 100644 (file)
 #include <config.h>
 #endif
 
-#include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
 #include <unistd.h>
 #include <pwd.h>
 #include <ctype.h>
 #include <errno.h>
-#include <assert.h>
 #include "proto.h"
 
-#ifdef HAVE_REGEX_H
-#ifdef BROKEN_REGEXEC
-/* Work around a potential segfault in glibc 2.2.3's regexec(). */
-int safe_regexec(const regex_t *preg, const char *string, size_t nmatch,
-       regmatch_t pmatch[], int eflags)
-{
-    if (string != NULL && *string != '\0')
-       return regexec(preg, string, nmatch, pmatch, eflags);
-
-    return REG_NOMATCH;
-}
-#endif
-
-int regexp_bol_or_eol(const regex_t *preg, const char *string)
-{
-    return (regexec(preg, string, 0, NULL, 0) == 0 &&
-       regexec(preg, string, 0, NULL, REG_NOTBOL | REG_NOTEOL) ==
-       REG_NOMATCH);
-}
-#endif /* HAVE_REGEX_H */
-
 int digits(size_t n)
 {
     int i = 1;
@@ -240,6 +217,52 @@ ssize_t ngetdelim(char **lineptr, size_t *n, int delim, FILE *stream)
 #endif
 #endif /* !NANO_SMALL && ENABLE_NANORC */
 
+#ifdef HAVE_REGEX_H
+#ifdef BROKEN_REGEXEC
+/* Work around a potential segfault in glibc 2.2.3's regexec(). */
+int safe_regexec(const regex_t *preg, const char *string, size_t nmatch,
+       regmatch_t pmatch[], int eflags)
+{
+    if (string != NULL && *string != '\0')
+       return regexec(preg, string, nmatch, pmatch, eflags);
+
+    return REG_NOMATCH;
+}
+#endif
+
+int regexp_bol_or_eol(const regex_t *preg, const char *string)
+{
+    return (regexec(preg, string, 0, NULL, 0) == 0 &&
+       regexec(preg, string, 0, NULL, REG_NOTBOL | REG_NOTEOL) ==
+       REG_NOMATCH);
+}
+#endif /* HAVE_REGEX_H */
+
+/* Is the word starting at position pos in buf a whole word? */
+bool is_whole_word(size_t pos, const char *buf, const char *word)
+{
+    char *p = charalloc(mb_cur_max()), *r = charalloc(mb_cur_max());
+    size_t word_end = pos + strlen(word);
+    bool retval;
+
+    assert(buf != NULL && pos <= strlen(buf) && word != NULL);
+
+    parse_mbchar(buf + move_mbleft(buf, pos), p, NULL, NULL);
+    parse_mbchar(buf + word_end, r, NULL, NULL);
+
+    /* If we're at the beginning of the line or the character before the
+     * word isn't a non-punctuation "word" character, and if we're at
+     * the end of the line or the character after the word isn't a
+     * non-punctuation "word" character, we have a whole word. */
+    retval = (pos == 0 || !is_word_mbchar(p, FALSE)) &&
+       (word_end == strlen(buf) || !is_word_mbchar(r, FALSE));
+
+    free(p);
+    free(r);
+
+    return retval;
+}
+
 /* If we are searching backwards, we will find the last match that
  * starts no later than start.  Otherwise we find the first match
  * starting no earlier than start.  If we are doing a regexp search, we
index fe690a89e7f8d58bd0522184c68d7e5002765fee..6e93749cd4ff74f1cc17d0144de0e3bf12975204 100644 (file)
 
 #include <stdarg.h>
 #include <string.h>
-#include <stdlib.h>
 #include <unistd.h>
 #include <ctype.h>
-#include <assert.h>
 #include "proto.h"
 
 static int *key_buffer = NULL;