updated for version 7.0084
diff --git a/runtime/bugreport.vim b/runtime/bugreport.vim
index a6c36b9..f0c045e 100644
--- a/runtime/bugreport.vim
+++ b/runtime/bugreport.vim
@@ -2,7 +2,7 @@
 :" information about the environment of a possible bug in Vim.
 :"
 :" Maintainer:	Bram Moolenaar <Bram@vim.org>
-:" Last change:	2001 Feb 02
+:" Last change:	2005 Jun 12
 :"
 :" To use inside Vim:
 :"	:so $VIMRUNTIME/bugreport.vim
@@ -49,6 +49,8 @@
 :  call <SID>CheckFile($VIMRUNTIME . "/syntax/synload.vim")
 :  delfun <SID>CheckDir
 :  delfun <SID>CheckFile
+:  echo "--- Scripts sourced ---"
+:  scriptnames
 :endif
 :set all
 :set termcap
diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt
index cceda32..dc3d695 100644
--- a/runtime/doc/develop.txt
+++ b/runtime/doc/develop.txt
@@ -1,4 +1,4 @@
-*develop.txt*   For Vim version 7.0aa.  Last change: 2005 Jun 04
+*develop.txt*   For Vim version 7.0aa.  Last change: 2005 Jun 13
 
 
 		  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -375,8 +375,10 @@
 
 - Missing support for multi-byte encodings.  At least UTF-8 must be supported,
   so that more than one language can be used in the same file.
+  Doing on-the-fly conversion is not always possible (would require iconv
+  support).
 - For the programs and libraries: Using them as-is would require installing
-  them separately from Vim.  That's not impossible, but a drawback.
+  them separately from Vim.  That's mostly not impossible, but a drawback.
 - Performance: A few tests showed that it's possible to check spelling on the
   fly (while redrawing), just like syntax highlighting.  But the mechanisms
   used by other code are much slower.  Myspell uses a simplistic hashtable,
@@ -392,7 +394,9 @@
   all English words and highlight non-Canadian words differently.
 - Missing support for rare words.  Many words are correct but hardly ever used
   and could be a misspelled often-used word.
-
+- For making suggestions the speed is less important and requiring to install
+  another program or library would be acceptable.  But the word lists probably
+  differ, the suggestions may be wrong words.
 
 ==============================================================================
 4. Assumptions						*design-assumptions*
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 8c98ff0..a8d54a5 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -1,4 +1,4 @@
-*eval.txt*      For Vim version 7.0aa.  Last change: 2005 Jun 07
+*eval.txt*      For Vim version 7.0aa.  Last change: 2005 Jun 11
 
 
 		  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -235,6 +235,18 @@
 	:echo alist == blist
 <	1
 
+Note about comparing lists: Two lists are considered equal if they have the
+same length and all items compare equal, as with using "==".  There is one
+exception: When comparing a number with a string and the string contains extra
+characters beside the number they are not equal. Example: >
+	echo 4 == "4x"
+<	1 >
+	echo [4] == ["4x"]
+<	0
+
+This is to fix the odd behavior of == that can't be changed for backward
+compatibility reasons.
+
 
 List unpack ~
 
@@ -2593,7 +2605,8 @@
 
 getwinvar({nr}, {varname})				*getwinvar()*
 		The result is the value of option or local window variable
-		{varname} in window {nr}.
+		{varname} in window {nr}.  When {nr} is zero the current
+		window is used.
 		This also works for a global option, buffer-local option and
 		window-local option, but it doesn't work for a global variable
 		or buffer-local variable.
@@ -3692,7 +3705,7 @@
 
 setwinvar({nr}, {varname}, {val})			*setwinvar()*
 		Set option or local variable {varname} in window {nr} to
-		{val}.
+		{val}.  When {nr} is zero the current window is used.
 		This also works for a global or local buffer option, but it
 		doesn't work for a global or local buffer variable.
 		For a local buffer option the global value is unchanged.
@@ -3804,6 +3817,7 @@
 			Number		123
 			Funcref		function('name')
 			List		[item, item]
+			Dictionary	{key: value, key: value}
 		Note that in String values the ' character is doubled.
 
 							*strlen()*
diff --git a/runtime/doc/intro.txt b/runtime/doc/intro.txt
index ee187c9..3f8e35d 100644
--- a/runtime/doc/intro.txt
+++ b/runtime/doc/intro.txt
@@ -1,4 +1,4 @@
-*intro.txt*     For Vim version 7.0aa.  Last change: 2005 Mar 29
+*intro.txt*     For Vim version 7.0aa.  Last change: 2005 Jun 12
 
 
 		  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -198,7 +198,7 @@
 introduce Y2K problems, but those are not really part of Vim itself.
 
 ==============================================================================
-3. Credits						*credits* *author*
+3. Credits				*credits* *author* *Bram* *Moolenaar*
 
 Most of Vim was written by Bram Moolenaar <Bram@vim.org>.
 
diff --git a/runtime/doc/spell.txt b/runtime/doc/spell.txt
index 590b80e..aa7c745 100644
--- a/runtime/doc/spell.txt
+++ b/runtime/doc/spell.txt
@@ -1,4 +1,4 @@
-*spell.txt*	For Vim version 7.0aa.  Last change: 2005 Jun 08
+*spell.txt*	For Vim version 7.0aa.  Last change: 2005 Jun 13
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -51,7 +51,7 @@
 [S			Like "]S" but search backwards.
 
 
-To add words to your own word list:
+To add words to your own word list:				*E764*
 
 							*zg*
 zg			Add word under the cursor as a good word to
@@ -73,6 +73,21 @@
 |spell-wordlist-format|.
 
 
+Finding suggestions for bad words:
+
+							*z?*
+z?			For the badly spelled word under the cursor suggest
+			the correctly spelled word.
+			When there is no badly spelled word under the cursor
+			use the one after the cursor, in the same line.
+			The results are sorted on similarity to the badly
+			spelled word.
+			This may take a long time.  Hit CTRL-C when you are
+			bored.
+			You can enter the number of your choice or press
+			<Enter> if you don't want to replace.
+
+
 PERFORMANCE
 
 Note that Vim does on-the-fly spell checking.  To make this work fast the
@@ -170,6 +185,10 @@
 include characters like '-' in 'iskeyword'.  The word characters do depend on
 'encoding'.
 
+The table with word characters is stored in the main .spl file.  Therefore it
+matters what the current locale is when generating it!  A .add.spl file does
+not contain a word table.
+
 A word that starts with a digit is always ignored.  That includes hex numbers
 in the form 0xff and 0XFF.
 
@@ -224,6 +243,9 @@
 <			This combines the English word lists for US, CA and AU
 			into one en.spl file.
 			Up to eight regions can be combined. *E754* *755*
+			The REP and SAL items of the first .aff file where
+			they appear are used. |spell-affix-REP|
+			|spell-affix-SAL|
 
 			When the spell file was written all currently used
 			spell files will be reloaded.
@@ -452,4 +474,53 @@
 a typing mistake anyway.
 
 
+REPLACEMENTS						*spell-affix-REP*
+
+In the affix file REP items can be used to define common mistakes.  This is
+used to make spelling suggestions.  The items define the "from" text and the
+"to" replacement.  Example:
+
+	REP 4 ~
+	REP f ph ~
+	REP ph f ~
+	REP k ch ~
+	REP ch k ~
+
+The first line specifies the number of REP lines following.  Vim ignores it.
+
+
+SIMILAR CHARACTERS					*spell-affix-MAP*
+
+In the affix file MAP items can be used to define letters that very much
+alike.  This is mostly used for a letter with different accents.  This is used
+to prefer suggestions with these letters substituted.  Example:
+
+	MAP 2 ~
+	MAP eéëêè ~
+	MAP uüùúû ~
+
+The first line specifies the number of MAP lines following.  Vim ignores it.
+
+
+SOUNDS-A-LIKE						*spell-affix-SAL*
+
+In the affix file SAL items can be used to define the sounds-a-like mechanism
+to be used.  The main items define the "from" text and the "to" replacement.
+Example:
+
+	SAL CIA                  X ~
+	SAL CH                   X ~
+	SAL C                    K ~
+	SAL K                    K ~
+
+TODO: explain how it works.
+
+There are a few special items:
+
+	SAL followup		true ~
+	SAL collapse_result	true ~
+	SAL remove_accents	true ~
+
+"1" has the same meaning as "true".  Any other value means "false".
+
  vim:tw=78:sw=4:ts=8:ft=help:norl:
diff --git a/runtime/doc/tags b/runtime/doc/tags
index 7eb179e..76030d6 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -2835,6 +2835,7 @@
 B	motion.txt	/*B*
 BeBox	os_beos.txt	/*BeBox*
 BeOS	os_beos.txt	/*BeOS*
+Bram	intro.txt	/*Bram*
 BufAdd	autocmd.txt	/*BufAdd*
 BufCreate	autocmd.txt	/*BufCreate*
 BufDelete	autocmd.txt	/*BufDelete*
@@ -3805,6 +3806,7 @@
 Macintosh	os_mac.txt	/*Macintosh*
 Mark	motion.txt	/*Mark*
 MiNT	os_mint.txt	/*MiNT*
+Moolenaar	intro.txt	/*Moolenaar*
 MorphOS	os_amiga.txt	/*MorphOS*
 Motif	gui_x11.txt	/*Motif*
 MzScheme	if_mzsch.txt	/*MzScheme*
@@ -5125,6 +5127,7 @@
 hebrew.txt	hebrew.txt	/*hebrew.txt*
 help	various.txt	/*help*
 help-context	help.txt	/*help-context*
+help-tags	tags	1
 help-translated	various.txt	/*help-translated*
 help-xterm-window	various.txt	/*help-xterm-window*
 help.txt	help.txt	/*help.txt*
@@ -6250,9 +6253,12 @@
 spell	spell.txt	/*spell*
 spell-affix-KEP	spell.txt	/*spell-affix-KEP*
 spell-affix-RAR	spell.txt	/*spell-affix-RAR*
+spell-affix-REP	spell.txt	/*spell-affix-REP*
+spell-affix-SAL	spell.txt	/*spell-affix-SAL*
 spell-affix-chars	spell.txt	/*spell-affix-chars*
 spell-affix-mbyte	spell.txt	/*spell-affix-mbyte*
 spell-affix-vim	spell.txt	/*spell-affix-vim*
+spell-dic-format	spell.txt	/*spell-dic-format*
 spell-file-format	spell.txt	/*spell-file-format*
 spell-mkspell	spell.txt	/*spell-mkspell*
 spell-quickstart	spell.txt	/*spell-quickstart*
@@ -7070,6 +7076,7 @@
 z<CR>	scroll.txt	/*z<CR>*
 z<Left>	scroll.txt	/*z<Left>*
 z<Right>	scroll.txt	/*z<Right>*
+z?	spell.txt	/*z?*
 zA	fold.txt	/*zA*
 zC	fold.txt	/*zC*
 zD	fold.txt	/*zD*
diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt
index a46f407..f33cc07 100644
--- a/runtime/doc/todo.txt
+++ b/runtime/doc/todo.txt
@@ -1,4 +1,4 @@
-*todo.txt*      For Vim version 7.0aa.  Last change: 2005 Jun 08
+*todo.txt*      For Vim version 7.0aa.  Last change: 2005 Jun 13
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -30,7 +30,15 @@
 							*known-bugs*
 -------------------- Known bugs and current work -----------------------
 
-New menu file doesn't work with older vim.  exists("spell") doesn't work?
+Range(0) should return an empty list (Servatius Brandt).
+
+a few builtin functions call set_var() internally to set a variable.  They do
+not check for a valid variable name.  Example: setbufvar(1, ";", 1) sets a
+variable named ";". (Servatius Brandt)
+
+Patch for if_python to make exit work better with threads. (ugo)
+Still seems to fail 15% of the time.
+Vim 7 breaks, works OK with 6.3 version of if_python (Thakkar)
 
 Add extra list of file locations.  Can be used with:
     :ltag	      list of matching tags, like :tselect
@@ -86,18 +94,25 @@
 -   Win32: tearoff menu window should have a scrollbar when it's taller than
     the screen.
 
-Patch for if_python to make exit work better with threads. (ugo)
-Still seems to fail 15% of the time.
 
 
 PLANNED FOR VERSION 7.0:
 
 -   Add SPELLCHECKER, with support for many languages.
     - Spell checking code todo's:
-	- How about making suggestions?  Use an external program like aspell?
-	  Or include the myspell suggestion code in Vim?
-	- Support for approximate-regexps will help with finding similar words
-	  (agrep http://www.tgries.de/agrep/).
+	- Code for making suggestions:
+	  - Also need to store "toupper" in the .spl file.
+	  - Give better score for words that sound like the bad word?
+	  - "sounds-like" matching: Also try variants of the soundslike word.
+	  - Aspell has the "special" character, useful?
+	  - Support for approximate-regexps will help with finding similar
+	    words (agrep http://www.tgries.de/agrep/).
+	- Give a warning for ":mkspell it_IT wordfile", thus using a region
+	  name with only one input file.
+	- Also put list of word characters in word list file.  Otherwise the
+	  one for Italian may differ from the one used for English.
+	- Somehow mark "frequent" words, so that suggestions with "a" and
+	  "the" can be preferred?
 	- Make "en-rare" spell file.
 	  Convention: use en_US (language_region) and en-rare (language-field)
           Add hl groups to 'spelllang'?
@@ -130,7 +145,8 @@
     Later:
     - Implement compound words when it works for Myspell.  Current idea has
       the problem that "foo/X" always allows "foofoo", there is no way to
-      specify a word can only be at the start or end.
+      specify a word can only be at the start or end, or that only certain
+      words combine.
 
 -   REFACTORING: The main() function is very long.  Move parts to separate
     functions, especially loops.  Ideas from Walter Briscoe (2003 Apr 3, 2004
@@ -266,7 +282,7 @@
 Add strtol() to avoid the problems with leading zero causing octal conversion.
 
 Try new POSIX tests, made after my comments. (Geoff Clare, 2005 April 7)
-Before April 23 if possible.
+Version 1.5 is in ~/src/posix/1.5. (Lynne Canal)
 
 Add a 'tool' window: behaves like a preview window but there can be several.
 Don't count it in only_one_window(). (Alexei Alexandrov)
@@ -2342,6 +2358,9 @@
 
 
 'cindent', 'smartindent':
+8   Wrong indent below ? : with ():
+	if ((a ? (b) : c) != 0)
+		       aligns with ":".
 8   Wrong indent for ":" after a method with line break in arguments:
 	Foo::Foo (int one,
 		    int two)
@@ -2927,12 +2946,8 @@
 8   When using ":mksession", also store a command to reset all options to
     their default value, before setting the options that are not at their
     default value.
-8   Should ":mksession" restore the current directory when writing the
-    session, or the directory where the session file is?  Probably need a word
-    in 'sessionoptions' to make a choice:
-    "curdir" (cd to current directory when session file was generated)
-    "sessiondir" (cd to directory of session file)
-    "nodir" (don't cd at all)
+7   With ":mksession" also store the tag stack and jump history. (Michal
+    Malecki)
 8   Make "old" number options that really give a number of effects into string
     options that are a comma separated list.  The old number values should
     also be supported.
diff --git a/runtime/doc/usr_11.txt b/runtime/doc/usr_11.txt
index 28900b2..05af29e 100644
--- a/runtime/doc/usr_11.txt
+++ b/runtime/doc/usr_11.txt
@@ -1,4 +1,4 @@
-*usr_11.txt*	For Vim version 7.0aa.  Last change: 2005 Apr 01
+*usr_11.txt*	For Vim version 7.0aa.  Last change: 2005 Jun 09
 
 		     VIM USER MANUAL - by Bram Moolenaar
 
@@ -34,7 +34,7 @@
 
 	Using swap file ".help.txt.swp" ~
 	Original file "~/vim/runtime/doc/help.txt" ~
-	Recovery completed.  You should check if everything is OK. ~
+	Recovery completed. You should check if everything is OK. ~
 	(You might want to write out this file under another name ~
 	and run diff with the original file to check for changes) ~
 	Delete the .swp file afterwards. ~
diff --git a/runtime/doc/usr_41.txt b/runtime/doc/usr_41.txt
index ebf44fa..1c0a09d 100644
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -1,4 +1,4 @@
-*usr_41.txt*	For Vim version 7.0aa.  Last change: 2005 May 18
+*usr_41.txt*	For Vim version 7.0aa.  Last change: 2005 Jun 09
 
 		     VIM USER MANUAL - by Bram Moolenaar
 
@@ -612,13 +612,13 @@
 	join()			join List items into a String
 	string()		String representation of a List
 	call()			call a function with List as arguments
-	index()			index of a value in a list
+	index()			index of a value in a List
 	max()			maximum value in a List
 	min()			minimum value in a List
 	count()			count number of times a value appears in a List
 
 Dictionary manipulation:
-	get()			get an entries without error for wrong key
+	get()			get an entry without an error for a wrong key
 	len()			number of entries in a Dictionary
 	has_key()		check whether a key appears in a Dictionary
 	empty()			check if Dictionary is empty
@@ -1117,7 +1117,7 @@
 	one ~
 	two ~
 
-The will notice the items are not ordered.  You can sort the list to get a
+The will notice the keys are not ordered.  You can sort the list to get a
 specific order: >
 
 	:for key in sort(keys(uk2nl))
@@ -2237,8 +2237,8 @@
 
 You can put many other functions in the mylib.vim script, you are free to
 organize your functions in library scripts.  But you must use function names
-where the part before the colon matches the script name.  Otherwise Vim
-would not know what script to load.
+where the part before the '#' matches the script name.  Otherwise Vim would
+not know what script to load.
 
 If you get really enthousiastic and write lots of library scripts, you may
 want to use subdirectories.  Example: >
@@ -2256,7 +2256,7 @@
 	endfunction
 
 Notice that the name the function is defined with is exactly the same as the
-name used for calling the function.  And the part before the last colon
+name used for calling the function.  And the part before the last '#'
 exactly matches the subdirectory and script name.
 
 You can use the same mechanism for variables: >
diff --git a/runtime/doc/version7.txt b/runtime/doc/version7.txt
index 31f8ccb..322137f 100644
--- a/runtime/doc/version7.txt
+++ b/runtime/doc/version7.txt
@@ -1,4 +1,4 @@
-*version7.txt*  For Vim version 7.0aa.  Last change: 2005 Jun 07
+*version7.txt*  For Vim version 7.0aa.  Last change: 2005 Jun 13
 
 
 		  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -144,6 +144,11 @@
 
 The 'spell' option is used to switch spell checking on or off.
 The 'spelllang' option is used to specify the languages that are accepted.
+The 'spellfile' option specifies where new words are added.
+
+The |[s| and |]s| commands can be used to move to the next or previous error.
+The |zg| and |zw| commands can be used to add good and wrong words.
+The |z?| command can be used to correct the word.
 
 The "undercurl" highlighting attribute was added to nicely point out spelling
 mistakes in the GUI (based on patch from Marcin Dalecki).
@@ -649,6 +654,9 @@
 GTK GUI: use the GTK file dialog when it's available.  Mix from patches by
 Grahame Bowland and Evan Webb.
 
+Added ":scriptnames" to bugreport.vim, so that we can see what plugins were
+used.
+
 ==============================================================================
 COMPILE TIME CHANGES					*compile-changes-7*
 
@@ -1074,6 +1082,7 @@
 
 When using a Python "atexit" function it was not invoked when Vim exits.  Now
 call Py_Finalize() for that. (Ugo Di Girolamo)
+This breaks the thread stuff though, fixed by Ugo.
 
 GTK GUI: using a .vimrc with "set cmdheight=2 lines=43" and ":split" right
 after startup, the window layout is messed up. (Michael Schaap)  Added
diff --git a/runtime/indent/lua.vim b/runtime/indent/lua.vim
index 2ea16db..ace7fd1 100644
--- a/runtime/indent/lua.vim
+++ b/runtime/indent/lua.vim
@@ -2,7 +2,13 @@
 " Language:	Lua script
 " Maintainer:	Marcus Aurelius Farias <marcus.cf 'at' bol.com.br>
 " First Author:	Max Ischenko <mfi 'at' ukr.net>
-" Last Change:	2004 Aug 29
+" Last Change:	2005 Jun 09
+
+" Only load this indent file when no other was loaded.
+if exists("b:did_indent")
+  finish
+endif
+let b:did_indent = 1
 
 " Only define the function once.
 if exists("*GetLuaIndent")
diff --git a/runtime/indent/mupad.vim b/runtime/indent/mupad.vim
index b74f034..aeef520 100644
--- a/runtime/indent/mupad.vim
+++ b/runtime/indent/mupad.vim
@@ -1,35 +1,35 @@
-" Vim indent file

-" Language:    MuPAD source files

-" Maintainer:  Dave Silvia <dsilvia@mchsi.com>

-" Filenames:   *.mu

-" Date:        6/30/2004

-

-if exists("b:did_indent")

-	finish

-endif

-

-let b:did_indent = 1

-

-runtime indent/GenericIndent.vim

-

-let b:indentStmts=''

-let b:dedentStmts=''

-let b:allStmts=''

-" NOTE:  b:indentStmts, b:dedentStmts, and b:allStmts need to be initialized

-"        to '' before callin the functions because 'indent.vim' explicitly

-"        'unlet's b:did_indent.  This means that the lists will compound if

-"        you change back and forth between buffers.  This is true as of

-"        version 6.3, 6/23/2004.

-setlocal indentexpr=GenericIndent()

-setlocal indentkeys==end_proc,=then,=else,=elif,=end_if,=end_case,=until,=end_repeat,=end_domain,=end_for,=end_while,=end,o,O

-

-call GenericIndentStmts('begin,if,then,else,elif,case,repeat,until,domain,do')

-call GenericDedentStmts('end_proc,then,else,elif,end_if,end_case,until,end_repeat,end_domain,end_for,end_while,end')

-call GenericAllStmts()

-

-

-" TODO:  More comprehensive indentstmt, dedentstmt, and indentkeys values.

-"

-" BUGS:  You tell me!  Probably.  I just haven't found one yet or haven't been

-"        told about one.

-"        

+" Vim indent file
+" Language:    MuPAD source files
+" Maintainer:  Dave Silvia <dsilvia@mchsi.com>
+" Filenames:   *.mu
+" Date:        6/30/2004
+
+if exists("b:did_indent")
+	finish
+endif
+
+let b:did_indent = 1
+
+runtime indent/GenericIndent.vim
+
+let b:indentStmts=''
+let b:dedentStmts=''
+let b:allStmts=''
+" NOTE:  b:indentStmts, b:dedentStmts, and b:allStmts need to be initialized
+"        to '' before callin the functions because 'indent.vim' explicitly
+"        'unlet's b:did_indent.  This means that the lists will compound if
+"        you change back and forth between buffers.  This is true as of
+"        version 6.3, 6/23/2004.
+setlocal indentexpr=GenericIndent()
+setlocal indentkeys==end_proc,=then,=else,=elif,=end_if,=end_case,=until,=end_repeat,=end_domain,=end_for,=end_while,=end,o,O
+
+call GenericIndentStmts('begin,if,then,else,elif,case,repeat,until,domain,do')
+call GenericDedentStmts('end_proc,then,else,elif,end_if,end_case,until,end_repeat,end_domain,end_for,end_while,end')
+call GenericAllStmts()
+
+
+" TODO:  More comprehensive indentstmt, dedentstmt, and indentkeys values.
+"
+" BUGS:  You tell me!  Probably.  I just haven't found one yet or haven't been
+"        told about one.
+"        
diff --git a/runtime/indent/xsd.vim b/runtime/indent/xsd.vim
new file mode 100644
index 0000000..59e0b60
--- /dev/null
+++ b/runtime/indent/xsd.vim
@@ -0,0 +1,13 @@
+" Vim indent file
+" Language: 	.xsd files (XML Schema)
+" Maintainer:	Nobody
+" Last Change:	2005 Jun 09
+
+" Only load this indent file when no other was loaded.
+if exists("b:did_indent")
+  finish
+endif
+
+" Use XML formatting rules
+runtime! indent/xml.vim
+
diff --git a/runtime/menu.vim b/runtime/menu.vim
index cf59f16..08a6baa 100644
--- a/runtime/menu.vim
+++ b/runtime/menu.vim
@@ -2,7 +2,7 @@
 " You can also use this as a start for your own set of menus.
 "
 " Maintainer:	Bram Moolenaar <Bram@vim.org>
-" Last Change:	2005 Jun 08
+" Last Change:	2005 Jun 11
 
 " Note that ":an" (short for ":anoremenu") is often used to make a menu work
 " in all modes and avoid side effects from mappings defined by the user.
diff --git a/runtime/spell/en.ascii.spl b/runtime/spell/en.ascii.spl
index a90bb07..2eeec8b 100644
--- a/runtime/spell/en.ascii.spl
+++ b/runtime/spell/en.ascii.spl
Binary files differ
diff --git a/runtime/spell/en.latin1.spl b/runtime/spell/en.latin1.spl
index 6748269..a9dabbf 100644
--- a/runtime/spell/en.latin1.spl
+++ b/runtime/spell/en.latin1.spl
Binary files differ
diff --git a/runtime/spell/en.utf-8.spl b/runtime/spell/en.utf-8.spl
index f3bfa8d..68d8211 100644
--- a/runtime/spell/en.utf-8.spl
+++ b/runtime/spell/en.utf-8.spl
Binary files differ
diff --git a/runtime/syntax/squid.vim b/runtime/syntax/squid.vim
index 554bdca..a8462bb 100644
--- a/runtime/syntax/squid.vim
+++ b/runtime/syntax/squid.vim
@@ -1,9 +1,10 @@
 " Vim syntax file
 " Language:	Squid config file
 " Maintainer:	Klaus Muth <klaus@hampft.de>
-" Last Change:	2004 Feb 01
+" Last Change:	2005 Jun 12
 " URL:		http://www.hampft.de/vim/syntax/squid.vim
-" ThanksTo:	Ilya Sher <iso8601@mail.ru>
+" ThanksTo:	Ilya Sher <iso8601@mail.ru>,
+"               Michael Dotzler <Michael.Dotzler@leoni.com>
 
 
 " For version 5.x: Clear all syntax items
@@ -22,63 +23,62 @@
 syn match	squidTag	contained "TAG: .*$"
 
 " Lots & lots of Keywords!
-syn keyword	squidConf	acl always_direct announce_host
-syn keyword	squidConf	announce_period announce_port announce_to
-syn keyword	squidConf	anonymize_headers append_domain
-syn keyword	squidConf	as_whois_server authenticate_children
-syn keyword	squidConf	authenticate_program authenticate_ttl
-syn keyword	squidConf	broken_posts buffered_logs cache_access_log
-syn keyword	squidConf	cache_announce cache_dir cache_dns_program
-syn keyword	squidConf	cache_effective_group cache_effective_user
-syn keyword	squidConf	cache_host cache_host_acl cache_host_domain
-syn keyword	squidConf	cache_log cache_mem cache_mem_high
-syn keyword	squidConf	cache_mem_low cache_mgr cachemgr_passwd
-syn keyword	squidConf	cache_peer cache_stoplist
-syn keyword	squidConf	cache_stoplist_pattern cache_store_log
-syn keyword	squidConf	cache_swap cache_swap_high cache_swap_log
-syn keyword	squidConf	cache_swap_low client_db client_lifetime
-syn keyword	squidConf	client_netmask connect_timeout coredump_dir
-syn keyword	squidConf	dead_peer_timeout debug_options delay_access
-syn keyword	squidConf	delay_class delay_initial_bucket_level
-syn keyword	squidConf	delay_parameters delay_pools dns_children
-syn keyword	squidConf	dns_defnames dns_nameservers dns_testnames
-syn keyword	squidConf	emulate_httpd_log err_html_text
-syn keyword	squidConf	fake_user_agent firewall_ip forwarded_for
-syn keyword	squidConf	forward_snmpd_port fqdncache_size
+syn keyword	squidConf	acl always_direct announce_host announce_period
+syn keyword	squidConf	announce_port announce_to anonymize_headers
+syn keyword	squidConf	append_domain as_whois_server auth_param_basic
+syn keyword	squidConf	authenticate_children authenticate_program
+syn keyword	squidConf	authenticate_ttl broken_posts buffered_logs
+syn keyword	squidConf	cache_access_log cache_announce cache_dir
+syn keyword	squidConf	cache_dns_program cache_effective_group
+syn keyword	squidConf	cache_effective_user cache_host cache_host_acl
+syn keyword	squidConf	cache_host_domain cache_log cache_mem
+syn keyword	squidConf	cache_mem_high cache_mem_low cache_mgr
+syn keyword	squidConf	cachemgr_passwd cache_peer cache_peer_access
+syn keyword	squidConf	cahce_replacement_policy cache_stoplist
+syn keyword	squidConf	cache_stoplist_pattern cache_store_log cache_swap
+syn keyword	squidConf	cache_swap_high cache_swap_log cache_swap_low
+syn keyword	squidConf	client_db client_lifetime client_netmask
+syn keyword	squidConf	connect_timeout coredump_dir dead_peer_timeout
+syn keyword	squidConf	debug_options delay_access delay_class
+syn keyword	squidConf	delay_initial_bucket_level delay_parameters
+syn keyword	squidConf	delay_pools deny_info dns_children dns_defnames
+syn keyword	squidConf	dns_nameservers dns_testnames emulate_httpd_log
+syn keyword	squidConf	err_html_text fake_user_agent firewall_ip
+syn keyword	squidConf	forwarded_for forward_snmpd_port fqdncache_size
 syn keyword	squidConf	ftpget_options ftpget_program ftp_list_width
-syn keyword	squidConf	ftp_user half_closed_clients
-syn keyword	squidConf	hierarchy_stoplist htcp_port http_access
-syn keyword	squidConf	http_anonymizer httpd_accel httpd_accel_host
-syn keyword	squidConf	httpd_accel_port httpd_accel_uses_host_header
-syn keyword	squidConf	httpd_accel_with_proxy http_port
-syn keyword	squidConf	http_reply_access icp_access icp_hit_stale
-syn keyword	squidConf	icp_port icp_query_timeout ident_lookup
-syn keyword	squidConf	ident_lookup_access ident_timeout
-syn keyword	squidConf	incoming_http_average incoming_icp_average
-syn keyword	squidConf	inside_firewall ipcache_high ipcache_low
-syn keyword	squidConf	ipcache_size local_domain local_ip
+syn keyword	squidConf	ftp_passive ftp_user half_closed_clients
+syn keyword	squidConf	header_access header_replace hierarchy_stoplist
+syn keyword	squidConf	high_response_time_warning high_page_fault_warning
+syn keyword	squidConf	htcp_port http_access http_anonymizer httpd_accel
+syn keyword	squidConf	httpd_accel_host httpd_accel_port
+syn keyword	squidConf	httpd_accel_uses_host_header
+syn keyword	squidConf	httpd_accel_with_proxy http_port http_reply_access
+syn keyword	squidConf	icp_access icp_hit_stale icp_port
+syn keyword	squidConf	icp_query_timeout ident_lookup ident_lookup_access
+syn keyword	squidConf	ident_timeout incoming_http_average
+syn keyword	squidConf	incoming_icp_average inside_firewall ipcache_high
+syn keyword	squidConf	ipcache_low ipcache_size local_domain local_ip
 syn keyword	squidConf	logfile_rotate log_fqdn log_icp_queries
 syn keyword	squidConf	log_mime_hdrs maximum_object_size
 syn keyword	squidConf	maximum_single_addr_tries mcast_groups
 syn keyword	squidConf	mcast_icp_query_timeout mcast_miss_addr
-syn keyword	squidConf	mcast_miss_encode_key mcast_miss_port
-syn keyword	squidConf	memory_pools mime_table min_http_poll_cnt
-syn keyword	squidConf	min_icp_poll_cnt minimum_direct_hops
-syn keyword	squidConf	minimum_retry_timeout miss_access
-syn keyword	squidConf	negative_dns_ttl negative_ttl
-syn keyword	squidConf	neighbor_timeout neighbor_type_domain
+syn keyword	squidConf	mcast_miss_encode_key mcast_miss_port memory_pools
+syn keyword	squidConf	memory_pools_limit memory_replacement_policy
+syn keyword	squidConf	mime_table min_http_poll_cnt min_icp_poll_cnt
+syn keyword	squidConf	minimum_direct_hops minimum_object_size
+syn keyword	squidConf	minimum_retry_timeout miss_access negative_dns_ttl
+syn keyword	squidConf	negative_ttl neighbor_timeout neighbor_type_domain
 syn keyword	squidConf	netdb_high netdb_low netdb_ping_period
-syn keyword	squidConf	netdb_ping_rate no_cache passthrough_proxy
-syn keyword	squidConf	pconn_timeout pid_filename pinger_program
-syn keyword	squidConf	positive_dns_ttl prefer_direct proxy_auth
-syn keyword	squidConf	proxy_auth_realm query_icmp quick_abort
+syn keyword	squidConf	netdb_ping_rate never_direct no_cache
+syn keyword	squidConf	passthrough_proxy pconn_timeout pid_filename
+syn keyword	squidConf	pinger_program positive_dns_ttl prefer_direct
+syn keyword	squidConf	proxy_auth proxy_auth_realm query_icmp quick_abort
 syn keyword	squidConf	quick_abort quick_abort_max quick_abort_min
-syn keyword	squidConf	quick_abort_pct range_offset_limit
-syn keyword	squidConf	read_timeout redirect_children
-syn keyword	squidConf	redirect_program
+syn keyword	squidConf	quick_abort_pct range_offset_limit read_timeout
+syn keyword	squidConf	redirect_children redirect_program
 syn keyword	squidConf	redirect_rewrites_host_header reference_age
 syn keyword	squidConf	reference_age refresh_pattern reload_into_ims
-syn keyword	squidConf	request_size request_timeout
+syn keyword	squidConf	request_body_max_size request_size request_timeout
 syn keyword	squidConf	shutdown_lifetime single_parent_bypass
 syn keyword	squidConf	siteselect_timeout snmp_access
 syn keyword	squidConf	snmp_incoming_address snmp_port source_ping
@@ -86,16 +86,19 @@
 syn keyword	squidConf	store_objects_per_bucket strip_query_terms
 syn keyword	squidConf	swap_level1_dirs swap_level2_dirs
 syn keyword	squidConf	tcp_incoming_address tcp_outgoing_address
-syn keyword	squidConf	tcp_recv_bufsize test_reachability
-syn keyword	squidConf	udp_hit_obj udp_hit_obj_size
-syn keyword	squidConf	udp_incoming_address udp_outgoing_address
-syn keyword	squidConf	unique_hostname unlinkd_program
-syn keyword	squidConf	uri_whitespace useragent_log visible_hostname
-syn keyword	squidConf	wais_relay wais_relay_host wais_relay_port
+syn keyword	squidConf	tcp_recv_bufsize test_reachability udp_hit_obj
+syn keyword	squidConf	udp_hit_obj_size udp_incoming_address
+syn keyword	squidConf	udp_outgoing_address unique_hostname
+syn keyword	squidConf	unlinkd_program uri_whitespace useragent_log
+syn keyword	squidConf	visible_hostname wais_relay wais_relay_host
+syn keyword	squidConf	wais_relay_port
 
 syn keyword	squidOpt	proxy-only weight ttl no-query default
 syn keyword	squidOpt	round-robin multicast-responder
 syn keyword	squidOpt	on off all deny allow
+syn keyword	squidopt	via parent no-digest heap lru realm
+syn keyword	squidopt	children credentialsttl none disable
+syn keyword	squidopt	offline_toggle diskd q1 q2
 
 " Security Actions for cachemgr_passwd
 syn keyword	squidAction	shutdown info parameter server_list
@@ -108,6 +111,7 @@
 syn keyword	squidAcl	url_regex urlpath_regex referer_regex port proto
 syn keyword	squidAcl	req_mime_type rep_mime_type
 syn keyword	squidAcl	method browser user src dst
+syn keyword	squidAcl	time dstdomain ident snmp_community
 
 syn match	squidNumber	"\<\d\+\>"
 syn match	squidIP		"\<\d\{1,3}\.\d\{1,3}\.\d\{1,3}\.\d\{1,3}\>"
diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak
index 4d4ea22..eb5aa7e 100644
--- a/src/Make_mvc.mak
+++ b/src/Make_mvc.mak
@@ -110,7 +110,8 @@
 
 TARGETOS = BOTH
 
-# Select one of eight object code directories, depends on GUI, OLE and DEBUG.
+# Select one of eight object code directories, depends on GUI, OLE, DEBUG and
+# interfaces.
 # If you change something else, do "make clean" first!
 !if "$(GUI)" == "yes"
 OBJDIR = .\ObjG
@@ -120,6 +121,18 @@
 !if "$(OLE)" == "yes"
 OBJDIR = $(OBJDIR)O
 !endif
+!ifdef PERL
+OBJDIR = $(OBJDIR)L
+!endif
+!ifdef PYTHON
+OBJDIR = $(OBJDIR)Y
+!endif
+!ifdef TCL
+OBJDIR = $(OBJDIR)T
+!endif
+!ifdef RUBY
+OBJDIR = $(OBJDIR)R
+!endif
 !ifdef MZSCHEME
 OBJDIR = $(OBJDIR)Z
 !endif
diff --git a/src/eval.c b/src/eval.c
index aa39350..913f8ed 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -264,12 +264,11 @@
 #define VV_RO		2	/* read-only */
 #define VV_RO_SBX	4	/* read-only in the sandbox */
 
-#define VV_NAME(s, t)	s, sizeof(s) - 1, {{t}}, {0}
+#define VV_NAME(s, t)	s, {{t}}, {0}
 
 static struct vimvar
 {
     char	*vv_name;	/* name of variable, without v: */
-    int		vv_len;		/* length of name */
     dictitem_T	vv_di;		/* value and name for key */
     char	vv_filler[16];	/* space for LONGEST name below!!! */
     char	vv_flags;	/* VV_COMPAT, VV_RO, VV_RO_SBX */
@@ -594,9 +593,12 @@
 static void clear_tv __ARGS((typval_T *varp));
 static void init_tv __ARGS((typval_T *varp));
 static long get_tv_number __ARGS((typval_T *varp));
+static long get_tv_number_chk __ARGS((typval_T *varp, int *denote));
 static linenr_T get_tv_lnum __ARGS((typval_T *argvars));
 static char_u *get_tv_string __ARGS((typval_T *varp));
+static char_u *get_tv_string_chk __ARGS((typval_T *varp));
 static char_u *get_tv_string_buf __ARGS((typval_T *varp, char_u *buf));
+static char_u *get_tv_string_buf_chk __ARGS((typval_T *varp, char_u *buf));
 static dictitem_T *find_var __ARGS((char_u *name, hashtab_T **htp));
 static dictitem_T *find_var_in_ht __ARGS((hashtab_T *ht, char_u *varname, int writing));
 static hashtab_T *find_var_ht __ARGS((char_u *name, char_u **varname));
@@ -1023,7 +1025,7 @@
 	*error = FALSE;
 	if (!skip)
 	{
-	    retval = (get_tv_number(&tv) != 0);
+	    retval = (get_tv_number_chk(&tv, error) != 0);
 	    clear_tv(&tv);
 	}
     }
@@ -1137,7 +1139,7 @@
 	retval = -1;
     else
     {
-	retval = get_tv_number(&rettv);
+	retval = get_tv_number_chk(&rettv, NULL);
 	clear_tv(&rettv);
     }
     --emsg_off;
@@ -1775,8 +1777,8 @@
 	    {
 		c1 = name[len];
 		name[len] = NUL;
-		p = get_tv_string(tv);
-		if (op != NULL && *op == '.')
+		p = get_tv_string_chk(tv);
+		if (p != NULL && op != NULL && *op == '.')
 		{
 		    int	    mustfree = FALSE;
 		    char_u  *s = vim_getenv(name, &mustfree);
@@ -1789,15 +1791,18 @@
 		    }
 		}
 		if (p != NULL)
+		{
 		    vim_setenv(name, p);
-		if (STRICMP(name, "HOME") == 0)
-		    init_homedir();
-		else if (didset_vim && STRICMP(name, "VIM") == 0)
-		    didset_vim = FALSE;
-		else if (didset_vimruntime && STRICMP(name, "VIMRUNTIME") == 0)
-		    didset_vimruntime = FALSE;
+		    if (STRICMP(name, "HOME") == 0)
+			init_homedir();
+		    else if (didset_vim && STRICMP(name, "VIM") == 0)
+			didset_vim = FALSE;
+		    else if (didset_vimruntime
+					&& STRICMP(name, "VIMRUNTIME") == 0)
+			didset_vimruntime = FALSE;
+		    arg_end = arg;
+		}
 		name[len] = c1;
-		arg_end = arg;
 		vim_free(tofree);
 	    }
 	}
@@ -1827,8 +1832,8 @@
 	    *p = NUL;
 
 	    n = get_tv_number(tv);
-	    s = get_tv_string(tv);
-	    if (op != NULL && *op != '=')
+	    s = get_tv_string_chk(tv);	    /* != NULL if number or string */
+	    if (s != NULL && op != NULL && *op != '=')
 	    {
 		opt_type = get_option_value(arg, &numval,
 						       &stringval, opt_flags);
@@ -1852,9 +1857,12 @@
 		    }
 		}
 	    }
-	    set_option_value(arg, n, s, opt_flags);
+	    if (s != NULL)
+	    {
+		set_option_value(arg, n, s, opt_flags);
+		arg_end = p;
+	    }
 	    *p = c1;
-	    arg_end = p;
 	    vim_free(stringval);
 	}
     }
@@ -1875,8 +1883,8 @@
 	    char_u	*tofree = NULL;
 	    char_u	*s;
 
-	    p = get_tv_string(tv);
-	    if (op != NULL && *op == '.')
+	    p = get_tv_string_chk(tv);
+	    if (p != NULL && op != NULL && *op == '.')
 	    {
 		s = get_reg_contents(*arg == '@' ? '"' : *arg, FALSE, FALSE);
 		if (s != NULL)
@@ -1886,8 +1894,10 @@
 		}
 	    }
 	    if (p != NULL)
+	    {
 		write_reg_contents(*arg == '@' ? '"' : *arg, p, -1, FALSE);
-	    arg_end = arg + 1;
+		arg_end = arg + 1;
+	    }
 	    vim_free(tofree);
 	}
     }
@@ -2070,6 +2080,12 @@
 		empty1 = FALSE;
 		if (eval1(&p, &var1, TRUE) == FAIL)	/* recursive! */
 		    return NULL;
+		if (get_tv_string_chk(&var1) == NULL)
+		{
+		    /* not a number or string */
+		    clear_tv(&var1);
+		    return NULL;
+		}
 	    }
 
 	    /* Optionally get the second index [ :expr]. */
@@ -2104,6 +2120,14 @@
 			    clear_tv(&var1);
 			return NULL;
 		    }
+		    if (get_tv_string_chk(&var2) == NULL)
+		    {
+			/* not a number or string */
+			if (!empty1)
+			    clear_tv(&var1);
+			clear_tv(&var2);
+			return NULL;
+		    }
 		}
 		lp->ll_range = TRUE;
 	    }
@@ -2130,7 +2154,7 @@
 	    if (len == -1)
 	    {
 		/* "[key]": get key from "var1" */
-		key = get_tv_string(&var1);
+		key = get_tv_string(&var1);	/* is number or string */
 		if (*key == NUL)
 		{
 		    if (!quiet)
@@ -2176,7 +2200,7 @@
 		lp->ll_n1 = 0;
 	    else
 	    {
-		lp->ll_n1 = get_tv_number(&var1);
+		lp->ll_n1 = get_tv_number(&var1);   /* is number or string */
 		clear_tv(&var1);
 	    }
 	    lp->ll_dict = NULL;
@@ -2199,7 +2223,7 @@
 	     */
 	    if (lp->ll_range && !lp->ll_empty2)
 	    {
-		lp->ll_n2 = get_tv_number(&var2);
+		lp->ll_n2 = get_tv_number(&var2);   /* is number or string */
 		clear_tv(&var2);
 		if (lp->ll_n2 < 0)
 		{
@@ -3340,9 +3364,13 @@
 	result = FALSE;
 	if (evaluate)
 	{
-	    if (get_tv_number(rettv) != 0)
+	    int		error = FALSE;
+
+	    if (get_tv_number_chk(rettv, &error) != 0)
 		result = TRUE;
 	    clear_tv(rettv);
+	    if (error)
+		return FAIL;
 	}
 
 	/*
@@ -3398,6 +3426,7 @@
     typval_T	var2;
     long	result;
     int		first;
+    int		error = FALSE;
 
     /*
      * Get the first variable.
@@ -3414,9 +3443,11 @@
     {
 	if (evaluate && first)
 	{
-	    if (get_tv_number(rettv) != 0)
+	    if (get_tv_number_chk(rettv, &error) != 0)
 		result = TRUE;
 	    clear_tv(rettv);
+	    if (error)
+		return FAIL;
 	    first = FALSE;
 	}
 
@@ -3432,9 +3463,11 @@
 	 */
 	if (evaluate && !result)
 	{
-	    if (get_tv_number(&var2) != 0)
+	    if (get_tv_number_chk(&var2, &error) != 0)
 		result = TRUE;
 	    clear_tv(&var2);
+	    if (error)
+		return FAIL;
 	}
 	if (evaluate)
 	{
@@ -3464,6 +3497,7 @@
     typval_T	var2;
     long	result;
     int		first;
+    int		error = FALSE;
 
     /*
      * Get the first variable.
@@ -3480,9 +3514,11 @@
     {
 	if (evaluate && first)
 	{
-	    if (get_tv_number(rettv) == 0)
+	    if (get_tv_number_chk(rettv, &error) == 0)
 		result = FALSE;
 	    clear_tv(rettv);
+	    if (error)
+		return FAIL;
 	    first = FALSE;
 	}
 
@@ -3498,9 +3534,11 @@
 	 */
 	if (evaluate && result)
 	{
-	    if (get_tv_number(&var2) == 0)
+	    if (get_tv_number_chk(&var2, &error) == 0)
 		result = FALSE;
 	    clear_tv(&var2);
+	    if (error)
+		return FAIL;
 	}
 	if (evaluate)
 	{
@@ -3832,6 +3870,22 @@
 	if (op != '+' && op != '-' && op != '.')
 	    break;
 
+	if (op != '+' || rettv->v_type != VAR_LIST)
+	{
+	    /* For "list + ...", an illegal use of the first operand as
+	     * a number cannot be determined before evaluating the 2nd
+	     * operand: if this is also a list, all is ok.
+	     * For "something . ...", "something - ..." or "non-list + ...",
+	     * we know that the first operand needs to be a string or number
+	     * without evaluating the 2nd operand.  So check before to avoid
+	     * side effects after an error. */
+	    if (evaluate && get_tv_string_chk(rettv) == NULL)
+	    {
+		clear_tv(rettv);
+		return FAIL;
+	    }
+	}
+
 	/*
 	 * Get the second variable.
 	 */
@@ -3849,8 +3903,14 @@
 	     */
 	    if (op == '.')
 	    {
-		s1 = get_tv_string_buf(rettv, buf1);
-		s2 = get_tv_string_buf(&var2, buf2);
+		s1 = get_tv_string_buf(rettv, buf1);	/* already checked */
+		s2 = get_tv_string_buf_chk(&var2, buf2);
+		if (s2 == NULL)		/* type error ? */
+		{
+		    clear_tv(rettv);
+		    clear_tv(&var2);
+		    return FAIL;
+		}
 		p = concat_str(s1, s2);
 		clear_tv(rettv);
 		rettv->v_type = VAR_STRING;
@@ -3872,8 +3932,24 @@
 	    }
 	    else
 	    {
-		n1 = get_tv_number(rettv);
-		n2 = get_tv_number(&var2);
+		int	    error = FALSE;
+
+		n1 = get_tv_number_chk(rettv, &error);
+		if (error)
+		{
+		    /* This can only happen for "list + non-list".
+		     * For "non-list + ..." or "something - ...", we returned
+		     * before evaluating the 2nd operand. */
+		    clear_tv(rettv);
+		    return FAIL;
+		}
+		n2 = get_tv_number_chk(&var2, &error);
+		if (error)
+		{
+		    clear_tv(rettv);
+		    clear_tv(&var2);
+		    return FAIL;
+		}
 		clear_tv(rettv);
 		if (op == '+')
 		    n1 = n1 + n2;
@@ -3908,6 +3984,7 @@
     typval_T	var2;
     int		op;
     long	n1, n2;
+    int		error = FALSE;
 
     /*
      * Get the first variable.
@@ -3926,8 +4003,10 @@
 
 	if (evaluate)
 	{
-	    n1 = get_tv_number(rettv);
+	    n1 = get_tv_number_chk(rettv, &error);
 	    clear_tv(rettv);
+	    if (error)
+		return FAIL;
 	}
 	else
 	    n1 = 0;
@@ -3941,8 +4020,10 @@
 
 	if (evaluate)
 	{
-	    n2 = get_tv_number(&var2);
+	    n2 = get_tv_number_chk(&var2, &error);
 	    clear_tv(&var2);
+	    if (error)
+		return FAIL;
 
 	    /*
 	     * Compute the result.
@@ -4175,18 +4256,28 @@
      */
     if (ret == OK && evaluate && end_leader > start_leader)
     {
-	val = get_tv_number(rettv);
-	while (end_leader > start_leader)
+	int	    error = FALSE;
+
+	val = get_tv_number_chk(rettv, &error);
+	if (error)
 	{
-	    --end_leader;
-	    if (*end_leader == '!')
-		val = !val;
-	    else if (*end_leader == '-')
-		val = -val;
+	    clear_tv(rettv);
+	    ret = FAIL;
 	}
-	clear_tv(rettv);
-	rettv->v_type = VAR_NUMBER;
-	rettv->vval.v_number = val;
+	else
+	{
+	    while (end_leader > start_leader)
+	    {
+		--end_leader;
+		if (*end_leader == '!')
+		    val = !val;
+		else if (*end_leader == '-')
+		    val = -val;
+	    }
+	    clear_tv(rettv);
+	    rettv->v_type = VAR_NUMBER;
+	    rettv->vval.v_number = val;
+	}
     }
 
     return ret;
@@ -4243,6 +4334,12 @@
 	    empty1 = TRUE;
 	else if (eval1(arg, &var1, evaluate) == FAIL)	/* recursive! */
 	    return FAIL;
+	else if (evaluate && get_tv_string_chk(&var1) == NULL)
+	{
+	    /* not a number or string */
+	    clear_tv(&var1);
+	    return FAIL;
+	}
 
 	/*
 	 * Get the second variable from inside the [:].
@@ -4255,7 +4352,16 @@
 		empty2 = TRUE;
 	    else if (eval1(arg, &var2, evaluate) == FAIL)	/* recursive! */
 	    {
-		clear_tv(&var1);
+		if (!empty1)
+		    clear_tv(&var1);
+		return FAIL;
+	    }
+	    else if (evaluate && get_tv_string_chk(&var2) == NULL)
+	    {
+		/* not a number or string */
+		if (!empty1)
+		    clear_tv(&var1);
+		clear_tv(&var2);
 		return FAIL;
 	    }
 	}
@@ -4928,6 +5034,7 @@
     int	    ic;	    /* ignore case */
 {
     char_u	buf1[NUMBUFLEN], buf2[NUMBUFLEN];
+    char_u	*s1, *s2;
 
     if (tv1->v_type == VAR_LIST || tv2->v_type == VAR_LIST)
     {
@@ -4963,12 +5070,13 @@
 	if (get_tv_number(tv1) != get_tv_number(tv2))
 	    return FALSE;
     }
-    else if (!ic && STRCMP(get_tv_string_buf(tv1, buf1),
-				       get_tv_string_buf(tv2, buf2)) != 0)
-	return FALSE;
-    else if (ic && STRICMP(get_tv_string_buf(tv1, buf1),
-				       get_tv_string_buf(tv2, buf2)) != 0)
-	return FALSE;
+    else
+    {
+	s1 = get_tv_string_buf(tv1, buf1);
+	s2 = get_tv_string_buf(tv2, buf2);
+	if ((ic ? MB_STRICMP(s1, s2) : STRCMP(s1, s2)) != 0)
+	    return FALSE;
+    }
     return TRUE;
 }
 
@@ -5828,10 +5936,12 @@
 	    clear_tv(&tvkey);
 	    goto failret;
 	}
-	key = get_tv_string_buf(&tvkey, buf);
-	if (*key == NUL)
+	key = get_tv_string_buf_chk(&tvkey, buf);
+	if (key == NULL || *key == NUL)
 	{
-	    EMSG(_(e_emptykey));
+	    /* "key" is NULL when get_tv_string_buf_chk() gave an errmsg */
+	    if (key != NULL)
+		EMSG(_(e_emptykey));
 	    clear_tv(&tvkey);
 	    goto failret;
 	}
@@ -6731,12 +6841,12 @@
     typval_T	*rettv;
 {
     long	lnum;
+    char_u	*line;
     list_T	*l = NULL;
     listitem_T	*li = NULL;
     typval_T	*tv;
     long	added = 0;
 
-    rettv->vval.v_number = 1;		/* Default: Failed */
     lnum = get_tv_lnum(argvars);
     if (lnum >= 0
 	    && lnum <= curbuf->b_ml.ml_line_count
@@ -6749,6 +6859,7 @@
 		return;
 	    li = l->lv_first;
 	}
+	rettv->vval.v_number = 0;	/* Default: Success */
 	for (;;)
 	{
 	    if (l == NULL)
@@ -6757,7 +6868,13 @@
 		break;			/* end of list */
 	    else
 		tv = &li->li_tv;	/* append item from list */
-	    ml_append(lnum + added, get_tv_string(tv), (colnr_T)0, FALSE);
+	    line = get_tv_string_chk(tv);
+	    if (line == NULL)		/* type error */
+	    {
+		rettv->vval.v_number = 1;	/* Failed */
+		break;
+	    }
+	    ml_append(lnum + added, line, (colnr_T)0, FALSE);
 	    ++added;
 	    if (l == NULL)
 		break;
@@ -6767,8 +6884,9 @@
 	appended_lines_mark(lnum, added);
 	if (curwin->w_cursor.lnum > lnum)
 	    curwin->w_cursor.lnum += added;
-	rettv->vval.v_number = 0;	/* Success */
     }
+    else
+	rettv->vval.v_number = 1;	/* Failed */
 }
 
 /*
@@ -6805,7 +6923,7 @@
 {
     int		idx;
 
-    idx = get_tv_number(&argvars[0]);
+    idx = get_tv_number_chk(&argvars[0], NULL);
     if (idx >= 0 && idx < ARGCOUNT)
 	rettv->vval.v_string = vim_strsave(alist_name(&ARGLIST[idx]));
     else
@@ -6829,13 +6947,17 @@
     char_u	*defname;
     char_u	buf[NUMBUFLEN];
     char_u	buf2[NUMBUFLEN];
+    int		error = FALSE;
 
-    save = get_tv_number(&argvars[0]);
-    title = get_tv_string(&argvars[1]);
-    initdir = get_tv_string_buf(&argvars[2], buf);
-    defname = get_tv_string_buf(&argvars[3], buf2);
+    save = get_tv_number_chk(&argvars[0], &error);
+    title = get_tv_string_chk(&argvars[1]);
+    initdir = get_tv_string_buf_chk(&argvars[2], buf);
+    defname = get_tv_string_buf_chk(&argvars[3], buf2);
 
-    rettv->vval.v_string =
+    if (error || title == NULL || initdir == NULL || defname == NULL)
+	rettv->vval.v_string = NULL;
+    else
+	rettv->vval.v_string =
 		 do_browse(save ? BROWSE_SAVE : 0,
 				 title, defname, NULL, initdir, NULL, curbuf);
 #else
@@ -6858,10 +6980,13 @@
     char_u	*initdir;
     char_u	buf[NUMBUFLEN];
 
-    title = get_tv_string(&argvars[0]);
-    initdir = get_tv_string_buf(&argvars[1], buf);
+    title = get_tv_string_chk(&argvars[0]);
+    initdir = get_tv_string_buf_chk(&argvars[1], buf);
 
-    rettv->vval.v_string = do_browse(BROWSE_DIR,
+    if (title == NULL || initdir == NULL)
+	rettv->vval.v_string = NULL;
+    else
+	rettv->vval.v_string = do_browse(BROWSE_DIR,
 				    title, NULL, NULL, initdir, NULL, curbuf);
 #else
     rettv->vval.v_string = NULL;
@@ -6994,6 +7119,7 @@
 {
     buf_T	*buf;
 
+    (void)get_tv_number(&argvars[0]);	    /* issue errmsg if type error */
     ++emsg_off;
     buf = get_buf_tv(&argvars[0]);
     rettv->v_type = VAR_STRING;
@@ -7014,6 +7140,7 @@
 {
     buf_T	*buf;
 
+    (void)get_tv_number(&argvars[0]);	    /* issue errmsg if type error */
     ++emsg_off;
     buf = get_buf_tv(&argvars[0]);
     if (buf != NULL)
@@ -7037,6 +7164,7 @@
 #endif
     buf_T	*buf;
 
+    (void)get_tv_number(&argvars[0]);	    /* issue errmsg if type error */
     ++emsg_off;
     buf = get_buf_tv(&argvars[0]);
 #ifdef FEAT_WINDOWS
@@ -7067,7 +7195,7 @@
 #else
     long	boff = 0;
 
-    boff = get_tv_number(&argvars[0]) - 1;
+    boff = get_tv_number(&argvars[0]) - 1;  /* boff gets -1 on type error */
     if (boff < 0)
 	rettv->vval.v_number = -1;
     else
@@ -7091,10 +7219,10 @@
     char_u	*str;
     long	idx;
 
-    str = get_tv_string(&argvars[0]);
-    idx = get_tv_number(&argvars[1]);
+    str = get_tv_string_chk(&argvars[0]);
+    idx = get_tv_number_chk(&argvars[1], NULL);
     rettv->vval.v_number = -1;
-    if (idx < 0)
+    if (str == NULL || idx < 0)
 	return;
 
 #ifdef FEAT_MBYTE
@@ -7140,6 +7268,8 @@
 	func = argvars[0].vval.v_string;
     else
 	func = get_tv_string(&argvars[0]);
+    if (*func == NUL)
+	return;		/* type error or empty name */
 
     if (argvars[2].v_type != VAR_UNKNOWN)
     {
@@ -7185,8 +7315,7 @@
 {
 #ifdef FEAT_MBYTE
     if (has_mbyte)
-	rettv->vval.v_number =
-				(*mb_ptr2char)(get_tv_string(&argvars[0]));
+	rettv->vval.v_number = (*mb_ptr2char)(get_tv_string(&argvars[0]));
     else
 #endif
     rettv->vval.v_number = get_tv_string(&argvars[0])[0];
@@ -7285,26 +7414,35 @@
     char_u	buf2[NUMBUFLEN];
     int		def = 1;
     int		type = VIM_GENERIC;
-    int		c;
+    char_u	*typestr;
+    int		error = FALSE;
 
-    message = get_tv_string(&argvars[0]);
+    message = get_tv_string_chk(&argvars[0]);
+    if (message == NULL)
+	error = TRUE;
     if (argvars[1].v_type != VAR_UNKNOWN)
     {
-	buttons = get_tv_string_buf(&argvars[1], buf);
+	buttons = get_tv_string_buf_chk(&argvars[1], buf);
+	if (buttons == NULL)
+	    error = TRUE;
 	if (argvars[2].v_type != VAR_UNKNOWN)
 	{
-	    def = get_tv_number(&argvars[2]);
+	    def = get_tv_number_chk(&argvars[2], &error);
 	    if (argvars[3].v_type != VAR_UNKNOWN)
 	    {
-		/* avoid that TOUPPER_ASC calls get_tv_string_buf() twice */
-		c = *get_tv_string_buf(&argvars[3], buf2);
-		switch (TOUPPER_ASC(c))
+		typestr = get_tv_string_buf_chk(&argvars[3], buf2);
+		if (typestr == NULL)
+		    error = TRUE;
+		else
 		{
-		    case 'E': type = VIM_ERROR; break;
-		    case 'Q': type = VIM_QUESTION; break;
-		    case 'I': type = VIM_INFO; break;
-		    case 'W': type = VIM_WARNING; break;
-		    case 'G': type = VIM_GENERIC; break;
+		    switch (TOUPPER_ASC(*typestr))
+		    {
+			case 'E': type = VIM_ERROR; break;
+			case 'Q': type = VIM_QUESTION; break;
+			case 'I': type = VIM_INFO; break;
+			case 'W': type = VIM_WARNING; break;
+			case 'G': type = VIM_GENERIC; break;
+		    }
 		}
 	    }
 	}
@@ -7313,7 +7451,10 @@
     if (buttons == NULL || *buttons == NUL)
 	buttons = (char_u *)_("&Ok");
 
-    rettv->vval.v_number = do_dialog(type, NULL, message, buttons,
+    if (error)
+	rettv->vval.v_number = 0;
+    else
+	rettv->vval.v_number = do_dialog(type, NULL, message, buttons,
 								   def, NULL);
 #else
     rettv->vval.v_number = 0;
@@ -7353,14 +7494,21 @@
 	    li = l->lv_first;
 	    if (argvars[2].v_type != VAR_UNKNOWN)
 	    {
-		ic = get_tv_number(&argvars[2]);
+		int error = FALSE;
+
+		ic = get_tv_number_chk(&argvars[2], &error);
 		if (argvars[3].v_type != VAR_UNKNOWN)
 		{
-		    idx = get_tv_number(&argvars[3]);
-		    li = list_find(l, idx);
-		    if (li == NULL)
-			EMSGN(_(e_listidx), idx);
+		    idx = get_tv_number_chk(&argvars[3], &error);
+		    if (!error)
+		    {
+			li = list_find(l, idx);
+			if (li == NULL)
+			    EMSGN(_(e_listidx), idx);
+		    }
 		}
+		if (error)
+		    li = NULL;
 	    }
 
 	    for ( ; li != NULL; li = li->li_next)
@@ -7376,14 +7524,16 @@
 
 	if ((d = argvars[0].vval.v_dict) != NULL)
 	{
+	    int error = FALSE;
+
 	    if (argvars[2].v_type != VAR_UNKNOWN)
 	    {
-		ic = get_tv_number(&argvars[2]);
+		ic = get_tv_number_chk(&argvars[2], &error);
 		if (argvars[3].v_type != VAR_UNKNOWN)
 		    EMSG(_(e_invarg));
 	    }
 
-	    todo = d->dv_hashtab.ht_used;
+	    todo = error ? 0 : d->dv_hashtab.ht_used;
 	    for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
 	    {
 		if (!HASHITEM_EMPTY(hi))
@@ -7446,9 +7596,11 @@
     long	line, col;
 
     line = get_tv_lnum(argvars);
+    col = get_tv_number_chk(&argvars[1], NULL);
+    if (line < 0 || col < 0)
+	return;		/* type error; errmsg already given */
     if (line > 0)
 	curwin->w_cursor.lnum = line;
-    col = get_tv_number(&argvars[1]);
     if (col > 0)
 	curwin->w_cursor.col = col - 1;
 #ifdef FEAT_VIRTUALEDIT
@@ -7478,7 +7630,7 @@
     int		noref = 0;
 
     if (argvars[1].v_type != VAR_UNKNOWN)
-	noref = get_tv_number(&argvars[1]);
+	noref = get_tv_number_chk(&argvars[1], NULL);
     if (noref < 0 || noref > 1)
 	EMSG(_(e_invarg));
     else
@@ -7549,6 +7701,8 @@
     int			filler_lines;
     int			col;
 
+    if (lnum < 0)	/* ignore type error in {lnum} arg */
+	lnum = 0;
     if (lnum != prev_lnum
 	    || changedtick != curbuf->b_changedtick
 	    || fnum != curbuf->b_fnum)
@@ -7578,7 +7732,7 @@
 
     if (hlID == HLF_CHD || hlID == HLF_TXD)
     {
-	col = get_tv_number(&argvars[1]) - 1;
+	col = get_tv_number(&argvars[1]) - 1; /* ignore type error in {col} */
 	if (col >= change_start && col <= change_end)
 	    hlID = HLF_TXD;			/* changed text */
 	else
@@ -7650,11 +7804,15 @@
 {
     char_u	*s;
 
-    s = get_tv_string(&argvars[0]);
-    s = skipwhite(s);
+    s = get_tv_string_chk(&argvars[0]);
+    if (s != NULL)
+	s = skipwhite(s);
 
-    if (eval1(&s, rettv, TRUE) == FAIL)
+    if (s == NULL || eval1(&s, rettv, TRUE) == FAIL)
+    {
+	rettv->v_type = VAR_NUMBER;
 	rettv->vval.v_number = 0;
+    }
     else if (*s != NUL)
 	EMSG(_(e_trailing));
 }
@@ -7772,6 +7930,7 @@
     char_u	*errormsg;
     int		flags = WILD_SILENT|WILD_USE_NL|WILD_LIST_NOTFOUND;
     expand_T	xpc;
+    int		error = FALSE;
 
     rettv->v_type = VAR_STRING;
     s = get_tv_string(&argvars[0]);
@@ -7785,12 +7944,18 @@
     {
 	/* When the optional second argument is non-zero, don't remove matches
 	 * for 'suffixes' and 'wildignore' */
-	if (argvars[1].v_type != VAR_UNKNOWN && get_tv_number(&argvars[1]))
+	if (argvars[1].v_type != VAR_UNKNOWN
+				    && get_tv_number_chk(&argvars[1], &error))
 	    flags |= WILD_KEEP_ALL;
-	ExpandInit(&xpc);
-	xpc.xp_context = EXPAND_FILES;
-	rettv->vval.v_string = ExpandOne(&xpc, s, NULL, flags, WILD_ALL);
-	ExpandCleanup(&xpc);
+	if (!error)
+	{
+	    ExpandInit(&xpc);
+	    xpc.xp_context = EXPAND_FILES;
+	    rettv->vval.v_string = ExpandOne(&xpc, s, NULL, flags, WILD_ALL);
+	    ExpandCleanup(&xpc);
+	}
+	else
+	    rettv->vval.v_string = NULL;
     }
 }
 
@@ -7809,6 +7974,7 @@
 	list_T		*l1, *l2;
 	listitem_T	*item;
 	long		before;
+	int		error = FALSE;
 
 	l1 = argvars[0].vval.v_list;
 	l2 = argvars[1].vval.v_list;
@@ -7817,7 +7983,10 @@
 	{
 	    if (argvars[2].v_type != VAR_UNKNOWN)
 	    {
-		before = get_tv_number(&argvars[2]);
+		before = get_tv_number_chk(&argvars[2], &error);
+		if (error)
+		    return;		/* type error; errmsg already given */
+
 		if (before == l1->lv_len)
 		    item = NULL;
 		else
@@ -7857,7 +8026,9 @@
 	    {
 		static char *(av[]) = {"keep", "force", "error"};
 
-		action = get_tv_string(&argvars[2]);
+		action = get_tv_string_chk(&argvars[2]);
+		if (action == NULL)
+		    return;		/* type error; errmsg already given */
 		for (i = 0; i < 3; ++i)
 		    if (STRCMP(action, av[i]) == 0)
 			break;
@@ -7963,22 +8134,30 @@
 
     if (argvars[1].v_type != VAR_UNKNOWN)
     {
-	p = get_tv_string_buf(&argvars[1], pathbuf);
-	if (*p != NUL)
-	    path = p;
+	p = get_tv_string_buf_chk(&argvars[1], pathbuf);
+	if (p == NULL)
+	    count = -1;	    /* error */
+	else
+	{
+	    if (*p != NUL)
+		path = p;
 
-	if (argvars[2].v_type != VAR_UNKNOWN)
-	    count = get_tv_number(&argvars[2]);
+	    if (argvars[2].v_type != VAR_UNKNOWN)
+		count = get_tv_number_chk(&argvars[2], NULL); /* -1: error */
+	}
     }
 
-    do
+    if (*fname != NUL && count >= 0)
     {
-	vim_free(fresult);
-	fresult = find_file_in_path_option(first ? fname : NULL,
-					    first ? (int)STRLEN(fname) : 0,
-					    0, first, path, dir, NULL);
-	first = FALSE;
-    } while (--count > 0 && fresult != NULL);
+	do
+	{
+	    vim_free(fresult);
+	    fresult = find_file_in_path_option(first ? fname : NULL,
+					       first ? (int)STRLEN(fname) : 0,
+					       0, first, path, dir, NULL);
+	    first = FALSE;
+	} while (--count > 0 && fresult != NULL);
+    }
 
     rettv->vval.v_string = fresult;
 #else
@@ -8073,52 +8252,59 @@
 	return;
     }
 
-    prepare_vimvar(VV_VAL, &save_val);
-    expr = skipwhite(get_tv_string_buf(&argvars[1], buf));
-
-    if (argvars[0].v_type == VAR_DICT)
+    expr = get_tv_string_buf_chk(&argvars[1], buf);
+    /* On type errors, the preceding call has already displayed an error
+     * message.  Avoid a misleading error message for an empty string that
+     * was not passed as argument. */
+    if (expr != NULL)
     {
-	prepare_vimvar(VV_KEY, &save_key);
-	vimvars[VV_KEY].vv_type = VAR_STRING;
+	prepare_vimvar(VV_VAL, &save_val);
+	expr = skipwhite(expr);
 
-	ht = &d->dv_hashtab;
-	hash_lock(ht);
-	todo = ht->ht_used;
-	for (hi = ht->ht_array; todo > 0; ++hi)
+	if (argvars[0].v_type == VAR_DICT)
 	{
-	    if (!HASHITEM_EMPTY(hi))
+	    prepare_vimvar(VV_KEY, &save_key);
+	    vimvars[VV_KEY].vv_type = VAR_STRING;
+
+	    ht = &d->dv_hashtab;
+	    hash_lock(ht);
+	    todo = ht->ht_used;
+	    for (hi = ht->ht_array; todo > 0; ++hi)
 	    {
-		--todo;
-		di = HI2DI(hi);
-		if (tv_check_lock(di->di_tv.v_lock, msg))
+		if (!HASHITEM_EMPTY(hi))
+		{
+		    --todo;
+		    di = HI2DI(hi);
+		    if (tv_check_lock(di->di_tv.v_lock, msg))
+			break;
+		    vimvars[VV_KEY].vv_str = vim_strsave(di->di_key);
+		    if (filter_map_one(&di->di_tv, expr, map, &rem) == FAIL)
+			break;
+		    if (!map && rem)
+			dictitem_remove(d, di);
+		    clear_tv(&vimvars[VV_KEY].vv_tv);
+		}
+	    }
+	    hash_unlock(ht);
+
+	    restore_vimvar(VV_KEY, &save_key);
+	}
+	else
+	{
+	    for (li = l->lv_first; li != NULL; li = nli)
+	    {
+		if (tv_check_lock(li->li_tv.v_lock, msg))
 		    break;
-		vimvars[VV_KEY].vv_str = vim_strsave(di->di_key);
-		if (filter_map_one(&di->di_tv, expr, map, &rem) == FAIL)
+		nli = li->li_next;
+		if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL)
 		    break;
 		if (!map && rem)
-		    dictitem_remove(d, di);
-		clear_tv(&vimvars[VV_KEY].vv_tv);
+		    listitem_remove(l, li);
 	    }
 	}
-	hash_unlock(ht);
 
-	restore_vimvar(VV_KEY, &save_key);
+	restore_vimvar(VV_VAL, &save_val);
     }
-    else
-    {
-	for (li = l->lv_first; li != NULL; li = nli)
-	{
-	    if (tv_check_lock(li->li_tv.v_lock, msg))
-		break;
-	    nli = li->li_next;
-	    if (filter_map_one(&li->li_tv, expr, map, &rem) == FAIL)
-		break;
-	    if (!map && rem)
-		listitem_remove(l, li);
-	}
-    }
-
-    restore_vimvar(VV_VAL, &save_val);
 
     copy_tv(&argvars[0], rettv);
 }
@@ -8150,9 +8336,15 @@
     }
     else
     {
+	int	    error = FALSE;
+
 	/* filter(): when expr is zero remove the item */
-	*remp = (get_tv_number(&rettv) == 0);
+	*remp = (get_tv_number_chk(&rettv, &error) == 0);
 	clear_tv(&rettv);
+	/* On type error, nothing has been removed; return FAIL to stop the
+	 * loop.  The error message was given by get_tv_number_chk(). */
+	if (error)
+	    return FAIL;
     }
     clear_tv(&vimvars[VV_VAL].vv_tv);
     return OK;
@@ -8206,11 +8398,15 @@
     char_u	*fbuf = NULL;
     char_u	buf[NUMBUFLEN];
 
-    fname = get_tv_string(&argvars[0]);
-    mods = get_tv_string_buf(&argvars[1], buf);
-    len = (int)STRLEN(fname);
-
-    (void)modify_fname(mods, &usedlen, &fname, &fbuf, &len);
+    fname = get_tv_string_chk(&argvars[0]);
+    mods = get_tv_string_buf_chk(&argvars[1], buf);
+    if (fname == NULL || mods == NULL)
+	fname = NULL;
+    else
+    {
+	len = (int)STRLEN(fname);
+	(void)modify_fname(mods, &usedlen, &fname, &fbuf, &len);
+    }
 
     rettv->v_type = VAR_STRING;
     if (fname == NULL)
@@ -8381,6 +8577,9 @@
     rettv->vval.v_string = NULL;
 #ifdef FEAT_FOLDING
     lnum = get_tv_lnum(argvars);
+    /* treat illegal types and illegal string values for {lnum} the same */
+    if (lnum < 0)
+	lnum = 0;
     fold_count = foldedCount(curwin, lnum, &foldinfo);
     if (fold_count > 0)
     {
@@ -8455,8 +8654,10 @@
     {
 	if ((l = argvars[0].vval.v_list) != NULL)
 	{
-	    li = list_find(l, get_tv_number(&argvars[1]));
-	    if (li != NULL)
+	    int		error = FALSE;
+
+	    li = list_find(l, get_tv_number_chk(&argvars[1], &error));
+	    if (!error && li != NULL)
 		tv = &li->li_tv;
 	}
     }
@@ -8496,9 +8697,10 @@
     char_u	*varname;
     dictitem_T	*v;
 
+    (void)get_tv_number(&argvars[0]);	    /* issue errmsg if type error */
+    varname = get_tv_string_chk(&argvars[1]);
     ++emsg_off;
     buf = get_buf_tv(&argvars[0]);
-    varname = get_tv_string(&argvars[1]);
 
     rettv->v_type = VAR_STRING;
     rettv->vval.v_string = NULL;
@@ -8518,6 +8720,11 @@
 	}
 	else
 	{
+	    if (*varname == NUL)
+		/* let getbufvar({nr}, "") return the "b:" dictionary.  The
+		 * scope prefix before the NUL byte is required by
+		 * find_var_in_ht(). */
+		varname = (char_u *)"b:" + 2;
 	    /* look up the variable */
 	    v = find_var_in_ht(&buf->b_vars.dv_hashtab, varname, FALSE);
 	    if (v != NULL)
@@ -8537,17 +8744,18 @@
     typval_T	*rettv;
 {
     varnumber_T		n;
+    int			error = FALSE;
 
     ++no_mapping;
     ++allow_keys;
     if (argvars[0].v_type == VAR_UNKNOWN)
 	/* getchar(): blocking wait. */
 	n = safe_vgetc();
-    else if (get_tv_number(&argvars[0]) == 1)
+    else if (get_tv_number_chk(&argvars[0], &error) == 1)
 	/* getchar(1): only check if char avail */
 	n = vpeekc();
-    else if (vpeekc() == NUL)
-	/* getchar(0) and no char avail: return zero */
+    else if (error || vpeekc() == NUL)
+	/* illegal argument or getchar(0) and no char avail: return zero */
 	n = 0;
     else
 	/* getchar(0) and char avail: return char */
@@ -8858,8 +9066,9 @@
     listitem_T	*li;
 
     lnum = get_tv_lnum(argvars);
-
-    if (argvars[1].v_type == VAR_UNKNOWN)
+    if (lnum < 0)
+	rettv->vval.v_number = 0;   /* failure; error message already given */
+    else if (argvars[1].v_type == VAR_UNKNOWN)
     {
 	if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count)
 	    p = ml_get(lnum);
@@ -8874,7 +9083,8 @@
 	end = get_tv_lnum(&argvars[1]);
 	if (end < lnum)
 	{
-	    EMSG(_(e_invrange));
+	    if (end >= 0)	    /* else: error message already given */
+		EMSG(_(e_invrange));
 	    rettv->vval.v_number = 0;
 	}
 	else
@@ -8945,12 +9155,14 @@
     char_u	*strregname;
     int		regname;
     int		arg2 = FALSE;
+    int		error = FALSE;
 
     if (argvars[0].v_type != VAR_UNKNOWN)
     {
-	strregname = get_tv_string(&argvars[0]);
+	strregname = get_tv_string_chk(&argvars[0]);
+	error = strregname == NULL;
 	if (argvars[1].v_type != VAR_UNKNOWN)
-	    arg2 = get_tv_number(&argvars[1]);
+	    arg2 = get_tv_number_chk(&argvars[1], &error);
     }
     else
 	strregname = vimvars[VV_REG].vv_str;
@@ -8959,7 +9171,8 @@
 	regname = '"';
 
     rettv->v_type = VAR_STRING;
-    rettv->vval.v_string = get_reg_contents(regname, TRUE, arg2);
+    rettv->vval.v_string = error ? NULL :
+				    get_reg_contents(regname, TRUE, arg2);
 }
 
 /*
@@ -8976,7 +9189,15 @@
     long	reglen = 0;
 
     if (argvars[0].v_type != VAR_UNKNOWN)
-	strregname = get_tv_string(&argvars[0]);
+    {
+	strregname = get_tv_string_chk(&argvars[0]);
+	if (strregname == NULL)	    /* type error; errmsg already given */
+	{
+	    rettv->v_type = VAR_STRING;
+	    rettv->vval.v_string = NULL;
+	    return;
+	}
+    }
     else
 	/* Default to v:register */
 	strregname = vimvars[VV_REG].vv_str;
@@ -9056,9 +9277,9 @@
     char_u	*varname;
     dictitem_T	*v;
 
-    ++emsg_off;
     win = find_win_by_nr(&argvars[0]);
-    varname = get_tv_string(&argvars[1]);
+    varname = get_tv_string_chk(&argvars[1]);
+    ++emsg_off;
 
     rettv->v_type = VAR_STRING;
     rettv->vval.v_string = NULL;
@@ -9081,6 +9302,11 @@
 	}
 	else
 	{
+	    if (*varname == NUL)
+		/* let getwinvar({nr}, "") return the "w:" dictionary.  The
+		 * scope prefix before the NUL byte is required by
+		 * find_var_in_ht(). */
+		varname = (char_u *)"w:" + 2;
 	    /* look up the variable */
 	    v = find_var_in_ht(&win->w_vars.dv_hashtab, varname, FALSE);
 	    if (v != NULL)
@@ -9118,10 +9344,13 @@
     typval_T	*rettv;
 {
     char_u	buf1[NUMBUFLEN];
+    char_u	*file = get_tv_string_buf_chk(&argvars[1], buf1);
 
     rettv->v_type = VAR_STRING;
-    rettv->vval.v_string = globpath(get_tv_string(&argvars[0]),
-				     get_tv_string_buf(&argvars[1], buf1));
+    if (file == NULL)
+	rettv->vval.v_string = NULL;
+    else
+	rettv->vval.v_string = globpath(get_tv_string(&argvars[0]), file);
 }
 
 /*
@@ -9697,7 +9926,8 @@
     if (check_restricted() || check_secure())
 	return;
 #ifdef FEAT_CMDHIST
-    histype = get_histtype(get_tv_string(&argvars[0]));
+    str = get_tv_string_chk(&argvars[0]);	/* NULL on type error */
+    histype = str != NULL ? get_histtype(str) : -1;
     if (histype >= 0)
     {
 	str = get_tv_string_buf(&argvars[1], buf);
@@ -9723,17 +9953,21 @@
 #ifdef FEAT_CMDHIST
     int		n;
     char_u	buf[NUMBUFLEN];
+    char_u	*str;
 
-    if (argvars[1].v_type == VAR_UNKNOWN)
+    str = get_tv_string_chk(&argvars[0]);	/* NULL on type error */
+    if (str == NULL)
+	n = 0;
+    else if (argvars[1].v_type == VAR_UNKNOWN)
 	/* only one argument: clear entire history */
-	n = clr_history(get_histtype(get_tv_string(&argvars[0])));
+	n = clr_history(get_histtype(str));
     else if (argvars[1].v_type == VAR_NUMBER)
 	/* index given: remove that entry */
-	n = del_history_idx(get_histtype(get_tv_string(&argvars[0])),
+	n = del_history_idx(get_histtype(str),
 					  (int)get_tv_number(&argvars[1]));
     else
 	/* string given: remove all matching entries */
-	n = del_history_entry(get_histtype(get_tv_string(&argvars[0])),
+	n = del_history_entry(get_histtype(str),
 				      get_tv_string_buf(&argvars[1], buf));
     rettv->vval.v_number = n;
 #else
@@ -9753,13 +9987,21 @@
 #ifdef FEAT_CMDHIST
     int		type;
     int		idx;
+    char_u	*str;
 
-    type = get_histtype(get_tv_string(&argvars[0]));
-    if (argvars[1].v_type == VAR_UNKNOWN)
-	idx = get_history_idx(type);
+    str = get_tv_string_chk(&argvars[0]);	/* NULL on type error */
+    if (str == NULL)
+	rettv->vval.v_string = NULL;
     else
-	idx = (int)get_tv_number(&argvars[1]);
-    rettv->vval.v_string = vim_strsave(get_history_entry(type, idx));
+    {
+	type = get_histtype(str);
+	if (argvars[1].v_type == VAR_UNKNOWN)
+	    idx = get_history_idx(type);
+	else
+	    idx = (int)get_tv_number_chk(&argvars[1], NULL);
+						    /* -1 on type error */
+	rettv->vval.v_string = vim_strsave(get_history_entry(type, idx));
+    }
 #else
     rettv->vval.v_string = NULL;
 #endif
@@ -9778,7 +10020,9 @@
     int		i;
 
 #ifdef FEAT_CMDHIST
-    i = get_histtype(get_tv_string(&argvars[0]));
+    char_u	*history = get_tv_string_chk(&argvars[0]);
+
+    i = history == NULL ? HIST_CMD - 1 : get_histtype(history);
     if (i >= HIST_CMD && i < HIST_COUNT)
 	i = get_history_idx(i);
     else
@@ -9905,12 +10149,16 @@
 	item = l->lv_first;
 	if (argvars[2].v_type != VAR_UNKNOWN)
 	{
+	    int		error = FALSE;
+
 	    /* Start at specified item.  Use the cached index that list_find()
 	     * sets, so that a negative number also works. */
-	    item = list_find(l, get_tv_number(&argvars[2]));
+	    item = list_find(l, get_tv_number_chk(&argvars[2], &error));
 	    idx = l->lv_idx;
 	    if (argvars[3].v_type != VAR_UNKNOWN)
-		ic = get_tv_number(&argvars[3]);
+		ic = get_tv_number_chk(&argvars[3], &error);
+	    if (error)
+		item = NULL;
 	}
 
 	for ( ; item != NULL; item = item->li_next, ++idx)
@@ -9933,11 +10181,12 @@
     typval_T	*argvars;
     typval_T	*rettv;
 {
-    char_u	*prompt = get_tv_string(&argvars[0]);
+    char_u	*prompt = get_tv_string_chk(&argvars[0]);
     char_u	*p = NULL;
     int		c;
     char_u	buf[NUMBUFLEN];
     int		cmd_silent_save = cmd_silent;
+    char_u	*defstr = (char_u *)"";
 
     rettv->v_type = VAR_STRING;
 
@@ -9971,17 +10220,22 @@
 	    *p = c;
 	}
 	cmdline_row = msg_row;
-    }
 
-    if (argvars[1].v_type != VAR_UNKNOWN)
-	stuffReadbuffSpec(get_tv_string_buf(&argvars[1], buf));
+	if (argvars[1].v_type != VAR_UNKNOWN)
+	{
+	    defstr = get_tv_string_buf_chk(&argvars[1], buf);
+	    if (defstr != NULL)
+		stuffReadbuffSpec(defstr);
+	}
 
-    rettv->vval.v_string =
+	if (defstr != NULL)
+	    rettv->vval.v_string =
 		getcmdline_prompt(inputsecret_flag ? NUL : '@', p, echo_attr);
 
-    /* since the user typed this, no need to wait for return */
-    need_wait_return = FALSE;
-    msg_didout = FALSE;
+	/* since the user typed this, no need to wait for return */
+	need_wait_return = FALSE;
+	msg_didout = FALSE;
+    }
     cmd_silent = cmd_silent_save;
 }
 
@@ -9999,21 +10253,25 @@
     {
 	char_u	*message;
 	char_u	buf[NUMBUFLEN];
+	char_u	*defstr = (char_u *)"";
 
-	message = get_tv_string(&argvars[0]);
-	if (argvars[1].v_type != VAR_UNKNOWN)
+	message = get_tv_string_chk(&argvars[0]);
+	if (argvars[1].v_type != VAR_UNKNOWN
+	    && (defstr = get_tv_string_buf_chk(&argvars[1], buf)) != NULL)
 	{
-	    STRNCPY(IObuff, get_tv_string_buf(&argvars[1], buf), IOSIZE);
+	    STRNCPY(IObuff, defstr, IOSIZE);
 	    IObuff[IOSIZE - 1] = NUL;
 	}
 	else
 	    IObuff[0] = NUL;
-	if (do_dialog(VIM_QUESTION, NULL, message, (char_u *)_("&OK\n&Cancel"),
-							      1, IObuff) == 1)
+	if (message != NULL && defstr != NULL
+		&& do_dialog(VIM_QUESTION, NULL, message,
+				(char_u *)_("&OK\n&Cancel"), 1, IObuff) == 1)
 	    rettv->vval.v_string = vim_strsave(IObuff);
 	else
 	{
-	    if (argvars[1].v_type != VAR_UNKNOWN
+	    if (message != NULL && defstr != NULL
+					&& argvars[1].v_type != VAR_UNKNOWN
 					&& argvars[2].v_type != VAR_UNKNOWN)
 		rettv->vval.v_string = vim_strsave(
 				      get_tv_string_buf(&argvars[2], buf));
@@ -10099,6 +10357,7 @@
     long	before = 0;
     listitem_T	*item;
     list_T	*l;
+    int		error = FALSE;
 
     rettv->vval.v_number = 0;
     if (argvars[0].v_type != VAR_LIST)
@@ -10107,7 +10366,9 @@
 	    && !tv_check_lock(l->lv_lock, (char_u *)"insert()"))
     {
 	if (argvars[2].v_type != VAR_UNKNOWN)
-	    before = get_tv_number(&argvars[2]);
+	    before = get_tv_number_chk(&argvars[2], &error);
+	if (error)
+	    return;		/* type error; errmsg already given */
 
 	if (before == l->lv_len)
 	    item = NULL;
@@ -10339,14 +10600,19 @@
     if (argvars[1].v_type == VAR_UNKNOWN)
 	sep = (char_u *)" ";
     else
-	sep = get_tv_string(&argvars[1]);
-
-    ga_init2(&ga, (int)sizeof(char), 80);
-    list_join(&ga, argvars[0].vval.v_list, sep, TRUE);
-    ga_append(&ga, NUL);
+	sep = get_tv_string_chk(&argvars[1]);
 
     rettv->v_type = VAR_STRING;
-    rettv->vval.v_string = (char_u *)ga.ga_data;
+
+    if (sep != NULL)
+    {
+	ga_init2(&ga, (int)sizeof(char), 80);
+	list_join(&ga, argvars[0].vval.v_list, sep, TRUE);
+	ga_append(&ga, NUL);
+	rettv->vval.v_string = (char_u *)ga.ga_data;
+    }
+    else
+	rettv->vval.v_string = NULL;
 }
 
 /*
@@ -10577,9 +10843,12 @@
 	return;
 
     if (argvars[1].v_type != VAR_UNKNOWN)
-	which = get_tv_string_buf(&argvars[1], buf);
+	which = get_tv_string_buf_chk(&argvars[1], buf);
     else
 	which = (char_u *)"";
+    if (which == NULL)
+	return;
+
     mode = get_map_mode(&which, 0);
 
     keys = replace_termcodes(keys, &keys_buf, TRUE, TRUE);
@@ -10683,11 +10952,17 @@
     else
 	expr = str = get_tv_string(&argvars[0]);
 
-    pat = get_tv_string_buf(&argvars[1], patbuf);
+    pat = get_tv_string_buf_chk(&argvars[1], patbuf);
+    if (pat == NULL)
+	goto theend;
 
     if (argvars[2].v_type != VAR_UNKNOWN)
     {
-	start = get_tv_number(&argvars[2]);
+	int	    error = FALSE;
+
+	start = get_tv_number_chk(&argvars[2], &error);
+	if (error)
+	    goto theend;
 	if (l != NULL)
 	{
 	    li = list_find(l, start);
@@ -10705,7 +10980,9 @@
 	}
 
 	if (argvars[3].v_type != VAR_UNKNOWN)
-	    nth = get_tv_number(&argvars[3]);
+	    nth = get_tv_number_chk(&argvars[3], &error);
+	if (error)
+	    goto theend;
     }
 
     regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
@@ -10856,6 +11133,7 @@
 {
     long	n = 0;
     long	i;
+    int		error = FALSE;
 
     if (argvars[0].v_type == VAR_LIST)
     {
@@ -10868,13 +11146,13 @@
 	    li = l->lv_first;
 	    if (li != NULL)
 	    {
-		n = get_tv_number(&li->li_tv);
+		n = get_tv_number_chk(&li->li_tv, &error);
 		while (1)
 		{
 		    li = li->li_next;
 		    if (li == NULL)
 			break;
-		    i = get_tv_number(&li->li_tv);
+		    i = get_tv_number_chk(&li->li_tv, &error);
 		    if (domax ? i > n : i < n)
 			n = i;
 		}
@@ -10897,7 +11175,7 @@
 		if (!HASHITEM_EMPTY(hi))
 		{
 		    --todo;
-		    i = get_tv_number(&HI2DI(hi)->di_tv);
+		    i = get_tv_number_chk(&HI2DI(hi)->di_tv, &error);
 		    if (first)
 		    {
 			n = i;
@@ -10911,7 +11189,7 @@
     }
     else
 	EMSG(_(e_listdictarg));
-    rettv->vval.v_number = n;
+    rettv->vval.v_number = error ? 0 : n;
 }
 
 /*
@@ -10990,11 +11268,11 @@
     if (argvars[1].v_type != VAR_UNKNOWN)
     {
 	if (argvars[2].v_type != VAR_UNKNOWN)
-	    prot = get_tv_number(&argvars[2]);
-	if (STRCMP(get_tv_string(&argvars[1]), "p") == 0)
+	    prot = get_tv_number_chk(&argvars[2], NULL);
+	if (prot != -1 && STRCMP(get_tv_string(&argvars[1]), "p") == 0)
 	    mkdir_recurse(dir, prot);
     }
-    rettv->vval.v_number = vim_mkdir_emsg(dir, prot);
+    rettv->vval.v_number = prot != -1 ? vim_mkdir_emsg(dir, prot) : 0;
 }
 #endif
 
@@ -11050,7 +11328,7 @@
 
     for (lnum = get_tv_lnum(argvars); ; ++lnum)
     {
-	if (lnum > curbuf->b_ml.ml_line_count)
+	if (lnum < 0 || lnum > curbuf->b_ml.ml_line_count)
 	{
 	    lnum = 0;
 	    break;
@@ -11117,8 +11395,9 @@
     long	i;
     list_T	*l;
     listitem_T	*li;
+    int		error = FALSE;
 
-    start = get_tv_number(&argvars[0]);
+    start = get_tv_number_chk(&argvars[0], &error);
     if (argvars[1].v_type == VAR_UNKNOWN)
     {
 	end = start - 1;
@@ -11126,12 +11405,14 @@
     }
     else
     {
-	end = get_tv_number(&argvars[1]);
+	end = get_tv_number_chk(&argvars[1], &error);
 	if (argvars[2].v_type != VAR_UNKNOWN)
-	    stride = get_tv_number(&argvars[2]);
+	    stride = get_tv_number_chk(&argvars[2], &error);
     }
 
     rettv->vval.v_number = 0;
+    if (error)
+	return;		/* type error; errmsg already given */
     if (stride == 0)
 	EMSG(_("E726: Stride is zero"));
     else if (stride > 0 ? end < start : end > start)
@@ -11375,7 +11656,9 @@
 	return;
 # endif
 
-    server_name = get_tv_string(&argvars[0]);
+    server_name = get_tv_string_chk(&argvars[0]);
+    if (server_name == NULL)
+	return;		/* type error; errmsg already given */
     keys = get_tv_string_buf(&argvars[1], buf);
 # ifdef WIN32
     if (serverSendToVim(server_name, keys, &r, &w, expr, TRUE) < 0)
@@ -11397,11 +11680,14 @@
     {
 	dictitem_T	v;
 	char_u		str[30];
+	char_u		*idvar;
 
 	sprintf((char *)str, "0x%x", (unsigned int)w);
 	v.di_tv.v_type = VAR_STRING;
 	v.di_tv.vval.v_string = vim_strsave(str);
-	set_var(get_tv_string(&argvars[2]), &v.di_tv, FALSE);
+	idvar = get_tv_string_chk(&argvars[2]);
+	if (idvar != NULL)
+	    set_var(idvar, &v.di_tv, FALSE);
 	vim_free(v.di_tv.vval.v_string);
     }
 }
@@ -11436,7 +11722,12 @@
 #ifdef FEAT_CLIENTSERVER
 # ifdef WIN32
     /* On Win32 it's done in this application. */
-    serverForeground(get_tv_string(&argvars[0]));
+    {
+	char_u	*server_name = get_tv_string_chk(&argvars[0]);
+
+	if (server_name != NULL)
+	    serverForeground(server_name);
+    }
 # else
     /* Send a foreground() expression to the server. */
     argvars[1].v_type = VAR_STRING;
@@ -11460,14 +11751,21 @@
 # ifdef WIN32
     int		n = 0;
 # endif
+    char_u	*serverid;
 
     if (check_restricted() || check_secure())
     {
 	rettv->vval.v_number = -1;
 	return;
     }
+    serverid = get_tv_string_chk(&argvars[0]);
+    if (serverid == NULL)
+    {
+	rettv->vval.v_number = -1;
+	return;		/* type error; errmsg already given */
+    }
 # ifdef WIN32
-    sscanf(get_tv_string(&argvars[0]), "%x", &n);
+    sscanf(serverid, "%x", &n);
     if (n == 0)
 	rettv->vval.v_number = -1;
     else
@@ -11481,14 +11779,18 @@
 	return;
 
     rettv->vval.v_number = serverPeekReply(X_DISPLAY,
-			   serverStrToWin(get_tv_string(&argvars[0])), &s);
+						serverStrToWin(serverid), &s);
 # endif
 
     if (argvars[1].v_type != VAR_UNKNOWN && rettv->vval.v_number > 0)
     {
+	char_u		*retvar;
+
 	v.di_tv.v_type = VAR_STRING;
 	v.di_tv.vval.v_string = vim_strsave(s);
-	set_var(get_tv_string(&argvars[1]), &v.di_tv, FALSE);
+	retvar = get_tv_string_chk(&argvars[1]);
+	if (retvar != NULL)
+	    set_var(retvar, &v.di_tv, FALSE);
 	vim_free(v.di_tv.vval.v_string);
     }
 #else
@@ -11505,19 +11807,21 @@
     char_u	*r = NULL;
 
 #ifdef FEAT_CLIENTSERVER
-    if (!check_restricted() && !check_secure())
+    char_u	*serverid = get_tv_string_chk(&argvars[0]);
+
+    if (serverid != NULL && !check_restricted() && !check_secure())
     {
 # ifdef WIN32
 	/* The server's HWND is encoded in the 'id' parameter */
 	int		n = 0;
 
-	sscanf(get_tv_string(&argvars[0]), "%x", &n);
+	sscanf(serverid, "%x", &n);
 	if (n != 0)
 	    r = serverGetReply((HWND)n, FALSE, TRUE, TRUE);
 	if (r == NULL)
 # else
 	if (check_connection() == FAIL || serverReadReply(X_DISPLAY,
-		serverStrToWin(get_tv_string(&argvars[0])), &r, FALSE) < 0)
+		serverStrToWin(serverid), &r, FALSE) < 0)
 # endif
 	    EMSG(_("E277: Unable to read a server reply"));
     }
@@ -11567,15 +11871,18 @@
 	else if ((d = argvars[0].vval.v_dict) != NULL
 		&& !tv_check_lock(d->dv_lock, (char_u *)"remove()"))
 	{
-	    key = get_tv_string(&argvars[1]);
-	    di = dict_find(d, key, -1);
-	    if (di == NULL)
-		EMSG2(_(e_dictkey), key);
-	    else
+	    key = get_tv_string_chk(&argvars[1]);
+	    if (key != NULL)
 	    {
-		*rettv = di->di_tv;
-		init_tv(&di->di_tv);
-		dictitem_remove(d, di);
+		di = dict_find(d, key, -1);
+		if (di == NULL)
+		    EMSG2(_(e_dictkey), key);
+		else
+		{
+		    *rettv = di->di_tv;
+		    init_tv(&di->di_tv);
+		    dictitem_remove(d, di);
+		}
 	    }
 	}
     }
@@ -11584,9 +11891,12 @@
     else if ((l = argvars[0].vval.v_list) != NULL
 	    && !tv_check_lock(l->lv_lock, (char_u *)"remove()"))
     {
-	idx = get_tv_number(&argvars[1]);
-	item = list_find(l, idx);
-	if (item == NULL)
+	int	    error = FALSE;
+
+	idx = get_tv_number_chk(&argvars[1], &error);
+	if (error)
+	    ;		/* type error: do nothing, errmsg already given */
+	else if ((item = list_find(l, idx)) == NULL)
 	    EMSGN(_(e_listidx), idx);
 	else
 	{
@@ -11600,9 +11910,10 @@
 	    else
 	    {
 		/* Remove range of items, return list with values. */
-		end = get_tv_number(&argvars[2]);
-		item2 = list_find(l, end);
-		if (item2 == NULL)
+		end = get_tv_number_chk(&argvars[2], &error);
+		if (error)
+		    ;		/* type error: do nothing */
+		else if ((item2 = list_find(l, end)) == NULL)
 		    EMSGN(_(e_listidx), end);
 		else
 		{
@@ -11964,7 +12275,9 @@
 
     if (varp->v_type != VAR_UNKNOWN)
     {
-	flags = get_tv_string_buf(varp, nbuf);
+	flags = get_tv_string_buf_chk(varp, nbuf);
+	if (flags == NULL)
+	    return 0;		/* type error; errmsg already given */
 	while (*flags != NUL)
 	{
 	    switch (*flags)
@@ -12051,7 +12364,7 @@
 {
     char_u	*spat, *mpat, *epat;
     char_u	*skip;
-    char_u	*pat, *pat2, *pat3;
+    char_u	*pat, *pat2 = NULL, *pat3 = NULL;
     pos_T	pos;
     pos_T	firstpos;
     pos_T	foundpos;
@@ -12076,9 +12389,11 @@
     p_cpo = (char_u *)"";
 
     /* Get the three pattern arguments: start, middle, end. */
-    spat = get_tv_string(&argvars[0]);
-    mpat = get_tv_string_buf(&argvars[1], nbuf1);
-    epat = get_tv_string_buf(&argvars[2], nbuf2);
+    spat = get_tv_string_chk(&argvars[0]);
+    mpat = get_tv_string_buf_chk(&argvars[1], nbuf1);
+    epat = get_tv_string_buf_chk(&argvars[2], nbuf2);
+    if (spat == NULL || mpat == NULL || epat == NULL)
+	goto theend;	    /* type error */
 
     /* Make two search patterns: start/end (pat2, for in nested pairs) and
      * start/middle/end (pat3, for the top pair). */
@@ -12103,7 +12418,9 @@
 	    || argvars[4].v_type == VAR_UNKNOWN)
 	skip = (char_u *)"";
     else
-	skip = get_tv_string_buf(&argvars[4], nbuf3);
+	skip = get_tv_string_buf_chk(&argvars[4], nbuf3);
+    if (skip == NULL)
+	goto theend;	    /* type error */
 
     save_cursor = curwin->w_cursor;
     pos = curwin->w_cursor;
@@ -12198,10 +12515,12 @@
 {
 #ifdef FEAT_CLIENTSERVER
     char_u	buf[NUMBUFLEN];
-    char_u	*server = get_tv_string(&argvars[0]);
-    char_u	*reply = get_tv_string_buf(&argvars[1], buf);
+    char_u	*server = get_tv_string_chk(&argvars[0]);
+    char_u	*reply = get_tv_string_buf_chk(&argvars[1], buf);
 
     rettv->vval.v_number = -1;
+    if (server == NULL || reply == NULL)
+	return;
     if (check_restricted() || check_secure())
 	return;
 # ifdef FEAT_X11
@@ -12260,11 +12579,14 @@
     typval_T	*varp;
     char_u	nbuf[NUMBUFLEN];
 
+    rettv->vval.v_number = 0;
+
     if (check_restricted() || check_secure())
 	return;
+    (void)get_tv_number(&argvars[0]);	    /* issue errmsg if type error */
+    varname = get_tv_string_chk(&argvars[1]);
     ++emsg_off;
     buf = get_buf_tv(&argvars[0]);
-    varname = get_tv_string(&argvars[1]);
     varp = &argvars[2];
 
     if (buf != NULL && varname != NULL && varp != NULL)
@@ -12279,9 +12601,17 @@
 
 	if (*varname == '&')
 	{
+	    long	numval;
+	    char_u	*strval;
+	    int		error = FALSE;
+
 	    ++varname;
-	    set_option_value(varname, get_tv_number(varp),
-				 get_tv_string_buf(varp, nbuf), OPT_LOCAL);
+	    --emsg_off;
+	    numval = get_tv_number_chk(varp, &error);
+	    strval = get_tv_string_buf_chk(varp, nbuf);
+	    ++emsg_off;
+	    if (!error && strval != NULL)
+		set_option_value(varname, numval, strval, OPT_LOCAL);
 	}
 	else
 	{
@@ -12313,8 +12643,10 @@
     typval_T	*argvars;
     typval_T	*rettv;
 {
-    rettv->vval.v_number = set_cmdline_pos(
-				      (int)get_tv_number(&argvars[0]) - 1);
+    int		pos = (int)get_tv_number(&argvars[0]) - 1;
+
+    if (pos >= 0)
+	rettv->vval.v_number = set_cmdline_pos(pos);
 }
 
 /*
@@ -12339,7 +12671,7 @@
 	li = l->lv_first;
     }
     else
-	line = get_tv_string(&argvars[1]);
+	line = get_tv_string_chk(&argvars[1]);
 
     rettv->vval.v_number = 0;		/* OK */
     for (;;)
@@ -12349,12 +12681,12 @@
 	    /* list argument, get next string */
 	    if (li == NULL)
 		break;
-	    line = get_tv_string(&li->li_tv);
+	    line = get_tv_string_chk(&li->li_tv);
 	    li = li->li_next;
 	}
 
 	rettv->vval.v_number = 1;	/* FAIL */
-	if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1)
+	if (line == NULL || lnum < 1 || lnum > curbuf->b_ml.ml_line_count + 1)
 	    break;
 	if (lnum <= curbuf->b_ml.ml_line_count)
 	{
@@ -12406,7 +12738,9 @@
 
 	if (argvars[1].v_type == VAR_STRING)
 	{
-	    act = get_tv_string(&argvars[1]);
+	    act = get_tv_string_chk(&argvars[1]);
+	    if (act == NULL)
+		return;		/* type error; errmsg already given */
 	    if (*act == 'a' || *act == 'r')
 		action = *act;
 	}
@@ -12428,6 +12762,7 @@
     int		regname;
     char_u	*strregname;
     char_u	*stropt;
+    char_u	*strval;
     int		append;
     char_u	yank_type;
     long	block_len;
@@ -12436,10 +12771,12 @@
     yank_type = MAUTO;
     append = FALSE;
 
-    strregname = get_tv_string(argvars);
+    strregname = get_tv_string_chk(argvars);
     rettv->vval.v_number = 1;		/* FAIL is default */
 
-    regname = (strregname == NULL ? '"' : *strregname);
+    if (strregname == NULL)
+	return;		/* type error; errmsg already given */
+    regname = *strregname;
     if (regname == 0 || regname == '@')
 	regname = '"';
     else if (regname == '=')
@@ -12447,7 +12784,10 @@
 
     if (argvars[2].v_type != VAR_UNKNOWN)
     {
-	for (stropt = get_tv_string(&argvars[2]); *stropt != NUL; ++stropt)
+	stropt = get_tv_string_chk(&argvars[2]);
+	if (stropt == NULL)
+	    return;		/* type error */
+	for (; *stropt != NUL; ++stropt)
 	    switch (*stropt)
 	    {
 		case 'a': case 'A':	/* append */
@@ -12473,7 +12813,9 @@
 	    }
     }
 
-    write_reg_contents_ex(regname, get_tv_string(&argvars[1]), -1,
+    strval = get_tv_string_chk(&argvars[1]);
+    if (strval != NULL)
+	write_reg_contents_ex(regname, strval, -1,
 						append, yank_type, block_len);
     rettv->vval.v_number = 0;
 }
@@ -12496,11 +12838,13 @@
     typval_T	*varp;
     char_u	nbuf[NUMBUFLEN];
 
+    rettv->vval.v_number = 0;
+
     if (check_restricted() || check_secure())
 	return;
-    ++emsg_off;
     win = find_win_by_nr(&argvars[0]);
-    varname = get_tv_string(&argvars[1]);
+    varname = get_tv_string_chk(&argvars[1]);
+    ++emsg_off;
     varp = &argvars[2];
 
     if (win != NULL && varname != NULL && varp != NULL)
@@ -12514,9 +12858,17 @@
 
 	if (*varname == '&')
 	{
+	    long	numval;
+	    char_u	*strval;
+	    int		error = FALSE;
+
 	    ++varname;
-	    set_option_value(varname, get_tv_number(varp),
-				 get_tv_string_buf(varp, nbuf), OPT_LOCAL);
+	    --emsg_off;
+	    numval = get_tv_number_chk(varp, &error);
+	    strval = get_tv_string_buf_chk(varp, nbuf);
+	    ++emsg_off;
+	    if (!error && strval != NULL)
+		set_option_value(varname, numval, strval, OPT_LOCAL);
 	}
 	else
 	{
@@ -12572,6 +12924,7 @@
 
 static int	item_compare_ic;
 static char_u	*item_compare_func;
+static int	item_compare_func_err;
 #define ITEM_COMPARE_FAIL 999
 
 /*
@@ -12615,6 +12968,10 @@
     typval_T	argv[2];
     int		dummy;
 
+    /* shortcut after failure in previous call; compare all items equal */
+    if (item_compare_func_err)
+	return 0;
+
     /* copy the values.  This is needed to be able to set v_lock to VAR_FIXED
      * in the copy without changing the original list items. */
     copy_tv(&(*(listitem_T **)s1)->li_tv, &argv[0]);
@@ -12629,7 +12986,10 @@
     if (res == FAIL)
 	res = ITEM_COMPARE_FAIL;
     else
-	res = get_tv_number(&rettv);
+	/* return value has wrong type */
+	res = get_tv_number_chk(&rettv, &item_compare_func_err);
+    if (item_compare_func_err)
+	res = ITEM_COMPARE_FAIL;
     clear_tv(&rettv);
     return res;
 }
@@ -12669,10 +13029,14 @@
 	if (argvars[1].v_type != VAR_UNKNOWN)
 	{
 	    if (argvars[1].v_type == VAR_FUNC)
-		item_compare_func = argvars[0].vval.v_string;
+		item_compare_func = argvars[1].vval.v_string;
 	    else
 	    {
-		i = get_tv_number(&argvars[1]);
+		int	    error = FALSE;
+
+		i = get_tv_number_chk(&argvars[1], &error);
+		if (error)
+		    return;		/* type error; errmsg already given */
 		if (i == 1)
 		    item_compare_ic = TRUE;
 		else
@@ -12688,6 +13052,7 @@
 	for (li = l->lv_first; li != NULL; li = li->li_next)
 	    ptrs[i++] = li;
 
+	item_compare_func_err = FALSE;
 	/* test the compare function */
 	if (item_compare_func != NULL
 		&& item_compare2((void *)&ptrs[0], (void *)&ptrs[1])
@@ -12699,11 +13064,14 @@
 	    qsort((void *)ptrs, (size_t)len, sizeof(listitem_T *),
 		    item_compare_func == NULL ? item_compare : item_compare2);
 
-	    /* Clear the List and append the items in the sorted order. */
-	    l->lv_first = l->lv_last = NULL;
-	    l->lv_len = 0;
-	    for (i = 0; i < len; ++i)
-		list_append(l, ptrs[i]);
+	    if (!item_compare_func_err)
+	    {
+		/* Clear the List and append the items in the sorted order. */
+		l->lv_first = l->lv_last = NULL;
+		l->lv_len = 0;
+		for (i = 0; i < len; ++i)
+		    list_append(l, ptrs[i]);
+	    }
 	}
 
 	vim_free(ptrs);
@@ -12726,6 +13094,7 @@
     list_T	*l;
     colnr_T	col = 0;
     int		keepempty = FALSE;
+    int		typeerr = FALSE;
 
     /* Make 'cpoptions' empty, the 'l' flag should not be used here. */
     save_cpo = p_cpo;
@@ -12734,9 +13103,11 @@
     str = get_tv_string(&argvars[0]);
     if (argvars[1].v_type != VAR_UNKNOWN)
     {
-	pat = get_tv_string_buf(&argvars[1], patbuf);
+	pat = get_tv_string_buf_chk(&argvars[1], patbuf);
+	if (pat == NULL)
+	    typeerr = TRUE;
 	if (argvars[2].v_type != VAR_UNKNOWN)
-	    keepempty = get_tv_number(&argvars[2]);
+	    keepempty = get_tv_number_chk(&argvars[2], &typeerr);
     }
     if (pat == NULL || *pat == NUL)
 	pat = (char_u *)"[\\x01- ]\\+";
@@ -12747,6 +13118,8 @@
     rettv->v_type = VAR_LIST;
     rettv->vval.v_list = l;
     ++l->lv_refcount;
+    if (typeerr)
+	return;
 
     regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
     if (regmatch.regprog != NULL)
@@ -12873,14 +13246,18 @@
     char_u	*pos;
     int		start_idx;
 
-    needle = get_tv_string(&argvars[1]);
-    save_haystack = haystack = get_tv_string_buf(&argvars[0], buf);
+    needle = get_tv_string_chk(&argvars[1]);
+    save_haystack = haystack = get_tv_string_buf_chk(&argvars[0], buf);
     rettv->vval.v_number = -1;
+    if (needle == NULL || haystack == NULL)
+	return;		/* type error; errmsg already given */
 
     if (argvars[2].v_type != VAR_UNKNOWN)
     {
-	start_idx = get_tv_number(&argvars[2]);
-	if (start_idx >= (int)STRLEN(haystack))
+	int	    error = FALSE;
+
+	start_idx = get_tv_number_chk(&argvars[2], &error);
+	if (error || start_idx >= (int)STRLEN(haystack))
 	    return;
 	if (start_idx >= 0)
 	    haystack += start_idx;
@@ -12932,12 +13309,15 @@
     int		n;
     int		len;
     int		slen;
+    int		error = FALSE;
 
     p = get_tv_string(&argvars[0]);
     slen = (int)STRLEN(p);
 
-    n = get_tv_number(&argvars[1]);
-    if (argvars[2].v_type != VAR_UNKNOWN)
+    n = get_tv_number_chk(&argvars[1], &error);
+    if (error)
+	len = 0;
+    else if (argvars[2].v_type != VAR_UNKNOWN)
 	len = get_tv_number(&argvars[2]);
     else
 	len = slen - n;	    /* default len: all bytes that are available. */
@@ -12977,19 +13357,19 @@
     char_u	*lastmatch = NULL;
     int		haystack_len, end_idx;
 
-    needle = get_tv_string(&argvars[1]);
-    haystack = get_tv_string_buf(&argvars[0], buf);
+    needle = get_tv_string_chk(&argvars[1]);
+    haystack = get_tv_string_buf_chk(&argvars[0], buf);
     haystack_len = STRLEN(haystack);
+
+    rettv->vval.v_number = -1;
+    if (needle == NULL || haystack == NULL)
+	return;		/* type error; errmsg already given */
     if (argvars[2].v_type != VAR_UNKNOWN)
     {
 	/* Third argument: upper limit for index */
-	end_idx = get_tv_number(&argvars[2]);
+	end_idx = get_tv_number_chk(&argvars[2], NULL);
 	if (end_idx < 0)
-	{
-	    /* can never find a match */
-	    rettv->vval.v_number = -1;
-	    return;
-	}
+	    return;	/* can never find a match */
     }
     else
 	end_idx = haystack_len;
@@ -13037,7 +13417,8 @@
     typval_T	*rettv;
 {
     rettv->v_type = VAR_STRING;
-    rettv->vval.v_string = reg_submatch((int)get_tv_number(&argvars[0]));
+    rettv->vval.v_string =
+		    reg_submatch((int)get_tv_number_chk(&argvars[0], NULL));
 }
 
 /*
@@ -13052,12 +13433,16 @@
     char_u	subbuf[NUMBUFLEN];
     char_u	flagsbuf[NUMBUFLEN];
 
+    char_u	*str = get_tv_string_chk(&argvars[0]);
+    char_u	*pat = get_tv_string_buf_chk(&argvars[1], patbuf);
+    char_u	*sub = get_tv_string_buf_chk(&argvars[2], subbuf);
+    char_u	*flg = get_tv_string_buf_chk(&argvars[3], flagsbuf);
+
     rettv->v_type = VAR_STRING;
-    rettv->vval.v_string = do_string_sub(
-	    get_tv_string(&argvars[0]),
-	    get_tv_string_buf(&argvars[1], patbuf),
-	    get_tv_string_buf(&argvars[2], subbuf),
-	    get_tv_string_buf(&argvars[3], flagsbuf));
+    if (str == NULL || pat == NULL || sub == NULL || flg == NULL)
+	rettv->vval.v_string = NULL;
+    else
+	rettv->vval.v_string = do_string_sub(str, pat, sub, flg);
 }
 
 /*
@@ -13074,12 +13459,13 @@
     long	lnum;
     long	col;
     int		trans;
+    int		transerr;
 
-    lnum = get_tv_lnum(argvars);
-    col = get_tv_number(&argvars[1]) - 1;
-    trans = get_tv_number(&argvars[2]);
+    lnum = get_tv_lnum(argvars);		/* -1 on type error */
+    col = get_tv_number(&argvars[1]) - 1;	/* -1 on type error */
+    trans = get_tv_number_chk(&argvars[2], &transerr);
 
-    if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count
+    if (!transerr && lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count
 	    && col >= 0 && col < (long)STRLEN(ml_get(lnum)))
 	id = syn_get_id(lnum, (colnr_T)col, trans, NULL);
 #endif
@@ -13236,7 +13622,9 @@
 	    EMSG2(_(e_notopen), infile);
 	    goto done;
 	}
-	p = get_tv_string_buf(&argvars[1], buf);
+	p = get_tv_string_buf_chk(&argvars[1], buf);
+	if (p == NULL)
+	    goto done;		/* type error; errmsg already given */
 	if (fwrite(p, STRLEN(p), 1, fd) != 1)
 	    err = TRUE;
 	if (fclose(fd) != 0)
@@ -13305,6 +13693,8 @@
     tag_pattern = get_tv_string(&argvars[0]);
 
     rettv->vval.v_number = FALSE;
+    if (*tag_pattern == NUL)
+	return;
 
     l = list_alloc();
     if (l != NULL)
@@ -13407,39 +13797,8 @@
     typval_T	*argvars;
     typval_T	*rettv;
 {
-    char_u	*p;
-
-    p = vim_strsave(get_tv_string(&argvars[0]));
     rettv->v_type = VAR_STRING;
-    rettv->vval.v_string = p;
-
-    if (p != NULL)
-	while (*p != NUL)
-	{
-#ifdef FEAT_MBYTE
-	    int		l;
-
-	    if (enc_utf8)
-	    {
-		int c, uc;
-
-		c = utf_ptr2char(p);
-		uc = utf_toupper(c);
-		l = utf_ptr2len_check(p);
-		/* TODO: reallocate string when byte count changes. */
-		if (utf_char2len(uc) == l)
-		    utf_char2bytes(uc, p);
-		p += l;
-	    }
-	    else if (has_mbyte && (l = (*mb_ptr2len_check)(p)) > 1)
-		p += l;		/* skip multi-byte character */
-	    else
-#endif
-	    {
-		*p = TOUPPER_LOC(*p); /* note that toupper() can be a macro */
-		p++;
-	    }
-	}
+    rettv->vval.v_string = strup_save(get_tv_string(&argvars[0]));
 }
 
 /*
@@ -13468,12 +13827,14 @@
     garray_T	ga;
 
     instr = get_tv_string(&argvars[0]);
-    fromstr = get_tv_string_buf(&argvars[1], buf);
-    tostr = get_tv_string_buf(&argvars[2], buf2);
+    fromstr = get_tv_string_buf_chk(&argvars[1], buf);
+    tostr = get_tv_string_buf_chk(&argvars[2], buf2);
 
     /* Default return value: empty string. */
     rettv->v_type = VAR_STRING;
     rettv->vval.v_string = NULL;
+    if (fromstr == NULL || tostr == NULL)
+	    return;		/* type error; errmsg already given */
     ga_init2(&ga, (int)sizeof(char), 80);
 
 #ifdef FEAT_MBYTE
@@ -13718,8 +14079,10 @@
 
     if (argvars[0].v_type != VAR_UNKNOWN)
     {
-	arg = get_tv_string(&argvars[0]);
-	if (STRCMP(arg, "$") == 0)
+	arg = get_tv_string_chk(&argvars[0]);
+	if (arg == NULL)
+	    nr = 0;		/* type error; errmsg already given */
+	else if (STRCMP(arg, "$") == 0)
 	    twin = lastwin;
 	else if (STRCMP(arg, "#") == 0)
 	{
@@ -13806,9 +14169,11 @@
 #endif
     int		nr;
 
-    nr = get_tv_number(vp);
+    nr = get_tv_number_chk(vp, NULL);
 
 #ifdef FEAT_WINDOWS
+    if (nr < 0)
+	return NULL;
     if (nr == 0)
 	return curwin;
 
@@ -13906,7 +14271,9 @@
     static pos_T	pos;
     pos_T	*pp;
 
-    name = get_tv_string(varp);
+    name = get_tv_string_chk(varp);
+    if (name == NULL)
+	return NULL;
     if (name[0] == '.')		/* cursor */
 	return &curwin->w_cursor;
     if (name[0] == '\'')	/* mark */
@@ -14621,18 +14988,31 @@
 /*
  * Get the number value of a variable.
  * If it is a String variable, uses vim_str2nr().
+ * For incompatible types, return 0.
+ * get_tv_number_chk() is similar to get_tv_number(), but informs the
+ * caller of incompatible types: it sets *denote to TRUE if "denote"
+ * is not NULL or returns -1 otherwise.
  */
     static long
 get_tv_number(varp)
     typval_T	*varp;
 {
+    int		error = FALSE;
+
+    return get_tv_number_chk(varp, &error);	/* return 0L on error */
+}
+
+    static long
+get_tv_number_chk(varp, denote)
+    typval_T	*varp;
+    int		*denote;
+{
     long	n = 0L;
 
     switch (varp->v_type)
     {
 	case VAR_NUMBER:
-	    n = (long)(varp->vval.v_number);
-	    break;
+	    return (long)(varp->vval.v_number);
 	case VAR_FUNC:
 	    EMSG(_("E703: Using a Funcref as a number"));
 	    break;
@@ -14640,7 +15020,7 @@
 	    if (varp->vval.v_string != NULL)
 		vim_str2nr(varp->vval.v_string, NULL, NULL,
 							TRUE, TRUE, &n, NULL);
-	    break;
+	    return n;
 	case VAR_LIST:
 	    EMSG(_("E745: Using a List as a number"));
 	    break;
@@ -14651,11 +15031,16 @@
 	    EMSG2(_(e_intern2), "get_tv_number()");
 	    break;
     }
+    if (denote == NULL)		/* useful for values that must be unsigned */
+	n = -1;
+    else
+	*denote = TRUE;
     return n;
 }
 
 /*
  * Get the lnum from the first argument.  Also accepts ".", "$", etc.
+ * Returns -1 on error.
  */
     static linenr_T
 get_tv_lnum(argvars)
@@ -14664,7 +15049,7 @@
     typval_T	rettv;
     linenr_T	lnum;
 
-    lnum = get_tv_number(&argvars[0]);
+    lnum = get_tv_number_chk(&argvars[0], NULL);
     if (lnum == 0)  /* no valid number, try using line() */
     {
 	rettv.v_type = VAR_NUMBER;
@@ -14682,6 +15067,8 @@
  * get_tv_string_buf() uses a given buffer.
  * If the String variable has never been set, return an empty string.
  * Never returns NULL;
+ * get_tv_string_chk() and get_tv_string_buf_chk() are similar, but return
+ * NULL on error.
  */
     static char_u *
 get_tv_string(varp)
@@ -14697,6 +15084,25 @@
     typval_T	*varp;
     char_u	*buf;
 {
+    char_u	*res =  get_tv_string_buf_chk(varp, buf);
+
+    return res != NULL ? res : (char_u *)"";
+}
+
+    static char_u *
+get_tv_string_chk(varp)
+    typval_T	*varp;
+{
+    static char_u   mybuf[NUMBUFLEN];
+
+    return get_tv_string_buf_chk(varp, mybuf);
+}
+
+    static char_u *
+get_tv_string_buf_chk(varp, buf)
+    typval_T	*varp;
+    char_u	*buf;
+{
     switch (varp->v_type)
     {
 	case VAR_NUMBER:
@@ -14714,12 +15120,12 @@
 	case VAR_STRING:
 	    if (varp->vval.v_string != NULL)
 		return varp->vval.v_string;
-	    break;
+	    return (char_u *)"";
 	default:
 	    EMSG2(_(e_intern2), "get_tv_string_buf()");
 	    break;
     }
-    return (char_u *)"";
+    return NULL;
 }
 
 /*
diff --git a/src/ex_cmds.c b/src/ex_cmds.c
index 9bef62d..8346145 100644
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -5349,13 +5349,13 @@
     int		i;
     static char *(mtable[]) = {"*", "g*", "[*", "]*", ":*",
 			       "/*", "/\\*", "\"*", "/\\(\\)",
-			       "?", ":?", "?<CR>", "g?", "g?g?", "g??",
+			       "?", ":?", "?<CR>", "g?", "g?g?", "g??", "z?",
 			       "/\\?", "/\\z(\\)", "\\=", ":s\\=",
 			       "[count]", "[quotex]", "[range]",
 			       "[pattern]", "\\|", "\\%$"};
     static char *(rtable[]) = {"star", "gstar", "[star", "]star", ":star",
 			       "/star", "/\\\\star", "quotestar", "/\\\\(\\\\)",
-			       "?", ":?", "?<CR>", "g?", "g?g?", "g??",
+			       "?", ":?", "?<CR>", "g?", "g?g?", "g??", "z?",
 			       "/\\\\?", "/\\\\z(\\\\)", "\\\\=", ":s\\\\=",
 			       "\\[count]", "\\[quotex]", "\\[range]",
 			       "\\[pattern]", "\\\\bar", "/\\\\%\\$"};
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index 09e12bc..fbb0f5a 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -8139,6 +8139,7 @@
 
 #if ((defined(FEAT_SESSION) || defined(FEAT_EVAL)) && defined(vim_mkdir)) \
 	|| defined(PROTO)
+/*ARGSUSED*/
     int
 vim_mkdir_emsg(name, prot)
     char_u	*name;
diff --git a/src/ex_getln.c b/src/ex_getln.c
index 5d04ad3..51b2f25 100644
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -2997,10 +2997,10 @@
 	    v = OK;
 	if (v == OK)
 	{
-	    vim_strncpy(&ccline.cmdbuff[ccline.cmdpos + difflen],
-					       &ccline.cmdbuff[ccline.cmdpos],
-		    ccline.cmdlen - ccline.cmdpos + 1);
-	    STRNCPY(&ccline.cmdbuff[i], p2, STRLEN(p2));
+	    mch_memmove(&ccline.cmdbuff[ccline.cmdpos + difflen],
+		    &ccline.cmdbuff[ccline.cmdpos],
+		    (size_t)(ccline.cmdlen - ccline.cmdpos + 1));
+	    mch_memmove(&ccline.cmdbuff[i], p2, STRLEN(p2));
 	    ccline.cmdlen += difflen;
 	    ccline.cmdpos += difflen;
 	}
diff --git a/src/gui_w32.c b/src/gui_w32.c
index 8d0a4ce..8046a8d 100644
--- a/src/gui_w32.c
+++ b/src/gui_w32.c
@@ -556,7 +556,6 @@
 
     static int
 _DuringSizing(
-    HWND hwnd,
     UINT fwSide,
     LPRECT lprc)
 {
@@ -681,7 +680,7 @@
 #endif
 
     case WM_SIZING:	/* HANDLE_MSG doesn't seem to handle this one */
-	return _DuringSizing(hwnd, (UINT)wParam, (LPRECT)lParam);
+	return _DuringSizing((UINT)wParam, (LPRECT)lParam);
 
     case WM_MOUSEWHEEL:
 	_OnMouseWheel(hwnd, HIWORD(wParam));
@@ -842,12 +841,14 @@
     }
 }
 
+#ifndef FEAT_OLE
     static void
 ole_error(char *arg)
 {
     EMSG2(_("E243: Argument not supported: \"-%s\"; Use the OLE version."),
 									 arg);
 }
+#endif
 
 /*
  * Parse the GUI related command-line arguments.  Any arguments used are
@@ -1260,6 +1261,7 @@
 /*
  * Set the size of the window to the given width and height in pixels.
  */
+/*ARGSUSED*/
     void
 gui_mch_set_shellsize(int width, int height,
 	int min_width, int min_height, int base_width, int base_height)
@@ -2045,7 +2047,7 @@
     {
 	int			x;
 	int			offset;
-	const static int	val[8] = {1, 0, 0, 0, 1, 2, 2, 2 };
+	static const int	val[8] = {1, 0, 0, 0, 1, 2, 2, 2 };
 
 	y = FILL_Y(row + 1) - 1;
 	for (x = FILL_X(col); x < FILL_X(col + len); ++x)
@@ -2451,6 +2453,7 @@
  * pressed, return that button's ID - IDCANCEL (2), which is the button's
  * number.
  */
+/*ARGSUSED*/
     static LRESULT CALLBACK
 dialog_callback(
     HWND hwnd,
@@ -4037,6 +4040,7 @@
     DestroyWindow(beval->balloon);
 }
 
+/*ARGSUSED*/
     static VOID CALLBACK
 BevalTimerProc(hwnd, uMsg, idEvent, dwTime)
     HWND    hwnd;
@@ -4078,6 +4082,7 @@
     }
 }
 
+/*ARGSUSED*/
     void
 gui_mch_disable_beval_area(beval)
     BalloonEval	*beval;
@@ -4087,6 +4092,7 @@
     // TRACE0("gui_mch_disable_beval_area }}}");
 }
 
+/*ARGSUSED*/
     void
 gui_mch_enable_beval_area(beval)
     BalloonEval	*beval;
@@ -4162,6 +4168,7 @@
     return beval;
 }
 
+/*ARGSUSED*/
     static void
 Handle_WM_Notify(hwnd, pnmh)
     HWND hwnd;
diff --git a/src/gui_w48.c b/src/gui_w48.c
index 4729f9b..be23546 100644
--- a/src/gui_w48.c
+++ b/src/gui_w48.c
@@ -314,7 +314,7 @@
 static LRESULT _OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData);
 #endif
 
-#ifdef DEBUG
+#ifdef DEBUG_PRINT_ERROR
 /*
  * Print out the last Windows error message
  */
@@ -330,7 +330,7 @@
     TRACE1("Error: %s\n", lpMsgBuf);
     LocalFree(lpMsgBuf);
 }
-#endif /* DEBUG */
+#endif
 
 /*
  * Cursor blink functions.
@@ -445,6 +445,7 @@
  * Call-back routines.
  */
 
+/*ARGSUSED*/
     static VOID CALLBACK
 _OnTimer(
     HWND hwnd,
@@ -467,6 +468,7 @@
 	s_wait_timer = 0;
 }
 
+/*ARGSUSED*/
     static void
 _OnDeadChar(
     HWND hwnd,
@@ -555,6 +557,7 @@
 /*
  * Key hit, add it to the input buffer.
  */
+/*ARGSUSED*/
     static void
 _OnChar(
     HWND hwnd,
@@ -577,6 +580,7 @@
 /*
  * Alt-Key hit, add it to the input buffer.
  */
+/*ARGSUSED*/
     static void
 _OnSysChar(
     HWND hwnd,
@@ -656,6 +660,7 @@
     gui_send_mouse_event(button, x, y, repeated_click, vim_modifiers);
 }
 
+/*ARGSUSED*/
     static void
 _OnMouseButtonDown(
     HWND hwnd,
@@ -756,6 +761,7 @@
     }
 }
 
+/*ARGSUSED*/
     static void
 _OnMouseMoveOrRelease(
     HWND hwnd,
@@ -832,6 +838,7 @@
     return pMenu;
 }
 
+/*ARGSUSED*/
     static void
 _OnMenu(
     HWND	hwnd,
@@ -1255,6 +1262,7 @@
  * Return the name of font "font" in allocated memory.
  * Don't know how to get the actual name, thus use the provided name.
  */
+/*ARGSUSED*/
     char_u *
 gui_mch_get_fontname(font, name)
     GuiFont font;
@@ -1937,6 +1945,7 @@
 #endif
 }
 
+/*ARGSUSED*/
     void
 gui_mch_set_menu_pos(
     int	    x,
@@ -2291,6 +2300,7 @@
  * Get this message when the user clicks on the cross in the top right corner
  * of a Windows95 window.
  */
+/*ARGSUSED*/
     static void
 _OnClose(
     HWND hwnd)
@@ -2344,6 +2354,7 @@
     }
 }
 
+/*ARGSUSED*/
     static void
 _OnSize(
     HWND hwnd,
@@ -2588,6 +2599,7 @@
 }
 
 
+/*ARGSUSED*/
     void
 gui_mch_exit(int rc)
 {
@@ -2657,6 +2669,7 @@
  * Initialise vim to use the font with the given name.
  * Return FAIL if the font could not be loaded, OK otherwise.
  */
+/*ARGSUSED*/
     int
 gui_mch_init_font(char_u *font_name, int fontset)
 {
@@ -2761,6 +2774,7 @@
 /*
  * Set the window title
  */
+/*ARGSUSED*/
     void
 gui_mch_settitle(
     char_u  *title,
@@ -3162,6 +3176,7 @@
 }
 #endif /* FEAT_BROWSE */
 
+/*ARGSUSED*/
     static void
 _OnDropFiles(
     HWND hwnd,
@@ -3229,6 +3244,7 @@
 #endif
 }
 
+/*ARGSUSED*/
     static int
 _OnScroll(
     HWND hwnd,
@@ -3361,6 +3377,7 @@
  * Return pointer to buffer in "tofree".
  * Returns zero when out of memory.
  */
+/*ARGSUSED*/
     int
 get_cmd_args(char *prog, char *cmdline, char ***argvp, char **tofree)
 {
@@ -3473,7 +3490,7 @@
 		}
 	    }
 
-	    if (pnew != NUL)
+	    if (pnew != NULL)
 		*pnew++ = NUL;
 	    while (*p == ' ' || *p == '\t')
 		++p;		    /* advance until a non-space */
diff --git a/src/if_cscope.c b/src/if_cscope.c
index 7163c8a..7653308 100644
--- a/src/if_cscope.c
+++ b/src/if_cscope.c
@@ -1226,11 +1226,13 @@
     return msg;
 }
 #endif
+
 /*
  * PRIVATE: cs_insert_filelist
  *
  * insert a new cscope database filename into the filelist
  */
+/*ARGSUSED*/
     static int
 cs_insert_filelist(fname, ppath, flags, sb)
     char *fname;
diff --git a/src/if_python.c b/src/if_python.c
index b799047..8aca835 100644
--- a/src/if_python.c
+++ b/src/if_python.c
@@ -384,10 +384,13 @@
 
 #if PYTHON_API_VERSION < 1007 /* Python 1.4 */
 typedef PyObject PyThreadState;
-#endif /* Python 1.4 */
+#endif
 
-#ifndef PY_CAN_RECURSE
+#ifdef PY_CAN_RECURSE
+static PyGILState_STATE pygilstate = PyGILState_UNLOCKED;
+#else
 static PyThreadState *saved_python_thread = NULL;
+#endif
 
 /*
  * Suspend a thread of the Python interpreter, other threads are allowed to
@@ -396,7 +399,11 @@
     static void
 Python_SaveThread(void)
 {
+#ifdef PY_CAN_RECURSE
+    PyGILState_Release(pygilstate);
+#else
     saved_python_thread = PyEval_SaveThread();
+#endif
 }
 
 /*
@@ -406,10 +413,13 @@
     static void
 Python_RestoreThread(void)
 {
+#ifdef PY_CAN_RECURSE
+    pygilstate = PyGILState_Ensure();
+#else
     PyEval_RestoreThread(saved_python_thread);
     saved_python_thread = NULL;
-}
 #endif
+}
 
 /*
  * obtain a lock on the Vim data structures
@@ -430,11 +440,17 @@
 {
 #ifdef DYNAMIC_PYTHON
     if (hinstPython && Py_IsInitialized())
+    {
+        Python_RestoreThread();	    /* enter python */
         Py_Finalize();
+    }
     end_dynamic_python();
 #else
     if (Py_IsInitialized())
+    {
+        Python_RestoreThread();	    /* enter python */
         Py_Finalize();
+    }
 #endif
 }
 
@@ -470,11 +486,7 @@
 	    goto fail;
 
 	/* the first python thread is vim's, release the lock */
-#ifdef PY_CAN_RECURSE
-	PyEval_SaveThread();
-#else
 	Python_SaveThread();
-#endif
 
 	initialised = 1;
     }
@@ -497,9 +509,7 @@
     static void
 DoPythonCommand(exarg_T *eap, const char *cmd)
 {
-#ifdef PY_CAN_RECURSE
-    PyGILState_STATE	pygilstate;
-#else
+#ifndef PY_CAN_RECURSE
     static int		recursive = 0;
 #endif
 #if defined(MACOS) && !defined(MACOS_X_UNIX)
@@ -544,19 +554,11 @@
     }
 #endif
 
-#ifdef PY_CAN_RECURSE
-    pygilstate = PyGILState_Ensure();
-#else
     Python_RestoreThread();	    /* enter python */
-#endif
 
     PyRun_SimpleString((char *)(cmd));
 
-#ifdef PY_CAN_RECURSE
-    PyGILState_Release(pygilstate);
-#else
     Python_SaveThread();	    /* leave python */
-#endif
 
 #if defined(HAVE_LOCALE_H) || defined(X_LOCALE)
     if (saved_locale != NULL)
diff --git a/src/misc1.c b/src/misc1.c
index d4dabe6..7209a1a 100644
--- a/src/misc1.c
+++ b/src/misc1.c
@@ -3137,6 +3137,27 @@
     return n;
 }
 
+/*
+ * Ask the user to enter a number.
+ */
+    int
+prompt_for_number()
+{
+    int		i;
+
+    /* When using ":silent" assume that <CR> was entered. */
+    MSG_PUTS(_("Choice number (<Enter> cancels): "));
+    i = get_number(TRUE);
+    if (KeyTyped)		/* don't call wait_return() now */
+    {
+	msg_putchar('\n');
+	cmdline_row = msg_row - 1;
+	need_wait_return = FALSE;
+	msg_didany = FALSE;
+    }
+    return i;
+}
+
     void
 msgmore(n)
     long n;
diff --git a/src/misc2.c b/src/misc2.c
index b3ab1f2..6775932 100644
--- a/src/misc2.c
+++ b/src/misc2.c
@@ -1082,6 +1082,69 @@
     }
 }
 
+#if defined(FEAT_EVAL) || defined(FEAT_SYN_HL) || defined(PROTO)
+/*
+ * Make string "s" all upper-case and return it in allocated memory.
+ * Handles multi-byte characters as well as possible.
+ * Returns NULL when out of memory.
+ */
+    char_u *
+strup_save(orig)
+    char_u	*orig;
+{
+    char_u	*p;
+    char_u	*res;
+
+    res = p = vim_strsave(orig);
+
+    if (res != NULL)
+	while (*p != NUL)
+	{
+# ifdef FEAT_MBYTE
+	    int		l;
+
+	    if (enc_utf8)
+	    {
+		int	c, uc;
+		int	nl;
+		char_u	*s;
+
+		c = utf_ptr2char(p);
+		uc = utf_toupper(c);
+
+		/* Reallocate string when byte count changes.  This is rare,
+		 * thus it's OK to do another malloc()/free(). */
+		l = utf_ptr2len_check(p);
+		nl = utf_char2len(uc);
+		if (nl != l)
+		{
+		    s = alloc((unsigned)STRLEN(res) + 1 + nl - l);
+		    if (s == NULL)
+			break;
+		    mch_memmove(s, res, p - res);
+		    STRCPY(s + (p - res) + nl, p + l);
+		    p = s + (p - res);
+		    vim_free(res);
+		    res = s;
+		}
+
+		utf_char2bytes(uc, p);
+		p += nl;
+	    }
+	    else if (has_mbyte && (l = (*mb_ptr2len_check)(p)) > 1)
+		p += l;		/* skip multi-byte character */
+	    else
+# endif
+	    {
+		*p = TOUPPER_LOC(*p); /* note that toupper() can be a macro */
+		p++;
+	    }
+	}
+
+    return res;
+}
+#endif
+
 /*
  * copy a space a number of times
  */
@@ -1131,43 +1194,16 @@
 }
 
 /*
- * This is here because strncpy() does not guarantee successful results when
- * the to and from strings overlap.  It is only currently called from
- * nextwild() which copies part of the command line to another part of the
- * command line.  This produced garbage when expanding files etc in the middle
- * of the command line (on my terminal, anyway) -- webb.
- * Note: strncpy() pads the remainder of the buffer with NUL bytes,
- * vim_strncpy() doesn't do that.
+ * Like strncpy(), but always terminate the result with one NUL.
  */
     void
 vim_strncpy(to, from, len)
-    char_u *to;
-    char_u *from;
-    int len;
+    char_u	*to;
+    char_u	*from;
+    int		len;
 {
-    int i;
-
-    if (to <= from)
-    {
-	while (len-- && *from)
-	    *to++ = *from++;
-	if (len >= 0)
-	    *to = *from;    /* Copy NUL */
-    }
-    else
-    {
-	for (i = 0; i < len; i++)
-	{
-	    to++;
-	    if (*from++ == NUL)
-	    {
-		i++;
-		break;
-	    }
-	}
-	for (; i > 0; i--)
-	    *--to = *--from;
-    }
+    STRNCPY(to, from, len);
+    to[len] = NUL;
 }
 
 /*
diff --git a/src/normal.c b/src/normal.c
index e4bd396..fa5e355 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -4685,6 +4685,11 @@
 		    spell_add_word(ptr, len, nchar == 'w');
 		}
 		break;
+
+    case '?':	/* "z?": suggestions for a badly spelled word  */
+		if (!checkclearopq(cap->oap))
+		    spell_suggest();
+		break;
 #endif
 
     default:	clearopbeep(cap->oap);
@@ -6106,7 +6111,7 @@
 	setpcmark();
 	for (n = 0; n < cap->count1; ++n)
 	    if (spell_move_to(cap->cmdchar == ']' ? FORWARD : BACKWARD,
-				    cap->nchar == 's' ? TRUE : FALSE) == FAIL)
+			     cap->nchar == 's' ? TRUE : FALSE, FALSE) == FAIL)
 	    {
 		clearopbeep(cap->oap);
 		break;
diff --git a/src/option.c b/src/option.c
index 14f8ade..c0ea094 100644
--- a/src/option.c
+++ b/src/option.c
@@ -5683,20 +5683,32 @@
 #endif
 
 #ifdef FEAT_SYN_HL
-    /* When 'spelllang' is set and there is a window for this buffer in which
-     * 'spell' is set load the wordlists. */
-    else if (varp == &(curbuf->b_p_spl))
+    /* When 'spelllang' or 'spellfile' is set and there is a window for this
+     * buffer in which 'spell' is set load the wordlists. */
+    else if (varp == &(curbuf->b_p_spl) || varp == &(curbuf->b_p_spf))
     {
 	win_T	    *wp;
+	int	    l;
 
-	FOR_ALL_WINDOWS(wp)
-	    if (wp->w_buffer == curbuf && wp->w_p_spell)
-	    {
-		errmsg = did_set_spelllang(curbuf);
+	if (varp == &(curbuf->b_p_spf))
+	{
+	    l = STRLEN(curbuf->b_p_spf);
+	    if (l > 0 && (l < 4 || STRCMP(curbuf->b_p_spf + l - 4,
+								".add") != 0))
+		errmsg = e_invarg;
+	}
+
+	if (errmsg == NULL)
+	{
+	    FOR_ALL_WINDOWS(wp)
+		if (wp->w_buffer == curbuf && wp->w_p_spell)
+		{
+		    errmsg = did_set_spelllang(curbuf);
 # ifdef FEAT_WINDOWS
-		break;
+		    break;
 # endif
-	    }
+		}
+	}
     }
 #endif
 
diff --git a/src/os_mswin.c b/src/os_mswin.c
index 8a47dd1..b0c137f 100644
--- a/src/os_mswin.c
+++ b/src/os_mswin.c
@@ -332,6 +332,7 @@
  *  2: Just restore icon (which we don't have)
  *  3: Restore title and icon (which we don't have)
  */
+/*ARGSUSED*/
     void
 mch_restore_title(
     int which)
@@ -370,6 +371,7 @@
  * When 'shellslash' set do it the other way around.
  * Return OK or FAIL.
  */
+/*ARGSUSED*/
     int
 mch_FullName(
     char_u	*fname,
@@ -525,6 +527,7 @@
 }
 
 #if defined(FEAT_GUI_MSWIN) || defined(PROTO)
+/*ARGSUSED*/
     void
 mch_settmode(int tmode)
 {
@@ -701,6 +704,7 @@
  * Switching off termcap mode is only allowed when Columns is 80, otherwise a
  * crash may result.  It's always allowed on NT or when running the GUI.
  */
+/*ARGSUSED*/
     int
 can_end_termcap_mode(
     int give_msg)
@@ -732,6 +736,7 @@
 /*
  * set screen mode, always fails.
  */
+/*ARGSUSED*/
     int
 mch_screenmode(
     char_u *arg)
@@ -1028,6 +1033,7 @@
 /*
  * Make vim the owner of the current selection.  Return OK upon success.
  */
+/*ARGSUSED*/
     int
 clip_mch_own_selection(VimClipboard *cbd)
 {
@@ -1041,6 +1047,7 @@
 /*
  * Make vim NOT the owner of the current selection.
  */
+/*ARGSUSED*/
     void
 clip_mch_lose_selection(VimClipboard *cbd)
 {
@@ -1228,7 +1235,6 @@
 #ifdef FEAT_MBYTE
     HGLOBAL		rawh = NULL;
 #endif
-    char_u		*hMemStr = NULL;
     int			str_size = 0;
     int			maxlen;
     size_t		n;
@@ -1327,7 +1333,7 @@
     {
 	if ((hMem = GetClipboardData(CF_TEXT)) != NULL)
 	{
-	    str = hMemStr = (char_u *)GlobalLock(hMem);
+	    str = (char_u *)GlobalLock(hMem);
 
 	    /* The length is either what our metadata says or the strlen().
 	     * But limit it to the GlobalSize() for safety. */
@@ -1587,6 +1593,7 @@
 /*
  * Debugging helper: expose the MCH_WRITE_DUMP stuff to other modules
  */
+/*ARGSUSED*/
     void
 DumpPutS(
     const char *psz)
@@ -1736,6 +1743,7 @@
     return colorref;
 }
 
+/*ARGSUSED*/
     static BOOL CALLBACK
 PrintDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
 {
@@ -1798,6 +1806,7 @@
     return FALSE;
 }
 
+/*ARGSUSED*/
     static BOOL CALLBACK
 AbortProc(HDC hdcPrn, int iCode)
 {
@@ -2247,6 +2256,7 @@
     return (ret > 0);
 }
 
+/*ARGSUSED*/
     void
 mch_print_end(prt_settings_T *psettings)
 {
@@ -2935,8 +2945,7 @@
     HWND	server;		/* server window */
     char_u	*reply;		/* reply string */
     int		expr_result;	/* 0 for REPLY, 1 for RESULT 2 for error */
-}
-reply_T;
+} reply_T;
 
 static garray_T reply_list = {0, 0, sizeof(reply_T), 5, 0};
 
@@ -3186,6 +3195,7 @@
     return pixels;
 }
 
+/*ARGSUSED*/
     static int CALLBACK
 font_enumproc(
     ENUMLOGFONT	    *elf,
diff --git a/src/os_w32exe.c b/src/os_w32exe.c
index ac9bf3c..8c48fb3 100644
--- a/src/os_w32exe.c
+++ b/src/os_w32exe.c
@@ -38,6 +38,7 @@
 void (_cdecl *pSaveInst)(HINSTANCE);
 #endif
 
+/*ARGSUSED*/
     int WINAPI
 WinMain(
     HINSTANCE	hInstance,
diff --git a/src/os_win32.c b/src/os_win32.c
index a55f46c..82a3ce0 100644
--- a/src/os_win32.c
+++ b/src/os_win32.c
@@ -300,8 +300,7 @@
     }
 
     /* The bind_textdomain_codeset() function is optional. */
-    (FARPROC)dyn_libintl_bind_textdomain_codeset =
-					  (FARPROC)GetProcAddress(hLibintlDLL,
+    dyn_libintl_bind_textdomain_codeset = (void *)GetProcAddress(hLibintlDLL,
 						   "bind_textdomain_codeset");
     if (dyn_libintl_bind_textdomain_codeset == NULL)
 	dyn_libintl_bind_textdomain_codeset =
@@ -322,18 +321,21 @@
     dyn_libintl_bind_textdomain_codeset = null_libintl_bind_textdomain_codeset;
 }
 
+/*ARGSUSED*/
     static char *
 null_libintl_gettext(const char *msgid)
 {
     return (char*)msgid;
 }
 
+/*ARGSUSED*/
     static char *
 null_libintl_bindtextdomain(const char *domainname, const char *dirname)
 {
     return NULL;
 }
 
+/*ARGSUSED*/
     static char *
 null_libintl_bind_textdomain_codeset(const char *domainname,
 							  const char *codeset)
@@ -341,6 +343,7 @@
     return NULL;
 }
 
+/*ARGSUSED*/
     static char *
 null_libintl_textdomain(const char *domainname)
 {
@@ -446,6 +449,7 @@
 static int old_num_windows;
 static int num_windows;
 
+/*ARGSUSED*/
     static BOOL CALLBACK
 win32ssynch_cb(HWND hwnd, LPARAM lparam)
 {
@@ -762,6 +766,7 @@
  * For the GUI the mouse handling is in gui_w32.c.
  */
 # ifdef FEAT_GUI_W32
+/*ARGSUSED*/
     void
 mch_setmouse(int on)
 {
@@ -2211,6 +2216,7 @@
 /*
  * Do we have an interactive window?
  */
+/*ARGSUSED*/
     int
 mch_check_win(
     int argc,
@@ -3444,6 +3450,7 @@
 
 
 #ifdef FEAT_GUI_W32
+/*ARGSUSED*/
     void
 mch_write(
     char_u  *s,
@@ -4099,6 +4106,7 @@
 /*
  * Delay for half a second.
  */
+/*ARGSUSED*/
     void
 mch_delay(
     long    msec,
@@ -4716,7 +4724,7 @@
 		/* Advance to the next stream.  We might try seeking too far,
 		 * but BackupSeek() doesn't skip over stream borders, thus
 		 * that's OK. */
-		(void)BackupSeek(sh, sid.Size.LowPart, sid.Size.u.HighPart,
+		(void)BackupSeek(sh, sid.Size.u.LowPart, sid.Size.u.HighPart,
 							  &lo, &hi, &context);
 	    }
 
diff --git a/src/proto/misc1.pro b/src/proto/misc1.pro
index 5a42c13..c2ae831 100644
--- a/src/proto/misc1.pro
+++ b/src/proto/misc1.pro
@@ -42,6 +42,7 @@
 int ask_yesno __ARGS((char_u *str, int direct));
 int get_keystroke __ARGS((void));
 int get_number __ARGS((int colon));
+int prompt_for_number __ARGS((void));
 void msgmore __ARGS((long n));
 void beep_flush __ARGS((void));
 void vim_beep __ARGS((void));
diff --git a/src/proto/misc2.pro b/src/proto/misc2.pro
index 365f2ea..38d50a9 100644
--- a/src/proto/misc2.pro
+++ b/src/proto/misc2.pro
@@ -31,6 +31,7 @@
 char_u *vim_strsave_up __ARGS((char_u *string));
 char_u *vim_strnsave_up __ARGS((char_u *string, int len));
 void vim_strup __ARGS((char_u *p));
+char_u *strup_save __ARGS((char_u *orig));
 void copy_spaces __ARGS((char_u *ptr, size_t count));
 void copy_chars __ARGS((char_u *ptr, size_t count, int c));
 void del_trailing_spaces __ARGS((char_u *ptr));
diff --git a/src/proto/spell.pro b/src/proto/spell.pro
index 71d6dbc..c932b1f 100644
--- a/src/proto/spell.pro
+++ b/src/proto/spell.pro
@@ -1,6 +1,6 @@
 /* spell.c */
 int spell_check __ARGS((win_T *wp, char_u *ptr, int *attrp));
-int spell_move_to __ARGS((int dir, int allwords));
+int spell_move_to __ARGS((int dir, int allwords, int curline));
 char_u *did_set_spelllang __ARGS((buf_T *buf));
 void spell_reload __ARGS((void));
 void put_bytes __ARGS((FILE *fd, long_u nr, int len));
@@ -8,4 +8,5 @@
 void ex_spell __ARGS((exarg_T *eap));
 void spell_add_word __ARGS((char_u *word, int len, int bad));
 void init_spell_chartab __ARGS((void));
+void spell_suggest __ARGS((void));
 /* vim: set ft=c : */
diff --git a/src/spell.c b/src/spell.c
index cbe2f9f..2b37f63 100644
--- a/src/spell.c
+++ b/src/spell.c
@@ -29,12 +29,18 @@
  */
 
 /*
+ * Use this to let the score depend in how much a suggestion sounds like the
+ * bad word.  It's quite slow and doesn't make the sorting much better....
+ * #define SOUNDFOLD_SCORE
+ */
+
+/*
  * Vim spell file format:  <HEADER> <SUGGEST> <LWORDTREE> <KWORDTREE>
  *
  * <HEADER>: <fileID> <regioncnt> <regionname> ...
  *		 <charflagslen> <charflags> <fcharslen> <fchars>
  *
- * <fileID>     10 bytes    "VIMspell05"
+ * <fileID>     10 bytes    "VIMspell06"
  * <regioncnt>  1 byte	    number of regions following (8 supported)
  * <regionname>	2 bytes     Region name: ca, au, etc.  Lower case.
  *			    First <regionname> is region 1.
@@ -47,11 +53,41 @@
  * <fchars>     N bytes	    Folded characters, first one is for character 128.
  *
  *
- * <SUGGEST> : <suggestlen> <more> ...
+ * <SUGGEST> : <repcount> <rep> ...
+ *	       <salflags> <salcount> <sal> ...
+ *	       <maplen> <mapstr>
  *
- * <suggestlen> 4 bytes	    Length of <SUGGEST> in bytes, excluding
- *			    <suggestlen>.  MSB first.
- * <more>		    To be defined.
+ * <repcount>	2 bytes	    number of <rep> items, MSB first.
+ *
+ * <rep> : <repfromlen> <repfrom> <reptolen> <repto>
+ *
+ * <repfromlen>	1 byte	    length of <repfrom>
+ *
+ * <repfrom>	N bytes	    "from" part of replacement
+ *
+ * <reptolen>	1 byte	    length of <repto>
+ *
+ * <repto>	N bytes	    "to" part of replacement
+ *
+ * <salflags>	1 byte	    flags for soundsalike conversion:
+ *			    SAL_F0LLOWUP
+ *			    SAL_COLLAPSE
+ *			    SAL_REM_ACCENTS
+ *
+ * <sal> : <salfromlen> <salfrom> <saltolen> <salto>
+ *
+ * <salfromlen>	1 byte	    length of <salfrom>
+ *
+ * <salfrom>	N bytes	    "from" part of soundsalike
+ *
+ * <saltolen>	1 byte	    length of <salto>
+ *
+ * <salto>	N bytes	    "to" part of soundsalike
+ *
+ * <maplen>	2 bytes	    length of <mapstr>, MSB first
+ *
+ * <mapstr>	N bytes	    String with sequences of similar characters,
+ *			    separated by slashes.
  *
  *
  * <LWORDTREE>: <wordtree>
@@ -90,9 +126,7 @@
  *
  * <KWORDTREE>: <wordtree>
  *
- *
  * All text characters are in 'encoding', but stored as single bytes.
- * The region name is ASCII.
  */
 
 #if defined(MSDOS) || defined(WIN16) || defined(WIN32) || defined(_WIN64)
@@ -107,7 +141,9 @@
 # include <fcntl.h>
 #endif
 
-#define MAXWLEN 250		/* assume max. word len is this many bytes */
+#define MAXWLEN 250		/* Assume max. word len is this many bytes.
+				   Some places assume a word length fits in a
+				   byte, thus it can't be above 255. */
 
 /* Flags used for a word. */
 #define WF_REGION   0x01	/* region byte follows */
@@ -115,21 +151,23 @@
 #define WF_ALLCAP   0x04	/* word must be all capitals */
 #define WF_RARE	    0x08	/* rare word */
 #define WF_BANNED   0x10	/* bad word */
+#define WF_KEEPCAP  0x80	/* keep-case word */
 
-#define WF_KEEPCAP  0x100	/* keep-case word (not stored in file) */
+#define WF_CAPMASK (WF_ONECAP | WF_ALLCAP | WF_KEEPCAP)
 
 #define BY_NOFLAGS  0		/* end of word without flags or region */
 #define BY_FLAGS    1		/* end of word, flag byte follows */
 #define BY_INDEX    2		/* child is shared, index follows */
 #define BY_SPECIAL  BY_INDEX	/* hightest special byte value */
 
-/* Info from "REP" entries in ".aff" file used in af_rep.
- * TODO: This is not used yet.  Either use it or remove it. */
-typedef struct repentry_S
+/* Info from "REP" and "SAL" entries in ".aff" file used in si_rep, sl_rep,
+ * si_sal and sl_sal.
+ * One replacement: from "ft_from" to "ft_to". */
+typedef struct fromto_S
 {
-    char_u	*re_from;
-    char_u	*re_to;
-} repentry_T;
+    char_u	*ft_from;
+    char_u	*ft_to;
+} fromto_T;
 
 /*
  * Structure used to store words and other info for one language, loaded from
@@ -152,22 +190,34 @@
     slang_T	*sl_next;	/* next language */
     char_u	*sl_name;	/* language name "en", "en.rare", "nl", etc. */
     char_u	*sl_fname;	/* name of .spl file */
-    int		sl_add;		/* TRUE if it's an addition. */
+    int		sl_add;		/* TRUE if it's a .add file. */
     char_u	*sl_fbyts;	/* case-folded word bytes */
     int		*sl_fidxs;	/* case-folded word indexes */
     char_u	*sl_kbyts;	/* keep-case word bytes */
     int		*sl_kidxs;	/* keep-case word indexes */
-    char_u	*sl_try;	/* "TRY" from .aff file  TODO: not used */
-    garray_T	sl_rep;		/* list of repentry_T entries from REP lines
-				 * TODO not used */
     char_u	sl_regions[17];	/* table with up to 8 region names plus NUL */
-    int		sl_error;	/* error while loading */
+
+    garray_T	sl_rep;		/* list of fromto_T entries from REP lines */
+    short	sl_rep_first[256];  /* indexes where byte first appears, -1 if
+				       there is none */
+    garray_T	sl_sal;		/* list of fromto_T entries from SAL lines */
+    short	sl_sal_first[256];  /* indexes where byte first appears, -1 if
+				       there is none */
+    int		sl_followup;	/* SAL followup */
+    int		sl_collapse;	/* SAL collapse_result */
+    int		sl_rem_accents;	/* SAL remove_accents */
+    char_u	*sl_map;	/* string with similar chars from MAP lines */
 };
 
 /* First language that is loaded, start of the linked list of loaded
  * languages. */
 static slang_T *first_lang = NULL;
 
+/* Flags used in .spl file for soundsalike flags. */
+#define SAL_F0LLOWUP		1
+#define SAL_COLLAPSE		2
+#define SAL_REM_ACCENTS		4
+
 /*
  * Structure used in "b_langp", filled from 'spelllang'.
  */
@@ -188,16 +238,71 @@
 #define SP_LOCAL	2
 #define SP_BAD		3
 
-#define VIMSPELLMAGIC "VIMspell05"  /* string at start of Vim spell file */
+#define VIMSPELLMAGIC "VIMspell06"  /* string at start of Vim spell file */
 #define VIMSPELLMAGICL 10
 
 /*
+ * Information used when looking for suggestions.
+ */
+typedef struct suginfo_S
+{
+    garray_T	su_ga;		    /* suggestions, contains "suggest_T" */
+    int		su_maxscore;	    /* maximum score for adding to su_ga */
+    int		su_icase;	    /* accept words with wrong case */
+    int		su_icase_add;	    /* add matches while ignoring case */
+    char_u	*su_badptr;	    /* start of bad word in line */
+    int		su_badlen;	    /* length of detected bad word in line */
+    char_u	su_badword[MAXWLEN]; /* bad word truncated at su_badlen */
+    char_u	su_fbadword[MAXWLEN]; /* su_badword case-folded */
+    hashtab_T	su_banned;	    /* table with banned words */
+#ifdef SOUNDFOLD_SCORE
+    slang_T	*su_slang;	    /* currently used slang_T */
+    char_u	su_salword[MAXWLEN]; /* soundfolded badword */
+#endif
+} suginfo_T;
+
+/* One word suggestion.  Used in "si_ga". */
+typedef struct suggest_S
+{
+    char_u	*st_word;	/* suggested word, allocated string */
+    int		st_orglen;	/* length of replaced text */
+    int		st_score;	/* lower is better */
+} suggest_T;
+
+#define SUG(sup, i) (((suggest_T *)(sup)->su_ga.ga_data)[i])
+
+/* Number of suggestions displayed. */
+#define SUG_PROMPT_COUNT    ((int)Rows - 2)
+
+/* Threshold for sorting and cleaning up suggestions. */
+#define SUG_CLEANUP_COUNT   (SUG_PROMPT_COUNT + 50)
+
+/* score for various changes */
+#define SCORE_SPLIT	99	/* split bad word */
+#define SCORE_ICASE	52	/* slightly different case */
+#define SCORE_ALLCAP	120	/* need all-cap case */
+#define SCORE_REGION	70	/* word is for different region */
+#define SCORE_RARE	180	/* rare word */
+
+/* score for edit distance */
+#define SCORE_SWAP	90	/* swap two characters */
+#define SCORE_SWAP3	110	/* swap two characters in three */
+#define SCORE_REP	87	/* REP replacement */
+#define SCORE_SUBST	93	/* substitute a character */
+#define SCORE_SIMILAR	33	/* substitute a similar character */
+#define SCORE_DEL	96	/* delete a character */
+#define SCORE_INS	94	/* insert a character */
+
+#define SCORE_MAXINIT	350	/* Initial maximum score: higher == slower.
+				 * 350 allows for about three changes. */
+#define SCORE_MAXMAX	999999	/* accept any score */
+
+/*
  * Structure to store info for word matching.
  */
 typedef struct matchinf_S
 {
     langp_T	*mi_lp;			/* info for language and region */
-    slang_T	*mi_slang;		/* info for the language */
 
     /* pointers to original text to be checked */
     char_u	*mi_word;		/* start of word being checked */
@@ -248,23 +353,56 @@
 # define SPELL_ISWORDP(p) (spelltab.st_isw[*(p)])
 #endif
 
+/*
+ * Struct to keep the state at each level in spell_try_change().
+ */
+typedef struct trystate_S
+{
+    int		ts_state;	/* state at this level, STATE_ */
+    int		ts_score;	/* score */
+    int		ts_curi;	/* index in list of child nodes */
+    int		ts_fidx;	/* index in fword[], case-folded bad word */
+    int		ts_fidxtry;	/* ts_fidx at which bytes may be changed */
+    int		ts_twordlen;	/* valid length of tword[] */
+    int		ts_arridx;	/* index in tree array, start of node */
+    char_u	ts_save_prewordlen; /* saved "prewordlen" */
+    int		ts_save_splitoff;   /* su_splitoff saved here */
+    int		ts_save_badflags;   /* badflags saved here */
+} trystate_T;
+
 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));
 static void find_word __ARGS((matchinf_T *mip, int keepcap));
+static int spell_valid_case __ARGS((int origflags, int treeflags));
 static void spell_load_lang __ARGS((char_u *lang));
 static char_u *spell_enc __ARGS((void));
 static void spell_load_cb __ARGS((char_u *fname, void *cookie));
-static void spell_load_file __ARGS((char_u *fname, char_u *lang, slang_T *old_lp));
+static slang_T *spell_load_file __ARGS((char_u *fname, char_u *lang, slang_T *old_lp, int silent));
 static int read_tree __ARGS((FILE *fd, char_u *byts, int *idxs, int maxidx, int startidx));
 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));
+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_chartab __ARGS((char_u *fol, char_u *low, char_u *upp));
 static void write_spell_chartab __ARGS((FILE *fd));
 static int spell_isupper __ARGS((int c));
 static int spell_casefold __ARGS((char_u *p, int len, char_u *buf, int buflen));
+static void onecap_copy __ARGS((char_u *word, int len, char_u *wcopy, int upper));
+static void spell_try_change __ARGS((suginfo_T *su));
+static int try_deeper __ARGS((suginfo_T *su, trystate_T *stack, int depth, int score_add));
+static void find_keepcap_word __ARGS((slang_T *slang, char_u *fword, char_u *kword));
+static void spell_try_soundalike __ARGS((suginfo_T *su));
+static void make_case_word __ARGS((char_u *fword, char_u *cword, int flags));
+static int similar_chars __ARGS((slang_T *slang, int c1, int c2));
+static void add_suggestion __ARGS((suginfo_T *su, char_u *goodword, int use_score));
+static void add_banned __ARGS((suginfo_T *su, char_u *word));
+static int was_banned __ARGS((suginfo_T *su, char_u *word));
+static void free_banned __ARGS((suginfo_T *su));
+static void cleanup_suggestions __ARGS((suginfo_T *su));
+static void spell_soundfold __ARGS((slang_T *slang, char_u *inword, char_u *res));
+static int spell_edit_score __ARGS((char_u *badword, char_u *goodword));
+
 
 static char *e_format = N_("E759: Format error in spell file");
 
@@ -274,6 +412,10 @@
  * "*attrp" is set to the attributes for a badly spelled word.  For a non-word
  * or when it's OK it remains unchanged.
  * This must only be called when 'spelllang' is not empty.
+ *
+ * "sug" is normally NULL.  When looking for suggestions it points to
+ * suginfo_T.  It's passed as a void pointer to keep the struct local.
+ *
  * Returns the length of the word in bytes, also when it's OK, so that the
  * caller can skip over the word.
  */
@@ -305,6 +447,7 @@
 	/* Find the end of the word. */
 	mi.mi_word = ptr;
 	mi.mi_fend = ptr;
+
 	if (SPELL_ISWORDP(mi.mi_fend))
 	{
 	    /* Make case-folded copy of the characters until the next non-word
@@ -313,18 +456,15 @@
 	    {
 		mb_ptr_adv(mi.mi_fend);
 	    } while (*mi.mi_fend != NUL && SPELL_ISWORDP(mi.mi_fend));
-
-	    /* Check the caps type of the word. */
-	    mi.mi_capflags = captype(ptr, mi.mi_fend);
 	}
-	else
-	    /* No word characters, caps type is always zero. */
-	    mi.mi_capflags = 0;
 
 	/* We always use the characters up to the next non-word character,
 	 * also for bad words. */
 	mi.mi_end = mi.mi_fend;
-	mi.mi_cend = mi.mi_fend;
+
+	/* Check caps type later. */
+	mi.mi_capflags = 0;
+	mi.mi_cend = NULL;
 
 	/* Include one non-word character so that we can check for the
 	 * word end. */
@@ -349,7 +489,6 @@
 	    /* Check for a matching word in case-folded words. */
 	    find_word(&mi, FALSE);
 
-	    /* Try keep-case words. */
 	    find_word(&mi, TRUE);
 	}
 
@@ -563,17 +702,13 @@
 		/* Check that the word is in the required case. */
 		if (mip->mi_cend != mip->mi_word + wlen)
 		{
-		    /* mi_capflags was set for a different word
-		     * length, need to do it again. */
+		    /* mi_capflags was set for a different word length, need
+		     * to do it again. */
 		    mip->mi_cend = mip->mi_word + wlen;
-		    mip->mi_capflags = captype(mip->mi_word,
-							mip->mi_cend);
+		    mip->mi_capflags = captype(mip->mi_word, mip->mi_cend);
 		}
 
-		valid = (mip->mi_capflags == WF_ALLCAP
-			|| ((flags & WF_ALLCAP) == 0
-			    && ((flags & WF_ONECAP) == 0
-				|| mip->mi_capflags == WF_ONECAP)));
+		valid = spell_valid_case(mip->mi_capflags, flags);
 	    }
 
 	    if (valid)
@@ -617,15 +752,31 @@
     }
 }
 
+/*
+ * Check case flags for a word.  Return TRUE if the word has the requested
+ * case.
+ */
+    static int
+spell_valid_case(origflags, treeflags)
+    int	    origflags;	    /* flags for the checked word. */
+    int	    treeflags;	    /* flags for the word in the spell tree */
+{
+    return (origflags == WF_ALLCAP
+	    || ((treeflags & (WF_ALLCAP | WF_KEEPCAP)) == 0
+		&& ((treeflags & WF_ONECAP) == 0 || origflags == WF_ONECAP)));
+}
+
 
 /*
  * Move to next spell error.
+ * "curline" is TRUE for "z?": find word under/after cursor in the same line.
  * Return OK if found, FAIL otherwise.
  */
     int
-spell_move_to(dir, allwords)
+spell_move_to(dir, allwords, curline)
     int		dir;		/* FORWARD or BACKWARD */
     int		allwords;	/* TRUE for "[s" and "]s" */
+    int		curline;
 {
     linenr_T	lnum;
     pos_T	found_pos;
@@ -680,7 +831,8 @@
 		    if (dir == BACKWARD
 			    || lnum > curwin->w_cursor.lnum
 			    || (lnum == curwin->w_cursor.lnum
-				&& (colnr_T)(p - line)
+				&& (colnr_T)(curline ? p - line + len
+						     : p - line)
 						  > curwin->w_cursor.col))
 		    {
 			if (has_syntax)
@@ -722,6 +874,9 @@
 		break;
 	}
 
+	if (curline)
+	    return FAIL;	/* only check cursor line */
+
 	/* Advance to next line. */
 	if (dir == BACKWARD)
 	{
@@ -819,7 +974,8 @@
     if (lp != NULL)
     {
 	lp->sl_name = vim_strsave(lang);
-	ga_init2(&lp->sl_rep, sizeof(repentry_T), 4);
+	ga_init2(&lp->sl_rep, sizeof(fromto_T), 10);
+	ga_init2(&lp->sl_sal, sizeof(fromto_T), 10);
     }
     return lp;
 }
@@ -844,6 +1000,10 @@
 slang_clear(lp)
     slang_T	*lp;
 {
+    garray_T	    *gap;
+    fromto_T	    *ftp;
+    int		    round;
+
     vim_free(lp->sl_fbyts);
     lp->sl_fbyts = NULL;
     vim_free(lp->sl_kbyts);
@@ -852,9 +1012,21 @@
     lp->sl_fidxs = NULL;
     vim_free(lp->sl_kidxs);
     lp->sl_kidxs = NULL;
-    ga_clear(&lp->sl_rep);
-    vim_free(lp->sl_try);
-    lp->sl_try = NULL;
+
+    for (round = 1; round <= 2; ++round)
+    {
+	gap = round == 1 ? &lp->sl_rep : &lp->sl_sal;
+	while (gap->ga_len > 0)
+	{
+	    ftp = &((fromto_T *)gap->ga_data)[--gap->ga_len];
+	    vim_free(ftp->ft_from);
+	    vim_free(ftp->ft_to);
+	}
+	ga_clear(gap);
+    }
+
+    vim_free(lp->sl_map);
+    lp->sl_map = NULL;
 }
 
 /*
@@ -866,7 +1038,7 @@
     char_u	*fname;
     void	*cookie;	    /* points to the language name */
 {
-    spell_load_file(fname, (char_u *)cookie, NULL);
+    (void)spell_load_file(fname, (char_u *)cookie, NULL, FALSE);
 }
 
 /*
@@ -877,12 +1049,14 @@
  *   the language name, "old_lp" is NULL.  Will allocate an slang_T.
  * - To reload a spell file that was changed.  "lang" is NULL and "old_lp"
  *   points to the existing slang_T.
+ * Returns the slang_T the spell file was loaded into.  NULL for error.
  */
-    static void
-spell_load_file(fname, lang, old_lp)
+    static slang_T *
+spell_load_file(fname, lang, old_lp, silent)
     char_u	*fname;
     char_u	*lang;
     slang_T	*old_lp;
+    int		silent;		/* no error if file doesn't exist */
 {
     FILE	*fd;
     char_u	buf[MAXWLEN + 1];
@@ -895,11 +1069,22 @@
     int		cnt, ccnt;
     char_u	*fol;
     slang_T	*lp = NULL;
+    garray_T	*gap;
+    fromto_T	*ftp;
+    int		rr;
+    short	*first;
 
     fd = mch_fopen((char *)fname, "r");
     if (fd == NULL)
     {
-	EMSG2(_(e_notopen), fname);
+	if (!silent)
+	    EMSG2(_(e_notopen), fname);
+	else if (p_verbose > 2)
+	{
+	    verbose_enter();
+	    smsg((char_u *)e_notopen, fname);
+	    verbose_leave();
+	}
 	goto endFAIL;
     }
     if (p_verbose > 2)
@@ -1000,12 +1185,88 @@
 	    goto formerr;
     }
 
-    /* <SUGGEST> : <suggestlen> <more> ... */
-    /* TODO, just skip this for now */
-    i = (getc(fd) << 24) + (getc(fd) << 16) + (getc(fd) << 8) + getc(fd);
-    while (i-- > 0)
-	if (getc(fd) == EOF)				/* <suggestlen> */
-	    goto truncerr;
+    /* <SUGGEST> : <repcount> <rep> ...
+     *             <salflags> <salcount> <sal> ...
+     *             <maplen> <mapstr> */
+    for (round = 1; round <= 2; ++round)
+    {
+	if (round == 1)
+	{
+	    gap = &lp->sl_rep;
+	    first = lp->sl_rep_first;
+	}
+	else
+	{
+	    gap = &lp->sl_sal;
+	    first = lp->sl_sal_first;
+
+	    i = getc(fd);				/* <salflags> */
+	    if (i & SAL_F0LLOWUP)
+		lp->sl_followup = TRUE;
+	    if (i & SAL_COLLAPSE)
+		lp->sl_collapse = TRUE;
+	    if (i & SAL_REM_ACCENTS)
+		lp->sl_rem_accents = TRUE;
+	}
+
+	cnt = (getc(fd) << 8) + getc(fd);	/* <repcount> or <salcount> */
+	if (cnt < 0)
+	    goto formerr;
+
+	if (ga_grow(gap, cnt) == FAIL)
+	    goto endFAIL;
+	for (; gap->ga_len < cnt; ++gap->ga_len)
+	{
+	    /* <rep> : <repfromlen> <repfrom> <reptolen> <repto> */
+	    /* <sal> : <salfromlen> <salfrom> <saltolen> <salto> */
+	    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 <salfrom> */
+		p[i] = NUL;
+		if (rr == 1)
+		    ftp->ft_from = p;
+		else
+		    ftp->ft_to = p;
+	    }
+	}
+
+	/* Fill the first-index table. */
+	for (i = 0; i < 256; ++i)
+	    first[i] = -1;
+	for (i = 0; i < gap->ga_len; ++i)
+	{
+	    ftp = &((fromto_T *)gap->ga_data)[i];
+	    if (first[*ftp->ft_from] == -1)
+		first[*ftp->ft_from] = i;
+	}
+    }
+
+    cnt = (getc(fd) << 8) + getc(fd);		/* <maplen> */
+    if (cnt < 0)
+	goto formerr;
+    p = alloc(cnt + 1);
+    if (p == NULL)
+	goto endFAIL;
+    for (i = 0; i < cnt; ++i)
+	p[i] = getc(fd);			/* <mapstr> */
+    p[i] = NUL;
+    lp->sl_map = p;
+
 
     /* round 1: <LWORDTREE>
      * round 2: <KWORDTREE> */
@@ -1063,13 +1324,18 @@
 	/* truncating the name signals the error to spell_load_lang() */
 	*lang = NUL;
     if (lp != NULL && old_lp == NULL)
+    {
 	slang_free(lp);
+	lp = NULL;
+    }
 
 endOK:
     if (fd != NULL)
 	fclose(fd);
     sourcing_name = save_sourcing_name;
     sourcing_lnum = save_sourcing_lnum;
+
+    return lp;
 }
 
 /*
@@ -1177,9 +1443,18 @@
     slang_T	*lp;
     int		c;
     char_u	lbuf[MAXWLEN + 1];
+    char_u	spf_name[MAXPATHL];
+    int		did_spf = FALSE;
 
     ga_init2(&ga, sizeof(langp_T), 2);
 
+    /* Get the name of the .spl file associated with 'spellfile'. */
+    if (*buf->b_p_spf == NUL)
+	did_spf = TRUE;
+    else
+	vim_snprintf((char *)spf_name, sizeof(spf_name), "%s.spl",
+								buf->b_p_spf);
+
     /* loop over comma separated languages. */
     for (lang = buf->b_p_spl; *lang != NUL; lang = e)
     {
@@ -1206,8 +1481,7 @@
 	if (lp == NULL)
 	{
 	    /* Not found, load the language. */
-	    STRNCPY(lbuf, lang, e - lang);
-	    lbuf[e - lang] = NUL;
+	    vim_strncpy(lbuf, lang, e - lang);
 	    if (region != NULL)
 		mch_memmove(lbuf + 2, lbuf + 5, e - lang - 4);
 	    spell_load_lang(lbuf);
@@ -1247,12 +1521,38 @@
 		LANGP_ENTRY(ga, ga.ga_len)->lp_slang = lp;
 		LANGP_ENTRY(ga, ga.ga_len)->lp_region = region_mask;
 		++ga.ga_len;
+
+		/* Check if this is the 'spellfile' spell file. */
+		if (fullpathcmp(spf_name, lp->sl_fname, FALSE) == FPC_SAME)
+		    did_spf = TRUE;
 	    }
 
 	if (*e == ',')
 	    ++e;
     }
 
+    /*
+     * Make sure the 'spellfile' file is loaded.  It may be in 'runtimepath',
+     * then it's probably loaded above already.  Otherwise load it here.
+     */
+    if (!did_spf)
+    {
+	for (lp = first_lang; lp != NULL; lp = lp->sl_next)
+	    if (fullpathcmp(spf_name, lp->sl_fname, FALSE) == FPC_SAME)
+		break;
+	if (lp == NULL)
+	{
+	    vim_strncpy(lbuf, gettail(spf_name), 2);
+	    lp = spell_load_file(spf_name, lbuf, NULL, TRUE);
+	}
+	if (lp != NULL && ga_grow(&ga, 1) == OK)
+	{
+	    LANGP_ENTRY(ga, ga.ga_len)->lp_slang = lp;
+	    LANGP_ENTRY(ga, ga.ga_len)->lp_region = REGION_ALL;
+	    ++ga.ga_len;
+	}
+    }
+
     /* Add a NULL entry to mark the end of the list. */
     if (ga_grow(&ga, 1) == FAIL)
     {
@@ -1292,7 +1592,7 @@
 }
 
 /*
- * Return type of word:
+ * Return case type of word:
  * w word	0
  * Word		WF_ONECAP
  * W WORD	WF_ALLCAP
@@ -1301,7 +1601,7 @@
     static int
 captype(word, end)
     char_u	*word;
-    char_u	*end;
+    char_u	*end;	    /* When NULL use up to NUL byte. */
 {
     char_u	*p;
     int		c;
@@ -1311,7 +1611,7 @@
 
     /* find first letter */
     for (p = word; !SPELL_ISWORDP(p); mb_ptr_adv(p))
-	if (p >= end)
+	if (end == NULL ? *p == NUL : p >= end)
 	    return 0;	    /* only non-word characters, illegal word */
 #ifdef FEAT_MBYTE
     if (has_mbyte)
@@ -1325,7 +1625,7 @@
      * Need to check all letters to find a word with mixed upper/lower.
      * But a word with an upper char only at start is a ONECAP.
      */
-    for ( ; p < end; mb_ptr_adv(p))
+    for ( ; end == NULL ? *p != NUL : p < end; mb_ptr_adv(p))
 	if (SPELL_ISWORDP(p))
 	{
 #ifdef FEAT_MBYTE
@@ -1402,18 +1702,26 @@
  * Reload the spell file "fname" if it's loaded.
  */
     static void
-spell_reload_one(fname)
+spell_reload_one(fname, added_word)
     char_u	*fname;
+    int		added_word;	/* invoked through "zg" */
 {
     slang_T	*lp;
+    int		didit = FALSE;
 
     for (lp = first_lang; lp != NULL; lp = lp->sl_next)
 	if (fullpathcmp(fname, lp->sl_fname, FALSE) == FPC_SAME)
 	{
 	    slang_clear(lp);
-	    spell_load_file(fname, NULL, lp);
+	    (void)spell_load_file(fname, NULL, lp, FALSE);
 	    redraw_all_later(NOT_VALID);
+	    didit = TRUE;
 	}
+
+    /* When "zg" was used and the file wasn't loaded yet, should redo
+     * 'spelllang' to get it loaded. */
+    if (added_word && !didit)
+	did_set_spelllang(curbuf);
 }
 
 
@@ -1429,12 +1737,10 @@
 typedef struct afffile_S
 {
     char_u	*af_enc;	/* "SET", normalized, alloc'ed string or NULL */
-    char_u	*af_try;	/* "TRY" line in "af_enc" encoding */
     int		af_rar;		/* RAR ID for rare word */
     int		af_kep;		/* KEP ID for keep-case word */
     hashtab_T	af_pref;	/* hashtable for prefixes, affheader_T */
     hashtab_T	af_suff;	/* hashtable for suffixes, affheader_T */
-    garray_T	af_rep;		/* list of repentry_T entries from REP lines */
 } afffile_T;
 
 typedef struct affentry_S affentry_T;
@@ -1510,9 +1816,18 @@
     int		si_region_count; /* number of regions supported (1 when there
 				    are no regions) */
     char_u	si_region_name[16]; /* region names (if count > 1) */
+
+    garray_T	si_rep;		/* list of fromto_T entries from REP lines */
+    garray_T	si_sal;		/* list of fromto_T entries from SAL lines */
+    int		si_followup;	/* soundsalike: ? */
+    int		si_collapse;	/* soundsalike: ? */
+    int		si_rem_accents;	/* soundsalike: remove accents */
+    garray_T	si_map;		/* MAP info concatenated */
 } spellinfo_T;
 
 static afffile_T *spell_read_aff __ARGS((char_u *fname, spellinfo_T *spin));
+static void add_fromto __ARGS((spellinfo_T *spin, garray_T *gap, char_u	*from, char_u *to));
+static int sal_to_bool __ARGS((char_u *s));
 static int has_non_ascii __ARGS((char_u *s));
 static void spell_free_aff __ARGS((afffile_T *aff));
 static int spell_read_dic __ARGS((char_u *fname, spellinfo_T *spin, afffile_T *affile));
@@ -1529,11 +1844,11 @@
 static int node_equal __ARGS((wordnode_T *n1, wordnode_T *n2));
 static void write_vim_spell __ARGS((char_u *fname, spellinfo_T *spin));
 static int put_tree __ARGS((FILE *fd, wordnode_T *node, int index, int regionmask));
-static void mkspell __ARGS((int fcount, char_u **fnames, int ascii, int overwrite, int verbose));
+static void mkspell __ARGS((int fcount, char_u **fnames, int ascii, int overwrite, int added_word));
 static void init_spellfile __ARGS((void));
 
 /*
- * Read an affix ".aff" file.
+ * Read the affix file "fname".
  * Returns an afffile_T, NULL for complete failure.
  */
     static afffile_T *
@@ -1557,6 +1872,10 @@
     char_u	*fol = NULL;
     char_u	*upp = NULL;
     static char *e_affname = N_("Affix name too long in %s line %d: %s");
+    int		do_rep;
+    int		do_sal;
+    int		do_map;
+    int		found_map = FALSE;
 
     /*
      * Open the file.
@@ -1578,6 +1897,15 @@
 	    verbose_leave();
     }
 
+    /* Only do REP lines when not done in another .aff file already. */
+    do_rep = spin->si_rep.ga_len == 0;
+
+    /* Only do SAL lines when not done in another .aff file already. */
+    do_sal = spin->si_sal.ga_len == 0;
+
+    /* Only do MAP lines when not done in another .aff file already. */
+    do_map = spin->si_map.ga_len == 0;
+
     /*
      * Allocate and init the afffile_T structure.
      */
@@ -1586,7 +1914,6 @@
 	return NULL;
     hash_init(&aff->af_pref);
     hash_init(&aff->af_suff);
-    ga_init2(&aff->af_rep, (int)sizeof(repentry_T), 20);
 
     /*
      * Read all the lines in the file one by one.
@@ -1660,12 +1987,11 @@
 	    }
 	    else if (STRCMP(items[0], "NOSPLITSUGS") == 0 && itemcnt == 1)
 	    {
-		/* ignored */
+		/* ignored, we always split */
 	    }
-	    else if (STRCMP(items[0], "TRY") == 0 && itemcnt == 2
-						       && aff->af_try == NULL)
+	    else if (STRCMP(items[0], "TRY") == 0 && itemcnt == 2)
 	    {
-		aff->af_try = getroom_save(&spin->si_blocks, items[1]);
+		/* ignored, we look in the tree for what chars may appear */
 	    }
 	    else if (STRCMP(items[0], "RAR") == 0 && itemcnt == 2
 						       && aff->af_rar == 0)
@@ -1784,18 +2110,55 @@
 		    upp = vim_strsave(items[1]);
 	    }
 	    else if (STRCMP(items[0], "REP") == 0 && itemcnt == 2)
+	    {
 		/* Ignore REP count */;
+		if (!isdigit(*items[1]))
+		    smsg((char_u *)_("Expected REP count in %s line %d"),
+								 fname, lnum);
+	    }
 	    else if (STRCMP(items[0], "REP") == 0 && itemcnt == 3)
 	    {
-		repentry_T  *rp;
-
 		/* REP item */
-		if (ga_grow(&aff->af_rep, 1) == FAIL)
-		    break;
-		rp = ((repentry_T *)aff->af_rep.ga_data) + aff->af_rep.ga_len;
-		rp->re_from = getroom_save(&spin->si_blocks, items[1]);
-		rp->re_to = getroom_save(&spin->si_blocks, items[2]);
-		++aff->af_rep.ga_len;
+		if (do_rep)
+		    add_fromto(spin, &spin->si_rep, items[1], items[2]);
+	    }
+	    else if (STRCMP(items[0], "MAP") == 0 && itemcnt == 2)
+	    {
+		/* MAP item or count */
+		if (!found_map)
+		{
+		    /* First line contains the count. */
+		    found_map = TRUE;
+		    if (!isdigit(*items[1]))
+			smsg((char_u *)_("Expected MAP count in %s line %d"),
+								 fname, lnum);
+		}
+		else if (do_map)
+		{
+		    /* We simply concatenate all the MAP strings, separated by
+		     * slashes. */
+		    ga_concat(&spin->si_map, items[1]);
+		    ga_append(&spin->si_map, '/');
+		}
+	    }
+	    else if (STRCMP(items[0], "SAL") == 0 && itemcnt == 3)
+	    {
+		if (do_sal)
+		{
+		    /* SAL item (sounds-a-like)
+		     * Either one of the known keys or a from-to pair. */
+		    if (STRCMP(items[1], "followup") == 0)
+			spin->si_followup = sal_to_bool(items[2]);
+		    else if (STRCMP(items[1], "collapse_result") == 0)
+			spin->si_collapse = sal_to_bool(items[2]);
+		    else if (STRCMP(items[1], "remove_accents") == 0)
+			spin->si_rem_accents = sal_to_bool(items[2]);
+		    else
+			/* when "to" is "_" it means empty */
+			add_fromto(spin, &spin->si_sal, items[1],
+				     STRCMP(items[2], "_") == 0 ? (char_u *)""
+								: items[2]);
+		}
 	    }
 	    else
 		smsg((char_u *)_("Unrecognized item in %s line %d: %s"),
@@ -1834,6 +2197,41 @@
 }
 
 /*
+ * Add a from-to item to "gap".  Used for REP and SAL items.
+ * They are stored case-folded.
+ */
+    static void
+add_fromto(spin, gap, from, to)
+    spellinfo_T	*spin;
+    garray_T	*gap;
+    char_u	*from;
+    char_u	*to;
+{
+    fromto_T	*ftp;
+    char_u	word[MAXWLEN];
+
+    if (ga_grow(gap, 1) == OK)
+    {
+	ftp = ((fromto_T *)gap->ga_data) + gap->ga_len;
+	(void)spell_casefold(from, STRLEN(from), word, MAXWLEN);
+	ftp->ft_from = getroom_save(&spin->si_blocks, word);
+	(void)spell_casefold(to, STRLEN(to), word, MAXWLEN);
+	ftp->ft_to = getroom_save(&spin->si_blocks, word);
+	++gap->ga_len;
+    }
+}
+
+/*
+ * Convert a boolean argument in a SAL line to TRUE or FALSE;
+ */
+    static int
+sal_to_bool(s)
+    char_u	*s;
+{
+    return STRCMP(s, "1") == 0 || STRCMP(s, "true") == 0;
+}
+
+/*
  * Return TRUE if string "s" contains a non-ASCII character (128 or higher).
  * When "s" is NULL FALSE is returned.
  */
@@ -1885,7 +2283,6 @@
 
     hash_clear(&aff->af_pref);
     hash_clear(&aff->af_suff);
-    ga_clear(&aff->af_rep);
 }
 
 /*
@@ -2490,15 +2887,9 @@
     char_u	foldword[MAXWLEN];
     int		res;
 
-    if (flags & WF_KEEPCAP)
-	res = OK;	/* keep-case specified, don't add as fold-case */
-    else
-    {
-	(void)spell_casefold(word, len, foldword, MAXWLEN);
-	res = tree_add_word(foldword, spin->si_foldroot,
-		(ct == WF_KEEPCAP ? WF_ALLCAP : ct) | flags,
-						    region, &spin->si_blocks);
-    }
+    (void)spell_casefold(word, len, foldword, MAXWLEN);
+    res = tree_add_word(foldword, spin->si_foldroot, ct | flags,
+						region, &spin->si_blocks);
 
     if (res == OK && (ct == WF_KEEPCAP || flags & WF_KEEPCAP))
 	res = tree_add_word(word, spin->si_keeproot, flags,
@@ -2731,6 +3122,29 @@
 	putc((int)(nr >> (i * 8)), fd);
 }
 
+static int
+#ifdef __BORLANDC__
+_RTLENTRYF
+#endif
+rep_compare __ARGS((const void *s1, const void *s2));
+
+/*
+ * Function given to qsort() to sort the REP items on "from" string.
+ */
+    static int
+#ifdef __BORLANDC__
+_RTLENTRYF
+#endif
+rep_compare(s1, s2)
+    const void	*s1;
+    const void	*s2;
+{
+    fromto_T	*p1 = (fromto_T *)s1;
+    fromto_T	*p2 = (fromto_T *)s2;
+
+    return STRCMP(p1->ft_from, p2->ft_from);
+}
+
 /*
  * Write the Vim spell file "fname".
  */
@@ -2744,6 +3158,12 @@
     int		round;
     wordnode_T	*tree;
     int		nodecount;
+    int		i;
+    int		l;
+    garray_T	*gap;
+    fromto_T	*ftp;
+    char_u	*p;
+    int		rr;
 
     fd = mch_fopen((char *)fname, "w");
     if (fd == NULL)
@@ -2773,11 +3193,15 @@
 	regionmask = 0;
     }
 
-    /* Write the table with character flags and table for case folding.
+    /*
+     * Write the table with character flags and table for case folding.
      * <charflagslen> <charflags>  <fcharlen> <fchars>
      * Skip this for ASCII, the table may conflict with the one used for
-     * 'encoding'. */
-    if (spin->si_ascii)
+     * 'encoding'.
+     * Also skip this for an .add.spl file, the main spell file must contain
+     * the table (avoids that it conflicts).  File is shorter too.
+     */
+    if (spin->si_ascii || spin->si_add)
     {
 	putc(0, fd);
 	putc(0, fd);
@@ -2786,16 +3210,56 @@
     else
 	write_spell_chartab(fd);
 
+    /* Sort the REP items. */
+    qsort(spin->si_rep.ga_data, (size_t)spin->si_rep.ga_len,
+					       sizeof(fromto_T), rep_compare);
 
-    /* <SUGGEST> : <suggestlen> <more> ...
-     *  TODO.  Only write a zero length for now. */
-    put_bytes(fd, 0L, 4);			/* <suggestlen> */
+    /* <SUGGEST> : <repcount> <rep> ...
+     *             <salflags> <salcount> <sal> ...
+     *             <maplen> <mapstr> */
+    for (round = 1; round <= 2; ++round)
+    {
+	if (round == 1)
+	    gap = &spin->si_rep;
+	else
+	{
+	    gap = &spin->si_sal;
 
-    spin->si_memtot = 0;
+	    i = 0;
+	    if (spin->si_followup)
+		i |= SAL_F0LLOWUP;
+	    if (spin->si_collapse)
+		i |= SAL_COLLAPSE;
+	    if (spin->si_rem_accents)
+		i |= SAL_REM_ACCENTS;
+	    putc(i, fd);			/* <salflags> */
+	}
+
+	put_bytes(fd, (long_u)gap->ga_len, 2);	/* <repcount> or <salcount> */
+	for (i = 0; i < gap->ga_len; ++i)
+	{
+	    /* <rep> : <repfromlen> <repfrom> <reptolen> <repto> */
+	    /* <sal> : <salfromlen> <salfrom> <saltolen> <salto> */
+	    ftp = &((fromto_T *)gap->ga_data)[i];
+	    for (rr = 1; rr <= 2; ++rr)
+	    {
+		p = rr == 1 ? ftp->ft_from : ftp->ft_to;
+		l = STRLEN(p);
+		putc(l, fd);
+		fwrite(p, l, (size_t)1, fd);
+	    }
+	}
+    }
+
+    put_bytes(fd, (long_u)spin->si_map.ga_len, 2);	/* <maplen> */
+    if (spin->si_map.ga_len > 0)			/* <mapstr> */
+	fwrite(spin->si_map.ga_data, (size_t)spin->si_map.ga_len,
+							       (size_t)1, fd);
 
     /*
      * <LWORDTREE>  <KWORDTREE>
      */
+    spin->si_memtot = 0;
     for (round = 1; round <= 2; ++round)
     {
 	tree = (round == 1) ? spin->si_foldroot : spin->si_keeproot;
@@ -2941,7 +3405,7 @@
     /* Expand all the remaining arguments (e.g., $VIMRUNTIME). */
     if (get_arglist_exp(arg, &fcount, &fnames) == OK)
     {
-	mkspell(fcount, fnames, ascii, eap->forceit, TRUE);
+	mkspell(fcount, fnames, ascii, eap->forceit, FALSE);
 	FreeWild(fcount, fnames);
     }
 }
@@ -2954,12 +3418,12 @@
  * and ".spl" is appended to make the output file name.
  */
     static void
-mkspell(fcount, fnames, ascii, overwrite, verbose)
+mkspell(fcount, fnames, ascii, overwrite, added_word)
     int		fcount;
     char_u	**fnames;
     int		ascii;		    /* -ascii argument given */
     int		overwrite;	    /* overwrite existing output file */
-    int		verbose;	    /* give progress messages */
+    int		added_word;	    /* invoked through "zg" */
 {
     char_u	fname[MAXPATHL];
     char_u	wfname[MAXPATHL];
@@ -2973,8 +3437,13 @@
     spellinfo_T spin;
 
     vim_memset(&spin, 0, sizeof(spin));
-    spin.si_verbose = verbose;
+    spin.si_verbose = !added_word;
     spin.si_ascii = ascii;
+    spin.si_followup = TRUE;
+    spin.si_rem_accents = TRUE;
+    ga_init2(&spin.si_rep, (int)sizeof(fromto_T), 20);
+    ga_init2(&spin.si_sal, (int)sizeof(fromto_T), 20);
+    ga_init2(&spin.si_map, (int)sizeof(char_u), 100);
 
     /* default: fnames[0] is output file, following are input files */
     innames = &fnames[1];
@@ -2994,8 +3463,7 @@
 	else if (len > 4 && STRCMP(fnames[0] + len - 4, ".spl") == 0)
 	{
 	    /* Name ends in ".spl", use as the file name. */
-	    STRNCPY(wfname, fnames[0], sizeof(wfname));
-	    wfname[sizeof(wfname) - 1] = NUL;
+	    vim_strncpy(wfname, fnames[0], sizeof(wfname) - 1);
 	}
 	else
 	    /* Name should be language, make the file name from it. */
@@ -3119,13 +3587,13 @@
 	    /*
 	     * Combine tails in the tree.
 	     */
-	    if (verbose || p_verbose > 2)
+	    if (!added_word || p_verbose > 2)
 	    {
-		if (!verbose)
+		if (added_word)
 		    verbose_enter();
 		MSG(_("Compressing word tree..."));
 		out_flush();
-		if (!verbose)
+		if (added_word)
 		    verbose_leave();
 	    }
 	    wordtree_compress(spin.si_foldroot, &spin);
@@ -3137,36 +3605,39 @@
 	    /*
 	     * Write the info in the spell file.
 	     */
-	    if (verbose || p_verbose > 2)
+	    if (!added_word || p_verbose > 2)
 	    {
-		if (!verbose)
+		if (added_word)
 		    verbose_enter();
 		smsg((char_u *)_("Writing spell file %s..."), wfname);
 		out_flush();
-		if (!verbose)
+		if (added_word)
 		    verbose_leave();
 	    }
 
 	    write_vim_spell(wfname, &spin);
 
-	    if (verbose || p_verbose > 2)
+	    if (!added_word || p_verbose > 2)
 	    {
-		if (!verbose)
+		if (added_word)
 		    verbose_enter();
 		MSG(_("Done!"));
 		smsg((char_u *)_("Estimated runtime memory use: %d bytes"),
 							      spin.si_memtot);
 		out_flush();
-		if (!verbose)
+		if (added_word)
 		    verbose_leave();
 	    }
 
 	    /* If the file is loaded need to reload it. */
-	    spell_reload_one(wfname);
+	    spell_reload_one(wfname, added_word);
 	}
 
 	/* Free the allocated memory. */
 	free_blocks(spin.si_blocks);
+	ga_clear(&spin.si_rep);
+	ga_clear(&spin.si_sal);
+	ga_clear(&spin.si_map);
 
 	/* Free the .aff file structures. */
 	for (i = 0; i < incount; ++i)
@@ -3202,7 +3673,7 @@
     if (*curbuf->b_p_spf == NUL)
 	init_spellfile();
     if (*curbuf->b_p_spf == NUL)
-	EMSG(_("E999: 'spellfile' is not set"));
+	EMSG(_("E764: 'spellfile' is not set"));
     else
     {
 	/* Check that the user isn't editing the .add file somewhere. */
@@ -3225,11 +3696,13 @@
 		fclose(fd);
 
 		/* Update the .add.spl file. */
-		mkspell(1, &curbuf->b_p_spf, FALSE, TRUE, FALSE);
+		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);
 	    }
 	}
     }
@@ -3615,5 +4088,1591 @@
     return OK;
 }
 
+/*
+ * "z?": Find badly spelled word under or after the cursor.
+ * Give suggestions for the properly spelled word.
+ * This is based on the mechanisms of Aspell, but completely reimplemented.
+ */
+    void
+spell_suggest()
+{
+    char_u	*line;
+    pos_T	prev_cursor = curwin->w_cursor;
+    int		attr;
+    char_u	wcopy[MAXWLEN + 2];
+    char_u	*p;
+    int		i;
+    int		c;
+    suginfo_T	sug;
+    suggest_T	*stp;
+
+    /*
+     * Find the start of the badly spelled word.
+     */
+    if (spell_move_to(FORWARD, TRUE, TRUE) == FAIL)
+    {
+	beep_flush();
+	return;
+    }
+
+    /*
+     * Set the info in "sug".
+     */
+    vim_memset(&sug, 0, sizeof(sug));
+    ga_init2(&sug.su_ga, (int)sizeof(suggest_T), 10);
+    hash_init(&sug.su_banned);
+    line = ml_get_curline();
+    sug.su_badptr = line + curwin->w_cursor.col;
+    sug.su_badlen = spell_check(curwin, sug.su_badptr, &attr);
+    if (sug.su_badlen >= MAXWLEN)
+	sug.su_badlen = MAXWLEN - 1;	/* just in case */
+    vim_strncpy(sug.su_badword, sug.su_badptr, sug.su_badlen);
+    (void)spell_casefold(sug.su_badptr, sug.su_badlen,
+						    sug.su_fbadword, MAXWLEN);
+
+    /* Ban the bad word itself.  It may appear in another region. */
+    add_banned(&sug, sug.su_badword);
+
+    /*
+     * 1. Try inserting/deleting/swapping/changing a letter, use REP entries
+     *    from the .aff file and inserting a space (split the word).
+     */
+    /* Set a maximum score to limit the combination of operations that is
+     * tried. */
+    sug.su_maxscore = SCORE_MAXINIT;
+    spell_try_change(&sug);
+    cleanup_suggestions(&sug);
+
+    /*
+     * 2. Try finding sound-a-like words.
+     */
+    /* Allow a higher score if we don't have many suggestions yet. */
+    if (sug.su_maxscore == SCORE_MAXINIT)
+	sug.su_maxscore = SCORE_MAXMAX;
+    spell_try_soundalike(&sug);
+
+    /* When CTRL-C was hit while searching do show the results. */
+    if (got_int)
+    {
+	(void)vgetc();
+	got_int = FALSE;
+    }
+
+    if (sug.su_ga.ga_len == 0)
+	MSG(_("Sorry, no suggestions"));
+    else
+    {
+	/* Cleanup, sort the suggestions and truncate at SUG_PROMPT_COUNT. */
+	cleanup_suggestions(&sug);
+
+	/* List the suggestions. */
+	msg_start();
+	vim_snprintf((char *)IObuff, IOSIZE, _("Change \"%.*s\" to:"),
+						sug.su_badlen, sug.su_badptr);
+	msg_puts(IObuff);
+	msg_clr_eos();
+	msg_putchar('\n');
+	msg_scroll = TRUE;
+	for (i = 0; i < sug.su_ga.ga_len; ++i)
+	{
+	    stp = &SUG(&sug, i);
+
+	    /* The suggested word may replace only part of the bad word, add
+	     * the not replaced part. */
+	    STRCPY(wcopy, stp->st_word);
+	    if (sug.su_badlen > stp->st_orglen)
+		vim_strncpy(wcopy + STRLEN(wcopy),
+					       sug.su_badptr + stp->st_orglen,
+					      sug.su_badlen - stp->st_orglen);
+	    /* TODO: remove score */
+	    vim_snprintf((char *)IObuff, IOSIZE, _("%2d \"%s\"  (%d)"),
+						 i + 1, wcopy, stp->st_score);
+	    msg_puts(IObuff);
+	    lines_left = 3;		/* avoid more prompt */
+	    msg_putchar('\n');
+	}
+
+	/* Ask for choice. */
+	i = prompt_for_number();
+	if (i > 0 && i <= sug.su_ga.ga_len && u_save_cursor())
+	{
+	    /* Replace the word. */
+	    stp = &SUG(&sug, i - 1);
+	    p = alloc(STRLEN(line) - stp->st_orglen + STRLEN(stp->st_word) + 1);
+	    if (p != NULL)
+	    {
+		c = sug.su_badptr - line;
+		mch_memmove(p, line, c);
+		STRCPY(p + c, stp->st_word);
+		STRCAT(p, sug.su_badptr + stp->st_orglen);
+		ml_replace(curwin->w_cursor.lnum, p, FALSE);
+		curwin->w_cursor.col = c;
+		changed_bytes(curwin->w_cursor.lnum, c);
+	    }
+	}
+	else
+	    curwin->w_cursor = prev_cursor;
+    }
+
+    /* Free the suggestions. */
+    for (i = 0; i < sug.su_ga.ga_len; ++i)
+	vim_free(SUG(&sug, i).st_word);
+    ga_clear(&sug.su_ga);
+
+    /* Free the banned words. */
+    free_banned(&sug);
+}
+
+/*
+ * Make a copy of "word[len]", with the first letter upper or lower cased,
+ * to "wcopy[MAXWLEN]".
+ */
+    static void
+onecap_copy(word, len, wcopy, upper)
+    char_u	*word;
+    int		len;
+    char_u	*wcopy;
+    int		upper;	    /* TRUE: first letter made upper case */
+{
+    char_u	*p;
+    int		c;
+    int		l;
+
+    p = word;
+#ifdef FEAT_MBYTE
+    if (has_mbyte)
+	c = mb_ptr2char_adv(&p);
+    else
+#endif
+	c = *p++;
+    if (upper)
+	c = MB_TOUPPER(c);
+    else
+	c = MB_TOLOWER(c);
+#ifdef FEAT_MBYTE
+    if (has_mbyte)
+	l = mb_char2bytes(c, wcopy);
+    else
+#endif
+    {
+	l = 1;
+	wcopy[0] = c;
+    }
+    vim_strncpy(wcopy + l, p, len - (p - word));
+}
+
+/*
+ * Make a copy of "word[len]" with all the letters upper cased into
+ * "wcopy[MAXWLEN]".
+ */
+    static void
+allcap_copy(word, wcopy)
+    char_u	*word;
+    char_u	*wcopy;
+{
+    char_u	*s;
+    char_u	*d;
+    int		c;
+
+    d = wcopy;
+    for (s = word; *s != NUL; )
+    {
+#ifdef FEAT_MBYTE
+	if (has_mbyte)
+	    c = mb_ptr2char_adv(&s);
+	else
+#endif
+	    c = *s++;
+
+	c = MB_TOUPPER(c);	/* TODO: use spell toupper */
+
+#ifdef FEAT_MBYTE
+	if (has_mbyte)
+	{
+	    if (d - wcopy >= MAXWLEN - MB_MAXBYTES)
+		break;
+	    d += mb_char2bytes(c, d);
+	}
+	else
+#endif
+	{
+	    if (d - wcopy >= MAXWLEN - 1)
+		break;
+	    *d++ = c;
+	}
+    }
+    *d = NUL;
+}
+
+/*
+ * Try finding suggestions by adding/removing/swapping letters.
+ */
+    static void
+spell_try_change(su)
+    suginfo_T	*su;
+{
+    char_u	fword[MAXWLEN];	    /* copy of the bad word, case-folded */
+    char_u	tword[MAXWLEN];	    /* good word collected so far */
+    trystate_T	stack[MAXWLEN];
+    char_u	preword[MAXWLEN * 3]; /* word found with proper case (appended
+				       * to for word split) */
+    char_u	prewordlen = 0;	    /* length of word in "preword" */
+    int		splitoff = 0;	    /* index in tword after last split */
+    trystate_T	*sp;
+    int		newscore;
+    langp_T	*lp;
+    char_u	*byts;
+    int		*idxs;
+    int		depth;
+    int		c;
+    int		n;
+    int		flags;
+    int		badflags;
+    garray_T	*gap;
+    int		arridx;
+    int		len;
+    char_u	*p;
+    fromto_T	*ftp;
+    int		fl, tl;
+
+    /* get caps flags for bad word */
+    badflags = captype(su->su_badptr, su->su_badptr + su->su_badlen);
+
+    /* We make a copy of the case-folded bad word, so that we can modify it
+     * to find matches (esp. REP items). */
+    STRCPY(fword, su->su_fbadword);
+
+    /*
+     * At each node in the tree these states are tried:
+     */
+#define STATE_START	0	/* At start of node, check if word may end or
+				 * split word. */
+#define STATE_SPLITUNDO	1	/* Undo word split. */
+#define STATE_ENDNUL	2	/* Past NUL bytes at start of the node. */
+#define STATE_PLAIN	3	/* Use each byte of the node. */
+#define STATE_DEL	4	/* Delete a byte from the bad word. */
+#define STATE_INS	5	/* Insert a byte in the bad word. */
+#define STATE_SWAP	6	/* Swap two bytes. */
+#define STATE_SWAP3A	7	/* Swap two bytes over three. */
+#define STATE_ROT3L	8	/* Rotate three bytes left */
+#define STATE_ROT3R	9	/* Rotate three bytes right */
+#define STATE_ROT_UNDO	10	/* undo rotating */
+#define STATE_REP_INI	11	/* Prepare for using REP items. */
+#define STATE_REP	12	/* Use matching REP items from the .aff file. */
+#define STATE_REP_UNDO	13	/* Undo a REP item replacement. */
+#define STATE_FINAL	99	/* End of this node. */
+
+
+    for (lp = LANGP_ENTRY(curwin->w_buffer->b_langp, 0);
+						   lp->lp_slang != NULL; ++lp)
+    {
+#ifdef SOUNDFOLD_SCORE
+	su->su_slang = lp->lp_slang;
+	if (lp->lp_slang->sl_sal.ga_len > 0)
+	    /* soundfold the bad word */
+	    spell_soundfold(lp->lp_slang, su->su_fbadword, su->su_salword);
+#endif
+
+	/*
+	 * Go through the whole case-fold tree, try changes at each node.
+	 * "tword[]" contains the word collected from nodes in the tree.
+	 * "fword[]" the word we are trying to match with (initially the bad
+	 * word).
+	 */
+	byts = lp->lp_slang->sl_fbyts;
+	idxs = lp->lp_slang->sl_fidxs;
+
+	depth = 0;
+	stack[0].ts_state = STATE_START;
+	stack[0].ts_score = 0;
+	stack[0].ts_curi = 1;
+	stack[0].ts_fidx = 0;
+	stack[0].ts_fidxtry = 0;
+	stack[0].ts_twordlen = 0;
+	stack[0].ts_arridx = 0;
+
+	while (depth >= 0 && !got_int)
+	{
+	    sp = &stack[depth];
+	    switch (sp->ts_state)
+	    {
+	    case STATE_START:
+		/*
+		 * Start of node: Deal with NUL bytes, which means
+		 * tword[] may end here.
+		 */
+		arridx = sp->ts_arridx;	    /* current node in the tree */
+		len = byts[arridx];	    /* bytes in this node */
+		arridx += sp->ts_curi;	    /* index of current byte */
+
+		if (sp->ts_curi > len || (c = byts[arridx]) != 0)
+		{
+		    /* Past bytes in node and/or past NUL bytes. */
+		    sp->ts_state = STATE_ENDNUL;
+		    break;
+		}
+
+		/*
+		 * End of word in tree.
+		 */
+		++sp->ts_curi;		/* eat one NUL byte */
+
+		flags = idxs[arridx];
+
+		/*
+		 * Form the word with proper case in preword.
+		 * If there is a word from a previous split, append.
+		 */
+		tword[sp->ts_twordlen] = NUL;
+		if (flags & WF_KEEPCAP)
+		    /* Must find the word in the keep-case tree. */
+		    find_keepcap_word(lp->lp_slang, tword + splitoff,
+							preword + prewordlen);
+		else
+		    /* Include badflags: if the badword is onecap or allcap
+		     * use that for the goodword too. */
+		    make_case_word(tword + splitoff,
+				      preword + prewordlen, flags | badflags);
+
+		/* Don't use a banned word.  It may appear again as a good
+		 * word, thus remember it. */
+		if (flags & WF_BANNED)
+		{
+		    add_banned(su, preword + prewordlen);
+		    break;
+		}
+		if (was_banned(su, preword + prewordlen))
+		    break;
+
+		newscore = 0;
+		if ((flags & WF_REGION)
+			     && (((unsigned)flags >> 8) & lp->lp_region) == 0)
+		    newscore += SCORE_REGION;
+		if (flags & WF_RARE)
+		    newscore += SCORE_RARE;
+
+		if (!spell_valid_case(badflags,
+					 captype(preword + prewordlen, NULL)))
+		    newscore += SCORE_ICASE;
+
+		if (fword[sp->ts_fidx] == 0)
+		{
+		    /* The badword also ends: add suggestions, */
+		    add_suggestion(su, preword, sp->ts_score + newscore);
+		}
+		else if (sp->ts_fidx >= sp->ts_fidxtry)
+		{
+		    /* The word in the tree ends but the badword
+		     * continues: try inserting a space and check that a valid
+		     * words starts at fword[sp->ts_fidx]. */
+		    if (try_deeper(su, stack, depth, newscore + SCORE_SPLIT))
+		    {
+			/* Save things to be restored at STATE_SPLITUNDO. */
+			sp->ts_save_prewordlen = prewordlen;
+			sp->ts_save_badflags = badflags;
+			sp->ts_save_splitoff = splitoff;
+
+			/* Append a space to preword. */
+			STRCAT(preword, " ");
+			prewordlen = STRLEN(preword);
+			splitoff = sp->ts_twordlen;
+			/* TODO: when case-folding changed the number of bytes
+			 * this doesn't work... */
+			badflags = captype(su->su_badptr + sp->ts_fidx,
+					       su->su_badptr + su->su_badlen);
+
+			sp->ts_state = STATE_SPLITUNDO;
+			++depth;
+			/* Restart at top of the tree. */
+			stack[depth].ts_arridx = 0;
+		    }
+		}
+		break;
+
+	    case STATE_SPLITUNDO:
+		/* Fixup the changes done for word split. */
+		badflags = sp->ts_save_badflags;
+		splitoff = sp->ts_save_splitoff;
+		prewordlen =  sp->ts_save_prewordlen;
+
+		/* Continue looking for NUL bytes. */
+		sp->ts_state = STATE_START;
+		break;
+
+	    case STATE_ENDNUL:
+		/* Past the NUL bytes in the node. */
+		if (fword[sp->ts_fidx] == 0)
+		{
+		    /* The badword ends, can't use the bytes in this node. */
+		    sp->ts_state = STATE_DEL;
+		    break;
+		}
+		sp->ts_state = STATE_PLAIN;
+		/*FALLTHROUGH*/
+
+	    case STATE_PLAIN:
+		/*
+		 * Go over all possible bytes at this node, add each to
+		 * tword[] and use child node.  "ts_curi" is the index.
+		 */
+		arridx = sp->ts_arridx;
+		if (sp->ts_curi > byts[arridx])
+		{
+		    /* Done all bytes at this node, do next state.  When still
+		     * at already changed bytes skip the other tricks. */
+		    if (sp->ts_fidx >= sp->ts_fidxtry)
+			sp->ts_state = STATE_DEL;
+		    else
+			sp->ts_state = STATE_FINAL;
+		}
+		else
+		{
+		    arridx += sp->ts_curi++;
+		    c = byts[arridx];
+
+		    /* Normal byte, go one level deeper.  If it's not equal to
+		     * the byte in the bad word adjust the score.  But don't
+		     * even try when the byte was already changed. */
+		    if (c == fword[sp->ts_fidx])
+			newscore = 0;
+			/* TODO: multi-byte characters */
+		    else if (lp->lp_slang->sl_map != NULL
+			    && similar_chars(lp->lp_slang,
+						       c, fword[sp->ts_fidx]))
+			newscore = SCORE_SIMILAR;
+		    else
+			newscore = SCORE_SUBST;
+		    if ((newscore == 0 || sp->ts_fidx >= sp->ts_fidxtry)
+				    && try_deeper(su, stack, depth, newscore))
+		    {
+			++depth;
+			++stack[depth].ts_fidx;
+			tword[stack[depth].ts_twordlen++] = c;
+			stack[depth].ts_arridx = idxs[arridx];
+		    }
+		}
+		break;
+
+	    case STATE_DEL:
+		/* Try skipping one byte in the bad word (delete it). */
+		sp->ts_state = STATE_INS;
+		sp->ts_curi = 1;
+		if (fword[sp->ts_fidx] != NUL
+			&& try_deeper(su, stack, depth, SCORE_DEL))
+		{
+		    ++depth;
+		    ++stack[depth].ts_fidx;
+		    break;
+		}
+		/*FALLTHROUGH*/
+
+	    case STATE_INS:
+		/* Insert one byte.  Do this for each possible bytes at this
+		 * node. */
+		n = sp->ts_arridx;
+		if (sp->ts_curi > byts[n])
+		{
+		    /* Done all bytes at this node, do next state. */
+		    sp->ts_state = STATE_SWAP;
+		    sp->ts_curi = 1;
+		}
+		else
+		{
+		    /* Do one more byte at this node. */
+		    n += sp->ts_curi++;
+		    c = byts[n];
+		    if (c != 0 && try_deeper(su, stack, depth, SCORE_INS))
+		    {
+			++depth;
+			tword[stack[depth].ts_twordlen++] = c;
+			stack[depth].ts_arridx = idxs[n];
+		    }
+		}
+		break;
+
+	    case STATE_SWAP:
+		/* Swap two bytes: "12" -> "21".  This means looking for the
+		 * following byte at the current node and the current byte at
+		 * its child node.  We change "fword" here, it's changed back
+		 * afterwards.  TODO: should swap characters instead of bytes.
+		 * */
+		c = fword[sp->ts_fidx];
+		if (c != NUL && fword[sp->ts_fidx + 1] != NUL
+				  && try_deeper(su, stack, depth, SCORE_SWAP))
+		{
+		    sp->ts_state = STATE_SWAP3A;
+		    ++depth;
+		    fword[sp->ts_fidx] = fword[sp->ts_fidx + 1];
+		    fword[sp->ts_fidx + 1] = c;
+		    stack[depth].ts_fidxtry = sp->ts_fidx + 2;
+		}
+		else
+		    /* If this swap doesn't work then SWAP3 won't either. */
+		    sp->ts_state = STATE_REP_INI;
+		break;
+
+	    case STATE_SWAP3A:
+		/* First undo the STATE_SWAP swap: "21" -> "12". */
+		c = fword[sp->ts_fidx];
+		fword[sp->ts_fidx] = fword[sp->ts_fidx + 1];
+		fword[sp->ts_fidx + 1] = c;
+
+		/* Swap two bytes, skipping one: "123" -> "321".  We change
+		 * "fword" here, it's changed back afterwards.  TODO: should
+		 * swap characters instead of bytes. */
+		c = fword[sp->ts_fidx];
+		if (c != NUL && fword[sp->ts_fidx + 1] != NUL
+			&& fword[sp->ts_fidx + 2] != NUL
+				  && try_deeper(su, stack, depth, SCORE_SWAP3))
+		{
+		    sp->ts_state = STATE_ROT3L;
+		    ++depth;
+		    fword[sp->ts_fidx] = fword[sp->ts_fidx + 2];
+		    fword[sp->ts_fidx + 2] = c;
+		    stack[depth].ts_fidxtry = sp->ts_fidx + 3;
+		}
+		else
+		    sp->ts_state = STATE_REP_INI;
+		break;
+
+	    case STATE_ROT3L:
+		/* First undo STATE_SWAP3A: "321" -> "123" */
+		c = fword[sp->ts_fidx];
+		fword[sp->ts_fidx] = fword[sp->ts_fidx + 2];
+		fword[sp->ts_fidx + 2] = c;
+
+		/* Rotate three bytes left: "123" -> "231".  We change
+		 * "fword" here, it's changed back afterwards.  TODO: should
+		 * swap characters instead of bytes. */
+		if (try_deeper(su, stack, depth, SCORE_SWAP3))
+		{
+		    sp->ts_state = STATE_ROT3R;
+		    ++depth;
+		    c = fword[sp->ts_fidx];
+		    fword[sp->ts_fidx] = fword[sp->ts_fidx + 1];
+		    fword[sp->ts_fidx + 1] = fword[sp->ts_fidx + 2];
+		    fword[sp->ts_fidx + 2] = c;
+		    stack[depth].ts_fidxtry = sp->ts_fidx + 3;
+		}
+		else
+		    sp->ts_state = STATE_REP_INI;
+		break;
+
+	    case STATE_ROT3R:
+		/* First undo STATE_ROT3L: "231" -> "123" */
+		c = fword[sp->ts_fidx + 2];
+		fword[sp->ts_fidx + 2] = fword[sp->ts_fidx + 1];
+		fword[sp->ts_fidx + 1] = fword[sp->ts_fidx];
+		fword[sp->ts_fidx] = c;
+
+		/* Rotate three bytes right: "123" -> "312".  We change
+		 * "fword" here, it's changed back afterwards.  TODO: should
+		 * swap characters instead of bytes. */
+		if (try_deeper(su, stack, depth, SCORE_SWAP3))
+		{
+		    sp->ts_state = STATE_ROT_UNDO;
+		    ++depth;
+		    c = fword[sp->ts_fidx + 2];
+		    fword[sp->ts_fidx + 2] = fword[sp->ts_fidx + 1];
+		    fword[sp->ts_fidx + 1] = fword[sp->ts_fidx];
+		    fword[sp->ts_fidx] = c;
+		    stack[depth].ts_fidxtry = sp->ts_fidx + 3;
+		}
+		else
+		    sp->ts_state = STATE_REP_INI;
+		break;
+
+	    case STATE_ROT_UNDO:
+		/* Undo STATE_ROT3R: "312" -> "123" */
+		c = fword[sp->ts_fidx];
+		fword[sp->ts_fidx] = fword[sp->ts_fidx + 1];
+		fword[sp->ts_fidx + 1] = fword[sp->ts_fidx + 2];
+		fword[sp->ts_fidx + 2] = c;
+		/*FALLTHROUGH*/
+
+	    case STATE_REP_INI:
+		/* Check if matching with REP items from the .aff file would
+		 * work.  Quickly skip if there are no REP items or the score
+		 * is going to be too high anyway. */
+		gap = &lp->lp_slang->sl_rep;
+		if (gap->ga_len == 0
+			       || sp->ts_score + SCORE_REP >= su->su_maxscore)
+		{
+		    sp->ts_state = STATE_FINAL;
+		    break;
+		}
+
+		/* Use the first byte to quickly find the first entry that
+		 * matches.  If the index is -1 there is none. */
+		sp->ts_curi = lp->lp_slang->sl_rep_first[fword[sp->ts_fidx]];
+		if (sp->ts_curi < 0)
+		{
+		    sp->ts_state = STATE_FINAL;
+		    break;
+		}
+
+		sp->ts_state = STATE_REP;
+		/*FALLTHROUGH*/
+
+	    case STATE_REP:
+		/* Try matching with REP items from the .aff file.  For each
+		 * match replace the charactes and check if the resulting word
+		 * is valid. */
+		p = fword + sp->ts_fidx;
+
+		gap = &lp->lp_slang->sl_rep;
+		while (sp->ts_curi < gap->ga_len)
+		{
+		    ftp = (fromto_T *)gap->ga_data + sp->ts_curi++;
+		    if (*ftp->ft_from != *p)
+		    {
+			/* past possible matching entries */
+			sp->ts_curi = gap->ga_len;
+			break;
+		    }
+		    if (STRNCMP(ftp->ft_from, p, STRLEN(ftp->ft_from)) == 0
+			    && try_deeper(su, stack, depth, SCORE_REP))
+		    {
+			/* Need to undo this afterwards. */
+			sp->ts_state = STATE_REP_UNDO;
+
+			/* Change the "from" to the "to" string. */
+			++depth;
+			fl = STRLEN(ftp->ft_from);
+			tl = STRLEN(ftp->ft_to);
+			if (fl != tl)
+			    mch_memmove(p + tl, p + fl, STRLEN(p + fl) + 1);
+			mch_memmove(p, ftp->ft_to, tl);
+			stack[depth].ts_fidxtry = sp->ts_fidx + tl;
+			break;
+		    }
+		}
+
+		if (sp->ts_curi >= gap->ga_len)
+		    /* No (more) matches. */
+		    sp->ts_state = STATE_FINAL;
+
+		break;
+
+	    case STATE_REP_UNDO:
+		/* Undo a REP replacement and continue with the next one. */
+		ftp = (fromto_T *)lp->lp_slang->sl_rep.ga_data
+							    + sp->ts_curi - 1;
+		fl = STRLEN(ftp->ft_from);
+		tl = STRLEN(ftp->ft_to);
+		p = fword + sp->ts_fidx;
+		if (fl != tl)
+		    mch_memmove(p + fl, p + tl, STRLEN(p + tl) + 1);
+		mch_memmove(p, ftp->ft_from, fl);
+		sp->ts_state = STATE_REP;
+		break;
+
+	    default:
+		/* Did all possible states at this level, go up one level. */
+		--depth;
+	    }
+
+	    line_breakcheck();
+	}
+    }
+}
+
+/*
+ * Try going one level deeper in the tree.
+ */
+    static int
+try_deeper(su, stack, depth, score_add)
+    suginfo_T	*su;
+    trystate_T	*stack;
+    int		depth;
+    int		score_add;
+{
+    int		newscore;
+
+    /* Refuse to go deeper if the scrore is getting too big. */
+    newscore = stack[depth].ts_score + score_add;
+    if (newscore >= su->su_maxscore)
+	return FALSE;
+
+    stack[depth + 1].ts_state = STATE_START;
+    stack[depth + 1].ts_score = newscore;
+    stack[depth + 1].ts_curi = 1;	/* start just after length byte */
+    stack[depth + 1].ts_fidx = stack[depth].ts_fidx;
+    stack[depth + 1].ts_fidxtry = stack[depth].ts_fidxtry;
+    stack[depth + 1].ts_twordlen = stack[depth].ts_twordlen;
+    stack[depth + 1].ts_arridx = stack[depth].ts_arridx;
+    return TRUE;
+}
+
+/*
+ * "fword" is a good word with case folded.  Find the matching keep-case
+ * words and put it in "kword".
+ * Theoretically there could be several keep-case words that result in the
+ * same case-folded word, but we only find one...
+ */
+    static void
+find_keepcap_word(slang, fword, kword)
+    slang_T	*slang;
+    char_u	*fword;
+    char_u	*kword;
+{
+    char_u	uword[MAXWLEN];		/* "fword" in upper-case */
+    int		depth;
+    int		tryidx;
+
+    /* The following arrays are used at each depth in the tree. */
+    int		arridx[MAXWLEN];
+    int		round[MAXWLEN];
+    int		fwordidx[MAXWLEN];
+    int		uwordidx[MAXWLEN];
+    int		kwordlen[MAXWLEN];
+
+    int		flen, ulen;
+    int		l;
+    int		len;
+    int		c;
+    unsigned	lo, hi, m;
+    char_u	*p;
+    char_u	*byts = slang->sl_kbyts;    /* array with bytes of the words */
+    int		*idxs = slang->sl_kidxs;    /* array with indexes */
+
+    if (byts == NULL)
+    {
+	/* array is empty: "cannot happen" */
+	*kword = NUL;
+	return;
+    }
+
+    /* Make an all-cap version of "fword". */
+    allcap_copy(fword, uword);
+
+    /*
+     * Each character needs to be tried both case-folded and upper-case.
+     * All this gets very complicated if we keep in mind that changing case
+     * may change the byte length of a multi-byte character...
+     */
+    depth = 0;
+    arridx[0] = 0;
+    round[0] = 0;
+    fwordidx[0] = 0;
+    uwordidx[0] = 0;
+    kwordlen[0] = 0;
+    while (depth >= 0)
+    {
+	if (fword[fwordidx[depth]] == NUL)
+	{
+	    /* We are at the end of "fword".  If the tree allows a word to end
+	     * here we have found a match. */
+	    if (byts[arridx[depth] + 1] == 0)
+	    {
+		kword[kwordlen[depth]] = NUL;
+		return;
+	    }
+
+	    /* kword is getting too long, continue one level up */
+	    --depth;
+	}
+	else if (++round[depth] > 2)
+	{
+	    /* tried both fold-case and upper-case character, continue one
+	     * level up */
+	    --depth;
+	}
+	else
+	{
+	    /*
+	     * round[depth] == 1: Try using the folded-case character.
+	     * round[depth] == 2: Try using the upper-case character.
+	     */
+#ifdef FEAT_MBYTE
+	    if (has_mbyte)
+	    {
+		flen = mb_ptr2len_check(fword + fwordidx[depth]);
+		ulen = mb_ptr2len_check(uword + uwordidx[depth]);
+	    }
+	    else
+#endif
+		ulen = flen = 1;
+	    if (round[depth] == 1)
+	    {
+		p = fword + fwordidx[depth];
+		l = flen;
+	    }
+	    else
+	    {
+		p = uword + uwordidx[depth];
+		l = ulen;
+	    }
+
+	    for (tryidx = arridx[depth]; l > 0; --l)
+	    {
+		/* Perform a binary search in the list of accepted bytes. */
+		len = byts[tryidx++];
+		c = *p++;
+		lo = tryidx;
+		hi = tryidx + len - 1;
+		while (lo < hi)
+		{
+		    m = (lo + hi) / 2;
+		    if (byts[m] > c)
+			hi = m - 1;
+		    else if (byts[m] < c)
+			lo = m + 1;
+		    else
+		    {
+			lo = hi = m;
+			break;
+		    }
+		}
+
+		/* Stop if there is no matching byte. */
+		if (hi < lo || byts[lo] != c)
+		    break;
+
+		/* Continue at the child (if there is one). */
+		tryidx = idxs[lo];
+	    }
+
+	    if (l == 0)
+	    {
+		/*
+		 * Found the matching char.  Copy it to "kword" and go a
+		 * level deeper.
+		 */
+		if (round[depth] == 1)
+		{
+		    STRNCPY(kword + kwordlen[depth], fword + fwordidx[depth],
+									flen);
+		    kwordlen[depth + 1] = kwordlen[depth] + flen;
+		}
+		else
+		{
+		    STRNCPY(kword + kwordlen[depth], uword + uwordidx[depth],
+									ulen);
+		    kwordlen[depth + 1] = kwordlen[depth] + ulen;
+		}
+		fwordidx[depth + 1] = fwordidx[depth] + flen;
+		uwordidx[depth + 1] = uwordidx[depth] + ulen;
+
+		++depth;
+		arridx[depth] = tryidx;
+		round[depth] = 0;
+	    }
+	}
+    }
+
+    /* Didn't find it: "cannot happen". */
+    *kword = NUL;
+}
+
+/*
+ * Find suggestions by comparing the word in a sound-a-like form.
+ */
+    static void
+spell_try_soundalike(su)
+    suginfo_T	*su;
+{
+    char_u	salword[MAXWLEN];
+    char_u	tword[MAXWLEN];
+    char_u	tfword[MAXWLEN];
+    char_u	tsalword[MAXWLEN];
+    int		arridx[MAXWLEN];
+    int		curi[MAXWLEN];
+    langp_T	*lp;
+    char_u	*byts;
+    int		*idxs;
+    int		depth;
+    int		c;
+    int		n;
+    int		round;
+    int		flags;
+
+    for (lp = LANGP_ENTRY(curwin->w_buffer->b_langp, 0);
+						   lp->lp_slang != NULL; ++lp)
+    {
+	if (lp->lp_slang->sl_sal.ga_len > 0)
+	{
+	    /* soundfold the bad word */
+	    spell_soundfold(lp->lp_slang, su->su_fbadword, salword);
+
+	    /*
+	     * Go through the whole tree, soundfold each word and compare.
+	     * round 1: use the case-folded tree.
+	     * round 2: use the keep-case tree.
+	     */
+	    for (round = 1; round <= 2; ++round)
+	    {
+		if (round == 1)
+		{
+		    byts = lp->lp_slang->sl_fbyts;
+		    idxs = lp->lp_slang->sl_fidxs;
+		}
+		else
+		{
+		    byts = lp->lp_slang->sl_kbyts;
+		    idxs = lp->lp_slang->sl_kidxs;
+		}
+
+		depth = 0;
+		arridx[0] = 0;
+		curi[0] = 1;
+		while (depth >= 0 && !got_int)
+		{
+		    if (curi[depth] > byts[arridx[depth]])
+			/* Done all bytes at this node, go up one level. */
+			--depth;
+		    else
+		    {
+			/* Do one more byte at this node. */
+			n = arridx[depth] + curi[depth];
+			++curi[depth];
+			c = byts[n];
+			if (c == 0)
+			{
+			    /* End of word, deal with the word. */
+			    flags = idxs[n];
+			    if (round == 2 || (flags & WF_KEEPCAP) == 0)
+			    {
+				tword[depth] = NUL;
+				if (round == 1)
+				    spell_soundfold(lp->lp_slang,
+							     tword, tsalword);
+				else
+				{
+				    /* In keep-case tree need to case-fold the
+				     * word. */
+				    (void)spell_casefold(tword, depth,
+							     tfword, MAXWLEN);
+				    spell_soundfold(lp->lp_slang,
+							    tfword, tsalword);
+				}
+
+				/* TODO: also compare with small changes
+				 * (insert char, swap char, etc.) */
+				if (STRCMP(salword, tsalword) == 0)
+				{
+				    if (round == 1 && flags != 0)
+				    {
+					char_u	cword[MAXWLEN];
+
+					make_case_word(tword, cword, flags);
+					add_suggestion(su, cword, 0);
+				    }
+				    else
+					add_suggestion(su, tword, 0);
+				}
+			    }
+
+			    /* Skip over other NUL bytes. */
+			    while (byts[n + 1] == 0)
+			    {
+				++n;
+				++curi[depth];
+			    }
+			}
+			else
+			{
+			    /* Normal char, go one level deeper. */
+			    tword[depth++] = c;
+			    arridx[depth] = idxs[n];
+			    curi[depth] = 1;
+			}
+		    }
+		}
+		line_breakcheck();
+	    }
+	}
+    }
+}
+
+/*
+ * Copy "fword" to "cword", fixing according to "flags".
+ */
+    static void
+make_case_word(fword, cword, flags)
+    char_u	*fword;
+    char_u	*cword;
+    int		flags;
+{
+    if (flags & WF_ALLCAP)
+	/* Make it all upper-case */
+	allcap_copy(fword, cword);
+    else if (flags & WF_ONECAP)
+	/* Make the first letter upper-case */
+	onecap_copy(fword, STRLEN(fword), cword, TRUE);
+    else
+	/* Use goodword as-is. */
+	STRCPY(cword, fword);
+}
+
+/*
+ * Return TRUE if "c1" and "c2" are similar characters according to the MAP
+ * lines in the .aff file.
+ */
+    static int
+similar_chars(slang, c1, c2)
+    slang_T	*slang;
+    int		c1;
+    int		c2;
+{
+    char_u	*p1;
+    char_u	*p2;
+
+    /* The similar characters are stored separated with slashes:
+     * "aaa/bbb/ccc/".  Search for each character and if the next slash is the
+     * same one they are in the same MAP entry. */
+    p1 = vim_strchr(slang->sl_map, c1);
+    if (p1 == NULL)
+	return FALSE;
+    p2 = vim_strchr(slang->sl_map, c2);
+    if (p2 == NULL)
+	return FALSE;
+    return vim_strchr(p1, '/') == vim_strchr(p2, '/');
+}
+
+/*
+ * Add a suggestion to the list of suggestions.
+ * Do not add a duplicate suggestion or suggestions with a bad score.
+ * When "use_score" is not zero it's used, otherwise the score is computed
+ * with spell_edit_score().
+ */
+    static void
+add_suggestion(su, goodword, use_score)
+    suginfo_T	*su;
+    char_u	*goodword;
+    int		use_score;
+{
+    suggest_T   *stp;
+    int		score;
+    int		i;
+#ifdef SOUNDFOLD_SCORE
+    char_u	fword[MAXWLEN];
+    char_u	salword[MAXWLEN];
+#endif
+
+    /* Check that the word wasn't banned. */
+    if (was_banned(su, goodword))
+	return;
+
+    /* Compute the score and add the suggestion if it's good enough. */
+    if (use_score != 0)
+	score = use_score;
+    else
+	score = spell_edit_score(su->su_badword, goodword);
+
+    if (score <= su->su_maxscore)
+    {
+#ifdef SOUNDFOLD_SCORE
+	/* Add to the score when the word sounds differently.
+	 * This is slow... */
+	if (su->su_slang->sl_sal.ga_len > 0)
+	{
+	    (void)spell_casefold(goodword, STRLEN(goodword), fword, MAXWLEN);
+	    spell_soundfold(su->su_slang, fword, salword);
+	    score += spell_edit_score(su->su_salword, salword);
+	}
+#endif
+
+	/* Check if the word is already there. */
+	stp = &SUG(su, 0);
+	for (i = su->su_ga.ga_len - 1; i >= 0; --i)
+	    if (STRCMP(stp[i].st_word, goodword) == 0)
+	    {
+		/* Found it.  Remember the lowest score. */
+		if (stp[i].st_score > score)
+		    stp[i].st_score = score;
+		break;
+	    }
+
+	if (i < 0 && ga_grow(&su->su_ga, 1) == OK)
+	{
+	    /* Add a suggestion. */
+	    stp = &SUG(su, su->su_ga.ga_len);
+	    stp->st_word = vim_strsave(goodword);
+	    if (stp->st_word != NULL)
+	    {
+		stp->st_score = score;
+		stp->st_orglen = su->su_badlen;
+		++su->su_ga.ga_len;
+
+		/* If we have too many suggestions now, sort the list and keep
+		 * the best suggestions. */
+		if (su->su_ga.ga_len > SUG_CLEANUP_COUNT)
+		    cleanup_suggestions(su);
+	    }
+	}
+    }
+}
+
+/*
+ * Add a word to be banned.
+ */
+    static void
+add_banned(su, word)
+    suginfo_T	*su;
+    char_u	*word;
+{
+    char_u	*s = vim_strsave(word);
+    hash_T	hash;
+    hashitem_T	*hi;
+
+    if (s != NULL)
+    {
+	hash = hash_hash(s);
+	hi = hash_lookup(&su->su_banned, s, hash);
+	if (HASHITEM_EMPTY(hi))
+	    hash_add_item(&su->su_banned, hi, s, hash);
+    }
+}
+
+/*
+ * Return TRUE if a word appears in the list of banned words.
+ */
+    static int
+was_banned(su, word)
+    suginfo_T	*su;
+    char_u	*word;
+{
+    return !HASHITEM_EMPTY(hash_find(&su->su_banned, word));
+}
+
+/*
+ * Free the banned words in "su".
+ */
+    static void
+free_banned(su)
+    suginfo_T	*su;
+{
+    int		todo;
+    hashitem_T	*hi;
+
+    todo = su->su_banned.ht_used;
+    for (hi = su->su_banned.ht_array; todo > 0; ++hi)
+    {
+	if (!HASHITEM_EMPTY(hi))
+	{
+	    vim_free(hi->hi_key);
+	    --todo;
+	}
+    }
+    hash_clear(&su->su_banned);
+}
+
+static int
+#ifdef __BORLANDC__
+_RTLENTRYF
+#endif
+sug_compare __ARGS((const void *s1, const void *s2));
+
+/*
+ * Function given to qsort() to sort the suggestions on st_score.
+ */
+    static int
+#ifdef __BORLANDC__
+_RTLENTRYF
+#endif
+sug_compare(s1, s2)
+    const void	*s1;
+    const void	*s2;
+{
+    suggest_T	*p1 = (suggest_T *)s1;
+    suggest_T	*p2 = (suggest_T *)s2;
+
+    return p1->st_score - p2->st_score;
+}
+
+/*
+ * Cleanup the suggestions:
+ * - Sort on score.
+ * - Remove words that won't be displayed.
+ */
+    static void
+cleanup_suggestions(su)
+    suginfo_T	*su;
+{
+    suggest_T   *stp = &SUG(su, 0);
+    int		i;
+
+    /* Sort the list. */
+    qsort(su->su_ga.ga_data, (size_t)su->su_ga.ga_len,
+					      sizeof(suggest_T), sug_compare);
+
+    /* Truncate the list to the number of suggestions that will be displayed. */
+    if (su->su_ga.ga_len > SUG_PROMPT_COUNT)
+    {
+	for (i = SUG_PROMPT_COUNT; i < su->su_ga.ga_len; ++i)
+	    vim_free(stp[i].st_word);
+	su->su_ga.ga_len = SUG_PROMPT_COUNT;
+	su->su_maxscore = stp[SUG_PROMPT_COUNT - 1].st_score;
+    }
+}
+
+/*
+ * Turn "inword" into its sound-a-like equivalent in "res[MAXWLEN]".
+ */
+    static void
+spell_soundfold(slang, inword, res)
+    slang_T	*slang;
+    char_u	*inword;
+    char_u	*res;
+{
+    fromto_T	*ftp;
+    char_u	word[MAXWLEN];
+#ifdef FEAT_MBYTE
+    int		l;
+#endif
+    char_u	*s;
+    char_u	*t;
+    int		i, j, z;
+    int		n, k = 0;
+    int		z0;
+    int		k0;
+    int		n0;
+    int		c;
+    int		pri;
+    int		p0 = -333;
+    int		c0;
+
+    /* Remove accents, if wanted.
+     * We actually remove all non-word characters. */
+    if (slang->sl_rem_accents)
+    {
+	t = word;
+	for (s = inword; *s != NUL; )
+	{
+#ifdef FEAT_MBYTE
+	    if (has_mbyte)
+	    {
+		l = mb_ptr2len_check(s);
+		if (SPELL_ISWORDP(s))
+		{
+		    mch_memmove(t, s, l);
+		    t += l;
+		}
+		s += l;
+	    }
+	    else
+#endif
+	    {
+		if (SPELL_ISWORDP(s))
+		    *t++ = *s;
+		++s;
+	    }
+	}
+	*t = NUL;
+    }
+    else
+	STRCPY(word, inword);
+
+    ftp = (fromto_T *)slang->sl_sal.ga_data;
+
+    /*
+     * This comes from Aspell phonet.cpp.  Converted from C++ to C.
+     * TODO: support for multi-byte chars.
+     */
+    i = j = z = 0;
+    while ((c = word[i]) != NUL)
+    {
+	n = slang->sl_sal_first[c];
+	z0 = 0;
+
+	if (n >= 0)
+	{
+	    /* check all rules for the same letter */
+	    while (ftp[n].ft_from[0] == c)
+	    {
+		/* check whole string */
+		k = 1;   /* number of found letters */
+		pri = 5;   /* default priority */
+		s = ftp[n].ft_from;
+		s++;     /* important for (see below)  "*(s-1)" */
+
+		/* Skip over normal letters that match with the word. */
+		while (*s != NUL && word[i + k] == *s
+			&& !vim_isdigit(*s) && strchr("(-<^$", *s) == NULL)
+		{
+		    k++;
+		    s++;
+		}
+
+		if (*s == '(')
+		{
+		    /* check alternate letters in "(..)" */
+		    for (t = s + 1; *t != ')' && *t != NUL; ++t)
+			if (*t == word[i + k])
+			{
+			    /* match */
+			    ++k;
+			    for (s = t + 1; *s != NUL; ++s)
+				if (*s == ')')
+				{
+				    ++s;
+				    break;
+				}
+			    break;
+			}
+		}
+
+		p0 = *s;
+		k0 = k;
+		while (*s == '-' && k > 1)
+		{
+		    k--;
+		    s++;
+		}
+		if (*s == '<')
+		    s++;
+		if (vim_isdigit(*s))
+		{
+		    /* determine priority */
+		    pri = *s - '0';
+		    s++;
+		}
+		if (*s == '^' && *(s + 1) == '^')
+		    s++;
+
+		if (*s == NUL
+			|| (*s == '^'
+			    && (i == 0 || !SPELL_ISWORDP(word + i - 1))
+			    && (*(s + 1) != '$'
+				|| (!SPELL_ISWORDP(word + i + k0))))
+			|| (*s == '$' && i > 0
+			    && SPELL_ISWORDP(word + i - 1)
+			    && (!SPELL_ISWORDP(word + i + k0))))
+		{
+		    /* search for followup rules, if:    */
+		    /* followup and k > 1  and  NO '-' in searchstring */
+		    c0 = word[i + k - 1];
+		    n0 = slang->sl_sal_first[c0];
+
+		    if (slang->sl_followup && k > 1 && n0 >= 0
+			    && p0 != '-' && word[i + k] != NUL)
+		    {
+			/* test follow-up rule for "word[i + k]" */
+			while (ftp[n0].ft_from[0] == c0)
+			{
+
+			    /* check whole string */
+			    k0 = k;
+			    p0 = 5;
+			    s = ftp[n0].ft_from;
+			    s++;
+			    while (*s != NUL && word[i+k0] == *s
+				    && !vim_isdigit(*s)
+						&& strchr("(-<^$",*s) == NULL)
+			    {
+				k0++;
+				s++;
+			    }
+			    if (*s == '(')
+			    {
+				/* check alternate letters in "(..)" */
+				for (t = s + 1; *t != ')' && *t != NUL; ++t)
+				    if (*t == word[i + k0])
+				    {
+					/* match */
+					++k0;
+					for (s = t + 1; *s != NUL; ++s)
+					    if (*s == ')')
+					    {
+						++s;
+						break;
+					    }
+					break;
+				    }
+			    }
+			    while (*s == '-')
+			    {
+				/* "k0" gets NOT reduced  */
+				/* because "if (k0 == k)" */
+				s++;
+			    }
+			    if (*s == '<')
+				s++;
+			    if (vim_isdigit(*s))
+			    {
+				p0 = *s - '0';
+				s++;
+			    }
+
+			    if (*s == NUL
+				    /* *s == '^' cuts */
+				    || (*s == '$'
+					    && !SPELL_ISWORDP(word + i + k0)))
+			    {
+				if (k0 == k)
+				{
+				    /* this is just a piece of the string */
+				    ++n0;
+				    continue;
+				}
+
+				if (p0 < pri)
+				{
+				    /* priority too low */
+				    ++n0;
+				    continue;
+				}
+				/* rule fits; stop search */
+				break;
+			    }
+			    ++n0;
+			}
+
+			if (p0 >= pri && ftp[n0].ft_from[0] == c0)
+			{
+			    ++n;
+			    continue;
+			}
+		    }
+
+		    /* replace string */
+		    s = ftp[n].ft_to;
+		    p0 = (ftp[n].ft_from[0] != NUL
+			    && vim_strchr(ftp[n].ft_from + 1,
+							'<') != NULL) ? 1 : 0;
+		    if (p0 == 1 && z == 0)
+		    {
+			/* rule with '<' is used */
+			if (j > 0 && *s != NUL
+				&& (res[j - 1] == c || res[j - 1] == *s))
+			    j--;
+			z0 = 1;
+			z = 1;
+			k0 = 0;
+			while (*s != NUL && word[i+k0] != NUL)
+			{
+			    word[i + k0] = *s;
+			    k0++;
+			    s++;
+			}
+			if (k > k0)
+			    mch_memmove(word + i + k0, word + i + k,
+						    STRLEN(word + i + k) + 1);
+
+			/* new "actual letter" */
+			c = word[i];
+		    }
+		    else
+		    {
+			/* no '<' rule used */
+			i += k - 1;
+			z = 0;
+			while (*s != NUL && s[1] != NUL && j < MAXWLEN)
+			{
+			    if (j == 0 || res[j - 1] != *s)
+			    {
+				res[j] = *s;
+				j++;
+			    }
+			    s++;
+			}
+			/* new "actual letter" */
+			c = *s;
+			if (ftp[n].ft_from[0] != NUL
+					 && strstr((char *)ftp[n].ft_from + 1,
+								"^^") != NULL)
+			{
+			    if (c != NUL)
+			    {
+				res[j] = c;
+				j++;
+			    }
+			    mch_memmove(word, word + i + 1,
+						    STRLEN(word + i + 1) + 1);
+			    i = 0;
+			    z0 = 1;
+			}
+		    }
+		    break;
+		}
+		++n;
+	    }
+	}
+
+	if (z0 == 0)
+	{
+	    if (k && !p0 && j < MAXWLEN && c != NUL
+		    && (!slang->sl_collapse || j == 0 || res[j - 1] != c))
+	    {
+		/* condense only double letters */
+		res[j] = c;
+		j++;
+	    }
+
+	    i++;
+	    z = 0;
+	    k = 0;
+	}
+    }
+
+    res[j] = NUL;
+}
+
+/*
+ * Compute the "edit distance" to turn "badword" into "goodword".  The less
+ * deletes/inserts/swaps are required the lower the score.
+ * The algorithm comes from Aspell editdist.cpp, edit_distance().
+ * TODO: make this work with multi-byte chars.
+ */
+    static int
+spell_edit_score(badword, goodword)
+    char_u	*badword;
+    char_u	*goodword;
+{
+    int		*cnt;
+    int		badlen, goodlen;
+    int		j, i;
+    int		t;
+    int		bc, gc;
+
+    /* We use "cnt" as an array: CNT(badword_idx, goodword_idx). */
+#define CNT(a, b)   cnt[(a) + (b) * (badlen + 1)]
+    badlen = STRLEN(badword) + 1;
+    goodlen = STRLEN(goodword) + 1;
+    cnt = (int *)lalloc((long_u)(sizeof(int) * (badlen + 1) * (goodlen + 1)),
+									TRUE);
+    if (cnt == 0)
+	return 0;
+
+    CNT(0, 0) = 0;
+    for (j = 1; j <= goodlen; ++j)
+	CNT(0, j) = CNT(0, j - 1) + SCORE_DEL;
+
+    for (i = 1; i <= badlen; ++i)
+    {
+	CNT(i, 0) = CNT(i - 1, 0) + SCORE_INS;
+	for (j = 1; j <= goodlen; ++j)
+	{
+	    bc = badword[i - 1];
+	    gc = goodword[j - 1];
+	    if (bc == gc)
+		CNT(i, j) = CNT(i - 1, j - 1);
+	    else
+	    {
+		/* Use a better score when there is only a case difference. */
+		if (spelltab.st_fold[bc] == spelltab.st_fold[gc])
+		    CNT(i, j) = SCORE_ICASE + CNT(i - 1, j - 1);
+		else
+		    CNT(i, j) = SCORE_SUBST + CNT(i - 1, j - 1);
+
+		if (i > 1 && j > 1 && bc == goodword[j - 2]
+						      && badword[i - 2] == gc)
+		{
+		    t = SCORE_SWAP + CNT(i - 2, j - 2);
+		    if (t < CNT(i, j))
+			CNT(i, j) = t;
+		}
+		t = SCORE_DEL + CNT(i - 1, j);
+		if (t < CNT(i, j))
+		    CNT(i, j) = t;
+		t = SCORE_INS + CNT(i, j - 1);
+		if (t < CNT(i, j))
+		    CNT(i, j) = t;
+	    }
+	}
+    }
+    return CNT(badlen - 1, goodlen - 1);
+}
 
 #endif  /* FEAT_SYN_HL */
diff --git a/src/tag.c b/src/tag.c
index 2cad495..e94322d 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -740,17 +740,8 @@
 	    {
 		/*
 		 * Ask to select a tag from the list.
-		 * When using ":silent" assume that <CR> was entered.
 		 */
-		MSG_PUTS(_("Enter nr of choice (<CR> to abort): "));
-		i = get_number(TRUE);
-		if (KeyTyped)		/* don't call wait_return() now */
-		{
-		    msg_putchar('\n');
-		    cmdline_row = msg_row - 1;
-		    need_wait_return = FALSE;
-		    msg_didany = FALSE;
-		}
+		i = prompt_for_number();
 		if (i <= 0 || i > num_matches || got_int)
 		{
 		    /* no valid choice: don't change anything */
diff --git a/src/ui.c b/src/ui.c
index 89b5cd6..3fe99c5 100644
--- a/src/ui.c
+++ b/src/ui.c
@@ -1680,6 +1680,7 @@
     return (int)maxlen;
 }
 
+/*ARGSUSED*/
     void
 fill_input_buf(exit_on_error)
     int	exit_on_error;
diff --git a/src/version.h b/src/version.h
index c97e078..94ec0ea 100644
--- a/src/version.h
+++ b/src/version.h
@@ -36,5 +36,5 @@
 #define VIM_VERSION_NODOT	"vim70aa"
 #define VIM_VERSION_SHORT	"7.0aa"
 #define VIM_VERSION_MEDIUM	"7.0aa ALPHA"
-#define VIM_VERSION_LONG	"VIM - Vi IMproved 7.0aa ALPHA (2005 Jun 8)"
-#define VIM_VERSION_LONG_DATE	"VIM - Vi IMproved 7.0aa ALPHA (2005 Jun 8, compiled "
+#define VIM_VERSION_LONG	"VIM - Vi IMproved 7.0aa ALPHA (2005 Jun 13)"
+#define VIM_VERSION_LONG_DATE	"VIM - Vi IMproved 7.0aa ALPHA (2005 Jun 13, compiled "