changeset 381:997a094e44d2

updated for version 7.0099
author vimboss
date Fri, 01 Jul 2005 22:33:52 +0000
parents da6e29ecd0fc
children ceab297e8af5
files runtime/optwin.vim src/Make_mvc.mak src/normal.c src/spell.c
diffstat 4 files changed, 487 insertions(+), 386 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/optwin.vim
+++ b/runtime/optwin.vim
@@ -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 highl
 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 
 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")
--- a/src/Make_mvc.mak
+++ b/src/Make_mvc.mak
@@ -3,74 +3,101 @@
 # 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:
 #
-# 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.
+#
+#	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)
+#
+#       Netbeans Support: NETBEANS=[yes or no] (default is yes if GUI is yes)
+#
+#       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)
-#	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]
+#
+#       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
--- a/src/normal.c
+++ b/src/normal.c
@@ -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;
 
--- a/src/spell.c
+++ b/src/spell.c
@@ -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 
 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((s
 
 
 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, sil
     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, sil
     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;
-	}
-
-	/* Set the word-char flags and fill SPELL_ISUPPER() table. */
-	i = set_spell_charflags(p, cnt, fol);
+    /* <charflagslen> <charflags> */
+    p = read_cnt_string(fd, 1, &cnt);
+    if (cnt == FAIL)
+	goto endFAIL;
+
+    /* <fcharslen> <fchars> */
+    fol = read_cnt_string(fd, 2, &cnt);
+    if (cnt == FAIL)
+    {
 	vim_free(p);
-	vim_free(fol);
-#if 0	/* tolerate the differences */
-	if (i == FAIL)
-	    goto formerr;
-#endif
-    }
-    else
-    {
-	/* When <charflagslen> is zero then <fcharlen> must also be zero. */
-	cnt = (getc(fd) << 8) + getc(fd);
-	if (cnt != 0)
-	    goto formerr;
-    }
+	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)
-	{
-	    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;
+	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)
+	{
+	    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;
-
-	    /* 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 */
-	}
+
+	/* Store the info in lp->sl_sal and/or lp->sl_sal_first. */
+	i = set_sofo(lp, bp, fol);
+
 	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
-	    }
-	}
-    }
-
-    cnt = (getc(fd) << 8) + getc(fd);		/* <maplen> */
-    if (cnt < 0)
-	goto formerr;
-    p = alloc(cnt + 1);
-    if (p == NULL)
+	set_sal_first(lp);
+    }
+
+    /* <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;
-	    }
-
-	    if (*line == '!')
-	    {
-		/* Bad, bad, wicked word. */
-		flags |= WF_BANNED;
-		++line;
-	    }
-	    else if (*line == '?')
-	    {
-		/* Rare word. */
-		flags |= WF_RARE;
-		++line;
-	    }
-
-	    if (VIM_ISDIGIT(*line))
-	    {
-		/* region number(s) */
-		regionmask = 0;
-		while (VIM_ISDIGIT(*line))
+	    smsg((char_u *)_("/ line ignored in %s line %d: %s"),
+						       fname, lnum, line - 1);
+	    continue;
+	}
+
+	flags = 0;
+	regionmask = spin->si_region;
+
+	/* Check for flags and region after a slash. */
+	p = vim_strchr(line, '/');
+	if (p != NULL)
+	{
+	    *p++ = NUL;
+	    while (*p != NUL)
+	    {
+		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
 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;
-
-    /* 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"));
+    char_u	*fname;
+
+    if (temp)	    /* use temp word list */
+    {
+	if (temp_wordlist == NULL)
+	{
+	    temp_wordlist = vim_tempname('s');
+	    if (temp_wordlist == NULL)
+		return;
+	}
+	fname = temp_wordlist;
+    }
     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));
+	    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)
+	{
+	    /* 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), fname);
+    else
+    {
+	if (bad)
+	    fprintf(fd, "%.*s/!\n", len, word);
 	else
-	{
-	    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");
-		}
-	    }
-
-	    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);
-
-		/* Update the .add.spl file. */
-		mkspell(1, &curbuf->b_p_spf, FALSE, TRUE, TRUE);
-
-		/* If the .add file is edited somewhere, reload it. */
-		if (buf != NULL)
-		    buf_reload(buf);
-
-		redraw_all_later(NOT_VALID);
-	    }
-	}
+	    fprintf(fd, "%.*s\n", len, word);
+	fclose(fd);
+
+	/* 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);
+
+	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)
-    {
-	STRCPY(badword, "/");
+    /* Add flags and regions after a slash. */
+    if ((flags & (WF_BANNED | WF_RARE | WF_REGION)) || keepcap)
+    {
+	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;
     }