:" 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
: call <SID>CheckFile($VIMRUNTIME . "/syntax/synload.vim")
: delfun <SID>CheckDir
: delfun <SID>CheckFile
+: echo "--- Scripts sourced ---"
+: scriptnames
:endif
:set all
:set termcap
-*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
- 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,
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*
-*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
: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 ~
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.
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.
Number 123
Funcref function('name')
List [item, item]
+ Dictionary {key: value, key: value}
Note that in String values the ' character is doubled.
*strlen()*
-*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
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>.
-*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
[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
|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
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.
< 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.
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:
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*
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*
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*
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*
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*
-*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
*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
- 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'?
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
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)
'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)
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.
-*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
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. ~
-*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
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
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))
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: >
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: >
-*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
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).
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*
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
" 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")
-" Vim indent file\r
-" Language: MuPAD source files\r
-" Maintainer: Dave Silvia <dsilvia@mchsi.com>\r
-" Filenames: *.mu\r
-" Date: 6/30/2004\r
-\r
-if exists("b:did_indent")\r
- finish\r
-endif\r
-\r
-let b:did_indent = 1\r
-\r
-runtime indent/GenericIndent.vim\r
-\r
-let b:indentStmts=''\r
-let b:dedentStmts=''\r
-let b:allStmts=''\r
-" NOTE: b:indentStmts, b:dedentStmts, and b:allStmts need to be initialized\r
-" to '' before callin the functions because 'indent.vim' explicitly\r
-" 'unlet's b:did_indent. This means that the lists will compound if\r
-" you change back and forth between buffers. This is true as of\r
-" version 6.3, 6/23/2004.\r
-setlocal indentexpr=GenericIndent()\r
-setlocal indentkeys==end_proc,=then,=else,=elif,=end_if,=end_case,=until,=end_repeat,=end_domain,=end_for,=end_while,=end,o,O\r
-\r
-call GenericIndentStmts('begin,if,then,else,elif,case,repeat,until,domain,do')\r
-call GenericDedentStmts('end_proc,then,else,elif,end_if,end_case,until,end_repeat,end_domain,end_for,end_while,end')\r
-call GenericAllStmts()\r
-\r
-\r
-" TODO: More comprehensive indentstmt, dedentstmt, and indentkeys values.\r
-"\r
-" BUGS: You tell me! Probably. I just haven't found one yet or haven't been\r
-" told about one.\r
-" \r
+" 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.
+"
--- /dev/null
+" 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
+
" 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.
" 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
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
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
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}\>"
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
!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
#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 */
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));
*error = FALSE;
if (!skip)
{
- retval = (get_tv_number(&tv) != 0);
+ retval = (get_tv_number_chk(&tv, error) != 0);
clear_tv(&tv);
}
}
retval = -1;
else
{
- retval = get_tv_number(&rettv);
+ retval = get_tv_number_chk(&rettv, NULL);
clear_tv(&rettv);
}
--emsg_off;
{
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);
}
}
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);
}
}
*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);
}
}
}
- 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);
}
}
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)
}
}
if (p != NULL)
+ {
write_reg_contents(*arg == '@' ? '"' : *arg, p, -1, FALSE);
- arg_end = arg + 1;
+ arg_end = arg + 1;
+ }
vim_free(tofree);
}
}
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]. */
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;
}
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)
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;
*/
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)
{
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;
}
/*
typval_T var2;
long result;
int first;
+ int error = FALSE;
/*
* Get the first variable.
{
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;
}
*/
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)
{
typval_T var2;
long result;
int first;
+ int error = FALSE;
/*
* Get the first variable.
{
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;
}
*/
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)
{
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.
*/
*/
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;
}
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;
typval_T var2;
int op;
long n1, n2;
+ int error = FALSE;
/*
* Get the first variable.
if (evaluate)
{
- n1 = get_tv_number(rettv);
+ n1 = get_tv_number_chk(rettv, &error);
clear_tv(rettv);
+ if (error)
+ return FAIL;
}
else
n1 = 0;
if (evaluate)
{
- n2 = get_tv_number(&var2);
+ n2 = get_tv_number_chk(&var2, &error);
clear_tv(&var2);
+ if (error)
+ return FAIL;
/*
* Compute the result.
*/
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;
+ }
+ 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;
}
- clear_tv(rettv);
- rettv->v_type = VAR_NUMBER;
- rettv->vval.v_number = val;
}
return ret;
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 [:].
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;
}
}
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)
{
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;
}
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;
}
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
return;
li = l->lv_first;
}
+ rettv->vval.v_number = 0; /* Default: Success */
for (;;)
{
if (l == NULL)
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;
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 */
}
/*
{
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
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
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;
{
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;
{
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)
#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
#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
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
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)
{
{
#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];
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;
+ }
}
}
}
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;
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)
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))
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
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
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)
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
{
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));
}
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]);
{
/* 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;
}
}
list_T *l1, *l2;
listitem_T *item;
long before;
+ int error = FALSE;
l1 = argvars[0].vval.v_list;
l2 = argvars[1].vval.v_list;
{
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
{
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;
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
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);
- }
- 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);
}
- restore_vimvar(VV_VAL, &save_val);
-
copy_tv(&argvars[0], rettv);
}
}
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;
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)
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)
{
{
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;
}
}
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;
}
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)
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 */
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);
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
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;
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);
}
/*
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;
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;
}
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)
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);
}
/*
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);
#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
#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
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
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)
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;
*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;
}
{
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));
long before = 0;
listitem_T *item;
list_T *l;
+ int error = FALSE;
rettv->vval.v_number = 0;
if (argvars[0].v_type != VAR_LIST)
&& !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;
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;
}
/*
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);
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);
}
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);
{
long n = 0;
long i;
+ int error = FALSE;
if (argvars[0].v_type == VAR_LIST)
{
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;
}
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;
}
else
EMSG(_(e_listdictarg));
- rettv->vval.v_number = n;
+ rettv->vval.v_number = error ? 0 : n;
}
/*
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
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;
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;
}
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)
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)
{
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);
}
}
#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;
# 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
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
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"));
}
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);
+ }
}
}
}
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
{
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
{
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)
{
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;
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). */
|| 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;
{
#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
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)
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
{
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);
}
/*
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 (;;)
/* 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)
{
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;
}
int regname;
char_u *strregname;
char_u *stropt;
+ char_u *strval;
int append;
char_u yank_type;
long block_len;
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 == '=')
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 */
}
}
- 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;
}
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)
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
{
static int item_compare_ic;
static char_u *item_compare_func;
+static int item_compare_func_err;
#define ITEM_COMPARE_FAIL 999
/*
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]);
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;
}
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
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])
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);
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;
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- ]\\+";
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)
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;
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. */
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;
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));
}
/*
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);
}
/*
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
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)
tag_pattern = get_tv_string(&argvars[0]);
rettv->vval.v_number = FALSE;
+ if (*tag_pattern == NUL)
+ return;
l = list_alloc();
if (l != NULL)
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]));
}
/*
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
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)
{
#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;
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 */
/*
* 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;
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;
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)
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;
* 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)
get_tv_string_buf(varp, buf)
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_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;
}
/*
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", "/\\\\%\\$"};
#if ((defined(FEAT_SESSION) || defined(FEAT_EVAL)) && defined(vim_mkdir)) \
|| defined(PROTO)
+/*ARGSUSED*/
int
vim_mkdir_emsg(name, prot)
char_u *name;
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;
}
static int
_DuringSizing(
- HWND hwnd,
UINT fwSide,
LPRECT lprc)
{
#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));
}
}
+#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
/*
* 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)
{
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)
* pressed, return that button's ID - IDCANCEL (2), which is the button's
* number.
*/
+/*ARGSUSED*/
static LRESULT CALLBACK
dialog_callback(
HWND hwnd,
DestroyWindow(beval->balloon);
}
+/*ARGSUSED*/
static VOID CALLBACK
BevalTimerProc(hwnd, uMsg, idEvent, dwTime)
HWND hwnd;
}
}
+/*ARGSUSED*/
void
gui_mch_disable_beval_area(beval)
BalloonEval *beval;
// TRACE0("gui_mch_disable_beval_area }}}");
}
+/*ARGSUSED*/
void
gui_mch_enable_beval_area(beval)
BalloonEval *beval;
return beval;
}
+/*ARGSUSED*/
static void
Handle_WM_Notify(hwnd, pnmh)
HWND hwnd;
static LRESULT _OnImeNotify(HWND hWnd, DWORD dwCommand, DWORD dwData);
#endif
-#ifdef DEBUG
+#ifdef DEBUG_PRINT_ERROR
/*
* Print out the last Windows error message
*/
TRACE1("Error: %s\n", lpMsgBuf);
LocalFree(lpMsgBuf);
}
-#endif /* DEBUG */
+#endif
/*
* Cursor blink functions.
* Call-back routines.
*/
+/*ARGSUSED*/
static VOID CALLBACK
_OnTimer(
HWND hwnd,
s_wait_timer = 0;
}
+/*ARGSUSED*/
static void
_OnDeadChar(
HWND hwnd,
/*
* Key hit, add it to the input buffer.
*/
+/*ARGSUSED*/
static void
_OnChar(
HWND hwnd,
/*
* Alt-Key hit, add it to the input buffer.
*/
+/*ARGSUSED*/
static void
_OnSysChar(
HWND hwnd,
gui_send_mouse_event(button, x, y, repeated_click, vim_modifiers);
}
+/*ARGSUSED*/
static void
_OnMouseButtonDown(
HWND hwnd,
}
}
+/*ARGSUSED*/
static void
_OnMouseMoveOrRelease(
HWND hwnd,
return pMenu;
}
+/*ARGSUSED*/
static void
_OnMenu(
HWND hwnd,
* 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;
#endif
}
+/*ARGSUSED*/
void
gui_mch_set_menu_pos(
int x,
* 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)
}
}
+/*ARGSUSED*/
static void
_OnSize(
HWND hwnd,
}
+/*ARGSUSED*/
void
gui_mch_exit(int rc)
{
* 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)
{
/*
* Set the window title
*/
+/*ARGSUSED*/
void
gui_mch_settitle(
char_u *title,
}
#endif /* FEAT_BROWSE */
+/*ARGSUSED*/
static void
_OnDropFiles(
HWND hwnd,
#endif
}
+/*ARGSUSED*/
static int
_OnScroll(
HWND hwnd,
* 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)
{
}
}
- if (pnew != NUL)
+ if (pnew != NULL)
*pnew++ = NUL;
while (*p == ' ' || *p == '\t')
++p; /* advance until a non-space */
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;
#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
static void
Python_SaveThread(void)
{
+#ifdef PY_CAN_RECURSE
+ PyGILState_Release(pygilstate);
+#else
saved_python_thread = PyEval_SaveThread();
+#endif
}
/*
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
{
#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
}
goto fail;
/* the first python thread is vim's, release the lock */
-#ifdef PY_CAN_RECURSE
- PyEval_SaveThread();
-#else
Python_SaveThread();
-#endif
initialised = 1;
}
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)
}
#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)
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;
}
}
+#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
*/
}
/*
- * 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;
}
/*
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);
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;
#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
* 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)
* When 'shellslash' set do it the other way around.
* Return OK or FAIL.
*/
+/*ARGSUSED*/
int
mch_FullName(
char_u *fname,
}
#if defined(FEAT_GUI_MSWIN) || defined(PROTO)
+/*ARGSUSED*/
void
mch_settmode(int tmode)
{
* 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)
/*
* set screen mode, always fails.
*/
+/*ARGSUSED*/
int
mch_screenmode(
char_u *arg)
/*
* Make vim the owner of the current selection. Return OK upon success.
*/
+/*ARGSUSED*/
int
clip_mch_own_selection(VimClipboard *cbd)
{
/*
* Make vim NOT the owner of the current selection.
*/
+/*ARGSUSED*/
void
clip_mch_lose_selection(VimClipboard *cbd)
{
#ifdef FEAT_MBYTE
HGLOBAL rawh = NULL;
#endif
- char_u *hMemStr = NULL;
int str_size = 0;
int maxlen;
size_t n;
{
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. */
/*
* Debugging helper: expose the MCH_WRITE_DUMP stuff to other modules
*/
+/*ARGSUSED*/
void
DumpPutS(
const char *psz)
return colorref;
}
+/*ARGSUSED*/
static BOOL CALLBACK
PrintDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
return FALSE;
}
+/*ARGSUSED*/
static BOOL CALLBACK
AbortProc(HDC hdcPrn, int iCode)
{
return (ret > 0);
}
+/*ARGSUSED*/
void
mch_print_end(prt_settings_T *psettings)
{
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};
return pixels;
}
+/*ARGSUSED*/
static int CALLBACK
font_enumproc(
ENUMLOGFONT *elf,
void (_cdecl *pSaveInst)(HINSTANCE);
#endif
+/*ARGSUSED*/
int WINAPI
WinMain(
HINSTANCE hInstance,
}
/* 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 =
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)
return NULL;
}
+/*ARGSUSED*/
static char *
null_libintl_textdomain(const char *domainname)
{
static int old_num_windows;
static int num_windows;
+/*ARGSUSED*/
static BOOL CALLBACK
win32ssynch_cb(HWND hwnd, LPARAM lparam)
{
* For the GUI the mouse handling is in gui_w32.c.
*/
# ifdef FEAT_GUI_W32
+/*ARGSUSED*/
void
mch_setmouse(int on)
{
/*
* Do we have an interactive window?
*/
+/*ARGSUSED*/
int
mch_check_win(
int argc,
#ifdef FEAT_GUI_W32
+/*ARGSUSED*/
void
mch_write(
char_u *s,
/*
* Delay for half a second.
*/
+/*ARGSUSED*/
void
mch_delay(
long msec,
/* 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);
}
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));
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));
/* 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));
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 : */
* See ":help develop-spell".
*/
+/*
+ * 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.
* <fchars> N bytes Folded characters, first one is for character 128.
*
*
- * <SUGGEST> : <suggestlen> <more> ...
+ * <SUGGEST> : <repcount> <rep> ...
+ * <salflags> <salcount> <sal> ...
+ * <maplen> <mapstr>
+ *
+ * <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
*
- * <suggestlen> 4 bytes Length of <SUGGEST> in bytes, excluding
- * <suggestlen>. MSB first.
- * <more> To be defined.
+ * <mapstr> N bytes String with sequences of similar characters,
+ * separated by slashes.
*
*
* <LWORDTREE>: <wordtree>
*
* <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)
# 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 */
#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
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'.
*/
#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 */
# 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");
* "*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.
*/
/* 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
{
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. */
/* Check for a matching word in case-folded words. */
find_word(&mi, FALSE);
- /* Try keep-case words. */
find_word(&mi, TRUE);
}
/* 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)
}
}
+/*
+ * 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;
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)
break;
}
+ if (curline)
+ return FAIL; /* only check cursor line */
+
/* Advance to next line. */
if (dir == BACKWARD)
{
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;
}
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);
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;
}
/*
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);
}
/*
* 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];
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)
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> */
/* 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;
}
/*
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)
{
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);
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)
{
}
/*
- * Return type of word:
+ * Return case type of word:
* w word 0
* Word WF_ONECAP
* W WORD WF_ALLCAP
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;
/* 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)
* 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
* 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);
}
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;
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));
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 *
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.
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.
*/
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.
}
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)
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"),
return aff;
}
+/*
+ * 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.
hash_clear(&aff->af_pref);
hash_clear(&aff->af_suff);
- ga_clear(&aff->af_rep);
}
/*
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,
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".
*/
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)
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);
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;
+
+ 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> */
+ }
- spin->si_memtot = 0;
+ 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;
/* 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);
}
}
* 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];
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];
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. */
/*
* 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);
/*
* 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)
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. */
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);
}
}
}
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 */
{
/*
* 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 */
return (int)maxlen;
}
+/*ARGSUSED*/
void
fill_input_buf(exit_on_error)
int exit_on_error;
#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 "