]> granicus.if.org Git - vim/commitdiff
updated for version 7.0099
authorBram Moolenaar <Bram@vim.org>
Fri, 1 Jul 2005 22:33:52 +0000 (22:33 +0000)
committerBram Moolenaar <Bram@vim.org>
Fri, 1 Jul 2005 22:33:52 +0000 (22:33 +0000)
runtime/optwin.vim
src/Make_mvc.mak
src/normal.c
src/spell.c

index 864426bffd7e1d2f41549e300bec416546d66e39..61535c70fc9bf0117d376051658bb4ba98174b37 100644 (file)
@@ -1,7 +1,7 @@
 " These commands create the option window.
 "
 " Maintainer:  Bram Moolenaar <Bram@vim.org>
-" Last Change: 2005 Mar 07
+" Last Change: 2005 Jul 01
 
 " If there already is an option window, jump to that one.
 if bufwinnr("option-window") > 0
@@ -368,7 +368,7 @@ if has("linebreak")
 endif
 
 
-call <SID>Header("syntax and highlighting")
+call <SID>Header("syntax, highlighting and spelling")
 call append("$", "background\t\"dark\" or \"light\"; the background color brightness")
 call <SID>OptionG("bg", &bg)
 if has("autocmd")
@@ -385,6 +385,19 @@ call append("$", "highlight\twhich highlighting to use for various occasions")
 call <SID>OptionG("hl", &hl)
 call append("$", "hlsearch\thighlight all matches for the last used search pattern")
 call <SID>BinOptionG("hls", &hls)
+if has("syntax")
+  call append("$", "spell\thighlight spelling mistakes")
+  call append("$", "\t(local to window)")
+  call <SID>BinOptionL("spell")
+  call append("$", "spelllang\tlist of accepted languages")
+  call append("$", "\t(local to buffer)")
+  call <SID>OptionL("spl")
+  call append("$", "spellfile\tfile that \"zg\" adds good words to")
+  call append("$", "\t(local to buffer)")
+  call <SID>OptionL("spf")
+  call append("$", "spellsuggest\tmethods used to suggest corrections")
+  call <SID>OptionG("sps", &sps)
+endif
 
 
 call <SID>Header("multiple windows")
@@ -614,6 +627,8 @@ call append("$", "report\tthreshold for reporting number of changed lines")
 call append("$", " \tset report=" . &report)
 call append("$", "verbose\tthe higher the more messages are given")
 call append("$", " \tset vbs=" . &vbs)
+call append("$", "verbosefile\tfile to write messages in")
+call <SID>OptionG("vfile", &vfile)
 call append("$", "more\tpause listings when the screen is full")
 call <SID>BinOptionG("more", &more)
 if has("dialog_con") || has("dialog_gui")
index eb5aa7e9e71203af3916af4fc3b161fe5bfb9647..757aa73800d158976763f874e57d6593891b1a1f 100644 (file)
 # It builds on Windows 95 and all four NT platforms: i386, Alpha, MIPS, and
 # PowerPC.  The NT/i386 binary and the Windows 95 binary are identical.
 #
+# To build using Borland C++, use Make_bc3.mak or Make_bc5.mak.
+#
 # This makefile can build the console, GUI, OLE-enable, Perl-enabled and
 # Python-enabled versions of vim for Win32 platforms.
 #
-# When compiling different versions, do "nmake clean" first!
-#
 # The basic command line to build vim is:
+#
 #      nmake -f Make_mvc.mak
+#
 # This will build the console version of vim with no additional interfaces.
-# To add interfaces, define any of the following:
+# To add features, define any of the following:
+#
+#      !!!!  After changing features do "nmake clean" first  !!!!
+#
+#       Feature Set: FEATURES=[TINY, SMALL, NORMAL, BIG, HUGE] (default is BIG)
+#
 #      GUI interface: GUI=yes (default is no)
+#
 #      OLE interface: OLE=yes (usually with GUI=yes)
+#
 #      Multibyte support: MBYTE=yes
+#
 #      IME support: IME=yes    (requires GUI=yes)
 #        DYNAMIC_IME=[yes or no]  (to load the imm32.dll dynamically, default
 #        is yes)
 #      Global IME support: GIME=yes (requires GUI=yes)
+#
 #      MzScheme interface:
 #        MZSCHEME=[Path to MzScheme directory]
 #        DYNAMIC_MZSCHEME=yes (to load the MzScheme DLLs dynamically)
 #        MZSCHEME_VER=[version, 205_000, ...]
+#
 #      Perl interface:
 #        PERL=[Path to Perl directory]
 #        DYNAMIC_PERL=yes (to load the Perl DLL dynamically)
-#        PERL_VER=[Perl version, in the form 55 (5.005), 56 (5.6.x), etc] (default is 56)
+#        PERL_VER=[Perl version, in the form 55 (5.005), 56 (5.6.x), etc]
+#        (default is 56)
+#
 #      Python interface:
 #        PYTHON=[Path to Python directory]
 #        DYNAMIC_PYTHON=yes (to load the Python DLL dynamically)
 #        PYTHON_VER=[Python version, eg 15, 20]  (default is 22)
+#
 #      Ruby interface:
 #        RUBY=[Path to Ruby directory]
 #        DYNAMIC_RUBY=yes (to load the Ruby DLL dynamically)
 #        RUBY_VER=[Ruby version, eg 16, 17] (default is 18)
 #        RUBY_VER_LONG=[Ruby version, eg 1.6, 1.7] (default is 1.8)
 #          You must set RUBY_VER_LONG when change RUBY_VER.
+#
 #      Tcl interface:
 #        TCL=[Path to Tcl directory]
 #        DYNAMIC_TCL=yes (to load the Tcl DLL dynamically)
 #        TCL_VER=[Tcl version, e.g. 80, 83]  (default is 83)
 #        TCL_VER_LONG=[Tcl version, eg 8.3] (default is 8.3)
 #          You must set TCL_VER_LONG when you set TCL_VER.
-#      Debug version: DEBUG=yes
-#      Mapfile: MAP=[no, yes or lines] (default is yes)
-#        no:    Don't write a mapfile.
-#        yes:   Write a normal mapfile.
-#        lines: Write a mapfile with line numbers (only for VC6 and later)
+#
 #      SNiFF+ interface: SNIFF=yes
+#
 #      Cscope support: CSCOPE=yes
+#
 #      Iconv library support (always dynamically loaded):
 #        ICONV=[yes or no]  (default is yes)
+#
 #      Intl library support (always dynamically loaded):
 #        GETTEXT=[yes or no]  (default is yes)
 #      See http://sourceforge.net/projects/gettext/
+#
 #       PostScript printing: POSTSCRIPT=yes (default is no)
-#       Feature Set: FEATURES=[TINY, SMALL, NORMAL, BIG, HUGE] (default is BIG)
-#       Version Support: WINVER=[0x0400, 0x0500] (default is 0x0400)
-#       Processor Version: CPUNR=[i386, i486, i586, i686, pentium4] (default is i386)
-#       Optimization: OPTIMIZE=[SPACE, SPEED, MAXSPEED] (default is MAXSPEED)
+#
 #       Netbeans Support: NETBEANS=[yes or no] (default is yes if GUI is yes)
-#       Netbeans Debugging Support: NBDEBUG=[yes or no] (default is no)
+#
 #       XPM Image Support: XPM=[path to XPM directory]
 #
+#       Optimization: OPTIMIZE=[SPACE, SPEED, MAXSPEED] (default is MAXSPEED)
+#
+#       Processor Version: CPUNR=[i386, i486, i586, i686, pentium4] (default is
+#       i386)
+#
+#       Version Support: WINVER=[0x0400, 0x0500] (default is 0x0400)
+#
+#      Debug version: DEBUG=yes
+#      Mapfile: MAP=[no, yes or lines] (default is yes)
+#        no:    Don't write a mapfile.
+#        yes:   Write a normal mapfile.
+#        lines: Write a mapfile with line numbers (only for VC6 and later)
+#
+#       Netbeans Debugging Support: NBDEBUG=[yes or no] (should be no, yes
+#       doesn't work)
+#
 # You can combine any of these interfaces
 #
 # Example: To build the non-debug, GUI version with Perl interface:
 #      nmake -f Make_mvc.mak GUI=yes PERL=C:\Perl
 #
-# To build using Borland C++, use Make_bc3.mak or Make_bc5.mak.
-#
 # DEBUG with Make_mvc.mak and Make_dvc.mak:
 #      This makefile gives a fineness of control which is not supported in
 #      Visual C++ configuration files.  Therefore, debugging requires a bit of
index 71cc3c783b2662a20da05000195c69c918a9e8dd..05dd1b2d35fc3c5502e543bdabba0cd11b55cb4e 100644 (file)
@@ -4665,6 +4665,8 @@ dozet:
 #ifdef FEAT_SYN_HL
     case 'g':  /* "zg": add good word to word list */
     case 'w':  /* "zw": add wrong word to word list */
+    case 'G':  /* "zG": add good word to temp word list */
+    case 'W':  /* "zW": add wrong word to temp word list */
                {
                    char_u  *ptr = NULL;
                    int     len;
@@ -4679,7 +4681,8 @@ dozet:
                    if (ptr == NULL && (len = find_ident_under_cursor(&ptr,
                                                            FIND_IDENT)) == 0)
                        return;
-                   spell_add_word(ptr, len, nchar == 'w');
+                   spell_add_word(ptr, len, nchar == 'w' || nchar == 'W',
+                                               nchar == 'G' || nchar == 'W');
                }
                break;
 
index 8eaa46e3a5528f63e19b2d9d59fb4da170acb121..853949b107217c9dc38e167b9e3e25cc42100721 100644 (file)
@@ -377,6 +377,9 @@ typedef struct langp_S
 #define VIMSPELLMAGIC "VIMspell08"  /* string at start of Vim spell file */
 #define VIMSPELLMAGICL 10
 
+/* file used for "zG" and "zW" */
+static char_u  *temp_wordlist = NULL;
+
 /*
  * Information used when looking for suggestions.
  */
@@ -574,10 +577,6 @@ typedef struct trystate_S
 #define FIND_KEEPWORD  1       /* find keep-case word */
 #define FIND_PREFIX    2       /* find word after prefix */
 
-/* values for read_cnt_string() */
-#define ERR_NOMEM      -1
-#define ERR_TRUNC      -2
-
 static slang_T *slang_alloc __ARGS((char_u *lang));
 static void slang_free __ARGS((slang_T *lp));
 static void slang_clear __ARGS((slang_T *lp));
@@ -592,6 +591,8 @@ static char_u *spell_enc __ARGS((void));
 static void spell_load_cb __ARGS((char_u *fname, void *cookie));
 static slang_T *spell_load_file __ARGS((char_u *fname, char_u *lang, slang_T *old_lp, int silent));
 static char_u *read_cnt_string __ARGS((FILE *fd, int cnt_bytes, int *errp));
+static int set_sofo __ARGS((slang_T *lp, char_u *from, char_u *to));
+static void set_sal_first __ARGS((slang_T *lp));
 #ifdef FEAT_MBYTE
 static int *mb_str2wide __ARGS((char_u *s));
 #endif
@@ -601,7 +602,7 @@ static void use_midword __ARGS((slang_T *lp, buf_T *buf));
 static int find_region __ARGS((char_u *rp, char_u *region));
 static int captype __ARGS((char_u *word, char_u *end));
 static void spell_reload_one __ARGS((char_u *fname, int added_word));
-static int set_spell_charflags __ARGS((char_u *flags, int cnt, char_u *upp));
+static int set_spell_charflags __ARGS((char_u *flags, char_u *upp));
 static int set_spell_chartab __ARGS((char_u *fol, char_u *low, char_u *upp));
 static void write_spell_chartab __ARGS((FILE *fd));
 static int spell_casefold __ARGS((char_u *p, int len, char_u *buf, int buflen));
@@ -683,6 +684,7 @@ static linenr_T apply_prefixes __ARGS((slang_T *slang, char_u *word, int round,
 
 
 static char *e_format = N_("E759: Format error in spell file");
+static char *e_spell_trunc = N_("E758: Truncated spell file");
 
 /*
  * Main spell-checking function.
@@ -1690,9 +1692,7 @@ spell_load_file(fname, lang, old_lp, silent)
     garray_T   *gap;
     fromto_T   *ftp;
     salitem_T  *smp;
-    int                rr;
     short      *first;
-    salfirst_T *sfirst;
     idx_T      idx;
     int                c = 0;
 
@@ -1756,7 +1756,7 @@ spell_load_file(fname, lang, old_lp, silent)
     if (cnt < 0)
     {
 truncerr:
-       EMSG(_("E758: Truncated spell file"));
+       EMSG(_(e_spell_trunc));
        goto endFAIL;
     }
     if (cnt > 8)
@@ -1772,48 +1772,33 @@ formerr:
     }
     lp->sl_regions[cnt * 2] = NUL;
 
-    cnt = getc(fd);                                    /* <charflagslen> */
-    if (cnt > 0)
-    {
-       p = alloc((unsigned)cnt);
-       if (p == NULL)
-           goto endFAIL;
-       for (i = 0; i < cnt; ++i)
-           p[i] = getc(fd);                            /* <charflags> */
-
-       /* <fcharslen> <fchars> */
-       fol = read_cnt_string(fd, 2, &ccnt);
-       if (ccnt != 0)
-       {
-           vim_free(p);
-           if (ccnt == ERR_NOMEM)
-               goto endFAIL;
-           if (ccnt == ERR_TRUNC)
-               goto formerr;
-       }
+    /* <charflagslen> <charflags> */
+    p = read_cnt_string(fd, 1, &cnt);
+    if (cnt == FAIL)
+       goto endFAIL;
 
-       /* Set the word-char flags and fill SPELL_ISUPPER() table. */
-       i = set_spell_charflags(p, cnt, fol);
-       vim_free(p);
-       vim_free(fol);
-#if 0  /* tolerate the differences */
-       if (i == FAIL)
-           goto formerr;
-#endif
-    }
-    else
+    /* <fcharslen> <fchars> */
+    fol = read_cnt_string(fd, 2, &cnt);
+    if (cnt == FAIL)
     {
-       /* When <charflagslen> is zero then <fcharlen> must also be zero. */
-       cnt = (getc(fd) << 8) + getc(fd);
-       if (cnt != 0)
-           goto formerr;
+       vim_free(p);
+       goto endFAIL;
     }
 
+    /* Set the word-char flags and fill SPELL_ISUPPER() table. */
+    if (p != NULL && fol != NULL)
+       i = set_spell_charflags(p, fol);
+
+    vim_free(p);
+    vim_free(fol);
+
+    /* When <charflagslen> is zero then <fcharlen> must also be zero. */
+    if ((p == NULL) != (fol == NULL))
+       goto formerr;
+
     /* <midwordlen> <midword> */
     lp->sl_midword = read_cnt_string(fd, 2, &cnt);
-    if (cnt == ERR_TRUNC)
-       goto truncerr;
-    if (cnt == ERR_NOMEM)
+    if (cnt == FAIL)
        goto endFAIL;
 
     /* <prefcondcnt> <prefcond> ... */
@@ -1863,28 +1848,14 @@ formerr:
     for (; gap->ga_len < cnt; ++gap->ga_len)
     {
        ftp = &((fromto_T *)gap->ga_data)[gap->ga_len];
-       for (rr = 1; rr <= 2; ++rr)
+       ftp->ft_from = read_cnt_string(fd, 1, &i);
+       if (i == FAIL)
+           goto endFAIL;
+       ftp->ft_to = read_cnt_string(fd, 1, &i);
+       if (i == FAIL)
        {
-           ccnt = getc(fd);
-           if (ccnt < 0)
-           {
-               if (rr == 2)
-                   vim_free(ftp->ft_from);
-               goto formerr;
-           }
-           if ((p = alloc(ccnt + 1)) == NULL)
-           {
-               if (rr == 2)
-                   vim_free(ftp->ft_from);
-               goto endFAIL;
-           }
-           for (i = 0; i < ccnt; ++i)
-               p[i] = getc(fd);                /* <repfrom> or <repto> */
-           p[i] = NUL;
-           if (rr == 1)
-               ftp->ft_from = p;
-           else
-               ftp->ft_to = p;
+           vim_free(ftp->ft_from);
+           goto endFAIL;
        }
     }
 
@@ -1921,117 +1892,26 @@ formerr:
        if (cnt != 1)
            goto formerr;
 
-       cnt = (getc(fd) << 8) + getc(fd);               /* <salfromlen> */
-       if (cnt < 0)
-           goto formerr;
-       if ((bp = alloc(cnt + 1)) == NULL)
+       /* <salfromlen> <salfrom> */
+       bp = read_cnt_string(fd, 2, &cnt);
+       if (cnt == FAIL)
            goto endFAIL;
-       for (i = 0; i < cnt; ++i)
-           bp[i] = getc(fd);                           /* <salfrom> */
-       bp[i] = NUL;
 
-       ccnt = (getc(fd) << 8) + getc(fd);              /* <saltolen> */
-       if (ccnt < 0)
-       {
-           vim_free(bp);
-           goto formerr;
-       }
-       if ((fol = alloc(ccnt + 1)) == NULL)
+       /* <saltolen> <salto> */
+       fol = read_cnt_string(fd, 2, &cnt);
+       if (cnt == FAIL)
        {
            vim_free(bp);
            goto endFAIL;
        }
-       for (i = 0; i < ccnt; ++i)
-           fol[i] = getc(fd);                          /* <salto> */
-       fol[i] = NUL;
 
-#ifdef FEAT_MBYTE
-       if (has_mbyte)
-       {
-           char_u      *s;
+       /* Store the info in lp->sl_sal and/or lp->sl_sal_first. */
+       i = set_sofo(lp, bp, fol);
 
-           /* Use "sl_sal" as an array with 256 pointers to a list of wide
-            * characters.  The index is the low byte of the character.
-            * The list contains from-to pairs with a terminating NUL.
-            * sl_sal_first[] is used for latin1 "from" characters. */
-           gap = &lp->sl_sal;
-           ga_init2(gap, sizeof(int *), 1);
-           if (ga_grow(gap, 256) == FAIL)
-           {
-sofoFAIL:
-               vim_free(bp);
-               vim_free(fol);
-               goto endFAIL;
-           }
-           vim_memset(gap->ga_data, 0, sizeof(int *) * 256);
-           gap->ga_len = 256;
-
-           /* First count the number of items for each list.  Temporarily use
-            * sl_sal_first[] for this. */
-           for (p = bp, s = fol; *p != NUL && *s != NUL; )
-           {
-               c = mb_ptr2char_adv(&p);
-               mb_ptr_adv(s);
-               if (c >= 256)
-                   ++lp->sl_sal_first[c & 0xff];
-           }
-           if (*p != NUL || *s != NUL)     /* lengths differ */
-               goto sofoerr;
-
-           /* Allocate the lists. */
-           for (i = 0; i < 256; ++i)
-               if (lp->sl_sal_first[i] > 0)
-               {
-                   p = alloc(sizeof(int) * (lp->sl_sal_first[i] * 2 + 1));
-                   if (p == NULL)
-                       goto sofoFAIL;
-                   ((int **)gap->ga_data)[i] = (int *)p;
-                   *(int *)p = 0;
-               }
-
-           /* Put the characters in sl_sal_first[] or a sl_sal list. */
-           vim_memset(lp->sl_sal_first, 0, sizeof(salfirst_T) * 256);
-           for (p = bp, s = fol; *p != NUL && *s != NUL; )
-           {
-               c = mb_ptr2char_adv(&p);
-               i = mb_ptr2char_adv(&s);
-               if (c >= 256)
-               {
-                   int *inp;
-
-                   /* Append the from-to chars at the end of the list with
-                    * the low byte. */
-                   inp = ((int **)gap->ga_data)[c & 0xff];
-                   while (*inp != 0)
-                       ++inp;
-                   *inp++ = c;         /* from char */
-                   *inp++ = i;         /* to char */
-                   *inp++ = NUL;       /* NUL at the end */
-               }
-               else
-                   /* mapping byte to char is done in sl_sal_first[] */
-                   lp->sl_sal_first[c] = i;
-           }
-       }
-       else
-#endif
-       {
-           /* mapping bytes to bytes is done in sl_sal_first[] */
-           if (cnt != ccnt)
-           {
-#ifdef FEAT_MBYTE
-sofoerr:
-#endif
-               vim_free(bp);
-               vim_free(fol);
-               goto formerr;
-           }
-           for (i = 0; i < cnt; ++i)
-               lp->sl_sal_first[bp[i]] = fol[i];
-           lp->sl_sal.ga_len = 1;      /* indicates we have soundfolding */
-       }
        vim_free(bp);
        vim_free(fol);
+       if (i == FAIL)
+           goto formerr;
     }
     else
     {
@@ -2091,22 +1971,13 @@ sofoerr:
                *p++ = getc(fd);                        /* <salfrom> */
            *p++ = NUL;
 
-           ccnt = getc(fd);                    /* <saltolen> */
-           if (ccnt < 0)
+           /* <saltolen> <salto> */
+           smp->sm_to = read_cnt_string(fd, 1, &ccnt);
+           if (ccnt == FAIL)
            {
                vim_free(smp->sm_lead);
                goto formerr;
            }
-           if ((p = alloc(ccnt + 1)) == NULL)
-           {
-               vim_free(smp->sm_lead);
-               goto endFAIL;
-           }
-           smp->sm_to = p;
-
-           for (i = 0; i < ccnt; ++i)
-               *p++ = getc(fd);                        /* <salto> */
-           *p++ = NUL;
 
 #ifdef FEAT_MBYTE
            if (has_mbyte)
@@ -2135,64 +2006,13 @@ sofoerr:
        }
 
        /* Fill the first-index table. */
-       sfirst = lp->sl_sal_first;
-       for (i = 0; i < 256; ++i)
-           sfirst[i] = -1;
-       smp = (salitem_T *)gap->ga_data;
-       for (i = 0; i < gap->ga_len; ++i)
-       {
-#ifdef FEAT_MBYTE
-           if (has_mbyte)
-               /* Use the lowest byte of the first character.  For latin1 it's
-                * the character, for other encodings it should differ for most
-                * characters. */
-               c = *smp[i].sm_lead_w & 0xff;
-           else
-#endif
-               c = *smp[i].sm_lead;
-           if (sfirst[c] == -1)
-           {
-               sfirst[c] = i;
-#ifdef FEAT_MBYTE
-               if (has_mbyte)
-               {
-                   /* Make sure all entries with this byte are following each
-                    * other.  Move the ones that are in the wrong position.  Do
-                    * keep the same ordering! */
-                   while (i + 1 < gap->ga_len
-                                          && (*smp[i + 1].sm_lead_w & 0xff) == c)
-                       /* Skip over entry with same index byte. */
-                       ++i;
-
-                   for (n = 1; i + n < gap->ga_len; ++n)
-                       if ((*smp[i + n].sm_lead_w & 0xff) == c)
-                       {
-                           salitem_T  tsal;
-
-                           /* Move entry with same index byte after the entries
-                            * we already found. */
-                           ++i;
-                           --n;
-                           tsal = smp[i + n];
-                           mch_memmove(smp + i + 1, smp + i,
-                                                          sizeof(salitem_T) * n);
-                           smp[i] = tsal;
-                       }
-               }
-#endif
-           }
-       }
+       set_sal_first(lp);
     }
 
-    cnt = (getc(fd) << 8) + getc(fd);          /* <maplen> */
-    if (cnt < 0)
-       goto formerr;
-    p = alloc(cnt + 1);
-    if (p == NULL)
+    /* <maplen> <mapstr> */
+    p = read_cnt_string(fd, 2, &cnt);
+    if (cnt == FAIL)
        goto endFAIL;
-    for (i = 0; i < cnt; ++i)
-       p[i] = getc(fd);                        /* <mapstr> */
-    p[i] = NUL;
     set_map_str(lp, p);
     vim_free(p);
 
@@ -2270,10 +2090,9 @@ endOK:
 
 /*
  * Read a length field from "fd" in "cnt_bytes" bytes.
- * Allocate memory and read the string into it.
+ * Allocate memory, read the string into it and add a NUL at the end.
  * Returns NULL when the count is zero.
- * Sets "errp" to ERR_TRUNC when reading failed, ERR_NOMEM when out of
- * memory, zero when OK.
+ * Sets "*errp" to FAIL when there is an error, OK otherwise.
  */
     static char_u *
 read_cnt_string(fd, cnt_bytes, errp)
@@ -2290,7 +2109,8 @@ read_cnt_string(fd, cnt_bytes, errp)
        cnt = (cnt << 8) + getc(fd);
     if (cnt < 0)
     {
-       *errp = ERR_TRUNC;
+       EMSG(_(e_spell_trunc));
+       *errp = FAIL;
        return NULL;
     }
 
@@ -2298,10 +2118,10 @@ read_cnt_string(fd, cnt_bytes, errp)
     str = alloc((unsigned)cnt + 1);
     if (str == NULL)
     {
-       *errp = ERR_NOMEM;
+       *errp = FAIL;
        return NULL;
     }
-    *errp = 0;
+    *errp = OK;
 
     /* Read the string.  Doesn't check for truncated file. */
     for (i = 0; i < cnt; ++i)
@@ -2311,6 +2131,162 @@ read_cnt_string(fd, cnt_bytes, errp)
     return str;
 }
 
+/*
+ * Set the SOFOFROM and SOFOTO items in language "lp".
+ * Returns FAIL when there is something wrong.
+ */
+    static int
+set_sofo(lp, from, to)
+    slang_T    *lp;
+    char_u     *from;
+    char_u     *to;
+{
+    int                i;
+
+#ifdef FEAT_MBYTE
+    garray_T   *gap;
+    char_u     *s;
+    char_u     *p;
+    int                c;
+    int                *inp;
+
+    if (has_mbyte)
+    {
+       /* Use "sl_sal" as an array with 256 pointers to a list of wide
+        * characters.  The index is the low byte of the character.
+        * The list contains from-to pairs with a terminating NUL.
+        * sl_sal_first[] is used for latin1 "from" characters. */
+       gap = &lp->sl_sal;
+       ga_init2(gap, sizeof(int *), 1);
+       if (ga_grow(gap, 256) == FAIL)
+           return FAIL;
+       vim_memset(gap->ga_data, 0, sizeof(int *) * 256);
+       gap->ga_len = 256;
+
+       /* First count the number of items for each list.  Temporarily use
+        * sl_sal_first[] for this. */
+       for (p = from, s = to; *p != NUL && *s != NUL; )
+       {
+           c = mb_ptr2char_adv(&p);
+           mb_ptr_adv(s);
+           if (c >= 256)
+               ++lp->sl_sal_first[c & 0xff];
+       }
+       if (*p != NUL || *s != NUL)         /* lengths differ */
+           return FAIL;
+
+       /* Allocate the lists. */
+       for (i = 0; i < 256; ++i)
+           if (lp->sl_sal_first[i] > 0)
+           {
+               p = alloc(sizeof(int) * (lp->sl_sal_first[i] * 2 + 1));
+               if (p == NULL)
+                   return FAIL;
+               ((int **)gap->ga_data)[i] = (int *)p;
+               *(int *)p = 0;
+           }
+
+       /* Put the characters up to 255 in sl_sal_first[] the rest in a sl_sal
+        * list. */
+       vim_memset(lp->sl_sal_first, 0, sizeof(salfirst_T) * 256);
+       for (p = from, s = to; *p != NUL && *s != NUL; )
+       {
+           c = mb_ptr2char_adv(&p);
+           i = mb_ptr2char_adv(&s);
+           if (c >= 256)
+           {
+               /* Append the from-to chars at the end of the list with
+                * the low byte. */
+               inp = ((int **)gap->ga_data)[c & 0xff];
+               while (*inp != 0)
+                   ++inp;
+               *inp++ = c;             /* from char */
+               *inp++ = i;             /* to char */
+               *inp++ = NUL;           /* NUL at the end */
+           }
+           else
+               /* mapping byte to char is done in sl_sal_first[] */
+               lp->sl_sal_first[c] = i;
+       }
+    }
+    else
+#endif
+    {
+       /* mapping bytes to bytes is done in sl_sal_first[] */
+       if (STRLEN(from) != STRLEN(to))
+           return FAIL;
+
+       for (i = 0; to[i] != NUL; ++i)
+           lp->sl_sal_first[from[i]] = to[i];
+       lp->sl_sal.ga_len = 1;          /* indicates we have soundfolding */
+    }
+
+    return OK;
+}
+
+/*
+ * Fill the first-index table for "lp".
+ */
+    static void
+set_sal_first(lp)
+    slang_T    *lp;
+{
+    salfirst_T *sfirst;
+    int                i;
+    salitem_T  *smp;
+    int                c;
+    garray_T   *gap = &lp->sl_sal;
+
+    sfirst = lp->sl_sal_first;
+    for (i = 0; i < 256; ++i)
+       sfirst[i] = -1;
+    smp = (salitem_T *)gap->ga_data;
+    for (i = 0; i < gap->ga_len; ++i)
+    {
+#ifdef FEAT_MBYTE
+       if (has_mbyte)
+           /* Use the lowest byte of the first character.  For latin1 it's
+            * the character, for other encodings it should differ for most
+            * characters. */
+           c = *smp[i].sm_lead_w & 0xff;
+       else
+#endif
+           c = *smp[i].sm_lead;
+       if (sfirst[c] == -1)
+       {
+           sfirst[c] = i;
+#ifdef FEAT_MBYTE
+           if (has_mbyte)
+           {
+               int             n;
+
+               /* Make sure all entries with this byte are following each
+                * other.  Move the ones that are in the wrong position.  Do
+                * keep the same ordering! */
+               while (i + 1 < gap->ga_len
+                                      && (*smp[i + 1].sm_lead_w & 0xff) == c)
+                   /* Skip over entry with same index byte. */
+                   ++i;
+
+               for (n = 1; i + n < gap->ga_len; ++n)
+                   if ((*smp[i + n].sm_lead_w & 0xff) == c)
+                   {
+                       salitem_T  tsal;
+
+                       /* Move entry with same index byte after the entries
+                        * we already found. */
+                       ++i;
+                       --n;
+                       tsal = smp[i + n];
+                       mch_memmove(smp + i + 1, smp + i,
+                                                      sizeof(salitem_T) * n);
+                       smp[i] = tsal;
+                   }
+           }
+#endif
+       }
+    }
+}
 
 #ifdef FEAT_MBYTE
 /*
@@ -2472,6 +2448,7 @@ did_set_spelllang(buf)
     int                load_spf;
     int                len;
     char_u     *p;
+    int                round;
 
     ga_init2(&ga, sizeof(langp_T), 2);
     clear_midword(buf);
@@ -2572,12 +2549,26 @@ did_set_spelllang(buf)
            }
     }
 
-    /*
-     * Make sure the 'spellfile' file is loaded.  It may be in 'runtimepath',
-     * then it's probably loaded above already.  Otherwise load it here.
-     */
-    if (load_spf)
+    /* round 1: load 'spellfile', if needed.
+     * round 2: load temp_wordlist, if possible. */
+    for (round = 1; round <= 2; ++round)
     {
+       if (round == 1)
+       {
+           /* Make sure the 'spellfile' file is loaded.  It may be in
+            * 'runtimepath', then it's probably loaded above already.
+            * Otherwise load it here. */
+           if (!load_spf)
+               continue;
+       }
+       else
+       {
+           if (temp_wordlist == NULL)
+               continue;
+           vim_snprintf((char *)spf_name, sizeof(spf_name), "%s.%s.spl",
+                                                 temp_wordlist, spell_enc());
+       }
+
        /* Check if it was loaded already. */
        for (lp = first_lang; lp != NULL; lp = lp->sl_next)
            if (fullpathcmp(spf_name, lp->sl_fname, FALSE) == FPC_SAME)
@@ -2585,11 +2576,17 @@ did_set_spelllang(buf)
        if (lp == NULL)
        {
            /* Not loaded, try loading it now.  The language name includes the
-            * region name, the region is ignored otherwise. */
-           vim_strncpy(lang, gettail(buf->b_p_spf), MAXWLEN);
-           p = vim_strchr(lang, '.');
-           if (p != NULL)
-               *p = NUL;       /* truncate at ".encoding.add" */
+            * region name, the region is ignored otherwise.  for
+            * temp_wordlist use an arbitrary name. */
+           if (round == 1)
+           {
+               vim_strncpy(lang, gettail(buf->b_p_spf), MAXWLEN);
+               p = vim_strchr(lang, '.');
+               if (p != NULL)
+                   *p = NUL;   /* truncate at ".encoding.add" */
+           }
+           else
+               STRCPY(lang, "temp wordlist");
            lp = spell_load_file(spf_name, lang, NULL, TRUE);
        }
        if (lp != NULL && ga_grow(&ga, 1) == OK)
@@ -2678,7 +2675,7 @@ use_midword(lp, buf)
 /*
  * Find the region "region[2]" in "rp" (points to "sl_regions").
  * Each region is simply stored as the two characters of it's name.
- * Returns the index if found, REGION_ALL if not found.
+ * Returns the index if found (first is 0), REGION_ALL if not found.
  */
     static int
 find_region(rp, region)
@@ -2780,6 +2777,13 @@ spell_free_all()
        slang_free(lp);
     }
 
+    if (temp_wordlist != NULL)
+    {
+       mch_remove(temp_wordlist);
+       vim_free(temp_wordlist);
+       temp_wordlist = NULL;
+    }
+
     init_spell_chartab();
 }
 # endif
@@ -3967,6 +3971,7 @@ spell_read_wordfile(fname, spin)
     char_u     rline[MAXLINELEN];
     char_u     *line;
     char_u     *pc = NULL;
+    char_u     *p;
     int                l;
     int                retval = OK;
     int                did_word = FALSE;
@@ -4035,13 +4040,9 @@ spell_read_wordfile(fname, spin)
            line = rline;
        }
 
-       flags = 0;
-       regionmask = spin->si_region;
-
        if (*line == '/')
        {
            ++line;
-
            if (STRNCMP(line, "encoding=", 9) == 0)
            {
                if (spin->si_conv.vc_type != CONV_NONE)
@@ -4092,50 +4093,49 @@ spell_read_wordfile(fname, spin)
                continue;
            }
 
-           if (*line == '=')
-           {
-               /* keep-case word */
-               flags |= WF_KEEPCAP;
-               ++line;
-           }
+           smsg((char_u *)_("/ line ignored in %s line %d: %s"),
+                                                      fname, lnum, line - 1);
+           continue;
+       }
 
-           if (*line == '!')
-           {
-               /* Bad, bad, wicked word. */
-               flags |= WF_BANNED;
-               ++line;
-           }
-           else if (*line == '?')
-           {
-               /* Rare word. */
-               flags |= WF_RARE;
-               ++line;
-           }
+       flags = 0;
+       regionmask = spin->si_region;
 
-           if (VIM_ISDIGIT(*line))
+       /* Check for flags and region after a slash. */
+       p = vim_strchr(line, '/');
+       if (p != NULL)
+       {
+           *p++ = NUL;
+           while (*p != NUL)
            {
-               /* region number(s) */
-               regionmask = 0;
-               while (VIM_ISDIGIT(*line))
+               if (*p == '=')          /* keep-case word */
+                   flags |= WF_KEEPCAP;
+               else if (*p == '!')     /* Bad, bad, wicked word. */
+                   flags |= WF_BANNED;
+               else if (*p == '?')     /* Rare word. */
+                   flags |= WF_RARE;
+               else if (VIM_ISDIGIT(*p)) /* region number(s) */
                {
-                   l = *line - '0';
+                   if ((flags & WF_REGION) == 0)   /* first one */
+                       regionmask = 0;
+                   flags |= WF_REGION;
+
+                   l = *p - '0';
                    if (l > spin->si_region_count)
                    {
                        smsg((char_u *)_("Invalid region nr in %s line %d: %s"),
-                                                          fname, lnum, line);
+                                                         fname, lnum, p);
                        break;
                    }
                    regionmask |= 1 << (l - 1);
-                   ++line;
                }
-               flags |= WF_REGION;
-           }
-
-           if (flags == 0)
-           {
-               smsg((char_u *)_("/ line ignored in %s line %d: %s"),
-                                                          fname, lnum, line);
-               continue;
+               else
+               {
+                   smsg((char_u *)_("Unrecognized flags in %s line %d: %s"),
+                                                             fname, lnum, p);
+                   break;
+               }
+               ++p;
            }
        }
 
@@ -5179,80 +5179,99 @@ mkspell(fcount, fnames, ascii, overwrite, added_word)
 ex_spell(eap)
     exarg_T *eap;
 {
-    spell_add_word(eap->arg, STRLEN(eap->arg), eap->cmdidx == CMD_spellwrong);
+    spell_add_word(eap->arg, STRLEN(eap->arg), eap->cmdidx == CMD_spellwrong,
+                                                               eap->forceit);
 }
 
 /*
  * Add "word[len]" to 'spellfile' as a good or bad word.
  */
     void
-spell_add_word(word, len, bad)
+spell_add_word(word, len, bad, temp)
     char_u     *word;
     int                len;
     int                bad;
+    int                temp;       /* "zG" and "zW": use temp word list */
 {
     FILE       *fd;
     buf_T      *buf;
     int                new_spf = FALSE;
     struct stat        st;
+    char_u     *fname;
 
-    /* If 'spellfile' isn't set figure out a good default value. */
-    if (*curbuf->b_p_spf == NUL)
+    if (temp)      /* use temp word list */
     {
-       init_spellfile();
-       new_spf = TRUE;
+       if (temp_wordlist == NULL)
+       {
+           temp_wordlist = vim_tempname('s');
+           if (temp_wordlist == NULL)
+               return;
+       }
+       fname = temp_wordlist;
     }
-
-    if (*curbuf->b_p_spf == NUL)
-       EMSG(_("E764: 'spellfile' is not set"));
     else
     {
+       /* If 'spellfile' isn't set figure out a good default value. */
+       if (*curbuf->b_p_spf == NUL)
+       {
+           init_spellfile();
+           new_spf = TRUE;
+       }
+
+       if (*curbuf->b_p_spf == NUL)
+       {
+           EMSG(_("E764: 'spellfile' is not set"));
+           return;
+       }
+
        /* Check that the user isn't editing the .add file somewhere. */
        buf = buflist_findname_exp(curbuf->b_p_spf);
        if (buf != NULL && buf->b_ml.ml_mfp == NULL)
            buf = NULL;
        if (buf != NULL && bufIsChanged(buf))
+       {
            EMSG(_(e_bufloaded));
-       else
+           return;
+       }
+
+       fname = curbuf->b_p_spf;
+    }
+
+    fd = mch_fopen((char *)fname, "a");
+    if (fd == NULL && new_spf)
+    {
+       /* We just initialized the 'spellfile' option and can't open the file.
+        * We may need to create the "spell" directory first.  We already
+        * checked the runtime directory is writable in init_spellfile(). */
+       STRCPY(NameBuff, fname);
+       *gettail_sep(NameBuff) = NUL;
+       if (mch_stat((char *)NameBuff, &st) < 0)
        {
-           fd = mch_fopen((char *)curbuf->b_p_spf, "a");
-           if (fd == NULL && new_spf)
-           {
-               /* We just initialized the 'spellfile' option and can't open
-                * the file.  We may need to create the "spell" directory
-                * first.  We already checked the runtime directory is
-                * writable in init_spellfile(). */
-               STRCPY(NameBuff, curbuf->b_p_spf);
-               *gettail_sep(NameBuff) = NUL;
-               if (mch_stat((char *)NameBuff, &st) < 0)
-               {
-                   /* The directory doesn't exist.  Try creating it and
-                    * opening the file again. */
-                   vim_mkdir(NameBuff, 0755);
-                   fd = mch_fopen((char *)curbuf->b_p_spf, "a");
-               }
-           }
+           /* The directory doesn't exist.  Try creating it and opening the
+            * file again. */
+           vim_mkdir(NameBuff, 0755);
+           fd = mch_fopen((char *)fname, "a");
+       }
+    }
 
-           if (fd == NULL)
-               EMSG2(_(e_notopen), curbuf->b_p_spf);
-           else
-           {
-               if (bad)
-                   fprintf(fd, "/!%.*s\n", len, word);
-               else
-                   fprintf(fd, "%.*s\n", len, word);
-               fclose(fd);
+    if (fd == NULL)
+       EMSG2(_(e_notopen), fname);
+    else
+    {
+       if (bad)
+           fprintf(fd, "%.*s/!\n", len, word);
+       else
+           fprintf(fd, "%.*s\n", len, word);
+       fclose(fd);
 
-               /* Update the .add.spl file. */
-               mkspell(1, &curbuf->b_p_spf, FALSE, TRUE, TRUE);
+       /* Update the .add.spl file. */
+       mkspell(1, &fname, FALSE, TRUE, TRUE);
 
-               /* If the .add file is edited somewhere, reload it. */
-               if (buf != NULL)
-                   buf_reload(buf);
+       /* If the .add file is edited somewhere, reload it. */
+       if (buf != NULL)
+           buf_reload(buf);
 
-               redraw_all_later(NOT_VALID);
-           }
-       }
+       redraw_all_later(NOT_VALID);
     }
 }
 
@@ -5475,9 +5494,8 @@ set_spell_chartab(fol, low, upp)
  * Set the spell character tables from strings in the .spl file.
  */
     static int
-set_spell_charflags(flags, cnt, upp)
+set_spell_charflags(flags, upp)
     char_u     *flags;
-    int                cnt;
     char_u     *upp;
 {
     /* We build the new tables here first, so that we can compare with the
@@ -5489,7 +5507,7 @@ set_spell_charflags(flags, cnt, upp)
 
     clear_spell_chartab(&new_st);
 
-    for (i = 0; i < cnt; ++i)
+    for (i = 0; flags[i] != NUL; ++i)
     {
        new_st.st_isw[i + 128] = (flags[i] & CF_WORD) != 0;
        new_st.st_isu[i + 128] = (flags[i] & CF_UPPER) != 0;
@@ -9362,6 +9380,9 @@ ex_spelldump(eap)
     int                depth;
     int                n;
     int                flags;
+    char_u     *region_names = NULL;       /* region names being used */
+    int                do_region = TRUE;           /* dump region names and numbers */
+    char_u     *p;
 
     if (no_spell_checking())
        return;
@@ -9371,6 +9392,34 @@ ex_spelldump(eap)
     if (!bufempty() || !buf_valid(buf))
        return;
 
+    /* Find out if we can support regions: All languages must support the same
+     * regions or none at all. */
+    for (lp = LANGP_ENTRY(buf->b_langp, 0); lp->lp_slang != NULL; ++lp)
+    {
+       p = lp->lp_slang->sl_regions;
+       if (p[0] != 0)
+       {
+           if (region_names == NULL)       /* first language with regions */
+               region_names = p;
+           else if (STRCMP(region_names, p) != 0)
+           {
+               do_region = FALSE;          /* region names are different */
+               break;
+           }
+       }
+    }
+
+    if (do_region && region_names != NULL)
+    {
+       vim_snprintf((char *)IObuff, IOSIZE, "/regions=%s", region_names);
+       ml_append(lnum++, IObuff, (colnr_T)0, FALSE);
+    }
+    else
+       do_region = FALSE;
+
+    /*
+     * Loop over all files loaded for the entries in 'spelllang'.
+     */
     for (lp = LANGP_ENTRY(buf->b_langp, 0); lp->lp_slang != NULL; ++lp)
     {
        slang = lp->lp_slang;
@@ -9420,11 +9469,14 @@ ex_spelldump(eap)
                         * Only use the word when the region matches. */
                        flags = (int)idxs[n];
                        if ((round == 2 || (flags & WF_KEEPCAP) == 0)
-                               && ((flags & WF_REGION) == 0
-                                       || (((unsigned)flags >> 8)
+                               && (do_region
+                                   || (flags & WF_REGION) == 0
+                                   || (((unsigned)flags >> 8)
                                                       & lp->lp_region) != 0))
                        {
                            word[depth] = NUL;
+                           if (!do_region)
+                               flags &= ~WF_REGION;
 
                            /* Dump the basic word if there is no prefix or
                             * when it's the first one. */
@@ -9470,7 +9522,8 @@ dump_word(word, round, flags, lnum)
     int                keepcap = FALSE;
     char_u     *p;
     char_u     cword[MAXWLEN];
-    char_u     badword[MAXWLEN + 3];
+    char_u     badword[MAXWLEN + 10];
+    int                i;
 
     if (round == 1 && (flags & WF_CAPMASK) != 0)
     {
@@ -9485,18 +9538,21 @@ dump_word(word, round, flags, lnum)
            keepcap = TRUE;
     }
 
-    /* Bad word is preceded by "/!" and some other
-     * flags. */
-    if ((flags & (WF_BANNED | WF_RARE)) || keepcap)
+    /* Add flags and regions after a slash. */
+    if ((flags & (WF_BANNED | WF_RARE | WF_REGION)) || keepcap)
     {
-       STRCPY(badword, "/");
+       STRCPY(badword, p);
+       STRCAT(badword, "/");
        if (keepcap)
            STRCAT(badword, "=");
        if (flags & WF_BANNED)
            STRCAT(badword, "!");
        else if (flags & WF_RARE)
            STRCAT(badword, "?");
-       STRCAT(badword, p);
+       if (flags & WF_REGION)
+           for (i = 0; i < 7; ++i)
+               if (flags & (0x100 << i))
+                   sprintf((char *)badword + STRLEN(badword), "%d", i + 1);
        p = badword;
     }