1 /* vi:set ts=8 sts=4 sw=4: 2 * 3 * VIM - Vi IMproved by Bram Moolenaar 4 * 5 * Do ":help uganda" in Vim to read copying and usage conditions. 6 * Do ":help credits" in Vim to see a list of people who contributed. 7 * See README.txt for an overview of the Vim source code. 8 */ 9 10 /* 11 * spell.c: code for spell checking 12 * 13 * The spell checking mechanism uses a tree (aka trie). Each node in the tree 14 * has a list of bytes that can appear (siblings). For each byte there is a 15 * pointer to the node with the byte that follows in the word (child). 16 * 17 * A NUL byte is used where the word may end. The bytes are sorted, so that 18 * binary searching can be used and the NUL bytes are at the start. The 19 * number of possible bytes is stored before the list of bytes. 20 * 21 * The tree uses two arrays: "byts" stores the characters, "idxs" stores 22 * either the next index or flags. The tree starts at index 0. For example, 23 * to lookup "vi" this sequence is followed: 24 * i = 0 25 * len = byts[i] 26 * n = where "v" appears in byts[i + 1] to byts[i + len] 27 * i = idxs[n] 28 * len = byts[i] 29 * n = where "i" appears in byts[i + 1] to byts[i + len] 30 * i = idxs[n] 31 * len = byts[i] 32 * find that byts[i + 1] is 0, idxs[i + 1] has flags for "vi". 33 * 34 * There are two word trees: one with case-folded words and one with words in 35 * original case. The second one is only used for keep-case words and is 36 * usually small. 37 * 38 * There is one additional tree for when not all prefixes are applied when 39 * generating the .spl file. This tree stores all the possible prefixes, as 40 * if they were words. At each word (prefix) end the prefix nr is stored, the 41 * following word must support this prefix nr. And the condition nr is 42 * stored, used to lookup the condition that the word must match with. 43 * 44 * Thanks to Olaf Seibert for providing an example implementation of this tree 45 * and the compression mechanism. 46 * LZ trie ideas: 47 * http://www.irb.hr/hr/home/ristov/papers/RistovLZtrieRevision1.pdf 48 * More papers: http://www-igm.univ-mlv.fr/~laporte/publi_en.html 49 * 50 * Matching involves checking the caps type: Onecap ALLCAP KeepCap. 51 * 52 * Why doesn't Vim use aspell/ispell/myspell/etc.? 53 * See ":help develop-spell". 54 */ 55 56 /* Use SPELL_PRINTTREE for debugging: dump the word tree after adding a word. 57 * Only use it for small word lists! */ 58 #if 0 59 # define SPELL_PRINTTREE 60 #endif 61 62 /* Use DEBUG_TRIEWALK to print the changes made in suggest_trie_walk() for a 63 * specific word. */ 64 #if 0 65 # define DEBUG_TRIEWALK 66 #endif 67 68 /* 69 * Use this to adjust the score after finding suggestions, based on the 70 * suggested word sounding like the bad word. This is much faster than doing 71 * it for every possible suggestion. 72 * Disadvantage: When "the" is typed as "hte" it sounds quite different ("@" 73 * vs "ht") and goes down in the list. 74 * Used when 'spellsuggest' is set to "best". 75 */ 76 #define RESCORE(word_score, sound_score) ((3 * word_score + sound_score) / 4) 77 78 /* 79 * Do the opposite: based on a maximum end score and a known sound score, 80 * compute the the maximum word score that can be used. 81 */ 82 #define MAXSCORE(word_score, sound_score) ((4 * word_score - sound_score) / 3) 83 84 /* 85 * Vim spell file format: <HEADER> 86 * <SECTIONS> 87 * <LWORDTREE> 88 * <KWORDTREE> 89 * <PREFIXTREE> 90 * 91 * <HEADER>: <fileID> <versionnr> 92 * 93 * <fileID> 8 bytes "VIMspell" 94 * <versionnr> 1 byte VIMSPELLVERSION 95 * 96 * 97 * Sections make it possible to add information to the .spl file without 98 * making it incompatible with previous versions. There are two kinds of 99 * sections: 100 * 1. Not essential for correct spell checking. E.g. for making suggestions. 101 * These are skipped when not supported. 102 * 2. Optional information, but essential for spell checking when present. 103 * E.g. conditions for affixes. When this section is present but not 104 * supported an error message is given. 105 * 106 * <SECTIONS>: <section> ... <sectionend> 107 * 108 * <section>: <sectionID> <sectionflags> <sectionlen> (section contents) 109 * 110 * <sectionID> 1 byte number from 0 to 254 identifying the section 111 * 112 * <sectionflags> 1 byte SNF_REQUIRED: this section is required for correct 113 * spell checking 114 * 115 * <sectionlen> 4 bytes length of section contents, MSB first 116 * 117 * <sectionend> 1 byte SN_END 118 * 119 * 120 * sectionID == SN_INFO: <infotext> 121 * <infotext> N bytes free format text with spell file info (version, 122 * website, etc) 123 * 124 * sectionID == SN_REGION: <regionname> ... 125 * <regionname> 2 bytes Up to 8 region names: ca, au, etc. Lower case. 126 * First <regionname> is region 1. 127 * 128 * sectionID == SN_CHARFLAGS: <charflagslen> <charflags> 129 * <folcharslen> <folchars> 130 * <charflagslen> 1 byte Number of bytes in <charflags> (should be 128). 131 * <charflags> N bytes List of flags (first one is for character 128): 132 * 0x01 word character CF_WORD 133 * 0x02 upper-case character CF_UPPER 134 * <folcharslen> 2 bytes Number of bytes in <folchars>. 135 * <folchars> N bytes Folded characters, first one is for character 128. 136 * 137 * sectionID == SN_MIDWORD: <midword> 138 * <midword> N bytes Characters that are word characters only when used 139 * in the middle of a word. 140 * 141 * sectionID == SN_PREFCOND: <prefcondcnt> <prefcond> ... 142 * <prefcondcnt> 2 bytes Number of <prefcond> items following. 143 * <prefcond> : <condlen> <condstr> 144 * <condlen> 1 byte Length of <condstr>. 145 * <condstr> N bytes Condition for the prefix. 146 * 147 * sectionID == SN_REP: <repcount> <rep> ... 148 * <repcount> 2 bytes number of <rep> items, MSB first. 149 * <rep> : <repfromlen> <repfrom> <reptolen> <repto> 150 * <repfromlen> 1 byte length of <repfrom> 151 * <repfrom> N bytes "from" part of replacement 152 * <reptolen> 1 byte length of <repto> 153 * <repto> N bytes "to" part of replacement 154 * 155 * sectionID == SN_REPSAL: <repcount> <rep> ... 156 * just like SN_REP but for soundfolded words 157 * 158 * sectionID == SN_SAL: <salflags> <salcount> <sal> ... 159 * <salflags> 1 byte flags for soundsalike conversion: 160 * SAL_F0LLOWUP 161 * SAL_COLLAPSE 162 * SAL_REM_ACCENTS 163 * <salcount> 2 bytes number of <sal> items following 164 * <sal> : <salfromlen> <salfrom> <saltolen> <salto> 165 * <salfromlen> 1 byte length of <salfrom> 166 * <salfrom> N bytes "from" part of soundsalike 167 * <saltolen> 1 byte length of <salto> 168 * <salto> N bytes "to" part of soundsalike 169 * 170 * sectionID == SN_SOFO: <sofofromlen> <sofofrom> <sofotolen> <sofoto> 171 * <sofofromlen> 2 bytes length of <sofofrom> 172 * <sofofrom> N bytes "from" part of soundfold 173 * <sofotolen> 2 bytes length of <sofoto> 174 * <sofoto> N bytes "to" part of soundfold 175 * 176 * sectionID == SN_SUGFILE: <timestamp> 177 * <timestamp> 8 bytes time in seconds that must match with .sug file 178 * 179 * sectionID == SN_NOSPLITSUGS: nothing 180 * 181 * sectionID == SN_WORDS: <word> ... 182 * <word> N bytes NUL terminated common word 183 * 184 * sectionID == SN_MAP: <mapstr> 185 * <mapstr> N bytes String with sequences of similar characters, 186 * separated by slashes. 187 * 188 * sectionID == SN_COMPOUND: <compmax> <compminlen> <compsylmax> <compoptions> 189 * <comppatcount> <comppattern> ... <compflags> 190 * <compmax> 1 byte Maximum nr of words in compound word. 191 * <compminlen> 1 byte Minimal word length for compounding. 192 * <compsylmax> 1 byte Maximum nr of syllables in compound word. 193 * <compoptions> 2 bytes COMP_ flags. 194 * <comppatcount> 2 bytes number of <comppattern> following 195 * <compflags> N bytes Flags from COMPOUNDRULE items, separated by 196 * slashes. 197 * 198 * <comppattern>: <comppatlen> <comppattext> 199 * <comppatlen> 1 byte length of <comppattext> 200 * <comppattext> N bytes end or begin chars from CHECKCOMPOUNDPATTERN 201 * 202 * sectionID == SN_NOBREAK: (empty, its presence is what matters) 203 * 204 * sectionID == SN_SYLLABLE: <syllable> 205 * <syllable> N bytes String from SYLLABLE item. 206 * 207 * <LWORDTREE>: <wordtree> 208 * 209 * <KWORDTREE>: <wordtree> 210 * 211 * <PREFIXTREE>: <wordtree> 212 * 213 * 214 * <wordtree>: <nodecount> <nodedata> ... 215 * 216 * <nodecount> 4 bytes Number of nodes following. MSB first. 217 * 218 * <nodedata>: <siblingcount> <sibling> ... 219 * 220 * <siblingcount> 1 byte Number of siblings in this node. The siblings 221 * follow in sorted order. 222 * 223 * <sibling>: <byte> [ <nodeidx> <xbyte> 224 * | <flags> [<flags2>] [<region>] [<affixID>] 225 * | [<pflags>] <affixID> <prefcondnr> ] 226 * 227 * <byte> 1 byte Byte value of the sibling. Special cases: 228 * BY_NOFLAGS: End of word without flags and for all 229 * regions. 230 * For PREFIXTREE <affixID> and 231 * <prefcondnr> follow. 232 * BY_FLAGS: End of word, <flags> follow. 233 * For PREFIXTREE <pflags>, <affixID> 234 * and <prefcondnr> follow. 235 * BY_FLAGS2: End of word, <flags> and <flags2> 236 * follow. Not used in PREFIXTREE. 237 * BY_INDEX: Child of sibling is shared, <nodeidx> 238 * and <xbyte> follow. 239 * 240 * <nodeidx> 3 bytes Index of child for this sibling, MSB first. 241 * 242 * <xbyte> 1 byte byte value of the sibling. 243 * 244 * <flags> 1 byte bitmask of: 245 * WF_ALLCAP word must have only capitals 246 * WF_ONECAP first char of word must be capital 247 * WF_KEEPCAP keep-case word 248 * WF_FIXCAP keep-case word, all caps not allowed 249 * WF_RARE rare word 250 * WF_BANNED bad word 251 * WF_REGION <region> follows 252 * WF_AFX <affixID> follows 253 * 254 * <flags2> 1 byte Bitmask of: 255 * WF_HAS_AFF >> 8 word includes affix 256 * WF_NEEDCOMP >> 8 word only valid in compound 257 * WF_NOSUGGEST >> 8 word not used for suggestions 258 * WF_COMPROOT >> 8 word already a compound 259 * WF_NOCOMPBEF >> 8 no compounding before this word 260 * WF_NOCOMPAFT >> 8 no compounding after this word 261 * 262 * <pflags> 1 byte bitmask of: 263 * WFP_RARE rare prefix 264 * WFP_NC non-combining prefix 265 * WFP_UP letter after prefix made upper case 266 * 267 * <region> 1 byte Bitmask for regions in which word is valid. When 268 * omitted it's valid in all regions. 269 * Lowest bit is for region 1. 270 * 271 * <affixID> 1 byte ID of affix that can be used with this word. In 272 * PREFIXTREE used for the required prefix ID. 273 * 274 * <prefcondnr> 2 bytes Prefix condition number, index in <prefcond> list 275 * from HEADER. 276 * 277 * All text characters are in 'encoding', but stored as single bytes. 278 */ 279 280 /* 281 * Vim .sug file format: <SUGHEADER> 282 * <SUGWORDTREE> 283 * <SUGTABLE> 284 * 285 * <SUGHEADER>: <fileID> <versionnr> <timestamp> 286 * 287 * <fileID> 6 bytes "VIMsug" 288 * <versionnr> 1 byte VIMSUGVERSION 289 * <timestamp> 8 bytes timestamp that must match with .spl file 290 * 291 * 292 * <SUGWORDTREE>: <wordtree> (see above, no flags or region used) 293 * 294 * 295 * <SUGTABLE>: <sugwcount> <sugline> ... 296 * 297 * <sugwcount> 4 bytes number of <sugline> following 298 * 299 * <sugline>: <sugnr> ... NUL 300 * 301 * <sugnr>: X bytes word number that results in this soundfolded word, 302 * stored as an offset to the previous number in as 303 * few bytes as possible, see offset2bytes()) 304 */ 305 306 #if defined(MSDOS) || defined(WIN16) || defined(WIN32) || defined(_WIN64) 307 # include "vimio.h" /* for lseek(), must be before vim.h */ 308 #endif 309 310 #include "vim.h" 311 312 #if defined(FEAT_SPELL) || defined(PROTO) 313 314 #ifdef HAVE_FCNTL_H 315 # include <fcntl.h> 316 #endif 317 318 #ifndef UNIX /* it's in os_unix.h for Unix */ 319 # include <time.h> /* for time_t */ 320 #endif 321 322 #define MAXWLEN 250 /* Assume max. word len is this many bytes. 323 Some places assume a word length fits in a 324 byte, thus it can't be above 255. */ 325 326 /* Type used for indexes in the word tree need to be at least 4 bytes. If int 327 * is 8 bytes we could use something smaller, but what? */ 328 #if SIZEOF_INT > 3 329 typedef int idx_T; 330 #else 331 typedef long idx_T; 332 #endif 333 334 /* Flags used for a word. Only the lowest byte can be used, the region byte 335 * comes above it. */ 336 #define WF_REGION 0x01 /* region byte follows */ 337 #define WF_ONECAP 0x02 /* word with one capital (or all capitals) */ 338 #define WF_ALLCAP 0x04 /* word must be all capitals */ 339 #define WF_RARE 0x08 /* rare word */ 340 #define WF_BANNED 0x10 /* bad word */ 341 #define WF_AFX 0x20 /* affix ID follows */ 342 #define WF_FIXCAP 0x40 /* keep-case word, allcap not allowed */ 343 #define WF_KEEPCAP 0x80 /* keep-case word */ 344 345 /* for <flags2>, shifted up one byte to be used in wn_flags */ 346 #define WF_HAS_AFF 0x0100 /* word includes affix */ 347 #define WF_NEEDCOMP 0x0200 /* word only valid in compound */ 348 #define WF_NOSUGGEST 0x0400 /* word not to be suggested */ 349 #define WF_COMPROOT 0x0800 /* already compounded word, COMPOUNDROOT */ 350 #define WF_NOCOMPBEF 0x1000 /* no compounding before this word */ 351 #define WF_NOCOMPAFT 0x2000 /* no compounding after this word */ 352 353 /* only used for su_badflags */ 354 #define WF_MIXCAP 0x20 /* mix of upper and lower case: macaRONI */ 355 356 #define WF_CAPMASK (WF_ONECAP | WF_ALLCAP | WF_KEEPCAP | WF_FIXCAP) 357 358 /* flags for <pflags> */ 359 #define WFP_RARE 0x01 /* rare prefix */ 360 #define WFP_NC 0x02 /* prefix is not combining */ 361 #define WFP_UP 0x04 /* to-upper prefix */ 362 #define WFP_COMPPERMIT 0x08 /* prefix with COMPOUNDPERMITFLAG */ 363 #define WFP_COMPFORBID 0x10 /* prefix with COMPOUNDFORBIDFLAG */ 364 365 /* Flags for postponed prefixes in "sl_pidxs". Must be above affixID (one 366 * byte) and prefcondnr (two bytes). */ 367 #define WF_RAREPFX (WFP_RARE << 24) /* rare postponed prefix */ 368 #define WF_PFX_NC (WFP_NC << 24) /* non-combining postponed prefix */ 369 #define WF_PFX_UP (WFP_UP << 24) /* to-upper postponed prefix */ 370 #define WF_PFX_COMPPERMIT (WFP_COMPPERMIT << 24) /* postponed prefix with 371 * COMPOUNDPERMITFLAG */ 372 #define WF_PFX_COMPFORBID (WFP_COMPFORBID << 24) /* postponed prefix with 373 * COMPOUNDFORBIDFLAG */ 374 375 376 /* flags for <compoptions> */ 377 #define COMP_CHECKDUP 1 /* CHECKCOMPOUNDDUP */ 378 #define COMP_CHECKREP 2 /* CHECKCOMPOUNDREP */ 379 #define COMP_CHECKCASE 4 /* CHECKCOMPOUNDCASE */ 380 #define COMP_CHECKTRIPLE 8 /* CHECKCOMPOUNDTRIPLE */ 381 382 /* Special byte values for <byte>. Some are only used in the tree for 383 * postponed prefixes, some only in the other trees. This is a bit messy... */ 384 #define BY_NOFLAGS 0 /* end of word without flags or region; for 385 * postponed prefix: no <pflags> */ 386 #define BY_INDEX 1 /* child is shared, index follows */ 387 #define BY_FLAGS 2 /* end of word, <flags> byte follows; for 388 * postponed prefix: <pflags> follows */ 389 #define BY_FLAGS2 3 /* end of word, <flags> and <flags2> bytes 390 * follow; never used in prefix tree */ 391 #define BY_SPECIAL BY_FLAGS2 /* highest special byte value */ 392 393 /* Info from "REP", "REPSAL" and "SAL" entries in ".aff" file used in si_rep, 394 * si_repsal, sl_rep, and si_sal. Not for sl_sal! 395 * One replacement: from "ft_from" to "ft_to". */ 396 typedef struct fromto_S 397 { 398 char_u *ft_from; 399 char_u *ft_to; 400 } fromto_T; 401 402 /* Info from "SAL" entries in ".aff" file used in sl_sal. 403 * The info is split for quick processing by spell_soundfold(). 404 * Note that "sm_oneof" and "sm_rules" point into sm_lead. */ 405 typedef struct salitem_S 406 { 407 char_u *sm_lead; /* leading letters */ 408 int sm_leadlen; /* length of "sm_lead" */ 409 char_u *sm_oneof; /* letters from () or NULL */ 410 char_u *sm_rules; /* rules like ^, $, priority */ 411 char_u *sm_to; /* replacement. */ 412 #ifdef FEAT_MBYTE 413 int *sm_lead_w; /* wide character copy of "sm_lead" */ 414 int *sm_oneof_w; /* wide character copy of "sm_oneof" */ 415 int *sm_to_w; /* wide character copy of "sm_to" */ 416 #endif 417 } salitem_T; 418 419 #ifdef FEAT_MBYTE 420 typedef int salfirst_T; 421 #else 422 typedef short salfirst_T; 423 #endif 424 425 /* Values for SP_*ERROR are negative, positive values are used by 426 * read_cnt_string(). */ 427 #define SP_TRUNCERROR -1 /* spell file truncated error */ 428 #define SP_FORMERROR -2 /* format error in spell file */ 429 #define SP_OTHERERROR -3 /* other error while reading spell file */ 430 431 /* 432 * Structure used to store words and other info for one language, loaded from 433 * a .spl file. 434 * The main access is through the tree in "sl_fbyts/sl_fidxs", storing the 435 * case-folded words. "sl_kbyts/sl_kidxs" is for keep-case words. 436 * 437 * The "byts" array stores the possible bytes in each tree node, preceded by 438 * the number of possible bytes, sorted on byte value: 439 * <len> <byte1> <byte2> ... 440 * The "idxs" array stores the index of the child node corresponding to the 441 * byte in "byts". 442 * Exception: when the byte is zero, the word may end here and "idxs" holds 443 * the flags, region mask and affixID for the word. There may be several 444 * zeros in sequence for alternative flag/region/affixID combinations. 445 */ 446 typedef struct slang_S slang_T; 447 struct slang_S 448 { 449 slang_T *sl_next; /* next language */ 450 char_u *sl_name; /* language name "en", "en.rare", "nl", etc. */ 451 char_u *sl_fname; /* name of .spl file */ 452 int sl_add; /* TRUE if it's a .add file. */ 453 454 char_u *sl_fbyts; /* case-folded word bytes */ 455 idx_T *sl_fidxs; /* case-folded word indexes */ 456 char_u *sl_kbyts; /* keep-case word bytes */ 457 idx_T *sl_kidxs; /* keep-case word indexes */ 458 char_u *sl_pbyts; /* prefix tree word bytes */ 459 idx_T *sl_pidxs; /* prefix tree word indexes */ 460 461 char_u *sl_info; /* infotext string or NULL */ 462 463 char_u sl_regions[17]; /* table with up to 8 region names plus NUL */ 464 465 char_u *sl_midword; /* MIDWORD string or NULL */ 466 467 hashtab_T sl_wordcount; /* hashtable with word count, wordcount_T */ 468 469 int sl_compmax; /* COMPOUNDWORDMAX (default: MAXWLEN) */ 470 int sl_compminlen; /* COMPOUNDMIN (default: 0) */ 471 int sl_compsylmax; /* COMPOUNDSYLMAX (default: MAXWLEN) */ 472 int sl_compoptions; /* COMP_* flags */ 473 garray_T sl_comppat; /* CHECKCOMPOUNDPATTERN items */ 474 regprog_T *sl_compprog; /* COMPOUNDRULE turned into a regexp progrm 475 * (NULL when no compounding) */ 476 char_u *sl_compstartflags; /* flags for first compound word */ 477 char_u *sl_compallflags; /* all flags for compound words */ 478 char_u sl_nobreak; /* When TRUE: no spaces between words */ 479 char_u *sl_syllable; /* SYLLABLE repeatable chars or NULL */ 480 garray_T sl_syl_items; /* syllable items */ 481 482 int sl_prefixcnt; /* number of items in "sl_prefprog" */ 483 regprog_T **sl_prefprog; /* table with regprogs for prefixes */ 484 485 garray_T sl_rep; /* list of fromto_T entries from REP lines */ 486 short sl_rep_first[256]; /* indexes where byte first appears, -1 if 487 there is none */ 488 garray_T sl_sal; /* list of salitem_T entries from SAL lines */ 489 salfirst_T sl_sal_first[256]; /* indexes where byte first appears, -1 if 490 there is none */ 491 int sl_followup; /* SAL followup */ 492 int sl_collapse; /* SAL collapse_result */ 493 int sl_rem_accents; /* SAL remove_accents */ 494 int sl_sofo; /* SOFOFROM and SOFOTO instead of SAL items: 495 * "sl_sal_first" maps chars, when has_mbyte 496 * "sl_sal" is a list of wide char lists. */ 497 garray_T sl_repsal; /* list of fromto_T entries from REPSAL lines */ 498 short sl_repsal_first[256]; /* sl_rep_first for REPSAL lines */ 499 int sl_nosplitsugs; /* don't suggest splitting a word */ 500 501 /* Info from the .sug file. Loaded on demand. */ 502 time_t sl_sugtime; /* timestamp for .sug file */ 503 char_u *sl_sbyts; /* soundfolded word bytes */ 504 idx_T *sl_sidxs; /* soundfolded word indexes */ 505 buf_T *sl_sugbuf; /* buffer with word number table */ 506 int sl_sugloaded; /* TRUE when .sug file was loaded or failed to 507 load */ 508 509 int sl_has_map; /* TRUE if there is a MAP line */ 510 #ifdef FEAT_MBYTE 511 hashtab_T sl_map_hash; /* MAP for multi-byte chars */ 512 int sl_map_array[256]; /* MAP for first 256 chars */ 513 #else 514 char_u sl_map_array[256]; /* MAP for first 256 chars */ 515 #endif 516 hashtab_T sl_sounddone; /* table with soundfolded words that have 517 handled, see add_sound_suggest() */ 518 }; 519 520 /* First language that is loaded, start of the linked list of loaded 521 * languages. */ 522 static slang_T *first_lang = NULL; 523 524 /* Flags used in .spl file for soundsalike flags. */ 525 #define SAL_F0LLOWUP 1 526 #define SAL_COLLAPSE 2 527 #define SAL_REM_ACCENTS 4 528 529 /* 530 * Structure used in "b_langp", filled from 'spelllang'. 531 */ 532 typedef struct langp_S 533 { 534 slang_T *lp_slang; /* info for this language */ 535 slang_T *lp_sallang; /* language used for sound folding or NULL */ 536 slang_T *lp_replang; /* language used for REP items or NULL */ 537 int lp_region; /* bitmask for region or REGION_ALL */ 538 } langp_T; 539 540 #define LANGP_ENTRY(ga, i) (((langp_T *)(ga).ga_data) + (i)) 541 542 #define REGION_ALL 0xff /* word valid in all regions */ 543 544 #define VIMSPELLMAGIC "VIMspell" /* string at start of Vim spell file */ 545 #define VIMSPELLMAGICL 8 546 #define VIMSPELLVERSION 50 547 548 #define VIMSUGMAGIC "VIMsug" /* string at start of Vim .sug file */ 549 #define VIMSUGMAGICL 6 550 #define VIMSUGVERSION 1 551 552 /* Section IDs. Only renumber them when VIMSPELLVERSION changes! */ 553 #define SN_REGION 0 /* <regionname> section */ 554 #define SN_CHARFLAGS 1 /* charflags section */ 555 #define SN_MIDWORD 2 /* <midword> section */ 556 #define SN_PREFCOND 3 /* <prefcond> section */ 557 #define SN_REP 4 /* REP items section */ 558 #define SN_SAL 5 /* SAL items section */ 559 #define SN_SOFO 6 /* soundfolding section */ 560 #define SN_MAP 7 /* MAP items section */ 561 #define SN_COMPOUND 8 /* compound words section */ 562 #define SN_SYLLABLE 9 /* syllable section */ 563 #define SN_NOBREAK 10 /* NOBREAK section */ 564 #define SN_SUGFILE 11 /* timestamp for .sug file */ 565 #define SN_REPSAL 12 /* REPSAL items section */ 566 #define SN_WORDS 13 /* common words */ 567 #define SN_NOSPLITSUGS 14 /* don't split word for suggestions */ 568 #define SN_INFO 15 /* info section */ 569 #define SN_END 255 /* end of sections */ 570 571 #define SNF_REQUIRED 1 /* <sectionflags>: required section */ 572 573 /* Result values. Lower number is accepted over higher one. */ 574 #define SP_BANNED -1 575 #define SP_OK 0 576 #define SP_RARE 1 577 #define SP_LOCAL 2 578 #define SP_BAD 3 579 580 /* file used for "zG" and "zW" */ 581 static char_u *int_wordlist = NULL; 582 583 typedef struct wordcount_S 584 { 585 short_u wc_count; /* nr of times word was seen */ 586 char_u wc_word[1]; /* word, actually longer */ 587 } wordcount_T; 588 589 static wordcount_T dumwc; 590 #define WC_KEY_OFF (unsigned)(dumwc.wc_word - (char_u *)&dumwc) 591 #define HI2WC(hi) ((wordcount_T *)((hi)->hi_key - WC_KEY_OFF)) 592 #define MAXWORDCOUNT 0xffff 593 594 /* 595 * Information used when looking for suggestions. 596 */ 597 typedef struct suginfo_S 598 { 599 garray_T su_ga; /* suggestions, contains "suggest_T" */ 600 int su_maxcount; /* max. number of suggestions displayed */ 601 int su_maxscore; /* maximum score for adding to su_ga */ 602 int su_sfmaxscore; /* idem, for when doing soundfold words */ 603 garray_T su_sga; /* like su_ga, sound-folded scoring */ 604 char_u *su_badptr; /* start of bad word in line */ 605 int su_badlen; /* length of detected bad word in line */ 606 int su_badflags; /* caps flags for bad word */ 607 char_u su_badword[MAXWLEN]; /* bad word truncated at su_badlen */ 608 char_u su_fbadword[MAXWLEN]; /* su_badword case-folded */ 609 char_u su_sal_badword[MAXWLEN]; /* su_badword soundfolded */ 610 hashtab_T su_banned; /* table with banned words */ 611 slang_T *su_sallang; /* default language for sound folding */ 612 } suginfo_T; 613 614 /* One word suggestion. Used in "si_ga". */ 615 typedef struct suggest_S 616 { 617 char_u *st_word; /* suggested word, allocated string */ 618 int st_wordlen; /* STRLEN(st_word) */ 619 int st_orglen; /* length of replaced text */ 620 int st_score; /* lower is better */ 621 int st_altscore; /* used when st_score compares equal */ 622 int st_salscore; /* st_score is for soundalike */ 623 int st_had_bonus; /* bonus already included in score */ 624 slang_T *st_slang; /* language used for sound folding */ 625 } suggest_T; 626 627 #define SUG(ga, i) (((suggest_T *)(ga).ga_data)[i]) 628 629 /* TRUE if a word appears in the list of banned words. */ 630 #define WAS_BANNED(su, word) (!HASHITEM_EMPTY(hash_find(&su->su_banned, word))) 631 632 /* Number of suggestions kept when cleaning up. we need to keep more than 633 * what is displayed, because when rescore_suggestions() is called the score 634 * may change and wrong suggestions may be removed later. */ 635 #define SUG_CLEAN_COUNT(su) ((su)->su_maxcount < 130 ? 150 : (su)->su_maxcount + 20) 636 637 /* Threshold for sorting and cleaning up suggestions. Don't want to keep lots 638 * of suggestions that are not going to be displayed. */ 639 #define SUG_MAX_COUNT(su) (SUG_CLEAN_COUNT(su) + 50) 640 641 /* score for various changes */ 642 #define SCORE_SPLIT 149 /* split bad word */ 643 #define SCORE_SPLIT_NO 249 /* split bad word with NOSPLITSUGS */ 644 #define SCORE_ICASE 52 /* slightly different case */ 645 #define SCORE_REGION 200 /* word is for different region */ 646 #define SCORE_RARE 180 /* rare word */ 647 #define SCORE_SWAP 75 /* swap two characters */ 648 #define SCORE_SWAP3 110 /* swap two characters in three */ 649 #define SCORE_REP 65 /* REP replacement */ 650 #define SCORE_SUBST 93 /* substitute a character */ 651 #define SCORE_SIMILAR 33 /* substitute a similar character */ 652 #define SCORE_SUBCOMP 33 /* substitute a composing character */ 653 #define SCORE_DEL 94 /* delete a character */ 654 #define SCORE_DELDUP 66 /* delete a duplicated character */ 655 #define SCORE_DELCOMP 28 /* delete a composing character */ 656 #define SCORE_INS 96 /* insert a character */ 657 #define SCORE_INSDUP 67 /* insert a duplicate character */ 658 #define SCORE_INSCOMP 30 /* insert a composing character */ 659 #define SCORE_NONWORD 103 /* change non-word to word char */ 660 661 #define SCORE_FILE 30 /* suggestion from a file */ 662 #define SCORE_MAXINIT 350 /* Initial maximum score: higher == slower. 663 * 350 allows for about three changes. */ 664 665 #define SCORE_COMMON1 30 /* subtracted for words seen before */ 666 #define SCORE_COMMON2 40 /* subtracted for words often seen */ 667 #define SCORE_COMMON3 50 /* subtracted for words very often seen */ 668 #define SCORE_THRES2 10 /* word count threshold for COMMON2 */ 669 #define SCORE_THRES3 100 /* word count threshold for COMMON3 */ 670 671 /* When trying changed soundfold words it becomes slow when trying more than 672 * two changes. With less then two changes it's slightly faster but we miss a 673 * few good suggestions. In rare cases we need to try three of four changes. 674 */ 675 #define SCORE_SFMAX1 200 /* maximum score for first try */ 676 #define SCORE_SFMAX2 300 /* maximum score for second try */ 677 #define SCORE_SFMAX3 400 /* maximum score for third try */ 678 679 #define SCORE_BIG SCORE_INS * 3 /* big difference */ 680 #define SCORE_MAXMAX 999999 /* accept any score */ 681 #define SCORE_LIMITMAX 350 /* for spell_edit_score_limit() */ 682 683 /* for spell_edit_score_limit() we need to know the minimum value of 684 * SCORE_ICASE, SCORE_SWAP, SCORE_DEL, SCORE_SIMILAR and SCORE_INS */ 685 #define SCORE_EDIT_MIN SCORE_SIMILAR 686 687 /* 688 * Structure to store info for word matching. 689 */ 690 typedef struct matchinf_S 691 { 692 langp_T *mi_lp; /* info for language and region */ 693 694 /* pointers to original text to be checked */ 695 char_u *mi_word; /* start of word being checked */ 696 char_u *mi_end; /* end of matching word so far */ 697 char_u *mi_fend; /* next char to be added to mi_fword */ 698 char_u *mi_cend; /* char after what was used for 699 mi_capflags */ 700 701 /* case-folded text */ 702 char_u mi_fword[MAXWLEN + 1]; /* mi_word case-folded */ 703 int mi_fwordlen; /* nr of valid bytes in mi_fword */ 704 705 /* for when checking word after a prefix */ 706 int mi_prefarridx; /* index in sl_pidxs with list of 707 affixID/condition */ 708 int mi_prefcnt; /* number of entries at mi_prefarridx */ 709 int mi_prefixlen; /* byte length of prefix */ 710 #ifdef FEAT_MBYTE 711 int mi_cprefixlen; /* byte length of prefix in original 712 case */ 713 #else 714 # define mi_cprefixlen mi_prefixlen /* it's the same value */ 715 #endif 716 717 /* for when checking a compound word */ 718 int mi_compoff; /* start of following word offset */ 719 char_u mi_compflags[MAXWLEN]; /* flags for compound words used */ 720 int mi_complen; /* nr of compound words used */ 721 int mi_compextra; /* nr of COMPOUNDROOT words */ 722 723 /* others */ 724 int mi_result; /* result so far: SP_BAD, SP_OK, etc. */ 725 int mi_capflags; /* WF_ONECAP WF_ALLCAP WF_KEEPCAP */ 726 buf_T *mi_buf; /* buffer being checked */ 727 728 /* for NOBREAK */ 729 int mi_result2; /* "mi_resul" without following word */ 730 char_u *mi_end2; /* "mi_end" without following word */ 731 } matchinf_T; 732 733 /* 734 * The tables used for recognizing word characters according to spelling. 735 * These are only used for the first 256 characters of 'encoding'. 736 */ 737 typedef struct spelltab_S 738 { 739 char_u st_isw[256]; /* flags: is word char */ 740 char_u st_isu[256]; /* flags: is uppercase char */ 741 char_u st_fold[256]; /* chars: folded case */ 742 char_u st_upper[256]; /* chars: upper case */ 743 } spelltab_T; 744 745 static spelltab_T spelltab; 746 static int did_set_spelltab; 747 748 #define CF_WORD 0x01 749 #define CF_UPPER 0x02 750 751 static void clear_spell_chartab __ARGS((spelltab_T *sp)); 752 static int set_spell_finish __ARGS((spelltab_T *new_st)); 753 static int spell_iswordp __ARGS((char_u *p, buf_T *buf)); 754 static int spell_iswordp_nmw __ARGS((char_u *p)); 755 #ifdef FEAT_MBYTE 756 static int spell_iswordp_w __ARGS((int *p, buf_T *buf)); 757 #endif 758 static int write_spell_prefcond __ARGS((FILE *fd, garray_T *gap)); 759 760 /* 761 * For finding suggestions: At each node in the tree these states are tried: 762 */ 763 typedef enum 764 { 765 STATE_START = 0, /* At start of node check for NUL bytes (goodword 766 * ends); if badword ends there is a match, otherwise 767 * try splitting word. */ 768 STATE_NOPREFIX, /* try without prefix */ 769 STATE_SPLITUNDO, /* Undo splitting. */ 770 STATE_ENDNUL, /* Past NUL bytes at start of the node. */ 771 STATE_PLAIN, /* Use each byte of the node. */ 772 STATE_DEL, /* Delete a byte from the bad word. */ 773 STATE_INS_PREP, /* Prepare for inserting bytes. */ 774 STATE_INS, /* Insert a byte in the bad word. */ 775 STATE_SWAP, /* Swap two bytes. */ 776 STATE_UNSWAP, /* Undo swap two characters. */ 777 STATE_SWAP3, /* Swap two characters over three. */ 778 STATE_UNSWAP3, /* Undo Swap two characters over three. */ 779 STATE_UNROT3L, /* Undo rotate three characters left */ 780 STATE_UNROT3R, /* Undo rotate three characters right */ 781 STATE_REP_INI, /* Prepare for using REP items. */ 782 STATE_REP, /* Use matching REP items from the .aff file. */ 783 STATE_REP_UNDO, /* Undo a REP item replacement. */ 784 STATE_FINAL /* End of this node. */ 785 } state_T; 786 787 /* 788 * Struct to keep the state at each level in suggest_try_change(). 789 */ 790 typedef struct trystate_S 791 { 792 state_T ts_state; /* state at this level, STATE_ */ 793 int ts_score; /* score */ 794 idx_T ts_arridx; /* index in tree array, start of node */ 795 short ts_curi; /* index in list of child nodes */ 796 char_u ts_fidx; /* index in fword[], case-folded bad word */ 797 char_u ts_fidxtry; /* ts_fidx at which bytes may be changed */ 798 char_u ts_twordlen; /* valid length of tword[] */ 799 char_u ts_prefixdepth; /* stack depth for end of prefix or 800 * PFD_PREFIXTREE or PFD_NOPREFIX */ 801 char_u ts_flags; /* TSF_ flags */ 802 #ifdef FEAT_MBYTE 803 char_u ts_tcharlen; /* number of bytes in tword character */ 804 char_u ts_tcharidx; /* current byte index in tword character */ 805 char_u ts_isdiff; /* DIFF_ values */ 806 char_u ts_fcharstart; /* index in fword where badword char started */ 807 #endif 808 char_u ts_prewordlen; /* length of word in "preword[]" */ 809 char_u ts_splitoff; /* index in "tword" after last split */ 810 char_u ts_splitfidx; /* "ts_fidx" at word split */ 811 char_u ts_complen; /* nr of compound words used */ 812 char_u ts_compsplit; /* index for "compflags" where word was spit */ 813 char_u ts_save_badflags; /* su_badflags saved here */ 814 char_u ts_delidx; /* index in fword for char that was deleted, 815 valid when "ts_flags" has TSF_DIDDEL */ 816 } trystate_T; 817 818 /* values for ts_isdiff */ 819 #define DIFF_NONE 0 /* no different byte (yet) */ 820 #define DIFF_YES 1 /* different byte found */ 821 #define DIFF_INSERT 2 /* inserting character */ 822 823 /* values for ts_flags */ 824 #define TSF_PREFIXOK 1 /* already checked that prefix is OK */ 825 #define TSF_DIDSPLIT 2 /* tried split at this point */ 826 #define TSF_DIDDEL 4 /* did a delete, "ts_delidx" has index */ 827 828 /* special values ts_prefixdepth */ 829 #define PFD_NOPREFIX 0xff /* not using prefixes */ 830 #define PFD_PREFIXTREE 0xfe /* walking through the prefix tree */ 831 #define PFD_NOTSPECIAL 0xfd /* highest value that's not special */ 832 833 /* mode values for find_word */ 834 #define FIND_FOLDWORD 0 /* find word case-folded */ 835 #define FIND_KEEPWORD 1 /* find keep-case word */ 836 #define FIND_PREFIX 2 /* find word after prefix */ 837 #define FIND_COMPOUND 3 /* find case-folded compound word */ 838 #define FIND_KEEPCOMPOUND 4 /* find keep-case compound word */ 839 840 static slang_T *slang_alloc __ARGS((char_u *lang)); 841 static void slang_free __ARGS((slang_T *lp)); 842 static void slang_clear __ARGS((slang_T *lp)); 843 static void slang_clear_sug __ARGS((slang_T *lp)); 844 static void find_word __ARGS((matchinf_T *mip, int mode)); 845 static int can_compound __ARGS((slang_T *slang, char_u *word, char_u *flags)); 846 static int valid_word_prefix __ARGS((int totprefcnt, int arridx, int flags, char_u *word, slang_T *slang, int cond_req)); 847 static void find_prefix __ARGS((matchinf_T *mip, int mode)); 848 static int fold_more __ARGS((matchinf_T *mip)); 849 static int spell_valid_case __ARGS((int wordflags, int treeflags)); 850 static int no_spell_checking __ARGS((win_T *wp)); 851 static void spell_load_lang __ARGS((char_u *lang)); 852 static char_u *spell_enc __ARGS((void)); 853 static void int_wordlist_spl __ARGS((char_u *fname)); 854 static void spell_load_cb __ARGS((char_u *fname, void *cookie)); 855 static slang_T *spell_load_file __ARGS((char_u *fname, char_u *lang, slang_T *old_lp, int silent)); 856 static int get2c __ARGS((FILE *fd)); 857 static int get3c __ARGS((FILE *fd)); 858 static int get4c __ARGS((FILE *fd)); 859 static time_t get8c __ARGS((FILE *fd)); 860 static char_u *read_cnt_string __ARGS((FILE *fd, int cnt_bytes, int *lenp)); 861 static char_u *read_string __ARGS((FILE *fd, int cnt)); 862 static int read_region_section __ARGS((FILE *fd, slang_T *slang, int len)); 863 static int read_charflags_section __ARGS((FILE *fd)); 864 static int read_prefcond_section __ARGS((FILE *fd, slang_T *lp)); 865 static int read_rep_section __ARGS((FILE *fd, garray_T *gap, short *first)); 866 static int read_sal_section __ARGS((FILE *fd, slang_T *slang)); 867 static int read_words_section __ARGS((FILE *fd, slang_T *lp, int len)); 868 static void count_common_word __ARGS((slang_T *lp, char_u *word, int len, int count)); 869 static int score_wordcount_adj __ARGS((slang_T *slang, int score, char_u *word, int split)); 870 static int read_sofo_section __ARGS((FILE *fd, slang_T *slang)); 871 static int read_compound __ARGS((FILE *fd, slang_T *slang, int len)); 872 static int byte_in_str __ARGS((char_u *str, int byte)); 873 static int init_syl_tab __ARGS((slang_T *slang)); 874 static int count_syllables __ARGS((slang_T *slang, char_u *word)); 875 static int set_sofo __ARGS((slang_T *lp, char_u *from, char_u *to)); 876 static void set_sal_first __ARGS((slang_T *lp)); 877 #ifdef FEAT_MBYTE 878 static int *mb_str2wide __ARGS((char_u *s)); 879 #endif 880 static int spell_read_tree __ARGS((FILE *fd, char_u **bytsp, idx_T **idxsp, int prefixtree, int prefixcnt)); 881 static idx_T read_tree_node __ARGS((FILE *fd, char_u *byts, idx_T *idxs, int maxidx, int startidx, int prefixtree, int maxprefcondnr)); 882 static void clear_midword __ARGS((buf_T *buf)); 883 static void use_midword __ARGS((slang_T *lp, buf_T *buf)); 884 static int find_region __ARGS((char_u *rp, char_u *region)); 885 static int captype __ARGS((char_u *word, char_u *end)); 886 static int badword_captype __ARGS((char_u *word, char_u *end)); 887 static void spell_reload_one __ARGS((char_u *fname, int added_word)); 888 static void set_spell_charflags __ARGS((char_u *flags, int cnt, char_u *upp)); 889 static int set_spell_chartab __ARGS((char_u *fol, char_u *low, char_u *upp)); 890 static int spell_casefold __ARGS((char_u *p, int len, char_u *buf, int buflen)); 891 static int check_need_cap __ARGS((linenr_T lnum, colnr_T col)); 892 static void spell_find_suggest __ARGS((char_u *badptr, int badlen, suginfo_T *su, int maxcount, int banbadword, int need_cap, int interactive)); 893 #ifdef FEAT_EVAL 894 static void spell_suggest_expr __ARGS((suginfo_T *su, char_u *expr)); 895 #endif 896 static void spell_suggest_file __ARGS((suginfo_T *su, char_u *fname)); 897 static void spell_suggest_intern __ARGS((suginfo_T *su, int interactive)); 898 static void suggest_load_files __ARGS((void)); 899 static void tree_count_words __ARGS((char_u *byts, idx_T *idxs)); 900 static void spell_find_cleanup __ARGS((suginfo_T *su)); 901 static void onecap_copy __ARGS((char_u *word, char_u *wcopy, int upper)); 902 static void allcap_copy __ARGS((char_u *word, char_u *wcopy)); 903 static void suggest_try_special __ARGS((suginfo_T *su)); 904 static void suggest_try_change __ARGS((suginfo_T *su)); 905 static void suggest_trie_walk __ARGS((suginfo_T *su, langp_T *lp, char_u *fword, int soundfold)); 906 static void go_deeper __ARGS((trystate_T *stack, int depth, int score_add)); 907 #ifdef FEAT_MBYTE 908 static int nofold_len __ARGS((char_u *fword, int flen, char_u *word)); 909 #endif 910 static void find_keepcap_word __ARGS((slang_T *slang, char_u *fword, char_u *kword)); 911 static void score_comp_sal __ARGS((suginfo_T *su)); 912 static void score_combine __ARGS((suginfo_T *su)); 913 static int stp_sal_score __ARGS((suggest_T *stp, suginfo_T *su, slang_T *slang, char_u *badsound)); 914 static void suggest_try_soundalike_prep __ARGS((void)); 915 static void suggest_try_soundalike __ARGS((suginfo_T *su)); 916 static void suggest_try_soundalike_finish __ARGS((void)); 917 static void add_sound_suggest __ARGS((suginfo_T *su, char_u *goodword, int score, langp_T *lp)); 918 static int soundfold_find __ARGS((slang_T *slang, char_u *word)); 919 static void make_case_word __ARGS((char_u *fword, char_u *cword, int flags)); 920 static void set_map_str __ARGS((slang_T *lp, char_u *map)); 921 static int similar_chars __ARGS((slang_T *slang, int c1, int c2)); 922 static void add_suggestion __ARGS((suginfo_T *su, garray_T *gap, char_u *goodword, int badlen, int score, int altscore, int had_bonus, slang_T *slang, int maxsf)); 923 static void check_suggestions __ARGS((suginfo_T *su, garray_T *gap)); 924 static void add_banned __ARGS((suginfo_T *su, char_u *word)); 925 static void rescore_suggestions __ARGS((suginfo_T *su)); 926 static void rescore_one __ARGS((suginfo_T *su, suggest_T *stp)); 927 static int cleanup_suggestions __ARGS((garray_T *gap, int maxscore, int keep)); 928 static void spell_soundfold __ARGS((slang_T *slang, char_u *inword, int folded, char_u *res)); 929 static void spell_soundfold_sofo __ARGS((slang_T *slang, char_u *inword, char_u *res)); 930 static void spell_soundfold_sal __ARGS((slang_T *slang, char_u *inword, char_u *res)); 931 #ifdef FEAT_MBYTE 932 static void spell_soundfold_wsal __ARGS((slang_T *slang, char_u *inword, char_u *res)); 933 #endif 934 static int soundalike_score __ARGS((char_u *goodsound, char_u *badsound)); 935 static int spell_edit_score __ARGS((slang_T *slang, char_u *badword, char_u *goodword)); 936 static int spell_edit_score_limit __ARGS((slang_T *slang, char_u *badword, char_u *goodword, int limit)); 937 #ifdef FEAT_MBYTE 938 static int spell_edit_score_limit_w __ARGS((slang_T *slang, char_u *badword, char_u *goodword, int limit)); 939 #endif 940 static void dump_word __ARGS((slang_T *slang, char_u *word, char_u *pat, int *dir, int round, int flags, linenr_T lnum)); 941 static linenr_T dump_prefixes __ARGS((slang_T *slang, char_u *word, char_u *pat, int *dir, int round, int flags, linenr_T startlnum)); 942 static buf_T *open_spellbuf __ARGS((void)); 943 static void close_spellbuf __ARGS((buf_T *buf)); 944 945 /* 946 * Use our own character-case definitions, because the current locale may 947 * differ from what the .spl file uses. 948 * These must not be called with negative number! 949 */ 950 #ifndef FEAT_MBYTE 951 /* Non-multi-byte implementation. */ 952 # define SPELL_TOFOLD(c) ((c) < 256 ? spelltab.st_fold[c] : (c)) 953 # define SPELL_TOUPPER(c) ((c) < 256 ? spelltab.st_upper[c] : (c)) 954 # define SPELL_ISUPPER(c) ((c) < 256 ? spelltab.st_isu[c] : FALSE) 955 #else 956 # if defined(HAVE_WCHAR_H) 957 # include <wchar.h> /* for towupper() and towlower() */ 958 # endif 959 /* Multi-byte implementation. For Unicode we can call utf_*(), but don't do 960 * that for ASCII, because we don't want to use 'casemap' here. Otherwise use 961 * the "w" library function for characters above 255 if available. */ 962 # ifdef HAVE_TOWLOWER 963 # define SPELL_TOFOLD(c) (enc_utf8 && (c) >= 128 ? utf_fold(c) \ 964 : (c) < 256 ? spelltab.st_fold[c] : towlower(c)) 965 # else 966 # define SPELL_TOFOLD(c) (enc_utf8 && (c) >= 128 ? utf_fold(c) \ 967 : (c) < 256 ? spelltab.st_fold[c] : (c)) 968 # endif 969 970 # ifdef HAVE_TOWUPPER 971 # define SPELL_TOUPPER(c) (enc_utf8 && (c) >= 128 ? utf_toupper(c) \ 972 : (c) < 256 ? spelltab.st_upper[c] : towupper(c)) 973 # else 974 # define SPELL_TOUPPER(c) (enc_utf8 && (c) >= 128 ? utf_toupper(c) \ 975 : (c) < 256 ? spelltab.st_upper[c] : (c)) 976 # endif 977 978 # ifdef HAVE_ISWUPPER 979 # define SPELL_ISUPPER(c) (enc_utf8 && (c) >= 128 ? utf_isupper(c) \ 980 : (c) < 256 ? spelltab.st_isu[c] : iswupper(c)) 981 # else 982 # define SPELL_ISUPPER(c) (enc_utf8 && (c) >= 128 ? utf_isupper(c) \ 983 : (c) < 256 ? spelltab.st_isu[c] : (FALSE)) 984 # endif 985 #endif 986 987 988 static char *e_format = N_("E759: Format error in spell file"); 989 static char *e_spell_trunc = N_("E758: Truncated spell file"); 990 static char *e_afftrailing = N_("Trailing text in %s line %d: %s"); 991 static char *e_affname = N_("Affix name too long in %s line %d: %s"); 992 static char *e_affform = N_("E761: Format error in affix file FOL, LOW or UPP"); 993 static char *e_affrange = N_("E762: Character in FOL, LOW or UPP is out of range"); 994 static char *msg_compressing = N_("Compressing word tree..."); 995 996 /* Remember what "z?" replaced. */ 997 static char_u *repl_from = NULL; 998 static char_u *repl_to = NULL; 999 1000 /* 1001 * Main spell-checking function. 1002 * "ptr" points to a character that could be the start of a word. 1003 * "*attrp" is set to the highlight index for a badly spelled word. For a 1004 * non-word or when it's OK it remains unchanged. 1005 * This must only be called when 'spelllang' is not empty. 1006 * 1007 * "capcol" is used to check for a Capitalised word after the end of a 1008 * sentence. If it's zero then perform the check. Return the column where to 1009 * check next, or -1 when no sentence end was found. If it's NULL then don't 1010 * worry. 1011 * 1012 * Returns the length of the word in bytes, also when it's OK, so that the 1013 * caller can skip over the word. 1014 */ 1015 int 1016 spell_check(wp, ptr, attrp, capcol, docount) 1017 win_T *wp; /* current window */ 1018 char_u *ptr; 1019 hlf_T *attrp; 1020 int *capcol; /* column to check for Capital */ 1021 int docount; /* count good words */ 1022 { 1023 matchinf_T mi; /* Most things are put in "mi" so that it can 1024 be passed to functions quickly. */ 1025 int nrlen = 0; /* found a number first */ 1026 int c; 1027 int wrongcaplen = 0; 1028 int lpi; 1029 int count_word = docount; 1030 1031 /* A word never starts at a space or a control character. Return quickly 1032 * then, skipping over the character. */ 1033 if (*ptr <= ' ') 1034 return 1; 1035 1036 /* Return here when loading language files failed. */ 1037 if (wp->w_buffer->b_langp.ga_len == 0) 1038 return 1; 1039 1040 vim_memset(&mi, 0, sizeof(matchinf_T)); 1041 1042 /* A number is always OK. Also skip hexadecimal numbers 0xFF99 and 1043 * 0X99FF. But always do check spelling to find "3GPP" and "11 1044 * julifeest". */ 1045 if (*ptr >= '0' && *ptr <= '9') 1046 { 1047 if (*ptr == '0' && (ptr[1] == 'x' || ptr[1] == 'X')) 1048 mi.mi_end = skiphex(ptr + 2); 1049 else 1050 mi.mi_end = skipdigits(ptr); 1051 nrlen = (int)(mi.mi_end - ptr); 1052 } 1053 1054 /* Find the normal end of the word (until the next non-word character). */ 1055 mi.mi_word = ptr; 1056 mi.mi_fend = ptr; 1057 if (spell_iswordp(mi.mi_fend, wp->w_buffer)) 1058 { 1059 do 1060 { 1061 mb_ptr_adv(mi.mi_fend); 1062 } while (*mi.mi_fend != NUL && spell_iswordp(mi.mi_fend, wp->w_buffer)); 1063 1064 if (capcol != NULL && *capcol == 0 && wp->w_buffer->b_cap_prog != NULL) 1065 { 1066 /* Check word starting with capital letter. */ 1067 c = PTR2CHAR(ptr); 1068 if (!SPELL_ISUPPER(c)) 1069 wrongcaplen = (int)(mi.mi_fend - ptr); 1070 } 1071 } 1072 if (capcol != NULL) 1073 *capcol = -1; 1074 1075 /* We always use the characters up to the next non-word character, 1076 * also for bad words. */ 1077 mi.mi_end = mi.mi_fend; 1078 1079 /* Check caps type later. */ 1080 mi.mi_buf = wp->w_buffer; 1081 1082 /* case-fold the word with one non-word character, so that we can check 1083 * for the word end. */ 1084 if (*mi.mi_fend != NUL) 1085 mb_ptr_adv(mi.mi_fend); 1086 1087 (void)spell_casefold(ptr, (int)(mi.mi_fend - ptr), mi.mi_fword, 1088 MAXWLEN + 1); 1089 mi.mi_fwordlen = (int)STRLEN(mi.mi_fword); 1090 1091 /* The word is bad unless we recognize it. */ 1092 mi.mi_result = SP_BAD; 1093 mi.mi_result2 = SP_BAD; 1094 1095 /* 1096 * Loop over the languages specified in 'spelllang'. 1097 * We check them all, because a word may be matched longer in another 1098 * language. 1099 */ 1100 for (lpi = 0; lpi < wp->w_buffer->b_langp.ga_len; ++lpi) 1101 { 1102 mi.mi_lp = LANGP_ENTRY(wp->w_buffer->b_langp, lpi); 1103 1104 /* If reloading fails the language is still in the list but everything 1105 * has been cleared. */ 1106 if (mi.mi_lp->lp_slang->sl_fidxs == NULL) 1107 continue; 1108 1109 /* Check for a matching word in case-folded words. */ 1110 find_word(&mi, FIND_FOLDWORD); 1111 1112 /* Check for a matching word in keep-case words. */ 1113 find_word(&mi, FIND_KEEPWORD); 1114 1115 /* Check for matching prefixes. */ 1116 find_prefix(&mi, FIND_FOLDWORD); 1117 1118 /* For a NOBREAK language, may want to use a word without a following 1119 * word as a backup. */ 1120 if (mi.mi_lp->lp_slang->sl_nobreak && mi.mi_result == SP_BAD 1121 && mi.mi_result2 != SP_BAD) 1122 { 1123 mi.mi_result = mi.mi_result2; 1124 mi.mi_end = mi.mi_end2; 1125 } 1126 1127 /* Count the word in the first language where it's found to be OK. */ 1128 if (count_word && mi.mi_result == SP_OK) 1129 { 1130 count_common_word(mi.mi_lp->lp_slang, ptr, 1131 (int)(mi.mi_end - ptr), 1); 1132 count_word = FALSE; 1133 } 1134 } 1135 1136 if (mi.mi_result != SP_OK) 1137 { 1138 /* If we found a number skip over it. Allows for "42nd". Do flag 1139 * rare and local words, e.g., "3GPP". */ 1140 if (nrlen > 0) 1141 { 1142 if (mi.mi_result == SP_BAD || mi.mi_result == SP_BANNED) 1143 return nrlen; 1144 } 1145 1146 /* When we are at a non-word character there is no error, just 1147 * skip over the character (try looking for a word after it). */ 1148 else if (!spell_iswordp_nmw(ptr)) 1149 { 1150 if (capcol != NULL && wp->w_buffer->b_cap_prog != NULL) 1151 { 1152 regmatch_T regmatch; 1153 1154 /* Check for end of sentence. */ 1155 regmatch.regprog = wp->w_buffer->b_cap_prog; 1156 regmatch.rm_ic = FALSE; 1157 if (vim_regexec(®match, ptr, 0)) 1158 *capcol = (int)(regmatch.endp[0] - ptr); 1159 } 1160 1161 #ifdef FEAT_MBYTE 1162 if (has_mbyte) 1163 return (*mb_ptr2len)(ptr); 1164 #endif 1165 return 1; 1166 } 1167 else if (mi.mi_end == ptr) 1168 /* Always include at least one character. Required for when there 1169 * is a mixup in "midword". */ 1170 mb_ptr_adv(mi.mi_end); 1171 else if (mi.mi_result == SP_BAD 1172 && LANGP_ENTRY(wp->w_buffer->b_langp, 0)->lp_slang->sl_nobreak) 1173 { 1174 char_u *p, *fp; 1175 int save_result = mi.mi_result; 1176 1177 /* First language in 'spelllang' is NOBREAK. Find first position 1178 * at which any word would be valid. */ 1179 mi.mi_lp = LANGP_ENTRY(wp->w_buffer->b_langp, 0); 1180 if (mi.mi_lp->lp_slang->sl_fidxs != NULL) 1181 { 1182 p = mi.mi_word; 1183 fp = mi.mi_fword; 1184 for (;;) 1185 { 1186 mb_ptr_adv(p); 1187 mb_ptr_adv(fp); 1188 if (p >= mi.mi_end) 1189 break; 1190 mi.mi_compoff = (int)(fp - mi.mi_fword); 1191 find_word(&mi, FIND_COMPOUND); 1192 if (mi.mi_result != SP_BAD) 1193 { 1194 mi.mi_end = p; 1195 break; 1196 } 1197 } 1198 mi.mi_result = save_result; 1199 } 1200 } 1201 1202 if (mi.mi_result == SP_BAD || mi.mi_result == SP_BANNED) 1203 *attrp = HLF_SPB; 1204 else if (mi.mi_result == SP_RARE) 1205 *attrp = HLF_SPR; 1206 else 1207 *attrp = HLF_SPL; 1208 } 1209 1210 if (wrongcaplen > 0 && (mi.mi_result == SP_OK || mi.mi_result == SP_RARE)) 1211 { 1212 /* Report SpellCap only when the word isn't badly spelled. */ 1213 *attrp = HLF_SPC; 1214 return wrongcaplen; 1215 } 1216 1217 return (int)(mi.mi_end - ptr); 1218 } 1219 1220 /* 1221 * Check if the word at "mip->mi_word" is in the tree. 1222 * When "mode" is FIND_FOLDWORD check in fold-case word tree. 1223 * When "mode" is FIND_KEEPWORD check in keep-case word tree. 1224 * When "mode" is FIND_PREFIX check for word after prefix in fold-case word 1225 * tree. 1226 * 1227 * For a match mip->mi_result is updated. 1228 */ 1229 static void 1230 find_word(mip, mode) 1231 matchinf_T *mip; 1232 int mode; 1233 { 1234 idx_T arridx = 0; 1235 int endlen[MAXWLEN]; /* length at possible word endings */ 1236 idx_T endidx[MAXWLEN]; /* possible word endings */ 1237 int endidxcnt = 0; 1238 int len; 1239 int wlen = 0; 1240 int flen; 1241 int c; 1242 char_u *ptr; 1243 idx_T lo, hi, m; 1244 #ifdef FEAT_MBYTE 1245 char_u *s; 1246 #endif 1247 char_u *p; 1248 int res = SP_BAD; 1249 slang_T *slang = mip->mi_lp->lp_slang; 1250 unsigned flags; 1251 char_u *byts; 1252 idx_T *idxs; 1253 int word_ends; 1254 int prefix_found; 1255 int nobreak_result; 1256 1257 if (mode == FIND_KEEPWORD || mode == FIND_KEEPCOMPOUND) 1258 { 1259 /* Check for word with matching case in keep-case tree. */ 1260 ptr = mip->mi_word; 1261 flen = 9999; /* no case folding, always enough bytes */ 1262 byts = slang->sl_kbyts; 1263 idxs = slang->sl_kidxs; 1264 1265 if (mode == FIND_KEEPCOMPOUND) 1266 /* Skip over the previously found word(s). */ 1267 wlen += mip->mi_compoff; 1268 } 1269 else 1270 { 1271 /* Check for case-folded in case-folded tree. */ 1272 ptr = mip->mi_fword; 1273 flen = mip->mi_fwordlen; /* available case-folded bytes */ 1274 byts = slang->sl_fbyts; 1275 idxs = slang->sl_fidxs; 1276 1277 if (mode == FIND_PREFIX) 1278 { 1279 /* Skip over the prefix. */ 1280 wlen = mip->mi_prefixlen; 1281 flen -= mip->mi_prefixlen; 1282 } 1283 else if (mode == FIND_COMPOUND) 1284 { 1285 /* Skip over the previously found word(s). */ 1286 wlen = mip->mi_compoff; 1287 flen -= mip->mi_compoff; 1288 } 1289 1290 } 1291 1292 if (byts == NULL) 1293 return; /* array is empty */ 1294 1295 /* 1296 * Repeat advancing in the tree until: 1297 * - there is a byte that doesn't match, 1298 * - we reach the end of the tree, 1299 * - or we reach the end of the line. 1300 */ 1301 for (;;) 1302 { 1303 if (flen <= 0 && *mip->mi_fend != NUL) 1304 flen = fold_more(mip); 1305 1306 len = byts[arridx++]; 1307 1308 /* If the first possible byte is a zero the word could end here. 1309 * Remember this index, we first check for the longest word. */ 1310 if (byts[arridx] == 0) 1311 { 1312 if (endidxcnt == MAXWLEN) 1313 { 1314 /* Must be a corrupted spell file. */ 1315 EMSG(_(e_format)); 1316 return; 1317 } 1318 endlen[endidxcnt] = wlen; 1319 endidx[endidxcnt++] = arridx++; 1320 --len; 1321 1322 /* Skip over the zeros, there can be several flag/region 1323 * combinations. */ 1324 while (len > 0 && byts[arridx] == 0) 1325 { 1326 ++arridx; 1327 --len; 1328 } 1329 if (len == 0) 1330 break; /* no children, word must end here */ 1331 } 1332 1333 /* Stop looking at end of the line. */ 1334 if (ptr[wlen] == NUL) 1335 break; 1336 1337 /* Perform a binary search in the list of accepted bytes. */ 1338 c = ptr[wlen]; 1339 if (c == TAB) /* <Tab> is handled like <Space> */ 1340 c = ' '; 1341 lo = arridx; 1342 hi = arridx + len - 1; 1343 while (lo < hi) 1344 { 1345 m = (lo + hi) / 2; 1346 if (byts[m] > c) 1347 hi = m - 1; 1348 else if (byts[m] < c) 1349 lo = m + 1; 1350 else 1351 { 1352 lo = hi = m; 1353 break; 1354 } 1355 } 1356 1357 /* Stop if there is no matching byte. */ 1358 if (hi < lo || byts[lo] != c) 1359 break; 1360 1361 /* Continue at the child (if there is one). */ 1362 arridx = idxs[lo]; 1363 ++wlen; 1364 --flen; 1365 1366 /* One space in the good word may stand for several spaces in the 1367 * checked word. */ 1368 if (c == ' ') 1369 { 1370 for (;;) 1371 { 1372 if (flen <= 0 && *mip->mi_fend != NUL) 1373 flen = fold_more(mip); 1374 if (ptr[wlen] != ' ' && ptr[wlen] != TAB) 1375 break; 1376 ++wlen; 1377 --flen; 1378 } 1379 } 1380 } 1381 1382 /* 1383 * Verify that one of the possible endings is valid. Try the longest 1384 * first. 1385 */ 1386 while (endidxcnt > 0) 1387 { 1388 --endidxcnt; 1389 arridx = endidx[endidxcnt]; 1390 wlen = endlen[endidxcnt]; 1391 1392 #ifdef FEAT_MBYTE 1393 if ((*mb_head_off)(ptr, ptr + wlen) > 0) 1394 continue; /* not at first byte of character */ 1395 #endif 1396 if (spell_iswordp(ptr + wlen, mip->mi_buf)) 1397 { 1398 if (slang->sl_compprog == NULL && !slang->sl_nobreak) 1399 continue; /* next char is a word character */ 1400 word_ends = FALSE; 1401 } 1402 else 1403 word_ends = TRUE; 1404 /* The prefix flag is before compound flags. Once a valid prefix flag 1405 * has been found we try compound flags. */ 1406 prefix_found = FALSE; 1407 1408 #ifdef FEAT_MBYTE 1409 if (mode != FIND_KEEPWORD && has_mbyte) 1410 { 1411 /* Compute byte length in original word, length may change 1412 * when folding case. This can be slow, take a shortcut when the 1413 * case-folded word is equal to the keep-case word. */ 1414 p = mip->mi_word; 1415 if (STRNCMP(ptr, p, wlen) != 0) 1416 { 1417 for (s = ptr; s < ptr + wlen; mb_ptr_adv(s)) 1418 mb_ptr_adv(p); 1419 wlen = (int)(p - mip->mi_word); 1420 } 1421 } 1422 #endif 1423 1424 /* Check flags and region. For FIND_PREFIX check the condition and 1425 * prefix ID. 1426 * Repeat this if there are more flags/region alternatives until there 1427 * is a match. */ 1428 res = SP_BAD; 1429 for (len = byts[arridx - 1]; len > 0 && byts[arridx] == 0; 1430 --len, ++arridx) 1431 { 1432 flags = idxs[arridx]; 1433 1434 /* For the fold-case tree check that the case of the checked word 1435 * matches with what the word in the tree requires. 1436 * For keep-case tree the case is always right. For prefixes we 1437 * don't bother to check. */ 1438 if (mode == FIND_FOLDWORD) 1439 { 1440 if (mip->mi_cend != mip->mi_word + wlen) 1441 { 1442 /* mi_capflags was set for a different word length, need 1443 * to do it again. */ 1444 mip->mi_cend = mip->mi_word + wlen; 1445 mip->mi_capflags = captype(mip->mi_word, mip->mi_cend); 1446 } 1447 1448 if (mip->mi_capflags == WF_KEEPCAP 1449 || !spell_valid_case(mip->mi_capflags, flags)) 1450 continue; 1451 } 1452 1453 /* When mode is FIND_PREFIX the word must support the prefix: 1454 * check the prefix ID and the condition. Do that for the list at 1455 * mip->mi_prefarridx that find_prefix() filled. */ 1456 else if (mode == FIND_PREFIX && !prefix_found) 1457 { 1458 c = valid_word_prefix(mip->mi_prefcnt, mip->mi_prefarridx, 1459 flags, 1460 mip->mi_word + mip->mi_cprefixlen, slang, 1461 FALSE); 1462 if (c == 0) 1463 continue; 1464 1465 /* Use the WF_RARE flag for a rare prefix. */ 1466 if (c & WF_RAREPFX) 1467 flags |= WF_RARE; 1468 prefix_found = TRUE; 1469 } 1470 1471 if (slang->sl_nobreak) 1472 { 1473 if ((mode == FIND_COMPOUND || mode == FIND_KEEPCOMPOUND) 1474 && (flags & WF_BANNED) == 0) 1475 { 1476 /* NOBREAK: found a valid following word. That's all we 1477 * need to know, so return. */ 1478 mip->mi_result = SP_OK; 1479 break; 1480 } 1481 } 1482 1483 else if ((mode == FIND_COMPOUND || mode == FIND_KEEPCOMPOUND 1484 || !word_ends)) 1485 { 1486 /* If there is no compound flag or the word is shorter than 1487 * COMPOUNDMIN reject it quickly. 1488 * Makes you wonder why someone puts a compound flag on a word 1489 * that's too short... Myspell compatibility requires this 1490 * anyway. */ 1491 if (((unsigned)flags >> 24) == 0 1492 || wlen - mip->mi_compoff < slang->sl_compminlen) 1493 continue; 1494 #ifdef FEAT_MBYTE 1495 /* For multi-byte chars check character length against 1496 * COMPOUNDMIN. */ 1497 if (has_mbyte 1498 && slang->sl_compminlen > 0 1499 && mb_charlen_len(mip->mi_word + mip->mi_compoff, 1500 wlen - mip->mi_compoff) < slang->sl_compminlen) 1501 continue; 1502 #endif 1503 1504 /* Limit the number of compound words to COMPOUNDWORDMAX if no 1505 * maximum for syllables is specified. */ 1506 if (!word_ends && mip->mi_complen + mip->mi_compextra + 2 1507 > slang->sl_compmax 1508 && slang->sl_compsylmax == MAXWLEN) 1509 continue; 1510 1511 /* Don't allow compounding on a side where an affix was added, 1512 * unless COMPOUNDPERMITFLAG was used. */ 1513 if (mip->mi_complen > 0 && (flags & WF_NOCOMPBEF)) 1514 continue; 1515 if (!word_ends && (flags & WF_NOCOMPAFT)) 1516 continue; 1517 1518 /* Quickly check if compounding is possible with this flag. */ 1519 if (!byte_in_str(mip->mi_complen == 0 1520 ? slang->sl_compstartflags 1521 : slang->sl_compallflags, 1522 ((unsigned)flags >> 24))) 1523 continue; 1524 1525 if (mode == FIND_COMPOUND) 1526 { 1527 int capflags; 1528 1529 /* Need to check the caps type of the appended compound 1530 * word. */ 1531 #ifdef FEAT_MBYTE 1532 if (has_mbyte && STRNCMP(ptr, mip->mi_word, 1533 mip->mi_compoff) != 0) 1534 { 1535 /* case folding may have changed the length */ 1536 p = mip->mi_word; 1537 for (s = ptr; s < ptr + mip->mi_compoff; mb_ptr_adv(s)) 1538 mb_ptr_adv(p); 1539 } 1540 else 1541 #endif 1542 p = mip->mi_word + mip->mi_compoff; 1543 capflags = captype(p, mip->mi_word + wlen); 1544 if (capflags == WF_KEEPCAP || (capflags == WF_ALLCAP 1545 && (flags & WF_FIXCAP) != 0)) 1546 continue; 1547 1548 if (capflags != WF_ALLCAP) 1549 { 1550 /* When the character before the word is a word 1551 * character we do not accept a Onecap word. We do 1552 * accept a no-caps word, even when the dictionary 1553 * word specifies ONECAP. */ 1554 mb_ptr_back(mip->mi_word, p); 1555 if (spell_iswordp_nmw(p) 1556 ? capflags == WF_ONECAP 1557 : (flags & WF_ONECAP) != 0 1558 && capflags != WF_ONECAP) 1559 continue; 1560 } 1561 } 1562 1563 /* If the word ends the sequence of compound flags of the 1564 * words must match with one of the COMPOUNDRULE items and 1565 * the number of syllables must not be too large. */ 1566 mip->mi_compflags[mip->mi_complen] = ((unsigned)flags >> 24); 1567 mip->mi_compflags[mip->mi_complen + 1] = NUL; 1568 if (word_ends) 1569 { 1570 char_u fword[MAXWLEN]; 1571 1572 if (slang->sl_compsylmax < MAXWLEN) 1573 { 1574 /* "fword" is only needed for checking syllables. */ 1575 if (ptr == mip->mi_word) 1576 (void)spell_casefold(ptr, wlen, fword, MAXWLEN); 1577 else 1578 vim_strncpy(fword, ptr, endlen[endidxcnt]); 1579 } 1580 if (!can_compound(slang, fword, mip->mi_compflags)) 1581 continue; 1582 } 1583 } 1584 1585 /* Check NEEDCOMPOUND: can't use word without compounding. */ 1586 else if (flags & WF_NEEDCOMP) 1587 continue; 1588 1589 nobreak_result = SP_OK; 1590 1591 if (!word_ends) 1592 { 1593 int save_result = mip->mi_result; 1594 char_u *save_end = mip->mi_end; 1595 langp_T *save_lp = mip->mi_lp; 1596 int lpi; 1597 1598 /* Check that a valid word follows. If there is one and we 1599 * are compounding, it will set "mi_result", thus we are 1600 * always finished here. For NOBREAK we only check that a 1601 * valid word follows. 1602 * Recursive! */ 1603 if (slang->sl_nobreak) 1604 mip->mi_result = SP_BAD; 1605 1606 /* Find following word in case-folded tree. */ 1607 mip->mi_compoff = endlen[endidxcnt]; 1608 #ifdef FEAT_MBYTE 1609 if (has_mbyte && mode == FIND_KEEPWORD) 1610 { 1611 /* Compute byte length in case-folded word from "wlen": 1612 * byte length in keep-case word. Length may change when 1613 * folding case. This can be slow, take a shortcut when 1614 * the case-folded word is equal to the keep-case word. */ 1615 p = mip->mi_fword; 1616 if (STRNCMP(ptr, p, wlen) != 0) 1617 { 1618 for (s = ptr; s < ptr + wlen; mb_ptr_adv(s)) 1619 mb_ptr_adv(p); 1620 mip->mi_compoff = (int)(p - mip->mi_fword); 1621 } 1622 } 1623 #endif 1624 c = mip->mi_compoff; 1625 ++mip->mi_complen; 1626 if (flags & WF_COMPROOT) 1627 ++mip->mi_compextra; 1628 1629 /* For NOBREAK we need to try all NOBREAK languages, at least 1630 * to find the ".add" file(s). */ 1631 for (lpi = 0; lpi < mip->mi_buf->b_langp.ga_len; ++lpi) 1632 { 1633 if (slang->sl_nobreak) 1634 { 1635 mip->mi_lp = LANGP_ENTRY(mip->mi_buf->b_langp, lpi); 1636 if (mip->mi_lp->lp_slang->sl_fidxs == NULL 1637 || !mip->mi_lp->lp_slang->sl_nobreak) 1638 continue; 1639 } 1640 1641 find_word(mip, FIND_COMPOUND); 1642 1643 /* When NOBREAK any word that matches is OK. Otherwise we 1644 * need to find the longest match, thus try with keep-case 1645 * and prefix too. */ 1646 if (!slang->sl_nobreak || mip->mi_result == SP_BAD) 1647 { 1648 /* Find following word in keep-case tree. */ 1649 mip->mi_compoff = wlen; 1650 find_word(mip, FIND_KEEPCOMPOUND); 1651 1652 #if 0 /* Disabled, a prefix must not appear halfway a compound word, 1653 unless the COMPOUNDPERMITFLAG is used and then it can't be a 1654 postponed prefix. */ 1655 if (!slang->sl_nobreak || mip->mi_result == SP_BAD) 1656 { 1657 /* Check for following word with prefix. */ 1658 mip->mi_compoff = c; 1659 find_prefix(mip, FIND_COMPOUND); 1660 } 1661 #endif 1662 } 1663 1664 if (!slang->sl_nobreak) 1665 break; 1666 } 1667 --mip->mi_complen; 1668 if (flags & WF_COMPROOT) 1669 --mip->mi_compextra; 1670 mip->mi_lp = save_lp; 1671 1672 if (slang->sl_nobreak) 1673 { 1674 nobreak_result = mip->mi_result; 1675 mip->mi_result = save_result; 1676 mip->mi_end = save_end; 1677 } 1678 else 1679 { 1680 if (mip->mi_result == SP_OK) 1681 break; 1682 continue; 1683 } 1684 } 1685 1686 if (flags & WF_BANNED) 1687 res = SP_BANNED; 1688 else if (flags & WF_REGION) 1689 { 1690 /* Check region. */ 1691 if ((mip->mi_lp->lp_region & (flags >> 16)) != 0) 1692 res = SP_OK; 1693 else 1694 res = SP_LOCAL; 1695 } 1696 else if (flags & WF_RARE) 1697 res = SP_RARE; 1698 else 1699 res = SP_OK; 1700 1701 /* Always use the longest match and the best result. For NOBREAK 1702 * we separately keep the longest match without a following good 1703 * word as a fall-back. */ 1704 if (nobreak_result == SP_BAD) 1705 { 1706 if (mip->mi_result2 > res) 1707 { 1708 mip->mi_result2 = res; 1709 mip->mi_end2 = mip->mi_word + wlen; 1710 } 1711 else if (mip->mi_result2 == res 1712 && mip->mi_end2 < mip->mi_word + wlen) 1713 mip->mi_end2 = mip->mi_word + wlen; 1714 } 1715 else if (mip->mi_result > res) 1716 { 1717 mip->mi_result = res; 1718 mip->mi_end = mip->mi_word + wlen; 1719 } 1720 else if (mip->mi_result == res && mip->mi_end < mip->mi_word + wlen) 1721 mip->mi_end = mip->mi_word + wlen; 1722 1723 if (mip->mi_result == SP_OK) 1724 break; 1725 } 1726 1727 if (mip->mi_result == SP_OK) 1728 break; 1729 } 1730 } 1731 1732 /* 1733 * Return TRUE if "flags" is a valid sequence of compound flags and "word" 1734 * does not have too many syllables. 1735 */ 1736 static int 1737 can_compound(slang, word, flags) 1738 slang_T *slang; 1739 char_u *word; 1740 char_u *flags; 1741 { 1742 regmatch_T regmatch; 1743 #ifdef FEAT_MBYTE 1744 char_u uflags[MAXWLEN * 2]; 1745 int i; 1746 #endif 1747 char_u *p; 1748 1749 if (slang->sl_compprog == NULL) 1750 return FALSE; 1751 #ifdef FEAT_MBYTE 1752 if (enc_utf8) 1753 { 1754 /* Need to convert the single byte flags to utf8 characters. */ 1755 p = uflags; 1756 for (i = 0; flags[i] != NUL; ++i) 1757 p += mb_char2bytes(flags[i], p); 1758 *p = NUL; 1759 p = uflags; 1760 } 1761 else 1762 #endif 1763 p = flags; 1764 regmatch.regprog = slang->sl_compprog; 1765 regmatch.rm_ic = FALSE; 1766 if (!vim_regexec(®match, p, 0)) 1767 return FALSE; 1768 1769 /* Count the number of syllables. This may be slow, do it last. If there 1770 * are too many syllables AND the number of compound words is above 1771 * COMPOUNDWORDMAX then compounding is not allowed. */ 1772 if (slang->sl_compsylmax < MAXWLEN 1773 && count_syllables(slang, word) > slang->sl_compsylmax) 1774 return (int)STRLEN(flags) < slang->sl_compmax; 1775 return TRUE; 1776 } 1777 1778 /* 1779 * Return non-zero if the prefix indicated by "arridx" matches with the prefix 1780 * ID in "flags" for the word "word". 1781 * The WF_RAREPFX flag is included in the return value for a rare prefix. 1782 */ 1783 static int 1784 valid_word_prefix(totprefcnt, arridx, flags, word, slang, cond_req) 1785 int totprefcnt; /* nr of prefix IDs */ 1786 int arridx; /* idx in sl_pidxs[] */ 1787 int flags; 1788 char_u *word; 1789 slang_T *slang; 1790 int cond_req; /* only use prefixes with a condition */ 1791 { 1792 int prefcnt; 1793 int pidx; 1794 regprog_T *rp; 1795 regmatch_T regmatch; 1796 int prefid; 1797 1798 prefid = (unsigned)flags >> 24; 1799 for (prefcnt = totprefcnt - 1; prefcnt >= 0; --prefcnt) 1800 { 1801 pidx = slang->sl_pidxs[arridx + prefcnt]; 1802 1803 /* Check the prefix ID. */ 1804 if (prefid != (pidx & 0xff)) 1805 continue; 1806 1807 /* Check if the prefix doesn't combine and the word already has a 1808 * suffix. */ 1809 if ((flags & WF_HAS_AFF) && (pidx & WF_PFX_NC)) 1810 continue; 1811 1812 /* Check the condition, if there is one. The condition index is 1813 * stored in the two bytes above the prefix ID byte. */ 1814 rp = slang->sl_prefprog[((unsigned)pidx >> 8) & 0xffff]; 1815 if (rp != NULL) 1816 { 1817 regmatch.regprog = rp; 1818 regmatch.rm_ic = FALSE; 1819 if (!vim_regexec(®match, word, 0)) 1820 continue; 1821 } 1822 else if (cond_req) 1823 continue; 1824 1825 /* It's a match! Return the WF_ flags. */ 1826 return pidx; 1827 } 1828 return 0; 1829 } 1830 1831 /* 1832 * Check if the word at "mip->mi_word" has a matching prefix. 1833 * If it does, then check the following word. 1834 * 1835 * If "mode" is "FIND_COMPOUND" then do the same after another word, find a 1836 * prefix in a compound word. 1837 * 1838 * For a match mip->mi_result is updated. 1839 */ 1840 static void 1841 find_prefix(mip, mode) 1842 matchinf_T *mip; 1843 int mode; 1844 { 1845 idx_T arridx = 0; 1846 int len; 1847 int wlen = 0; 1848 int flen; 1849 int c; 1850 char_u *ptr; 1851 idx_T lo, hi, m; 1852 slang_T *slang = mip->mi_lp->lp_slang; 1853 char_u *byts; 1854 idx_T *idxs; 1855 1856 byts = slang->sl_pbyts; 1857 if (byts == NULL) 1858 return; /* array is empty */ 1859 1860 /* We use the case-folded word here, since prefixes are always 1861 * case-folded. */ 1862 ptr = mip->mi_fword; 1863 flen = mip->mi_fwordlen; /* available case-folded bytes */ 1864 if (mode == FIND_COMPOUND) 1865 { 1866 /* Skip over the previously found word(s). */ 1867 ptr += mip->mi_compoff; 1868 flen -= mip->mi_compoff; 1869 } 1870 idxs = slang->sl_pidxs; 1871 1872 /* 1873 * Repeat advancing in the tree until: 1874 * - there is a byte that doesn't match, 1875 * - we reach the end of the tree, 1876 * - or we reach the end of the line. 1877 */ 1878 for (;;) 1879 { 1880 if (flen == 0 && *mip->mi_fend != NUL) 1881 flen = fold_more(mip); 1882 1883 len = byts[arridx++]; 1884 1885 /* If the first possible byte is a zero the prefix could end here. 1886 * Check if the following word matches and supports the prefix. */ 1887 if (byts[arridx] == 0) 1888 { 1889 /* There can be several prefixes with different conditions. We 1890 * try them all, since we don't know which one will give the 1891 * longest match. The word is the same each time, pass the list 1892 * of possible prefixes to find_word(). */ 1893 mip->mi_prefarridx = arridx; 1894 mip->mi_prefcnt = len; 1895 while (len > 0 && byts[arridx] == 0) 1896 { 1897 ++arridx; 1898 --len; 1899 } 1900 mip->mi_prefcnt -= len; 1901 1902 /* Find the word that comes after the prefix. */ 1903 mip->mi_prefixlen = wlen; 1904 if (mode == FIND_COMPOUND) 1905 /* Skip over the previously found word(s). */ 1906 mip->mi_prefixlen += mip->mi_compoff; 1907 1908 #ifdef FEAT_MBYTE 1909 if (has_mbyte) 1910 { 1911 /* Case-folded length may differ from original length. */ 1912 mip->mi_cprefixlen = nofold_len(mip->mi_fword, 1913 mip->mi_prefixlen, mip->mi_word); 1914 } 1915 else 1916 mip->mi_cprefixlen = mip->mi_prefixlen; 1917 #endif 1918 find_word(mip, FIND_PREFIX); 1919 1920 1921 if (len == 0) 1922 break; /* no children, word must end here */ 1923 } 1924 1925 /* Stop looking at end of the line. */ 1926 if (ptr[wlen] == NUL) 1927 break; 1928 1929 /* Perform a binary search in the list of accepted bytes. */ 1930 c = ptr[wlen]; 1931 lo = arridx; 1932 hi = arridx + len - 1; 1933 while (lo < hi) 1934 { 1935 m = (lo + hi) / 2; 1936 if (byts[m] > c) 1937 hi = m - 1; 1938 else if (byts[m] < c) 1939 lo = m + 1; 1940 else 1941 { 1942 lo = hi = m; 1943 break; 1944 } 1945 } 1946 1947 /* Stop if there is no matching byte. */ 1948 if (hi < lo || byts[lo] != c) 1949 break; 1950 1951 /* Continue at the child (if there is one). */ 1952 arridx = idxs[lo]; 1953 ++wlen; 1954 --flen; 1955 } 1956 } 1957 1958 /* 1959 * Need to fold at least one more character. Do until next non-word character 1960 * for efficiency. Include the non-word character too. 1961 * Return the length of the folded chars in bytes. 1962 */ 1963 static int 1964 fold_more(mip) 1965 matchinf_T *mip; 1966 { 1967 int flen; 1968 char_u *p; 1969 1970 p = mip->mi_fend; 1971 do 1972 { 1973 mb_ptr_adv(mip->mi_fend); 1974 } while (*mip->mi_fend != NUL && spell_iswordp(mip->mi_fend, mip->mi_buf)); 1975 1976 /* Include the non-word character so that we can check for the word end. */ 1977 if (*mip->mi_fend != NUL) 1978 mb_ptr_adv(mip->mi_fend); 1979 1980 (void)spell_casefold(p, (int)(mip->mi_fend - p), 1981 mip->mi_fword + mip->mi_fwordlen, 1982 MAXWLEN - mip->mi_fwordlen); 1983 flen = (int)STRLEN(mip->mi_fword + mip->mi_fwordlen); 1984 mip->mi_fwordlen += flen; 1985 return flen; 1986 } 1987 1988 /* 1989 * Check case flags for a word. Return TRUE if the word has the requested 1990 * case. 1991 */ 1992 static int 1993 spell_valid_case(wordflags, treeflags) 1994 int wordflags; /* flags for the checked word. */ 1995 int treeflags; /* flags for the word in the spell tree */ 1996 { 1997 return ((wordflags == WF_ALLCAP && (treeflags & WF_FIXCAP) == 0) 1998 || ((treeflags & (WF_ALLCAP | WF_KEEPCAP)) == 0 1999 && ((treeflags & WF_ONECAP) == 0 2000 || (wordflags & WF_ONECAP) != 0))); 2001 } 2002 2003 /* 2004 * Return TRUE if spell checking is not enabled. 2005 */ 2006 static int 2007 no_spell_checking(wp) 2008 win_T *wp; 2009 { 2010 if (!wp->w_p_spell || *wp->w_buffer->b_p_spl == NUL 2011 || wp->w_buffer->b_langp.ga_len == 0) 2012 { 2013 EMSG(_("E756: Spell checking is not enabled")); 2014 return TRUE; 2015 } 2016 return FALSE; 2017 } 2018 2019 /* 2020 * Move to next spell error. 2021 * "curline" is FALSE for "[s", "]s", "[S" and "]S". 2022 * "curline" is TRUE to find word under/after cursor in the same line. 2023 * For Insert mode completion "dir" is BACKWARD and "curline" is TRUE: move 2024 * to after badly spelled word before the cursor. 2025 * Return 0 if not found, length of the badly spelled word otherwise. 2026 */ 2027 int 2028 spell_move_to(wp, dir, allwords, curline, attrp) 2029 win_T *wp; 2030 int dir; /* FORWARD or BACKWARD */ 2031 int allwords; /* TRUE for "[s"/"]s", FALSE for "[S"/"]S" */ 2032 int curline; 2033 hlf_T *attrp; /* return: attributes of bad word or NULL 2034 (only when "dir" is FORWARD) */ 2035 { 2036 linenr_T lnum; 2037 pos_T found_pos; 2038 int found_len = 0; 2039 char_u *line; 2040 char_u *p; 2041 char_u *endp; 2042 hlf_T attr; 2043 int len; 2044 # ifdef FEAT_SYN_HL 2045 int has_syntax = syntax_present(wp->w_buffer); 2046 # endif 2047 int col; 2048 int can_spell; 2049 char_u *buf = NULL; 2050 int buflen = 0; 2051 int skip = 0; 2052 int capcol = -1; 2053 int found_one = FALSE; 2054 int wrapped = FALSE; 2055 2056 if (no_spell_checking(wp)) 2057 return 0; 2058 2059 /* 2060 * Start looking for bad word at the start of the line, because we can't 2061 * start halfway a word, we don't know where it starts or ends. 2062 * 2063 * When searching backwards, we continue in the line to find the last 2064 * bad word (in the cursor line: before the cursor). 2065 * 2066 * We concatenate the start of the next line, so that wrapped words work 2067 * (e.g. "et<line-break>cetera"). Doesn't work when searching backwards 2068 * though... 2069 */ 2070 lnum = wp->w_cursor.lnum; 2071 clearpos(&found_pos); 2072 2073 while (!got_int) 2074 { 2075 line = ml_get_buf(wp->w_buffer, lnum, FALSE); 2076 2077 len = (int)STRLEN(line); 2078 if (buflen < len + MAXWLEN + 2) 2079 { 2080 vim_free(buf); 2081 buflen = len + MAXWLEN + 2; 2082 buf = alloc(buflen); 2083 if (buf == NULL) 2084 break; 2085 } 2086 2087 /* In first line check first word for Capital. */ 2088 if (lnum == 1) 2089 capcol = 0; 2090 2091 /* For checking first word with a capital skip white space. */ 2092 if (capcol == 0) 2093 capcol = (int)(skipwhite(line) - line); 2094 else if (curline && wp == curwin) 2095 { 2096 /* For spellbadword(): check if first word needs a capital. */ 2097 col = (int)(skipwhite(line) - line); 2098 if (check_need_cap(lnum, col)) 2099 capcol = col; 2100 2101 /* Need to get the line again, may have looked at the previous 2102 * one. */ 2103 line = ml_get_buf(wp->w_buffer, lnum, FALSE); 2104 } 2105 2106 /* Copy the line into "buf" and append the start of the next line if 2107 * possible. */ 2108 STRCPY(buf, line); 2109 if (lnum < wp->w_buffer->b_ml.ml_line_count) 2110 spell_cat_line(buf + STRLEN(buf), 2111 ml_get_buf(wp->w_buffer, lnum + 1, FALSE), MAXWLEN); 2112 2113 p = buf + skip; 2114 endp = buf + len; 2115 while (p < endp) 2116 { 2117 /* When searching backward don't search after the cursor. Unless 2118 * we wrapped around the end of the buffer. */ 2119 if (dir == BACKWARD 2120 && lnum == wp->w_cursor.lnum 2121 && !wrapped 2122 && (colnr_T)(p - buf) >= wp->w_cursor.col) 2123 break; 2124 2125 /* start of word */ 2126 attr = HLF_COUNT; 2127 len = spell_check(wp, p, &attr, &capcol, FALSE); 2128 2129 if (attr != HLF_COUNT) 2130 { 2131 /* We found a bad word. Check the attribute. */ 2132 if (allwords || attr == HLF_SPB) 2133 { 2134 /* When searching forward only accept a bad word after 2135 * the cursor. */ 2136 if (dir == BACKWARD 2137 || lnum != wp->w_cursor.lnum 2138 || (lnum == wp->w_cursor.lnum 2139 && (wrapped 2140 || (colnr_T)(curline ? p - buf + len 2141 : p - buf) 2142 > wp->w_cursor.col))) 2143 { 2144 # ifdef FEAT_SYN_HL 2145 if (has_syntax) 2146 { 2147 col = (int)(p - buf); 2148 (void)syn_get_id(wp, lnum, (colnr_T)col, 2149 FALSE, &can_spell); 2150 if (!can_spell) 2151 attr = HLF_COUNT; 2152 } 2153 else 2154 #endif 2155 can_spell = TRUE; 2156 2157 if (can_spell) 2158 { 2159 found_one = TRUE; 2160 found_pos.lnum = lnum; 2161 found_pos.col = (int)(p - buf); 2162 #ifdef FEAT_VIRTUALEDIT 2163 found_pos.coladd = 0; 2164 #endif 2165 if (dir == FORWARD) 2166 { 2167 /* No need to search further. */ 2168 wp->w_cursor = found_pos; 2169 vim_free(buf); 2170 if (attrp != NULL) 2171 *attrp = attr; 2172 return len; 2173 } 2174 else if (curline) 2175 /* Insert mode completion: put cursor after 2176 * the bad word. */ 2177 found_pos.col += len; 2178 found_len = len; 2179 } 2180 } 2181 else 2182 found_one = TRUE; 2183 } 2184 } 2185 2186 /* advance to character after the word */ 2187 p += len; 2188 capcol -= len; 2189 } 2190 2191 if (dir == BACKWARD && found_pos.lnum != 0) 2192 { 2193 /* Use the last match in the line (before the cursor). */ 2194 wp->w_cursor = found_pos; 2195 vim_free(buf); 2196 return found_len; 2197 } 2198 2199 if (curline) 2200 break; /* only check cursor line */ 2201 2202 /* Advance to next line. */ 2203 if (dir == BACKWARD) 2204 { 2205 /* If we are back at the starting line and searched it again there 2206 * is no match, give up. */ 2207 if (lnum == wp->w_cursor.lnum && wrapped) 2208 break; 2209 2210 if (lnum > 1) 2211 --lnum; 2212 else if (!p_ws) 2213 break; /* at first line and 'nowrapscan' */ 2214 else 2215 { 2216 /* Wrap around to the end of the buffer. May search the 2217 * starting line again and accept the last match. */ 2218 lnum = wp->w_buffer->b_ml.ml_line_count; 2219 wrapped = TRUE; 2220 if (!shortmess(SHM_SEARCH)) 2221 give_warning((char_u *)_(top_bot_msg), TRUE); 2222 } 2223 capcol = -1; 2224 } 2225 else 2226 { 2227 if (lnum < wp->w_buffer->b_ml.ml_line_count) 2228 ++lnum; 2229 else if (!p_ws) 2230 break; /* at first line and 'nowrapscan' */ 2231 else 2232 { 2233 /* Wrap around to the start of the buffer. May search the 2234 * starting line again and accept the first match. */ 2235 lnum = 1; 2236 wrapped = TRUE; 2237 if (!shortmess(SHM_SEARCH)) 2238 give_warning((char_u *)_(bot_top_msg), TRUE); 2239 } 2240 2241 /* If we are back at the starting line and there is no match then 2242 * give up. */ 2243 if (lnum == wp->w_cursor.lnum && !found_one) 2244 break; 2245 2246 /* Skip the characters at the start of the next line that were 2247 * included in a match crossing line boundaries. */ 2248 if (attr == HLF_COUNT) 2249 skip = (int)(p - endp); 2250 else 2251 skip = 0; 2252 2253 /* Capcol skips over the inserted space. */ 2254 --capcol; 2255 2256 /* But after empty line check first word in next line */ 2257 if (*skipwhite(line) == NUL) 2258 capcol = 0; 2259 } 2260 2261 line_breakcheck(); 2262 } 2263 2264 vim_free(buf); 2265 return 0; 2266 } 2267 2268 /* 2269 * For spell checking: concatenate the start of the following line "line" into 2270 * "buf", blanking-out special characters. Copy less then "maxlen" bytes. 2271 */ 2272 void 2273 spell_cat_line(buf, line, maxlen) 2274 char_u *buf; 2275 char_u *line; 2276 int maxlen; 2277 { 2278 char_u *p; 2279 int n; 2280 2281 p = skipwhite(line); 2282 while (vim_strchr((char_u *)"*#/\"\t", *p) != NULL) 2283 p = skipwhite(p + 1); 2284 2285 if (*p != NUL) 2286 { 2287 *buf = ' '; 2288 vim_strncpy(buf + 1, line, maxlen - 2); 2289 n = (int)(p - line); 2290 if (n >= maxlen) 2291 n = maxlen - 1; 2292 vim_memset(buf + 1, ' ', n); 2293 } 2294 } 2295 2296 /* 2297 * Structure used for the cookie argument of do_in_runtimepath(). 2298 */ 2299 typedef struct spelload_S 2300 { 2301 char_u sl_lang[MAXWLEN + 1]; /* language name */ 2302 slang_T *sl_slang; /* resulting slang_T struct */ 2303 int sl_nobreak; /* NOBREAK language found */ 2304 } spelload_T; 2305 2306 /* 2307 * Load word list(s) for "lang" from Vim spell file(s). 2308 * "lang" must be the language without the region: e.g., "en". 2309 */ 2310 static void 2311 spell_load_lang(lang) 2312 char_u *lang; 2313 { 2314 char_u fname_enc[85]; 2315 int r; 2316 spelload_T sl; 2317 #ifdef FEAT_AUTOCMD 2318 int round; 2319 #endif 2320 2321 /* Copy the language name to pass it to spell_load_cb() as a cookie. 2322 * It's truncated when an error is detected. */ 2323 STRCPY(sl.sl_lang, lang); 2324 sl.sl_slang = NULL; 2325 sl.sl_nobreak = FALSE; 2326 2327 #ifdef FEAT_AUTOCMD 2328 /* We may retry when no spell file is found for the language, an 2329 * autocommand may load it then. */ 2330 for (round = 1; round <= 2; ++round) 2331 #endif 2332 { 2333 /* 2334 * Find the first spell file for "lang" in 'runtimepath' and load it. 2335 */ 2336 vim_snprintf((char *)fname_enc, sizeof(fname_enc) - 5, 2337 "spell/%s.%s.spl", lang, spell_enc()); 2338 r = do_in_runtimepath(fname_enc, FALSE, spell_load_cb, &sl); 2339 2340 if (r == FAIL && *sl.sl_lang != NUL) 2341 { 2342 /* Try loading the ASCII version. */ 2343 vim_snprintf((char *)fname_enc, sizeof(fname_enc) - 5, 2344 "spell/%s.ascii.spl", lang); 2345 r = do_in_runtimepath(fname_enc, FALSE, spell_load_cb, &sl); 2346 2347 #ifdef FEAT_AUTOCMD 2348 if (r == FAIL && *sl.sl_lang != NUL && round == 1 2349 && apply_autocmds(EVENT_SPELLFILEMISSING, lang, 2350 curbuf->b_fname, FALSE, curbuf)) 2351 continue; 2352 break; 2353 #endif 2354 } 2355 #ifdef FEAT_AUTOCMD 2356 break; 2357 #endif 2358 } 2359 2360 if (r == FAIL) 2361 { 2362 smsg((char_u *)_("Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\""), 2363 lang, spell_enc(), lang); 2364 } 2365 else if (sl.sl_slang != NULL) 2366 { 2367 /* At least one file was loaded, now load ALL the additions. */ 2368 STRCPY(fname_enc + STRLEN(fname_enc) - 3, "add.spl"); 2369 do_in_runtimepath(fname_enc, TRUE, spell_load_cb, &sl); 2370 } 2371 } 2372 2373 /* 2374 * Return the encoding used for spell checking: Use 'encoding', except that we 2375 * use "latin1" for "latin9". And limit to 60 characters (just in case). 2376 */ 2377 static char_u * 2378 spell_enc() 2379 { 2380 2381 #ifdef FEAT_MBYTE 2382 if (STRLEN(p_enc) < 60 && STRCMP(p_enc, "iso-8859-15") != 0) 2383 return p_enc; 2384 #endif 2385 return (char_u *)"latin1"; 2386 } 2387 2388 /* 2389 * Get the name of the .spl file for the internal wordlist into 2390 * "fname[MAXPATHL]". 2391 */ 2392 static void 2393 int_wordlist_spl(fname) 2394 char_u *fname; 2395 { 2396 vim_snprintf((char *)fname, MAXPATHL, "%s.%s.spl", 2397 int_wordlist, spell_enc()); 2398 } 2399 2400 /* 2401 * Allocate a new slang_T for language "lang". "lang" can be NULL. 2402 * Caller must fill "sl_next". 2403 */ 2404 static slang_T * 2405 slang_alloc(lang) 2406 char_u *lang; 2407 { 2408 slang_T *lp; 2409 2410 lp = (slang_T *)alloc_clear(sizeof(slang_T)); 2411 if (lp != NULL) 2412 { 2413 if (lang != NULL) 2414 lp->sl_name = vim_strsave(lang); 2415 ga_init2(&lp->sl_rep, sizeof(fromto_T), 10); 2416 ga_init2(&lp->sl_repsal, sizeof(fromto_T), 10); 2417 lp->sl_compmax = MAXWLEN; 2418 lp->sl_compsylmax = MAXWLEN; 2419 hash_init(&lp->sl_wordcount); 2420 } 2421 2422 return lp; 2423 } 2424 2425 /* 2426 * Free the contents of an slang_T and the structure itself. 2427 */ 2428 static void 2429 slang_free(lp) 2430 slang_T *lp; 2431 { 2432 vim_free(lp->sl_name); 2433 vim_free(lp->sl_fname); 2434 slang_clear(lp); 2435 vim_free(lp); 2436 } 2437 2438 /* 2439 * Clear an slang_T so that the file can be reloaded. 2440 */ 2441 static void 2442 slang_clear(lp) 2443 slang_T *lp; 2444 { 2445 garray_T *gap; 2446 fromto_T *ftp; 2447 salitem_T *smp; 2448 int i; 2449 int round; 2450 2451 vim_free(lp->sl_fbyts); 2452 lp->sl_fbyts = NULL; 2453 vim_free(lp->sl_kbyts); 2454 lp->sl_kbyts = NULL; 2455 vim_free(lp->sl_pbyts); 2456 lp->sl_pbyts = NULL; 2457 2458 vim_free(lp->sl_fidxs); 2459 lp->sl_fidxs = NULL; 2460 vim_free(lp->sl_kidxs); 2461 lp->sl_kidxs = NULL; 2462 vim_free(lp->sl_pidxs); 2463 lp->sl_pidxs = NULL; 2464 2465 for (round = 1; round <= 2; ++round) 2466 { 2467 gap = round == 1 ? &lp->sl_rep : &lp->sl_repsal; 2468 while (gap->ga_len > 0) 2469 { 2470 ftp = &((fromto_T *)gap->ga_data)[--gap->ga_len]; 2471 vim_free(ftp->ft_from); 2472 vim_free(ftp->ft_to); 2473 } 2474 ga_clear(gap); 2475 } 2476 2477 gap = &lp->sl_sal; 2478 if (lp->sl_sofo) 2479 { 2480 /* "ga_len" is set to 1 without adding an item for latin1 */ 2481 if (gap->ga_data != NULL) 2482 /* SOFOFROM and SOFOTO items: free lists of wide characters. */ 2483 for (i = 0; i < gap->ga_len; ++i) 2484 vim_free(((int **)gap->ga_data)[i]); 2485 } 2486 else 2487 /* SAL items: free salitem_T items */ 2488 while (gap->ga_len > 0) 2489 { 2490 smp = &((salitem_T *)gap->ga_data)[--gap->ga_len]; 2491 vim_free(smp->sm_lead); 2492 /* Don't free sm_oneof and sm_rules, they point into sm_lead. */ 2493 vim_free(smp->sm_to); 2494 #ifdef FEAT_MBYTE 2495 vim_free(smp->sm_lead_w); 2496 vim_free(smp->sm_oneof_w); 2497 vim_free(smp->sm_to_w); 2498 #endif 2499 } 2500 ga_clear(gap); 2501 2502 for (i = 0; i < lp->sl_prefixcnt; ++i) 2503 vim_free(lp->sl_prefprog[i]); 2504 lp->sl_prefixcnt = 0; 2505 vim_free(lp->sl_prefprog); 2506 lp->sl_prefprog = NULL; 2507 2508 vim_free(lp->sl_info); 2509 lp->sl_info = NULL; 2510 2511 vim_free(lp->sl_midword); 2512 lp->sl_midword = NULL; 2513 2514 vim_free(lp->sl_compprog); 2515 vim_free(lp->sl_compstartflags); 2516 vim_free(lp->sl_compallflags); 2517 lp->sl_compprog = NULL; 2518 lp->sl_compstartflags = NULL; 2519 lp->sl_compallflags = NULL; 2520 2521 vim_free(lp->sl_syllable); 2522 lp->sl_syllable = NULL; 2523 ga_clear(&lp->sl_syl_items); 2524 2525 ga_clear_strings(&lp->sl_comppat); 2526 2527 hash_clear_all(&lp->sl_wordcount, WC_KEY_OFF); 2528 hash_init(&lp->sl_wordcount); 2529 2530 #ifdef FEAT_MBYTE 2531 hash_clear_all(&lp->sl_map_hash, 0); 2532 #endif 2533 2534 /* Clear info from .sug file. */ 2535 slang_clear_sug(lp); 2536 2537 lp->sl_compmax = MAXWLEN; 2538 lp->sl_compminlen = 0; 2539 lp->sl_compsylmax = MAXWLEN; 2540 lp->sl_regions[0] = NUL; 2541 } 2542 2543 /* 2544 * Clear the info from the .sug file in "lp". 2545 */ 2546 static void 2547 slang_clear_sug(lp) 2548 slang_T *lp; 2549 { 2550 vim_free(lp->sl_sbyts); 2551 lp->sl_sbyts = NULL; 2552 vim_free(lp->sl_sidxs); 2553 lp->sl_sidxs = NULL; 2554 close_spellbuf(lp->sl_sugbuf); 2555 lp->sl_sugbuf = NULL; 2556 lp->sl_sugloaded = FALSE; 2557 lp->sl_sugtime = 0; 2558 } 2559 2560 /* 2561 * Load one spell file and store the info into a slang_T. 2562 * Invoked through do_in_runtimepath(). 2563 */ 2564 static void 2565 spell_load_cb(fname, cookie) 2566 char_u *fname; 2567 void *cookie; 2568 { 2569 spelload_T *slp = (spelload_T *)cookie; 2570 slang_T *slang; 2571 2572 slang = spell_load_file(fname, slp->sl_lang, NULL, FALSE); 2573 if (slang != NULL) 2574 { 2575 /* When a previously loaded file has NOBREAK also use it for the 2576 * ".add" files. */ 2577 if (slp->sl_nobreak && slang->sl_add) 2578 slang->sl_nobreak = TRUE; 2579 else if (slang->sl_nobreak) 2580 slp->sl_nobreak = TRUE; 2581 2582 slp->sl_slang = slang; 2583 } 2584 } 2585 2586 /* 2587 * Load one spell file and store the info into a slang_T. 2588 * 2589 * This is invoked in three ways: 2590 * - From spell_load_cb() to load a spell file for the first time. "lang" is 2591 * the language name, "old_lp" is NULL. Will allocate an slang_T. 2592 * - To reload a spell file that was changed. "lang" is NULL and "old_lp" 2593 * points to the existing slang_T. 2594 * - Just after writing a .spl file; it's read back to produce the .sug file. 2595 * "old_lp" is NULL and "lang" is NULL. Will allocate an slang_T. 2596 * 2597 * Returns the slang_T the spell file was loaded into. NULL for error. 2598 */ 2599 static slang_T * 2600 spell_load_file(fname, lang, old_lp, silent) 2601 char_u *fname; 2602 char_u *lang; 2603 slang_T *old_lp; 2604 int silent; /* no error if file doesn't exist */ 2605 { 2606 FILE *fd; 2607 char_u buf[VIMSPELLMAGICL]; 2608 char_u *p; 2609 int i; 2610 int n; 2611 int len; 2612 char_u *save_sourcing_name = sourcing_name; 2613 linenr_T save_sourcing_lnum = sourcing_lnum; 2614 slang_T *lp = NULL; 2615 int c = 0; 2616 int res; 2617 2618 fd = mch_fopen((char *)fname, "r"); 2619 if (fd == NULL) 2620 { 2621 if (!silent) 2622 EMSG2(_(e_notopen), fname); 2623 else if (p_verbose > 2) 2624 { 2625 verbose_enter(); 2626 smsg((char_u *)e_notopen, fname); 2627 verbose_leave(); 2628 } 2629 goto endFAIL; 2630 } 2631 if (p_verbose > 2) 2632 { 2633 verbose_enter(); 2634 smsg((char_u *)_("Reading spell file \"%s\""), fname); 2635 verbose_leave(); 2636 } 2637 2638 if (old_lp == NULL) 2639 { 2640 lp = slang_alloc(lang); 2641 if (lp == NULL) 2642 goto endFAIL; 2643 2644 /* Remember the file name, used to reload the file when it's updated. */ 2645 lp->sl_fname = vim_strsave(fname); 2646 if (lp->sl_fname == NULL) 2647 goto endFAIL; 2648 2649 /* Check for .add.spl. */ 2650 lp->sl_add = strstr((char *)gettail(fname), ".add.") != NULL; 2651 } 2652 else 2653 lp = old_lp; 2654 2655 /* Set sourcing_name, so that error messages mention the file name. */ 2656 sourcing_name = fname; 2657 sourcing_lnum = 0; 2658 2659 /* 2660 * <HEADER>: <fileID> 2661 */ 2662 for (i = 0; i < VIMSPELLMAGICL; ++i) 2663 buf[i] = getc(fd); /* <fileID> */ 2664 if (STRNCMP(buf, VIMSPELLMAGIC, VIMSPELLMAGICL) != 0) 2665 { 2666 EMSG(_("E757: This does not look like a spell file")); 2667 goto endFAIL; 2668 } 2669 c = getc(fd); /* <versionnr> */ 2670 if (c < VIMSPELLVERSION) 2671 { 2672 EMSG(_("E771: Old spell file, needs to be updated")); 2673 goto endFAIL; 2674 } 2675 else if (c > VIMSPELLVERSION) 2676 { 2677 EMSG(_("E772: Spell file is for newer version of Vim")); 2678 goto endFAIL; 2679 } 2680 2681 2682 /* 2683 * <SECTIONS>: <section> ... <sectionend> 2684 * <section>: <sectionID> <sectionflags> <sectionlen> (section contents) 2685 */ 2686 for (;;) 2687 { 2688 n = getc(fd); /* <sectionID> or <sectionend> */ 2689 if (n == SN_END) 2690 break; 2691 c = getc(fd); /* <sectionflags> */ 2692 len = get4c(fd); /* <sectionlen> */ 2693 if (len < 0) 2694 goto truncerr; 2695 2696 res = 0; 2697 switch (n) 2698 { 2699 case SN_INFO: 2700 lp->sl_info = read_string(fd, len); /* <infotext> */ 2701 if (lp->sl_info == NULL) 2702 goto endFAIL; 2703 break; 2704 2705 case SN_REGION: 2706 res = read_region_section(fd, lp, len); 2707 break; 2708 2709 case SN_CHARFLAGS: 2710 res = read_charflags_section(fd); 2711 break; 2712 2713 case SN_MIDWORD: 2714 lp->sl_midword = read_string(fd, len); /* <midword> */ 2715 if (lp->sl_midword == NULL) 2716 goto endFAIL; 2717 break; 2718 2719 case SN_PREFCOND: 2720 res = read_prefcond_section(fd, lp); 2721 break; 2722 2723 case SN_REP: 2724 res = read_rep_section(fd, &lp->sl_rep, lp->sl_rep_first); 2725 break; 2726 2727 case SN_REPSAL: 2728 res = read_rep_section(fd, &lp->sl_repsal, lp->sl_repsal_first); 2729 break; 2730 2731 case SN_SAL: 2732 res = read_sal_section(fd, lp); 2733 break; 2734 2735 case SN_SOFO: 2736 res = read_sofo_section(fd, lp); 2737 break; 2738 2739 case SN_MAP: 2740 p = read_string(fd, len); /* <mapstr> */ 2741 if (p == NULL) 2742 goto endFAIL; 2743 set_map_str(lp, p); 2744 vim_free(p); 2745 break; 2746 2747 case SN_WORDS: 2748 res = read_words_section(fd, lp, len); 2749 break; 2750 2751 case SN_SUGFILE: 2752 lp->sl_sugtime = get8c(fd); /* <timestamp> */ 2753 break; 2754 2755 case SN_NOSPLITSUGS: 2756 lp->sl_nosplitsugs = TRUE; /* <timestamp> */ 2757 break; 2758 2759 case SN_COMPOUND: 2760 res = read_compound(fd, lp, len); 2761 break; 2762 2763 case SN_NOBREAK: 2764 lp->sl_nobreak = TRUE; 2765 break; 2766 2767 case SN_SYLLABLE: 2768 lp->sl_syllable = read_string(fd, len); /* <syllable> */ 2769 if (lp->sl_syllable == NULL) 2770 goto endFAIL; 2771 if (init_syl_tab(lp) == FAIL) 2772 goto endFAIL; 2773 break; 2774 2775 default: 2776 /* Unsupported section. When it's required give an error 2777 * message. When it's not required skip the contents. */ 2778 if (c & SNF_REQUIRED) 2779 { 2780 EMSG(_("E770: Unsupported section in spell file")); 2781 goto endFAIL; 2782 } 2783 while (--len >= 0) 2784 if (getc(fd) < 0) 2785 goto truncerr; 2786 break; 2787 } 2788 someerror: 2789 if (res == SP_FORMERROR) 2790 { 2791 EMSG(_(e_format)); 2792 goto endFAIL; 2793 } 2794 if (res == SP_TRUNCERROR) 2795 { 2796 truncerr: 2797 EMSG(_(e_spell_trunc)); 2798 goto endFAIL; 2799 } 2800 if (res == SP_OTHERERROR) 2801 goto endFAIL; 2802 } 2803 2804 /* <LWORDTREE> */ 2805 res = spell_read_tree(fd, &lp->sl_fbyts, &lp->sl_fidxs, FALSE, 0); 2806 if (res != 0) 2807 goto someerror; 2808 2809 /* <KWORDTREE> */ 2810 res = spell_read_tree(fd, &lp->sl_kbyts, &lp->sl_kidxs, FALSE, 0); 2811 if (res != 0) 2812 goto someerror; 2813 2814 /* <PREFIXTREE> */ 2815 res = spell_read_tree(fd, &lp->sl_pbyts, &lp->sl_pidxs, TRUE, 2816 lp->sl_prefixcnt); 2817 if (res != 0) 2818 goto someerror; 2819 2820 /* For a new file link it in the list of spell files. */ 2821 if (old_lp == NULL && lang != NULL) 2822 { 2823 lp->sl_next = first_lang; 2824 first_lang = lp; 2825 } 2826 2827 goto endOK; 2828 2829 endFAIL: 2830 if (lang != NULL) 2831 /* truncating the name signals the error to spell_load_lang() */ 2832 *lang = NUL; 2833 if (lp != NULL && old_lp == NULL) 2834 slang_free(lp); 2835 lp = NULL; 2836 2837 endOK: 2838 if (fd != NULL) 2839 fclose(fd); 2840 sourcing_name = save_sourcing_name; 2841 sourcing_lnum = save_sourcing_lnum; 2842 2843 return lp; 2844 } 2845 2846 /* 2847 * Read 2 bytes from "fd" and turn them into an int, MSB first. 2848 */ 2849 static int 2850 get2c(fd) 2851 FILE *fd; 2852 { 2853 long n; 2854 2855 n = getc(fd); 2856 n = (n << 8) + getc(fd); 2857 return n; 2858 } 2859 2860 /* 2861 * Read 3 bytes from "fd" and turn them into an int, MSB first. 2862 */ 2863 static int 2864 get3c(fd) 2865 FILE *fd; 2866 { 2867 long n; 2868 2869 n = getc(fd); 2870 n = (n << 8) + getc(fd); 2871 n = (n << 8) + getc(fd); 2872 return n; 2873 } 2874 2875 /* 2876 * Read 4 bytes from "fd" and turn them into an int, MSB first. 2877 */ 2878 static int 2879 get4c(fd) 2880 FILE *fd; 2881 { 2882 long n; 2883 2884 n = getc(fd); 2885 n = (n << 8) + getc(fd); 2886 n = (n << 8) + getc(fd); 2887 n = (n << 8) + getc(fd); 2888 return n; 2889 } 2890 2891 /* 2892 * Read 8 bytes from "fd" and turn them into a time_t, MSB first. 2893 */ 2894 static time_t 2895 get8c(fd) 2896 FILE *fd; 2897 { 2898 time_t n = 0; 2899 int i; 2900 2901 for (i = 0; i < 8; ++i) 2902 n = (n << 8) + getc(fd); 2903 return n; 2904 } 2905 2906 /* 2907 * Read a length field from "fd" in "cnt_bytes" bytes. 2908 * Allocate memory, read the string into it and add a NUL at the end. 2909 * Returns NULL when the count is zero. 2910 * Sets "*cntp" to SP_*ERROR when there is an error, length of the result 2911 * otherwise. 2912 */ 2913 static char_u * 2914 read_cnt_string(fd, cnt_bytes, cntp) 2915 FILE *fd; 2916 int cnt_bytes; 2917 int *cntp; 2918 { 2919 int cnt = 0; 2920 int i; 2921 char_u *str; 2922 2923 /* read the length bytes, MSB first */ 2924 for (i = 0; i < cnt_bytes; ++i) 2925 cnt = (cnt << 8) + getc(fd); 2926 if (cnt < 0) 2927 { 2928 *cntp = SP_TRUNCERROR; 2929 return NULL; 2930 } 2931 *cntp = cnt; 2932 if (cnt == 0) 2933 return NULL; /* nothing to read, return NULL */ 2934 2935 str = read_string(fd, cnt); 2936 if (str == NULL) 2937 *cntp = SP_OTHERERROR; 2938 return str; 2939 } 2940 2941 /* 2942 * Read a string of length "cnt" from "fd" into allocated memory. 2943 * Returns NULL when out of memory. 2944 */ 2945 static char_u * 2946 read_string(fd, cnt) 2947 FILE *fd; 2948 int cnt; 2949 { 2950 char_u *str; 2951 int i; 2952 2953 /* allocate memory */ 2954 str = alloc((unsigned)cnt + 1); 2955 if (str != NULL) 2956 { 2957 /* Read the string. Doesn't check for truncated file. */ 2958 for (i = 0; i < cnt; ++i) 2959 str[i] = getc(fd); 2960 str[i] = NUL; 2961 } 2962 return str; 2963 } 2964 2965 /* 2966 * Read SN_REGION: <regionname> ... 2967 * Return SP_*ERROR flags. 2968 */ 2969 static int 2970 read_region_section(fd, lp, len) 2971 FILE *fd; 2972 slang_T *lp; 2973 int len; 2974 { 2975 int i; 2976 2977 if (len > 16) 2978 return SP_FORMERROR; 2979 for (i = 0; i < len; ++i) 2980 lp->sl_regions[i] = getc(fd); /* <regionname> */ 2981 lp->sl_regions[len] = NUL; 2982 return 0; 2983 } 2984 2985 /* 2986 * Read SN_CHARFLAGS section: <charflagslen> <charflags> 2987 * <folcharslen> <folchars> 2988 * Return SP_*ERROR flags. 2989 */ 2990 static int 2991 read_charflags_section(fd) 2992 FILE *fd; 2993 { 2994 char_u *flags; 2995 char_u *fol; 2996 int flagslen, follen; 2997 2998 /* <charflagslen> <charflags> */ 2999 flags = read_cnt_string(fd, 1, &flagslen); 3000 if (flagslen < 0) 3001 return flagslen; 3002 3003 /* <folcharslen> <folchars> */ 3004 fol = read_cnt_string(fd, 2, &follen); 3005 if (follen < 0) 3006 { 3007 vim_free(flags); 3008 return follen; 3009 } 3010 3011 /* Set the word-char flags and fill SPELL_ISUPPER() table. */ 3012 if (flags != NULL && fol != NULL) 3013 set_spell_charflags(flags, flagslen, fol); 3014 3015 vim_free(flags); 3016 vim_free(fol); 3017 3018 /* When <charflagslen> is zero then <fcharlen> must also be zero. */ 3019 if ((flags == NULL) != (fol == NULL)) 3020 return SP_FORMERROR; 3021 return 0; 3022 } 3023 3024 /* 3025 * Read SN_PREFCOND section. 3026 * Return SP_*ERROR flags. 3027 */ 3028 static int 3029 read_prefcond_section(fd, lp) 3030 FILE *fd; 3031 slang_T *lp; 3032 { 3033 int cnt; 3034 int i; 3035 int n; 3036 char_u *p; 3037 char_u buf[MAXWLEN + 1]; 3038 3039 /* <prefcondcnt> <prefcond> ... */ 3040 cnt = get2c(fd); /* <prefcondcnt> */ 3041 if (cnt <= 0) 3042 return SP_FORMERROR; 3043 3044 lp->sl_prefprog = (regprog_T **)alloc_clear( 3045 (unsigned)sizeof(regprog_T *) * cnt); 3046 if (lp->sl_prefprog == NULL) 3047 return SP_OTHERERROR; 3048 lp->sl_prefixcnt = cnt; 3049 3050 for (i = 0; i < cnt; ++i) 3051 { 3052 /* <prefcond> : <condlen> <condstr> */ 3053 n = getc(fd); /* <condlen> */ 3054 if (n < 0 || n >= MAXWLEN) 3055 return SP_FORMERROR; 3056 3057 /* When <condlen> is zero we have an empty condition. Otherwise 3058 * compile the regexp program used to check for the condition. */ 3059 if (n > 0) 3060 { 3061 buf[0] = '^'; /* always match at one position only */ 3062 p = buf + 1; 3063 while (n-- > 0) 3064 *p++ = getc(fd); /* <condstr> */ 3065 *p = NUL; 3066 lp->sl_prefprog[i] = vim_regcomp(buf, RE_MAGIC + RE_STRING); 3067 } 3068 } 3069 return 0; 3070 } 3071 3072 /* 3073 * Read REP or REPSAL items section from "fd": <repcount> <rep> ... 3074 * Return SP_*ERROR flags. 3075 */ 3076 static int 3077 read_rep_section(fd, gap, first) 3078 FILE *fd; 3079 garray_T *gap; 3080 short *first; 3081 { 3082 int cnt; 3083 fromto_T *ftp; 3084 int i; 3085 3086 cnt = get2c(fd); /* <repcount> */ 3087 if (cnt < 0) 3088 return SP_TRUNCERROR; 3089 3090 if (ga_grow(gap, cnt) == FAIL) 3091 return SP_OTHERERROR; 3092 3093 /* <rep> : <repfromlen> <repfrom> <reptolen> <repto> */ 3094 for (; gap->ga_len < cnt; ++gap->ga_len) 3095 { 3096 ftp = &((fromto_T *)gap->ga_data)[gap->ga_len]; 3097 ftp->ft_from = read_cnt_string(fd, 1, &i); 3098 if (i < 0) 3099 return i; 3100 if (i == 0) 3101 return SP_FORMERROR; 3102 ftp->ft_to = read_cnt_string(fd, 1, &i); 3103 if (i <= 0) 3104 { 3105 vim_free(ftp->ft_from); 3106 if (i < 0) 3107 return i; 3108 return SP_FORMERROR; 3109 } 3110 } 3111 3112 /* Fill the first-index table. */ 3113 for (i = 0; i < 256; ++i) 3114 first[i] = -1; 3115 for (i = 0; i < gap->ga_len; ++i) 3116 { 3117 ftp = &((fromto_T *)gap->ga_data)[i]; 3118 if (first[*ftp->ft_from] == -1) 3119 first[*ftp->ft_from] = i; 3120 } 3121 return 0; 3122 } 3123 3124 /* 3125 * Read SN_SAL section: <salflags> <salcount> <sal> ... 3126 * Return SP_*ERROR flags. 3127 */ 3128 static int 3129 read_sal_section(fd, slang) 3130 FILE *fd; 3131 slang_T *slang; 3132 { 3133 int i; 3134 int cnt; 3135 garray_T *gap; 3136 salitem_T *smp; 3137 int ccnt; 3138 char_u *p; 3139 int c = NUL; 3140 3141 slang->sl_sofo = FALSE; 3142 3143 i = getc(fd); /* <salflags> */ 3144 if (i & SAL_F0LLOWUP) 3145 slang->sl_followup = TRUE; 3146 if (i & SAL_COLLAPSE) 3147 slang->sl_collapse = TRUE; 3148 if (i & SAL_REM_ACCENTS) 3149 slang->sl_rem_accents = TRUE; 3150 3151 cnt = get2c(fd); /* <salcount> */ 3152 if (cnt < 0) 3153 return SP_TRUNCERROR; 3154 3155 gap = &slang->sl_sal; 3156 ga_init2(gap, sizeof(salitem_T), 10); 3157 if (ga_grow(gap, cnt + 1) == FAIL) 3158 return SP_OTHERERROR; 3159 3160 /* <sal> : <salfromlen> <salfrom> <saltolen> <salto> */ 3161 for (; gap->ga_len < cnt; ++gap->ga_len) 3162 { 3163 smp = &((salitem_T *)gap->ga_data)[gap->ga_len]; 3164 ccnt = getc(fd); /* <salfromlen> */ 3165 if (ccnt < 0) 3166 return SP_TRUNCERROR; 3167 if ((p = alloc(ccnt + 2)) == NULL) 3168 return SP_OTHERERROR; 3169 smp->sm_lead = p; 3170 3171 /* Read up to the first special char into sm_lead. */ 3172 for (i = 0; i < ccnt; ++i) 3173 { 3174 c = getc(fd); /* <salfrom> */ 3175 if (vim_strchr((char_u *)"0123456789(-<^$", c) != NULL) 3176 break; 3177 *p++ = c; 3178 } 3179 smp->sm_leadlen = (int)(p - smp->sm_lead); 3180 *p++ = NUL; 3181 3182 /* Put (abc) chars in sm_oneof, if any. */ 3183 if (c == '(') 3184 { 3185 smp->sm_oneof = p; 3186 for (++i; i < ccnt; ++i) 3187 { 3188 c = getc(fd); /* <salfrom> */ 3189 if (c == ')') 3190 break; 3191 *p++ = c; 3192 } 3193 *p++ = NUL; 3194 if (++i < ccnt) 3195 c = getc(fd); 3196 } 3197 else 3198 smp->sm_oneof = NULL; 3199 3200 /* Any following chars go in sm_rules. */ 3201 smp->sm_rules = p; 3202 if (i < ccnt) 3203 /* store the char we got while checking for end of sm_lead */ 3204 *p++ = c; 3205 for (++i; i < ccnt; ++i) 3206 *p++ = getc(fd); /* <salfrom> */ 3207 *p++ = NUL; 3208 3209 /* <saltolen> <salto> */ 3210 smp->sm_to = read_cnt_string(fd, 1, &ccnt); 3211 if (ccnt < 0) 3212 { 3213 vim_free(smp->sm_lead); 3214 return ccnt; 3215 } 3216 3217 #ifdef FEAT_MBYTE 3218 if (has_mbyte) 3219 { 3220 /* convert the multi-byte strings to wide char strings */ 3221 smp->sm_lead_w = mb_str2wide(smp->sm_lead); 3222 smp->sm_leadlen = mb_charlen(smp->sm_lead); 3223 if (smp->sm_oneof == NULL) 3224 smp->sm_oneof_w = NULL; 3225 else 3226 smp->sm_oneof_w = mb_str2wide(smp->sm_oneof); 3227 if (smp->sm_to == NULL) 3228 smp->sm_to_w = NULL; 3229 else 3230 smp->sm_to_w = mb_str2wide(smp->sm_to); 3231 if (smp->sm_lead_w == NULL 3232 || (smp->sm_oneof_w == NULL && smp->sm_oneof != NULL) 3233 || (smp->sm_to_w == NULL && smp->sm_to != NULL)) 3234 { 3235 vim_free(smp->sm_lead); 3236 vim_free(smp->sm_to); 3237 vim_free(smp->sm_lead_w); 3238 vim_free(smp->sm_oneof_w); 3239 vim_free(smp->sm_to_w); 3240 return SP_OTHERERROR; 3241 } 3242 } 3243 #endif 3244 } 3245 3246 if (gap->ga_len > 0) 3247 { 3248 /* Add one extra entry to mark the end with an empty sm_lead. Avoids 3249 * that we need to check the index every time. */ 3250 smp = &((salitem_T *)gap->ga_data)[gap->ga_len]; 3251 if ((p = alloc(1)) == NULL) 3252 return SP_OTHERERROR; 3253 p[0] = NUL; 3254 smp->sm_lead = p; 3255 smp->sm_leadlen = 0; 3256 smp->sm_oneof = NULL; 3257 smp->sm_rules = p; 3258 smp->sm_to = NULL; 3259 #ifdef FEAT_MBYTE 3260 if (has_mbyte) 3261 { 3262 smp->sm_lead_w = mb_str2wide(smp->sm_lead); 3263 smp->sm_leadlen = 0; 3264 smp->sm_oneof_w = NULL; 3265 smp->sm_to_w = NULL; 3266 } 3267 #endif 3268 ++gap->ga_len; 3269 } 3270 3271 /* Fill the first-index table. */ 3272 set_sal_first(slang); 3273 3274 return 0; 3275 } 3276 3277 /* 3278 * Read SN_WORDS: <word> ... 3279 * Return SP_*ERROR flags. 3280 */ 3281 static int 3282 read_words_section(fd, lp, len) 3283 FILE *fd; 3284 slang_T *lp; 3285 int len; 3286 { 3287 int done = 0; 3288 int i; 3289 char_u word[MAXWLEN]; 3290 3291 while (done < len) 3292 { 3293 /* Read one word at a time. */ 3294 for (i = 0; ; ++i) 3295 { 3296 word[i] = getc(fd); 3297 if (word[i] == NUL) 3298 break; 3299 if (i == MAXWLEN - 1) 3300 return SP_FORMERROR; 3301 } 3302 3303 /* Init the count to 10. */ 3304 count_common_word(lp, word, -1, 10); 3305 done += i + 1; 3306 } 3307 return 0; 3308 } 3309 3310 /* 3311 * Add a word to the hashtable of common words. 3312 * If it's already there then the counter is increased. 3313 */ 3314 static void 3315 count_common_word(lp, word, len, count) 3316 slang_T *lp; 3317 char_u *word; 3318 int len; /* word length, -1 for upto NUL */ 3319 int count; /* 1 to count once, 10 to init */ 3320 { 3321 hash_T hash; 3322 hashitem_T *hi; 3323 wordcount_T *wc; 3324 char_u buf[MAXWLEN]; 3325 char_u *p; 3326 3327 if (len == -1) 3328 p = word; 3329 else 3330 { 3331 vim_strncpy(buf, word, len); 3332 p = buf; 3333 } 3334 3335 hash = hash_hash(p); 3336 hi = hash_lookup(&lp->sl_wordcount, p, hash); 3337 if (HASHITEM_EMPTY(hi)) 3338 { 3339 wc = (wordcount_T *)alloc((unsigned)(sizeof(wordcount_T) + STRLEN(p))); 3340 if (wc == NULL) 3341 return; 3342 STRCPY(wc->wc_word, p); 3343 wc->wc_count = count; 3344 hash_add_item(&lp->sl_wordcount, hi, wc->wc_word, hash); 3345 } 3346 else 3347 { 3348 wc = HI2WC(hi); 3349 if ((wc->wc_count += count) < (unsigned)count) /* check for overflow */ 3350 wc->wc_count = MAXWORDCOUNT; 3351 } 3352 } 3353 3354 /* 3355 * Adjust the score of common words. 3356 */ 3357 static int 3358 score_wordcount_adj(slang, score, word, split) 3359 slang_T *slang; 3360 int score; 3361 char_u *word; 3362 int split; /* word was split, less bonus */ 3363 { 3364 hashitem_T *hi; 3365 wordcount_T *wc; 3366 int bonus; 3367 int newscore; 3368 3369 hi = hash_find(&slang->sl_wordcount, word); 3370 if (!HASHITEM_EMPTY(hi)) 3371 { 3372 wc = HI2WC(hi); 3373 if (wc->wc_count < SCORE_THRES2) 3374 bonus = SCORE_COMMON1; 3375 else if (wc->wc_count < SCORE_THRES3) 3376 bonus = SCORE_COMMON2; 3377 else 3378 bonus = SCORE_COMMON3; 3379 if (split) 3380 newscore = score - bonus / 2; 3381 else 3382 newscore = score - bonus; 3383 if (newscore < 0) 3384 return 0; 3385 return newscore; 3386 } 3387 return score; 3388 } 3389 3390 /* 3391 * SN_SOFO: <sofofromlen> <sofofrom> <sofotolen> <sofoto> 3392 * Return SP_*ERROR flags. 3393 */ 3394 static int 3395 read_sofo_section(fd, slang) 3396 FILE *fd; 3397 slang_T *slang; 3398 { 3399 int cnt; 3400 char_u *from, *to; 3401 int res; 3402 3403 slang->sl_sofo = TRUE; 3404 3405 /* <sofofromlen> <sofofrom> */ 3406 from = read_cnt_string(fd, 2, &cnt); 3407 if (cnt < 0) 3408 return cnt; 3409 3410 /* <sofotolen> <sofoto> */ 3411 to = read_cnt_string(fd, 2, &cnt); 3412 if (cnt < 0) 3413 { 3414 vim_free(from); 3415 return cnt; 3416 } 3417 3418 /* Store the info in slang->sl_sal and/or slang->sl_sal_first. */ 3419 if (from != NULL && to != NULL) 3420 res = set_sofo(slang, from, to); 3421 else if (from != NULL || to != NULL) 3422 res = SP_FORMERROR; /* only one of two strings is an error */ 3423 else 3424 res = 0; 3425 3426 vim_free(from); 3427 vim_free(to); 3428 return res; 3429 } 3430 3431 /* 3432 * Read the compound section from the .spl file: 3433 * <compmax> <compminlen> <compsylmax> <compoptions> <compflags> 3434 * Returns SP_*ERROR flags. 3435 */ 3436 static int 3437 read_compound(fd, slang, len) 3438 FILE *fd; 3439 slang_T *slang; 3440 int len; 3441 { 3442 int todo = len; 3443 int c; 3444 int atstart; 3445 char_u *pat; 3446 char_u *pp; 3447 char_u *cp; 3448 char_u *ap; 3449 int cnt; 3450 garray_T *gap; 3451 3452 if (todo < 2) 3453 return SP_FORMERROR; /* need at least two bytes */ 3454 3455 --todo; 3456 c = getc(fd); /* <compmax> */ 3457 if (c < 2) 3458 c = MAXWLEN; 3459 slang->sl_compmax = c; 3460 3461 --todo; 3462 c = getc(fd); /* <compminlen> */ 3463 if (c < 1) 3464 c = 0; 3465 slang->sl_compminlen = c; 3466 3467 --todo; 3468 c = getc(fd); /* <compsylmax> */ 3469 if (c < 1) 3470 c = MAXWLEN; 3471 slang->sl_compsylmax = c; 3472 3473 c = getc(fd); /* <compoptions> */ 3474 if (c != 0) 3475 ungetc(c, fd); /* be backwards compatible with Vim 7.0b */ 3476 else 3477 { 3478 --todo; 3479 c = getc(fd); /* only use the lower byte for now */ 3480 --todo; 3481 slang->sl_compoptions = c; 3482 3483 gap = &slang->sl_comppat; 3484 c = get2c(fd); /* <comppatcount> */ 3485 todo -= 2; 3486 ga_init2(gap, sizeof(char_u *), c); 3487 if (ga_grow(gap, c) == OK) 3488 while (--c >= 0) 3489 { 3490 ((char_u **)(gap->ga_data))[gap->ga_len++] = 3491 read_cnt_string(fd, 1, &cnt); 3492 /* <comppatlen> <comppattext> */ 3493 if (cnt < 0) 3494 return cnt; 3495 todo -= cnt + 1; 3496 } 3497 } 3498 if (todo < 0) 3499 return SP_FORMERROR; 3500 3501 /* Turn the COMPOUNDRULE items into a regexp pattern: 3502 * "a[bc]/a*b+" -> "^\(a[bc]\|a*b\+\)$". 3503 * Inserting backslashes may double the length, "^\(\)$<Nul>" is 7 bytes. 3504 * Conversion to utf-8 may double the size. */ 3505 c = todo * 2 + 7; 3506 #ifdef FEAT_MBYTE 3507 if (enc_utf8) 3508 c += todo * 2; 3509 #endif 3510 pat = alloc((unsigned)c); 3511 if (pat == NULL) 3512 return SP_OTHERERROR; 3513 3514 /* We also need a list of all flags that can appear at the start and one 3515 * for all flags. */ 3516 cp = alloc(todo + 1); 3517 if (cp == NULL) 3518 { 3519 vim_free(pat); 3520 return SP_OTHERERROR; 3521 } 3522 slang->sl_compstartflags = cp; 3523 *cp = NUL; 3524 3525 ap = alloc(todo + 1); 3526 if (ap == NULL) 3527 { 3528 vim_free(pat); 3529 return SP_OTHERERROR; 3530 } 3531 slang->sl_compallflags = ap; 3532 *ap = NUL; 3533 3534 pp = pat; 3535 *pp++ = '^'; 3536 *pp++ = '\\'; 3537 *pp++ = '('; 3538 3539 atstart = 1; 3540 while (todo-- > 0) 3541 { 3542 c = getc(fd); /* <compflags> */ 3543 3544 /* Add all flags to "sl_compallflags". */ 3545 if (vim_strchr((char_u *)"+*[]/", c) == NULL 3546 && !byte_in_str(slang->sl_compallflags, c)) 3547 { 3548 *ap++ = c; 3549 *ap = NUL; 3550 } 3551 3552 if (atstart != 0) 3553 { 3554 /* At start of item: copy flags to "sl_compstartflags". For a 3555 * [abc] item set "atstart" to 2 and copy up to the ']'. */ 3556 if (c == '[') 3557 atstart = 2; 3558 else if (c == ']') 3559 atstart = 0; 3560 else 3561 { 3562 if (!byte_in_str(slang->sl_compstartflags, c)) 3563 { 3564 *cp++ = c; 3565 *cp = NUL; 3566 } 3567 if (atstart == 1) 3568 atstart = 0; 3569 } 3570 } 3571 if (c == '/') /* slash separates two items */ 3572 { 3573 *pp++ = '\\'; 3574 *pp++ = '|'; 3575 atstart = 1; 3576 } 3577 else /* normal char, "[abc]" and '*' are copied as-is */ 3578 { 3579 if (c == '+' || c == '~') 3580 *pp++ = '\\'; /* "a+" becomes "a\+" */ 3581 #ifdef FEAT_MBYTE 3582 if (enc_utf8) 3583 pp += mb_char2bytes(c, pp); 3584 else 3585 #endif 3586 *pp++ = c; 3587 } 3588 } 3589 3590 *pp++ = '\\'; 3591 *pp++ = ')'; 3592 *pp++ = '$'; 3593 *pp = NUL; 3594 3595 slang->sl_compprog = vim_regcomp(pat, RE_MAGIC + RE_STRING + RE_STRICT); 3596 vim_free(pat); 3597 if (slang->sl_compprog == NULL) 3598 return SP_FORMERROR; 3599 3600 return 0; 3601 } 3602 3603 /* 3604 * Return TRUE if byte "n" appears in "str". 3605 * Like strchr() but independent of locale. 3606 */ 3607 static int 3608 byte_in_str(str, n) 3609 char_u *str; 3610 int n; 3611 { 3612 char_u *p; 3613 3614 for (p = str; *p != NUL; ++p) 3615 if (*p == n) 3616 return TRUE; 3617 return FALSE; 3618 } 3619 3620 #define SY_MAXLEN 30 3621 typedef struct syl_item_S 3622 { 3623 char_u sy_chars[SY_MAXLEN]; /* the sequence of chars */ 3624 int sy_len; 3625 } syl_item_T; 3626 3627 /* 3628 * Truncate "slang->sl_syllable" at the first slash and put the following items 3629 * in "slang->sl_syl_items". 3630 */ 3631 static int 3632 init_syl_tab(slang) 3633 slang_T *slang; 3634 { 3635 char_u *p; 3636 char_u *s; 3637 int l; 3638 syl_item_T *syl; 3639 3640 ga_init2(&slang->sl_syl_items, sizeof(syl_item_T), 4); 3641 p = vim_strchr(slang->sl_syllable, '/'); 3642 while (p != NULL) 3643 { 3644 *p++ = NUL; 3645 if (*p == NUL) /* trailing slash */ 3646 break; 3647 s = p; 3648 p = vim_strchr(p, '/'); 3649 if (p == NULL) 3650 l = (int)STRLEN(s); 3651 else 3652 l = (int)(p - s); 3653 if (l >= SY_MAXLEN) 3654 return SP_FORMERROR; 3655 if (ga_grow(&slang->sl_syl_items, 1) == FAIL) 3656 return SP_OTHERERROR; 3657 syl = ((syl_item_T *)slang->sl_syl_items.ga_data) 3658 + slang->sl_syl_items.ga_len++; 3659 vim_strncpy(syl->sy_chars, s, l); 3660 syl->sy_len = l; 3661 } 3662 return OK; 3663 } 3664 3665 /* 3666 * Count the number of syllables in "word". 3667 * When "word" contains spaces the syllables after the last space are counted. 3668 * Returns zero if syllables are not defines. 3669 */ 3670 static int 3671 count_syllables(slang, word) 3672 slang_T *slang; 3673 char_u *word; 3674 { 3675 int cnt = 0; 3676 int skip = FALSE; 3677 char_u *p; 3678 int len; 3679 int i; 3680 syl_item_T *syl; 3681 int c; 3682 3683 if (slang->sl_syllable == NULL) 3684 return 0; 3685 3686 for (p = word; *p != NUL; p += len) 3687 { 3688 /* When running into a space reset counter. */ 3689 if (*p == ' ') 3690 { 3691 len = 1; 3692 cnt = 0; 3693 continue; 3694 } 3695 3696 /* Find longest match of syllable items. */ 3697 len = 0; 3698 for (i = 0; i < slang->sl_syl_items.ga_len; ++i) 3699 { 3700 syl = ((syl_item_T *)slang->sl_syl_items.ga_data) + i; 3701 if (syl->sy_len > len 3702 && STRNCMP(p, syl->sy_chars, syl->sy_len) == 0) 3703 len = syl->sy_len; 3704 } 3705 if (len != 0) /* found a match, count syllable */ 3706 { 3707 ++cnt; 3708 skip = FALSE; 3709 } 3710 else 3711 { 3712 /* No recognized syllable item, at least a syllable char then? */ 3713 #ifdef FEAT_MBYTE 3714 c = mb_ptr2char(p); 3715 len = (*mb_ptr2len)(p); 3716 #else 3717 c = *p; 3718 len = 1; 3719 #endif 3720 if (vim_strchr(slang->sl_syllable, c) == NULL) 3721 skip = FALSE; /* No, search for next syllable */ 3722 else if (!skip) 3723 { 3724 ++cnt; /* Yes, count it */ 3725 skip = TRUE; /* don't count following syllable chars */ 3726 } 3727 } 3728 } 3729 return cnt; 3730 } 3731 3732 /* 3733 * Set the SOFOFROM and SOFOTO items in language "lp". 3734 * Returns SP_*ERROR flags when there is something wrong. 3735 */ 3736 static int 3737 set_sofo(lp, from, to) 3738 slang_T *lp; 3739 char_u *from; 3740 char_u *to; 3741 { 3742 int i; 3743 3744 #ifdef FEAT_MBYTE 3745 garray_T *gap; 3746 char_u *s; 3747 char_u *p; 3748 int c; 3749 int *inp; 3750 3751 if (has_mbyte) 3752 { 3753 /* Use "sl_sal" as an array with 256 pointers to a list of wide 3754 * characters. The index is the low byte of the character. 3755 * The list contains from-to pairs with a terminating NUL. 3756 * sl_sal_first[] is used for latin1 "from" characters. */ 3757 gap = &lp->sl_sal; 3758 ga_init2(gap, sizeof(int *), 1); 3759 if (ga_grow(gap, 256) == FAIL) 3760 return SP_OTHERERROR; 3761 vim_memset(gap->ga_data, 0, sizeof(int *) * 256); 3762 gap->ga_len = 256; 3763 3764 /* First count the number of items for each list. Temporarily use 3765 * sl_sal_first[] for this. */ 3766 for (p = from, s = to; *p != NUL && *s != NUL; ) 3767 { 3768 c = mb_cptr2char_adv(&p); 3769 mb_cptr_adv(s); 3770 if (c >= 256) 3771 ++lp->sl_sal_first[c & 0xff]; 3772 } 3773 if (*p != NUL || *s != NUL) /* lengths differ */ 3774 return SP_FORMERROR; 3775 3776 /* Allocate the lists. */ 3777 for (i = 0; i < 256; ++i) 3778 if (lp->sl_sal_first[i] > 0) 3779 { 3780 p = alloc(sizeof(int) * (lp->sl_sal_first[i] * 2 + 1)); 3781 if (p == NULL) 3782 return SP_OTHERERROR; 3783 ((int **)gap->ga_data)[i] = (int *)p; 3784 *(int *)p = 0; 3785 } 3786 3787 /* Put the characters up to 255 in sl_sal_first[] the rest in a sl_sal 3788 * list. */ 3789 vim_memset(lp->sl_sal_first, 0, sizeof(salfirst_T) * 256); 3790 for (p = from, s = to; *p != NUL && *s != NUL; ) 3791 { 3792 c = mb_cptr2char_adv(&p); 3793 i = mb_cptr2char_adv(&s); 3794 if (c >= 256) 3795 { 3796 /* Append the from-to chars at the end of the list with 3797 * the low byte. */ 3798 inp = ((int **)gap->ga_data)[c & 0xff]; 3799 while (*inp != 0) 3800 ++inp; 3801 *inp++ = c; /* from char */ 3802 *inp++ = i; /* to char */ 3803 *inp++ = NUL; /* NUL at the end */ 3804 } 3805 else 3806 /* mapping byte to char is done in sl_sal_first[] */ 3807 lp->sl_sal_first[c] = i; 3808 } 3809 } 3810 else 3811 #endif 3812 { 3813 /* mapping bytes to bytes is done in sl_sal_first[] */ 3814 if (STRLEN(from) != STRLEN(to)) 3815 return SP_FORMERROR; 3816 3817 for (i = 0; to[i] != NUL; ++i) 3818 lp->sl_sal_first[from[i]] = to[i]; 3819 lp->sl_sal.ga_len = 1; /* indicates we have soundfolding */ 3820 } 3821 3822 return 0; 3823 } 3824 3825 /* 3826 * Fill the first-index table for "lp". 3827 */ 3828 static void 3829 set_sal_first(lp) 3830 slang_T *lp; 3831 { 3832 salfirst_T *sfirst; 3833 int i; 3834 salitem_T *smp; 3835 int c; 3836 garray_T *gap = &lp->sl_sal; 3837 3838 sfirst = lp->sl_sal_first; 3839 for (i = 0; i < 256; ++i) 3840 sfirst[i] = -1; 3841 smp = (salitem_T *)gap->ga_data; 3842 for (i = 0; i < gap->ga_len; ++i) 3843 { 3844 #ifdef FEAT_MBYTE 3845 if (has_mbyte) 3846 /* Use the lowest byte of the first character. For latin1 it's 3847 * the character, for other encodings it should differ for most 3848 * characters. */ 3849 c = *smp[i].sm_lead_w & 0xff; 3850 else 3851 #endif 3852 c = *smp[i].sm_lead; 3853 if (sfirst[c] == -1) 3854 { 3855 sfirst[c] = i; 3856 #ifdef FEAT_MBYTE 3857 if (has_mbyte) 3858 { 3859 int n; 3860 3861 /* Make sure all entries with this byte are following each 3862 * other. Move the ones that are in the wrong position. Do 3863 * keep the same ordering! */ 3864 while (i + 1 < gap->ga_len 3865 && (*smp[i + 1].sm_lead_w & 0xff) == c) 3866 /* Skip over entry with same index byte. */ 3867 ++i; 3868 3869 for (n = 1; i + n < gap->ga_len; ++n) 3870 if ((*smp[i + n].sm_lead_w & 0xff) == c) 3871 { 3872 salitem_T tsal; 3873 3874 /* Move entry with same index byte after the entries 3875 * we already found. */ 3876 ++i; 3877 --n; 3878 tsal = smp[i + n]; 3879 mch_memmove(smp + i + 1, smp + i, 3880 sizeof(salitem_T) * n); 3881 smp[i] = tsal; 3882 } 3883 } 3884 #endif 3885 } 3886 } 3887 } 3888 3889 #ifdef FEAT_MBYTE 3890 /* 3891 * Turn a multi-byte string into a wide character string. 3892 * Return it in allocated memory (NULL for out-of-memory) 3893 */ 3894 static int * 3895 mb_str2wide(s) 3896 char_u *s; 3897 { 3898 int *res; 3899 char_u *p; 3900 int i = 0; 3901 3902 res = (int *)alloc(sizeof(int) * (mb_charlen(s) + 1)); 3903 if (res != NULL) 3904 { 3905 for (p = s; *p != NUL; ) 3906 res[i++] = mb_ptr2char_adv(&p); 3907 res[i] = NUL; 3908 } 3909 return res; 3910 } 3911 #endif 3912 3913 /* 3914 * Read a tree from the .spl or .sug file. 3915 * Allocates the memory and stores pointers in "bytsp" and "idxsp". 3916 * This is skipped when the tree has zero length. 3917 * Returns zero when OK, SP_ value for an error. 3918 */ 3919 static int 3920 spell_read_tree(fd, bytsp, idxsp, prefixtree, prefixcnt) 3921 FILE *fd; 3922 char_u **bytsp; 3923 idx_T **idxsp; 3924 int prefixtree; /* TRUE for the prefix tree */ 3925 int prefixcnt; /* when "prefixtree" is TRUE: prefix count */ 3926 { 3927 int len; 3928 int idx; 3929 char_u *bp; 3930 idx_T *ip; 3931 3932 /* The tree size was computed when writing the file, so that we can 3933 * allocate it as one long block. <nodecount> */ 3934 len = get4c(fd); 3935 if (len < 0) 3936 return SP_TRUNCERROR; 3937 if (len > 0) 3938 { 3939 /* Allocate the byte array. */ 3940 bp = lalloc((long_u)len, TRUE); 3941 if (bp == NULL) 3942 return SP_OTHERERROR; 3943 *bytsp = bp; 3944 3945 /* Allocate the index array. */ 3946 ip = (idx_T *)lalloc_clear((long_u)(len * sizeof(int)), TRUE); 3947 if (ip == NULL) 3948 return SP_OTHERERROR; 3949 *idxsp = ip; 3950 3951 /* Recursively read the tree and store it in the array. */ 3952 idx = read_tree_node(fd, bp, ip, len, 0, prefixtree, prefixcnt); 3953 if (idx < 0) 3954 return idx; 3955 } 3956 return 0; 3957 } 3958 3959 /* 3960 * Read one row of siblings from the spell file and store it in the byte array 3961 * "byts" and index array "idxs". Recursively read the children. 3962 * 3963 * NOTE: The code here must match put_node()! 3964 * 3965 * Returns the index (>= 0) following the siblings. 3966 * Returns SP_TRUNCERROR if the file is shorter than expected. 3967 * Returns SP_FORMERROR if there is a format error. 3968 */ 3969 static idx_T 3970 read_tree_node(fd, byts, idxs, maxidx, startidx, prefixtree, maxprefcondnr) 3971 FILE *fd; 3972 char_u *byts; 3973 idx_T *idxs; 3974 int maxidx; /* size of arrays */ 3975 idx_T startidx; /* current index in "byts" and "idxs" */ 3976 int prefixtree; /* TRUE for reading PREFIXTREE */ 3977 int maxprefcondnr; /* maximum for <prefcondnr> */ 3978 { 3979 int len; 3980 int i; 3981 int n; 3982 idx_T idx = startidx; 3983 int c; 3984 int c2; 3985 #define SHARED_MASK 0x8000000 3986 3987 len = getc(fd); /* <siblingcount> */ 3988 if (len <= 0) 3989 return SP_TRUNCERROR; 3990 3991 if (startidx + len >= maxidx) 3992 return SP_FORMERROR; 3993 byts[idx++] = len; 3994 3995 /* Read the byte values, flag/region bytes and shared indexes. */ 3996 for (i = 1; i <= len; ++i) 3997 { 3998 c = getc(fd); /* <byte> */ 3999 if (c < 0) 4000 return SP_TRUNCERROR; 4001 if (c <= BY_SPECIAL) 4002 { 4003 if (c == BY_NOFLAGS && !prefixtree) 4004 { 4005 /* No flags, all regions. */ 4006 idxs[idx] = 0; 4007 c = 0; 4008 } 4009 else if (c != BY_INDEX) 4010 { 4011 if (prefixtree) 4012 { 4013 /* Read the optional pflags byte, the prefix ID and the 4014 * condition nr. In idxs[] store the prefix ID in the low 4015 * byte, the condition index shifted up 8 bits, the flags 4016 * shifted up 24 bits. */ 4017 if (c == BY_FLAGS) 4018 c = getc(fd) << 24; /* <pflags> */ 4019 else 4020 c = 0; 4021 4022 c |= getc(fd); /* <affixID> */ 4023 4024 n = get2c(fd); /* <prefcondnr> */ 4025 if (n >= maxprefcondnr) 4026 return SP_FORMERROR; 4027 c |= (n << 8); 4028 } 4029 else /* c must be BY_FLAGS or BY_FLAGS2 */ 4030 { 4031 /* Read flags and optional region and prefix ID. In 4032 * idxs[] the flags go in the low two bytes, region above 4033 * that and prefix ID above the region. */ 4034 c2 = c; 4035 c = getc(fd); /* <flags> */ 4036 if (c2 == BY_FLAGS2) 4037 c = (getc(fd) << 8) + c; /* <flags2> */ 4038 if (c & WF_REGION) 4039 c = (getc(fd) << 16) + c; /* <region> */ 4040 if (c & WF_AFX) 4041 c = (getc(fd) << 24) + c; /* <affixID> */ 4042 } 4043 4044 idxs[idx] = c; 4045 c = 0; 4046 } 4047 else /* c == BY_INDEX */ 4048 { 4049 /* <nodeidx> */ 4050 n = get3c(fd); 4051 if (n < 0 || n >= maxidx) 4052 return SP_FORMERROR; 4053 idxs[idx] = n + SHARED_MASK; 4054 c = getc(fd); /* <xbyte> */ 4055 } 4056 } 4057 byts[idx++] = c; 4058 } 4059 4060 /* Recursively read the children for non-shared siblings. 4061 * Skip the end-of-word ones (zero byte value) and the shared ones (and 4062 * remove SHARED_MASK) */ 4063 for (i = 1; i <= len; ++i) 4064 if (byts[startidx + i] != 0) 4065 { 4066 if (idxs[startidx + i] & SHARED_MASK) 4067 idxs[startidx + i] &= ~SHARED_MASK; 4068 else 4069 { 4070 idxs[startidx + i] = idx; 4071 idx = read_tree_node(fd, byts, idxs, maxidx, idx, 4072 prefixtree, maxprefcondnr); 4073 if (idx < 0) 4074 break; 4075 } 4076 } 4077 4078 return idx; 4079 } 4080 4081 /* 4082 * Parse 'spelllang' and set buf->b_langp accordingly. 4083 * Returns NULL if it's OK, an error message otherwise. 4084 */ 4085 char_u * 4086 did_set_spelllang(buf) 4087 buf_T *buf; 4088 { 4089 garray_T ga; 4090 char_u *splp; 4091 char_u *region; 4092 char_u region_cp[3]; 4093 int filename; 4094 int region_mask; 4095 slang_T *slang; 4096 int c; 4097 char_u lang[MAXWLEN + 1]; 4098 char_u spf_name[MAXPATHL]; 4099 int len; 4100 char_u *p; 4101 int round; 4102 char_u *spf; 4103 char_u *use_region = NULL; 4104 int dont_use_region = FALSE; 4105 int nobreak = FALSE; 4106 int i, j; 4107 langp_T *lp, *lp2; 4108 static int recursive = FALSE; 4109 char_u *ret_msg = NULL; 4110 char_u *spl_copy; 4111 4112 /* We don't want to do this recursively. May happen when a language is 4113 * not available and the SpellFileMissing autocommand opens a new buffer 4114 * in which 'spell' is set. */ 4115 if (recursive) 4116 return NULL; 4117 recursive = TRUE; 4118 4119 ga_init2(&ga, sizeof(langp_T), 2); 4120 clear_midword(buf); 4121 4122 /* Make a copy of 'spellang', the SpellFileMissing autocommands may change 4123 * it under our fingers. */ 4124 spl_copy = vim_strsave(buf->b_p_spl); 4125 if (spl_copy == NULL) 4126 goto theend; 4127 4128 /* loop over comma separated language names. */ 4129 for (splp = spl_copy; *splp != NUL; ) 4130 { 4131 /* Get one language name. */ 4132 copy_option_part(&splp, lang, MAXWLEN, ","); 4133 4134 region = NULL; 4135 len = (int)STRLEN(lang); 4136 4137 /* If the name ends in ".spl" use it as the name of the spell file. 4138 * If there is a region name let "region" point to it and remove it 4139 * from the name. */ 4140 if (len > 4 && fnamecmp(lang + len - 4, ".spl") == 0) 4141 { 4142 filename = TRUE; 4143 4144 /* Locate a region and remove it from the file name. */ 4145 p = vim_strchr(gettail(lang), '_'); 4146 if (p != NULL && ASCII_ISALPHA(p[1]) && ASCII_ISALPHA(p[2]) 4147 && !ASCII_ISALPHA(p[3])) 4148 { 4149 vim_strncpy(region_cp, p + 1, 2); 4150 mch_memmove(p, p + 3, len - (p - lang) - 2); 4151 len -= 3; 4152 region = region_cp; 4153 } 4154 else 4155 dont_use_region = TRUE; 4156 4157 /* Check if we loaded this language before. */ 4158 for (slang = first_lang; slang != NULL; slang = slang->sl_next) 4159 if (fullpathcmp(lang, slang->sl_fname, FALSE) == FPC_SAME) 4160 break; 4161 } 4162 else 4163 { 4164 filename = FALSE; 4165 if (len > 3 && lang[len - 3] == '_') 4166 { 4167 region = lang + len - 2; 4168 len -= 3; 4169 lang[len] = NUL; 4170 } 4171 else 4172 dont_use_region = TRUE; 4173 4174 /* Check if we loaded this language before. */ 4175 for (slang = first_lang; slang != NULL; slang = slang->sl_next) 4176 if (STRICMP(lang, slang->sl_name) == 0) 4177 break; 4178 } 4179 4180 if (region != NULL) 4181 { 4182 /* If the region differs from what was used before then don't 4183 * use it for 'spellfile'. */ 4184 if (use_region != NULL && STRCMP(region, use_region) != 0) 4185 dont_use_region = TRUE; 4186 use_region = region; 4187 } 4188 4189 /* If not found try loading the language now. */ 4190 if (slang == NULL) 4191 { 4192 if (filename) 4193 (void)spell_load_file(lang, lang, NULL, FALSE); 4194 else 4195 { 4196 spell_load_lang(lang); 4197 #ifdef FEAT_AUTOCMD 4198 /* SpellFileMissing autocommands may do anything, including 4199 * destroying the buffer we are using... */ 4200 if (!buf_valid(buf)) 4201 { 4202 ret_msg = (char_u *)"E797: SpellFileMissing autocommand deleted buffer"; 4203 goto theend; 4204 } 4205 #endif 4206 } 4207 } 4208 4209 /* 4210 * Loop over the languages, there can be several files for "lang". 4211 */ 4212 for (slang = first_lang; slang != NULL; slang = slang->sl_next) 4213 if (filename ? fullpathcmp(lang, slang->sl_fname, FALSE) == FPC_SAME 4214 : STRICMP(lang, slang->sl_name) == 0) 4215 { 4216 region_mask = REGION_ALL; 4217 if (!filename && region != NULL) 4218 { 4219 /* find region in sl_regions */ 4220 c = find_region(slang->sl_regions, region); 4221 if (c == REGION_ALL) 4222 { 4223 if (slang->sl_add) 4224 { 4225 if (*slang->sl_regions != NUL) 4226 /* This addition file is for other regions. */ 4227 region_mask = 0; 4228 } 4229 else 4230 /* This is probably an error. Give a warning and 4231 * accept the words anyway. */ 4232 smsg((char_u *) 4233 _("Warning: region %s not supported"), 4234 region); 4235 } 4236 else 4237 region_mask = 1 << c; 4238 } 4239 4240 if (region_mask != 0) 4241 { 4242 if (ga_grow(&ga, 1) == FAIL) 4243 { 4244 ga_clear(&ga); 4245 ret_msg = e_outofmem; 4246 goto theend; 4247 } 4248 LANGP_ENTRY(ga, ga.ga_len)->lp_slang = slang; 4249 LANGP_ENTRY(ga, ga.ga_len)->lp_region = region_mask; 4250 ++ga.ga_len; 4251 use_midword(slang, buf); 4252 if (slang->sl_nobreak) 4253 nobreak = TRUE; 4254 } 4255 } 4256 } 4257 4258 /* round 0: load int_wordlist, if possible. 4259 * round 1: load first name in 'spellfile'. 4260 * round 2: load second name in 'spellfile. 4261 * etc. */ 4262 spf = buf->b_p_spf; 4263 for (round = 0; round == 0 || *spf != NUL; ++round) 4264 { 4265 if (round == 0) 4266 { 4267 /* Internal wordlist, if there is one. */ 4268 if (int_wordlist == NULL) 4269 continue; 4270 int_wordlist_spl(spf_name); 4271 } 4272 else 4273 { 4274 /* One entry in 'spellfile'. */ 4275 copy_option_part(&spf, spf_name, MAXPATHL - 5, ","); 4276 STRCAT(spf_name, ".spl"); 4277 4278 /* If it was already found above then skip it. */ 4279 for (c = 0; c < ga.ga_len; ++c) 4280 { 4281 p = LANGP_ENTRY(ga, c)->lp_slang->sl_fname; 4282 if (p != NULL && fullpathcmp(spf_name, p, FALSE) == FPC_SAME) 4283 break; 4284 } 4285 if (c < ga.ga_len) 4286 continue; 4287 } 4288 4289 /* Check if it was loaded already. */ 4290 for (slang = first_lang; slang != NULL; slang = slang->sl_next) 4291 if (fullpathcmp(spf_name, slang->sl_fname, FALSE) == FPC_SAME) 4292 break; 4293 if (slang == NULL) 4294 { 4295 /* Not loaded, try loading it now. The language name includes the 4296 * region name, the region is ignored otherwise. for int_wordlist 4297 * use an arbitrary name. */ 4298 if (round == 0) 4299 STRCPY(lang, "internal wordlist"); 4300 else 4301 { 4302 vim_strncpy(lang, gettail(spf_name), MAXWLEN); 4303 p = vim_strchr(lang, '.'); 4304 if (p != NULL) 4305 *p = NUL; /* truncate at ".encoding.add" */ 4306 } 4307 slang = spell_load_file(spf_name, lang, NULL, TRUE); 4308 4309 /* If one of the languages has NOBREAK we assume the addition 4310 * files also have this. */ 4311 if (slang != NULL && nobreak) 4312 slang->sl_nobreak = TRUE; 4313 } 4314 if (slang != NULL && ga_grow(&ga, 1) == OK) 4315 { 4316 region_mask = REGION_ALL; 4317 if (use_region != NULL && !dont_use_region) 4318 { 4319 /* find region in sl_regions */ 4320 c = find_region(slang->sl_regions, use_region); 4321 if (c != REGION_ALL) 4322 region_mask = 1 << c; 4323 else if (*slang->sl_regions != NUL) 4324 /* This spell file is for other regions. */ 4325 region_mask = 0; 4326 } 4327 4328 if (region_mask != 0) 4329 { 4330 LANGP_ENTRY(ga, ga.ga_len)->lp_slang = slang; 4331 LANGP_ENTRY(ga, ga.ga_len)->lp_sallang = NULL; 4332 LANGP_ENTRY(ga, ga.ga_len)->lp_replang = NULL; 4333 LANGP_ENTRY(ga, ga.ga_len)->lp_region = region_mask; 4334 ++ga.ga_len; 4335 use_midword(slang, buf); 4336 } 4337 } 4338 } 4339 4340 /* Everything is fine, store the new b_langp value. */ 4341 ga_clear(&buf->b_langp); 4342 buf->b_langp = ga; 4343 4344 /* For each language figure out what language to use for sound folding and 4345 * REP items. If the language doesn't support it itself use another one 4346 * with the same name. E.g. for "en-math" use "en". */ 4347 for (i = 0; i < ga.ga_len; ++i) 4348 { 4349 lp = LANGP_ENTRY(ga, i); 4350 4351 /* sound folding */ 4352 if (lp->lp_slang->sl_sal.ga_len > 0) 4353 /* language does sound folding itself */ 4354 lp->lp_sallang = lp->lp_slang; 4355 else 4356 /* find first similar language that does sound folding */ 4357 for (j = 0; j < ga.ga_len; ++j) 4358 { 4359 lp2 = LANGP_ENTRY(ga, j); 4360 if (lp2->lp_slang->sl_sal.ga_len > 0 4361 && STRNCMP(lp->lp_slang->sl_name, 4362 lp2->lp_slang->sl_name, 2) == 0) 4363 { 4364 lp->lp_sallang = lp2->lp_slang; 4365 break; 4366 } 4367 } 4368 4369 /* REP items */ 4370 if (lp->lp_slang->sl_rep.ga_len > 0) 4371 /* language has REP items itself */ 4372 lp->lp_replang = lp->lp_slang; 4373 else 4374 /* find first similar language that has REP items */ 4375 for (j = 0; j < ga.ga_len; ++j) 4376 { 4377 lp2 = LANGP_ENTRY(ga, j); 4378 if (lp2->lp_slang->sl_rep.ga_len > 0 4379 && STRNCMP(lp->lp_slang->sl_name, 4380 lp2->lp_slang->sl_name, 2) == 0) 4381 { 4382 lp->lp_replang = lp2->lp_slang; 4383 break; 4384 } 4385 } 4386 } 4387 4388 theend: 4389 vim_free(spl_copy); 4390 recursive = FALSE; 4391 return ret_msg; 4392 } 4393 4394 /* 4395 * Clear the midword characters for buffer "buf". 4396 */ 4397 static void 4398 clear_midword(buf) 4399 buf_T *buf; 4400 { 4401 vim_memset(buf->b_spell_ismw, 0, 256); 4402 #ifdef FEAT_MBYTE 4403 vim_free(buf->b_spell_ismw_mb); 4404 buf->b_spell_ismw_mb = NULL; 4405 #endif 4406 } 4407 4408 /* 4409 * Use the "sl_midword" field of language "lp" for buffer "buf". 4410 * They add up to any currently used midword characters. 4411 */ 4412 static void 4413 use_midword(lp, buf) 4414 slang_T *lp; 4415 buf_T *buf; 4416 { 4417 char_u *p; 4418 4419 if (lp->sl_midword == NULL) /* there aren't any */ 4420 return; 4421 4422 for (p = lp->sl_midword; *p != NUL; ) 4423 #ifdef FEAT_MBYTE 4424 if (has_mbyte) 4425 { 4426 int c, l, n; 4427 char_u *bp; 4428 4429 c = mb_ptr2char(p); 4430 l = (*mb_ptr2len)(p); 4431 if (c < 256 && l <= 2) 4432 buf->b_spell_ismw[c] = TRUE; 4433 else if (buf->b_spell_ismw_mb == NULL) 4434 /* First multi-byte char in "b_spell_ismw_mb". */ 4435 buf->b_spell_ismw_mb = vim_strnsave(p, l); 4436 else 4437 { 4438 /* Append multi-byte chars to "b_spell_ismw_mb". */ 4439 n = (int)STRLEN(buf->b_spell_ismw_mb); 4440 bp = vim_strnsave(buf->b_spell_ismw_mb, n + l); 4441 if (bp != NULL) 4442 { 4443 vim_free(buf->b_spell_ismw_mb); 4444 buf->b_spell_ismw_mb = bp; 4445 vim_strncpy(bp + n, p, l); 4446 } 4447 } 4448 p += l; 4449 } 4450 else 4451 #endif 4452 buf->b_spell_ismw[*p++] = TRUE; 4453 } 4454 4455 /* 4456 * Find the region "region[2]" in "rp" (points to "sl_regions"). 4457 * Each region is simply stored as the two characters of it's name. 4458 * Returns the index if found (first is 0), REGION_ALL if not found. 4459 */ 4460 static int 4461 find_region(rp, region) 4462 char_u *rp; 4463 char_u *region; 4464 { 4465 int i; 4466 4467 for (i = 0; ; i += 2) 4468 { 4469 if (rp[i] == NUL) 4470 return REGION_ALL; 4471 if (rp[i] == region[0] && rp[i + 1] == region[1]) 4472 break; 4473 } 4474 return i / 2; 4475 } 4476 4477 /* 4478 * Return case type of word: 4479 * w word 0 4480 * Word WF_ONECAP 4481 * W WORD WF_ALLCAP 4482 * WoRd wOrd WF_KEEPCAP 4483 */ 4484 static int 4485 captype(word, end) 4486 char_u *word; 4487 char_u *end; /* When NULL use up to NUL byte. */ 4488 { 4489 char_u *p; 4490 int c; 4491 int firstcap; 4492 int allcap; 4493 int past_second = FALSE; /* past second word char */ 4494 4495 /* find first letter */ 4496 for (p = word; !spell_iswordp_nmw(p); mb_ptr_adv(p)) 4497 if (end == NULL ? *p == NUL : p >= end) 4498 return 0; /* only non-word characters, illegal word */ 4499 #ifdef FEAT_MBYTE 4500 if (has_mbyte) 4501 c = mb_ptr2char_adv(&p); 4502 else 4503 #endif 4504 c = *p++; 4505 firstcap = allcap = SPELL_ISUPPER(c); 4506 4507 /* 4508 * Need to check all letters to find a word with mixed upper/lower. 4509 * But a word with an upper char only at start is a ONECAP. 4510 */ 4511 for ( ; end == NULL ? *p != NUL : p < end; mb_ptr_adv(p)) 4512 if (spell_iswordp_nmw(p)) 4513 { 4514 c = PTR2CHAR(p); 4515 if (!SPELL_ISUPPER(c)) 4516 { 4517 /* UUl -> KEEPCAP */ 4518 if (past_second && allcap) 4519 return WF_KEEPCAP; 4520 allcap = FALSE; 4521 } 4522 else if (!allcap) 4523 /* UlU -> KEEPCAP */ 4524 return WF_KEEPCAP; 4525 past_second = TRUE; 4526 } 4527 4528 if (allcap) 4529 return WF_ALLCAP; 4530 if (firstcap) 4531 return WF_ONECAP; 4532 return 0; 4533 } 4534 4535 /* 4536 * Like captype() but for a KEEPCAP word add ONECAP if the word starts with a 4537 * capital. So that make_case_word() can turn WOrd into Word. 4538 * Add ALLCAP for "WOrD". 4539 */ 4540 static int 4541 badword_captype(word, end) 4542 char_u *word; 4543 char_u *end; 4544 { 4545 int flags = captype(word, end); 4546 int c; 4547 int l, u; 4548 int first; 4549 char_u *p; 4550 4551 if (flags & WF_KEEPCAP) 4552 { 4553 /* Count the number of UPPER and lower case letters. */ 4554 l = u = 0; 4555 first = FALSE; 4556 for (p = word; p < end; mb_ptr_adv(p)) 4557 { 4558 c = PTR2CHAR(p); 4559 if (SPELL_ISUPPER(c)) 4560 { 4561 ++u; 4562 if (p == word) 4563 first = TRUE; 4564 } 4565 else 4566 ++l; 4567 } 4568 4569 /* If there are more UPPER than lower case letters suggest an 4570 * ALLCAP word. Otherwise, if the first letter is UPPER then 4571 * suggest ONECAP. Exception: "ALl" most likely should be "All", 4572 * require three upper case letters. */ 4573 if (u > l && u > 2) 4574 flags |= WF_ALLCAP; 4575 else if (first) 4576 flags |= WF_ONECAP; 4577 4578 if (u >= 2 && l >= 2) /* maCARONI maCAroni */ 4579 flags |= WF_MIXCAP; 4580 } 4581 return flags; 4582 } 4583 4584 # if defined(FEAT_MBYTE) || defined(EXITFREE) || defined(PROTO) 4585 /* 4586 * Free all languages. 4587 */ 4588 void 4589 spell_free_all() 4590 { 4591 slang_T *slang; 4592 buf_T *buf; 4593 char_u fname[MAXPATHL]; 4594 4595 /* Go through all buffers and handle 'spelllang'. */ 4596 for (buf = firstbuf; buf != NULL; buf = buf->b_next) 4597 ga_clear(&buf->b_langp); 4598 4599 while (first_lang != NULL) 4600 { 4601 slang = first_lang; 4602 first_lang = slang->sl_next; 4603 slang_free(slang); 4604 } 4605 4606 if (int_wordlist != NULL) 4607 { 4608 /* Delete the internal wordlist and its .spl file */ 4609 mch_remove(int_wordlist); 4610 int_wordlist_spl(fname); 4611 mch_remove(fname); 4612 vim_free(int_wordlist); 4613 int_wordlist = NULL; 4614 } 4615 4616 init_spell_chartab(); 4617 4618 vim_free(repl_to); 4619 repl_to = NULL; 4620 vim_free(repl_from); 4621 repl_from = NULL; 4622 } 4623 # endif 4624 4625 # if defined(FEAT_MBYTE) || defined(PROTO) 4626 /* 4627 * Clear all spelling tables and reload them. 4628 * Used after 'encoding' is set and when ":mkspell" was used. 4629 */ 4630 void 4631 spell_reload() 4632 { 4633 buf_T *buf; 4634 win_T *wp; 4635 4636 /* Initialize the table for spell_iswordp(). */ 4637 init_spell_chartab(); 4638 4639 /* Unload all allocated memory. */ 4640 spell_free_all(); 4641 4642 /* Go through all buffers and handle 'spelllang'. */ 4643 for (buf = firstbuf; buf != NULL; buf = buf->b_next) 4644 { 4645 /* Only load the wordlists when 'spelllang' is set and there is a 4646 * window for this buffer in which 'spell' is set. */ 4647 if (*buf->b_p_spl != NUL) 4648 { 4649 FOR_ALL_WINDOWS(wp) 4650 if (wp->w_buffer == buf && wp->w_p_spell) 4651 { 4652 (void)did_set_spelllang(buf); 4653 # ifdef FEAT_WINDOWS 4654 break; 4655 # endif 4656 } 4657 } 4658 } 4659 } 4660 # endif 4661 4662 /* 4663 * Reload the spell file "fname" if it's loaded. 4664 */ 4665 static void 4666 spell_reload_one(fname, added_word) 4667 char_u *fname; 4668 int added_word; /* invoked through "zg" */ 4669 { 4670 slang_T *slang; 4671 int didit = FALSE; 4672 4673 for (slang = first_lang; slang != NULL; slang = slang->sl_next) 4674 { 4675 if (fullpathcmp(fname, slang->sl_fname, FALSE) == FPC_SAME) 4676 { 4677 slang_clear(slang); 4678 if (spell_load_file(fname, NULL, slang, FALSE) == NULL) 4679 /* reloading failed, clear the language */ 4680 slang_clear(slang); 4681 redraw_all_later(SOME_VALID); 4682 didit = TRUE; 4683 } 4684 } 4685 4686 /* When "zg" was used and the file wasn't loaded yet, should redo 4687 * 'spelllang' to load it now. */ 4688 if (added_word && !didit) 4689 did_set_spelllang(curbuf); 4690 } 4691 4692 4693 /* 4694 * Functions for ":mkspell". 4695 */ 4696 4697 #define MAXLINELEN 500 /* Maximum length in bytes of a line in a .aff 4698 and .dic file. */ 4699 /* 4700 * Main structure to store the contents of a ".aff" file. 4701 */ 4702 typedef struct afffile_S 4703 { 4704 char_u *af_enc; /* "SET", normalized, alloc'ed string or NULL */ 4705 int af_flagtype; /* AFT_CHAR, AFT_LONG, AFT_NUM or AFT_CAPLONG */ 4706 unsigned af_rare; /* RARE ID for rare word */ 4707 unsigned af_keepcase; /* KEEPCASE ID for keep-case word */ 4708 unsigned af_bad; /* BAD ID for banned word */ 4709 unsigned af_needaffix; /* NEEDAFFIX ID */ 4710 unsigned af_circumfix; /* CIRCUMFIX ID */ 4711 unsigned af_needcomp; /* NEEDCOMPOUND ID */ 4712 unsigned af_comproot; /* COMPOUNDROOT ID */ 4713 unsigned af_compforbid; /* COMPOUNDFORBIDFLAG ID */ 4714 unsigned af_comppermit; /* COMPOUNDPERMITFLAG ID */ 4715 unsigned af_nosuggest; /* NOSUGGEST ID */ 4716 int af_pfxpostpone; /* postpone prefixes without chop string and 4717 without flags */ 4718 hashtab_T af_pref; /* hashtable for prefixes, affheader_T */ 4719 hashtab_T af_suff; /* hashtable for suffixes, affheader_T */ 4720 hashtab_T af_comp; /* hashtable for compound flags, compitem_T */ 4721 } afffile_T; 4722 4723 #define AFT_CHAR 0 /* flags are one character */ 4724 #define AFT_LONG 1 /* flags are two characters */ 4725 #define AFT_CAPLONG 2 /* flags are one or two characters */ 4726 #define AFT_NUM 3 /* flags are numbers, comma separated */ 4727 4728 typedef struct affentry_S affentry_T; 4729 /* Affix entry from ".aff" file. Used for prefixes and suffixes. */ 4730 struct affentry_S 4731 { 4732 affentry_T *ae_next; /* next affix with same name/number */ 4733 char_u *ae_chop; /* text to chop off basic word (can be NULL) */ 4734 char_u *ae_add; /* text to add to basic word (can be NULL) */ 4735 char_u *ae_flags; /* flags on the affix (can be NULL) */ 4736 char_u *ae_cond; /* condition (NULL for ".") */ 4737 regprog_T *ae_prog; /* regexp program for ae_cond or NULL */ 4738 char ae_compforbid; /* COMPOUNDFORBIDFLAG found */ 4739 char ae_comppermit; /* COMPOUNDPERMITFLAG found */ 4740 }; 4741 4742 #ifdef FEAT_MBYTE 4743 # define AH_KEY_LEN 17 /* 2 x 8 bytes + NUL */ 4744 #else 4745 # define AH_KEY_LEN 7 /* 6 digits + NUL */ 4746 #endif 4747 4748 /* Affix header from ".aff" file. Used for af_pref and af_suff. */ 4749 typedef struct affheader_S 4750 { 4751 char_u ah_key[AH_KEY_LEN]; /* key for hashtab == name of affix */ 4752 unsigned ah_flag; /* affix name as number, uses "af_flagtype" */ 4753 int ah_newID; /* prefix ID after renumbering; 0 if not used */ 4754 int ah_combine; /* suffix may combine with prefix */ 4755 int ah_follows; /* another affix block should be following */ 4756 affentry_T *ah_first; /* first affix entry */ 4757 } affheader_T; 4758 4759 #define HI2AH(hi) ((affheader_T *)(hi)->hi_key) 4760 4761 /* Flag used in compound items. */ 4762 typedef struct compitem_S 4763 { 4764 char_u ci_key[AH_KEY_LEN]; /* key for hashtab == name of compound */ 4765 unsigned ci_flag; /* affix name as number, uses "af_flagtype" */ 4766 int ci_newID; /* affix ID after renumbering. */ 4767 } compitem_T; 4768 4769 #define HI2CI(hi) ((compitem_T *)(hi)->hi_key) 4770 4771 /* 4772 * Structure that is used to store the items in the word tree. This avoids 4773 * the need to keep track of each allocated thing, everything is freed all at 4774 * once after ":mkspell" is done. 4775 */ 4776 #define SBLOCKSIZE 16000 /* size of sb_data */ 4777 typedef struct sblock_S sblock_T; 4778 struct sblock_S 4779 { 4780 sblock_T *sb_next; /* next block in list */ 4781 int sb_used; /* nr of bytes already in use */ 4782 char_u sb_data[1]; /* data, actually longer */ 4783 }; 4784 4785 /* 4786 * A node in the tree. 4787 */ 4788 typedef struct wordnode_S wordnode_T; 4789 struct wordnode_S 4790 { 4791 union /* shared to save space */ 4792 { 4793 char_u hashkey[6]; /* the hash key, only used while compressing */ 4794 int index; /* index in written nodes (valid after first 4795 round) */ 4796 } wn_u1; 4797 union /* shared to save space */ 4798 { 4799 wordnode_T *next; /* next node with same hash key */ 4800 wordnode_T *wnode; /* parent node that will write this node */ 4801 } wn_u2; 4802 wordnode_T *wn_child; /* child (next byte in word) */ 4803 wordnode_T *wn_sibling; /* next sibling (alternate byte in word, 4804 always sorted) */ 4805 int wn_refs; /* Nr. of references to this node. Only 4806 relevant for first node in a list of 4807 siblings, in following siblings it is 4808 always one. */ 4809 char_u wn_byte; /* Byte for this node. NUL for word end */ 4810 4811 /* Info for when "wn_byte" is NUL. 4812 * In PREFIXTREE "wn_region" is used for the prefcondnr. 4813 * In the soundfolded word tree "wn_flags" has the MSW of the wordnr and 4814 * "wn_region" the LSW of the wordnr. */ 4815 char_u wn_affixID; /* supported/required prefix ID or 0 */ 4816 short_u wn_flags; /* WF_ flags */ 4817 short wn_region; /* region mask */ 4818 4819 #ifdef SPELL_PRINTTREE 4820 int wn_nr; /* sequence nr for printing */ 4821 #endif 4822 }; 4823 4824 #define WN_MASK 0xffff /* mask relevant bits of "wn_flags" */ 4825 4826 #define HI2WN(hi) (wordnode_T *)((hi)->hi_key) 4827 4828 /* 4829 * Info used while reading the spell files. 4830 */ 4831 typedef struct spellinfo_S 4832 { 4833 wordnode_T *si_foldroot; /* tree with case-folded words */ 4834 long si_foldwcount; /* nr of words in si_foldroot */ 4835 4836 wordnode_T *si_keeproot; /* tree with keep-case words */ 4837 long si_keepwcount; /* nr of words in si_keeproot */ 4838 4839 wordnode_T *si_prefroot; /* tree with postponed prefixes */ 4840 4841 long si_sugtree; /* creating the soundfolding trie */ 4842 4843 sblock_T *si_blocks; /* memory blocks used */ 4844 long si_blocks_cnt; /* memory blocks allocated */ 4845 long si_compress_cnt; /* words to add before lowering 4846 compression limit */ 4847 wordnode_T *si_first_free; /* List of nodes that have been freed during 4848 compression, linked by "wn_child" field. */ 4849 long si_free_count; /* number of nodes in si_first_free */ 4850 #ifdef SPELL_PRINTTREE 4851 int si_wordnode_nr; /* sequence nr for nodes */ 4852 #endif 4853 buf_T *si_spellbuf; /* buffer used to store soundfold word table */ 4854 4855 int si_ascii; /* handling only ASCII words */ 4856 int si_add; /* addition file */ 4857 int si_clear_chartab; /* when TRUE clear char tables */ 4858 int si_region; /* region mask */ 4859 vimconv_T si_conv; /* for conversion to 'encoding' */ 4860 int si_memtot; /* runtime memory used */ 4861 int si_verbose; /* verbose messages */ 4862 int si_msg_count; /* number of words added since last message */ 4863 char_u *si_info; /* info text chars or NULL */ 4864 int si_region_count; /* number of regions supported (1 when there 4865 are no regions) */ 4866 char_u si_region_name[16]; /* region names; used only if 4867 * si_region_count > 1) */ 4868 4869 garray_T si_rep; /* list of fromto_T entries from REP lines */ 4870 garray_T si_repsal; /* list of fromto_T entries from REPSAL lines */ 4871 garray_T si_sal; /* list of fromto_T entries from SAL lines */ 4872 char_u *si_sofofr; /* SOFOFROM text */ 4873 char_u *si_sofoto; /* SOFOTO text */ 4874 int si_nosugfile; /* NOSUGFILE item found */ 4875 int si_nosplitsugs; /* NOSPLITSUGS item found */ 4876 int si_followup; /* soundsalike: ? */ 4877 int si_collapse; /* soundsalike: ? */ 4878 hashtab_T si_commonwords; /* hashtable for common words */ 4879 time_t si_sugtime; /* timestamp for .sug file */ 4880 int si_rem_accents; /* soundsalike: remove accents */ 4881 garray_T si_map; /* MAP info concatenated */ 4882 char_u *si_midword; /* MIDWORD chars or NULL */ 4883 int si_compmax; /* max nr of words for compounding */ 4884 int si_compminlen; /* minimal length for compounding */ 4885 int si_compsylmax; /* max nr of syllables for compounding */ 4886 int si_compoptions; /* COMP_ flags */ 4887 garray_T si_comppat; /* CHECKCOMPOUNDPATTERN items, each stored as 4888 a string */ 4889 char_u *si_compflags; /* flags used for compounding */ 4890 char_u si_nobreak; /* NOBREAK */ 4891 char_u *si_syllable; /* syllable string */ 4892 garray_T si_prefcond; /* table with conditions for postponed 4893 * prefixes, each stored as a string */ 4894 int si_newprefID; /* current value for ah_newID */ 4895 int si_newcompID; /* current value for compound ID */ 4896 } spellinfo_T; 4897 4898 static afffile_T *spell_read_aff __ARGS((spellinfo_T *spin, char_u *fname)); 4899 static void aff_process_flags __ARGS((afffile_T *affile, affentry_T *entry)); 4900 static int spell_info_item __ARGS((char_u *s)); 4901 static unsigned affitem2flag __ARGS((int flagtype, char_u *item, char_u *fname, int lnum)); 4902 static unsigned get_affitem __ARGS((int flagtype, char_u **pp)); 4903 static void process_compflags __ARGS((spellinfo_T *spin, afffile_T *aff, char_u *compflags)); 4904 static void check_renumber __ARGS((spellinfo_T *spin)); 4905 static int flag_in_afflist __ARGS((int flagtype, char_u *afflist, unsigned flag)); 4906 static void aff_check_number __ARGS((int spinval, int affval, char *name)); 4907 static void aff_check_string __ARGS((char_u *spinval, char_u *affval, char *name)); 4908 static int str_equal __ARGS((char_u *s1, char_u *s2)); 4909 static void add_fromto __ARGS((spellinfo_T *spin, garray_T *gap, char_u *from, char_u *to)); 4910 static int sal_to_bool __ARGS((char_u *s)); 4911 static int has_non_ascii __ARGS((char_u *s)); 4912 static void spell_free_aff __ARGS((afffile_T *aff)); 4913 static int spell_read_dic __ARGS((spellinfo_T *spin, char_u *fname, afffile_T *affile)); 4914 static int get_affix_flags __ARGS((afffile_T *affile, char_u *afflist)); 4915 static int get_pfxlist __ARGS((afffile_T *affile, char_u *afflist, char_u *store_afflist)); 4916 static void get_compflags __ARGS((afffile_T *affile, char_u *afflist, char_u *store_afflist)); 4917 static int store_aff_word __ARGS((spellinfo_T *spin, char_u *word, char_u *afflist, afffile_T *affile, hashtab_T *ht, hashtab_T *xht, int condit, int flags, char_u *pfxlist, int pfxlen)); 4918 static int spell_read_wordfile __ARGS((spellinfo_T *spin, char_u *fname)); 4919 static void *getroom __ARGS((spellinfo_T *spin, size_t len, int align)); 4920 static char_u *getroom_save __ARGS((spellinfo_T *spin, char_u *s)); 4921 static void free_blocks __ARGS((sblock_T *bl)); 4922 static wordnode_T *wordtree_alloc __ARGS((spellinfo_T *spin)); 4923 static int store_word __ARGS((spellinfo_T *spin, char_u *word, int flags, int region, char_u *pfxlist, int need_affix)); 4924 static int tree_add_word __ARGS((spellinfo_T *spin, char_u *word, wordnode_T *tree, int flags, int region, int affixID)); 4925 static wordnode_T *get_wordnode __ARGS((spellinfo_T *spin)); 4926 static int deref_wordnode __ARGS((spellinfo_T *spin, wordnode_T *node)); 4927 static void free_wordnode __ARGS((spellinfo_T *spin, wordnode_T *n)); 4928 static void wordtree_compress __ARGS((spellinfo_T *spin, wordnode_T *root)); 4929 static int node_compress __ARGS((spellinfo_T *spin, wordnode_T *node, hashtab_T *ht, int *tot)); 4930 static int node_equal __ARGS((wordnode_T *n1, wordnode_T *n2)); 4931 static void put_sugtime __ARGS((spellinfo_T *spin, FILE *fd)); 4932 static int write_vim_spell __ARGS((spellinfo_T *spin, char_u *fname)); 4933 static void clear_node __ARGS((wordnode_T *node)); 4934 static int put_node __ARGS((FILE *fd, wordnode_T *node, int index, int regionmask, int prefixtree)); 4935 static void spell_make_sugfile __ARGS((spellinfo_T *spin, char_u *wfname)); 4936 static int sug_filltree __ARGS((spellinfo_T *spin, slang_T *slang)); 4937 static int sug_maketable __ARGS((spellinfo_T *spin)); 4938 static int sug_filltable __ARGS((spellinfo_T *spin, wordnode_T *node, int startwordnr, garray_T *gap)); 4939 static int offset2bytes __ARGS((int nr, char_u *buf)); 4940 static int bytes2offset __ARGS((char_u **pp)); 4941 static void sug_write __ARGS((spellinfo_T *spin, char_u *fname)); 4942 static void mkspell __ARGS((int fcount, char_u **fnames, int ascii, int overwrite, int added_word)); 4943 static void spell_message __ARGS((spellinfo_T *spin, char_u *str)); 4944 static void init_spellfile __ARGS((void)); 4945 4946 /* In the postponed prefixes tree wn_flags is used to store the WFP_ flags, 4947 * but it must be negative to indicate the prefix tree to tree_add_word(). 4948 * Use a negative number with the lower 8 bits zero. */ 4949 #define PFX_FLAGS -256 4950 4951 /* flags for "condit" argument of store_aff_word() */ 4952 #define CONDIT_COMB 1 /* affix must combine */ 4953 #define CONDIT_CFIX 2 /* affix must have CIRCUMFIX flag */ 4954 #define CONDIT_SUF 4 /* add a suffix for matching flags */ 4955 #define CONDIT_AFF 8 /* word already has an affix */ 4956 4957 /* 4958 * Tunable parameters for when the tree is compressed. See 'mkspellmem'. 4959 */ 4960 static long compress_start = 30000; /* memory / SBLOCKSIZE */ 4961 static long compress_inc = 100; /* memory / SBLOCKSIZE */ 4962 static long compress_added = 500000; /* word count */ 4963 4964 #ifdef SPELL_PRINTTREE 4965 /* 4966 * For debugging the tree code: print the current tree in a (more or less) 4967 * readable format, so that we can see what happens when adding a word and/or 4968 * compressing the tree. 4969 * Based on code from Olaf Seibert. 4970 */ 4971 #define PRINTLINESIZE 1000 4972 #define PRINTWIDTH 6 4973 4974 #define PRINTSOME(l, depth, fmt, a1, a2) vim_snprintf(l + depth * PRINTWIDTH, \ 4975 PRINTLINESIZE - PRINTWIDTH * depth, fmt, a1, a2) 4976 4977 static char line1[PRINTLINESIZE]; 4978 static char line2[PRINTLINESIZE]; 4979 static char line3[PRINTLINESIZE]; 4980 4981 static void 4982 spell_clear_flags(wordnode_T *node) 4983 { 4984 wordnode_T *np; 4985 4986 for (np = node; np != NULL; np = np->wn_sibling) 4987 { 4988 np->wn_u1.index = FALSE; 4989 spell_clear_flags(np->wn_child); 4990 } 4991 } 4992 4993 static void 4994 spell_print_node(wordnode_T *node, int depth) 4995 { 4996 if (node->wn_u1.index) 4997 { 4998 /* Done this node before, print the reference. */ 4999 PRINTSOME(line1, depth, "(%d)", node->wn_nr, 0); 5000 PRINTSOME(line2, depth, " ", 0, 0); 5001 PRINTSOME(line3, depth, " ", 0, 0); 5002 msg(line1); 5003 msg(line2); 5004 msg(line3); 5005 } 5006 else 5007 { 5008 node->wn_u1.index = TRUE; 5009 5010 if (node->wn_byte != NUL) 5011 { 5012 if (node->wn_child != NULL) 5013 PRINTSOME(line1, depth, " %c -> ", node->wn_byte, 0); 5014 else 5015 /* Cannot happen? */ 5016 PRINTSOME(line1, depth, " %c ???", node->wn_byte, 0); 5017 } 5018 else 5019 PRINTSOME(line1, depth, " $ ", 0, 0); 5020 5021 PRINTSOME(line2, depth, "%d/%d ", node->wn_nr, node->wn_refs); 5022 5023 if (node->wn_sibling != NULL) 5024 PRINTSOME(line3, depth, " | ", 0, 0); 5025 else 5026 PRINTSOME(line3, depth, " ", 0, 0); 5027 5028 if (node->wn_byte == NUL) 5029 { 5030 msg(line1); 5031 msg(line2); 5032 msg(line3); 5033 } 5034 5035 /* do the children */ 5036 if (node->wn_byte != NUL && node->wn_child != NULL) 5037 spell_print_node(node->wn_child, depth + 1); 5038 5039 /* do the siblings */ 5040 if (node->wn_sibling != NULL) 5041 { 5042 /* get rid of all parent details except | */ 5043 STRCPY(line1, line3); 5044 STRCPY(line2, line3); 5045 spell_print_node(node->wn_sibling, depth); 5046 } 5047 } 5048 } 5049 5050 static void 5051 spell_print_tree(wordnode_T *root) 5052 { 5053 if (root != NULL) 5054 { 5055 /* Clear the "wn_u1.index" fields, used to remember what has been 5056 * done. */ 5057 spell_clear_flags(root); 5058 5059 /* Recursively print the tree. */ 5060 spell_print_node(root, 0); 5061 } 5062 } 5063 #endif /* SPELL_PRINTTREE */ 5064 5065 /* 5066 * Read the affix file "fname". 5067 * Returns an afffile_T, NULL for complete failure. 5068 */ 5069 static afffile_T * 5070 spell_read_aff(spin, fname) 5071 spellinfo_T *spin; 5072 char_u *fname; 5073 { 5074 FILE *fd; 5075 afffile_T *aff; 5076 char_u rline[MAXLINELEN]; 5077 char_u *line; 5078 char_u *pc = NULL; 5079 #define MAXITEMCNT 30 5080 char_u *(items[MAXITEMCNT]); 5081 int itemcnt; 5082 char_u *p; 5083 int lnum = 0; 5084 affheader_T *cur_aff = NULL; 5085 int did_postpone_prefix = FALSE; 5086 int aff_todo = 0; 5087 hashtab_T *tp; 5088 char_u *low = NULL; 5089 char_u *fol = NULL; 5090 char_u *upp = NULL; 5091 int do_rep; 5092 int do_repsal; 5093 int do_sal; 5094 int do_mapline; 5095 int found_map = FALSE; 5096 hashitem_T *hi; 5097 int l; 5098 int compminlen = 0; /* COMPOUNDMIN value */ 5099 int compsylmax = 0; /* COMPOUNDSYLMAX value */ 5100 int compoptions = 0; /* COMP_ flags */ 5101 int compmax = 0; /* COMPOUNDWORDMAX value */ 5102 char_u *compflags = NULL; /* COMPOUNDFLAG and COMPOUNDRULE 5103 concatenated */ 5104 char_u *midword = NULL; /* MIDWORD value */ 5105 char_u *syllable = NULL; /* SYLLABLE value */ 5106 char_u *sofofrom = NULL; /* SOFOFROM value */ 5107 char_u *sofoto = NULL; /* SOFOTO value */ 5108 5109 /* 5110 * Open the file. 5111 */ 5112 fd = mch_fopen((char *)fname, "r"); 5113 if (fd == NULL) 5114 { 5115 EMSG2(_(e_notopen), fname); 5116 return NULL; 5117 } 5118 5119 vim_snprintf((char *)IObuff, IOSIZE, _("Reading affix file %s ..."), fname); 5120 spell_message(spin, IObuff); 5121 5122 /* Only do REP lines when not done in another .aff file already. */ 5123 do_rep = spin->si_rep.ga_len == 0; 5124 5125 /* Only do REPSAL lines when not done in another .aff file already. */ 5126 do_repsal = spin->si_repsal.ga_len == 0; 5127 5128 /* Only do SAL lines when not done in another .aff file already. */ 5129 do_sal = spin->si_sal.ga_len == 0; 5130 5131 /* Only do MAP lines when not done in another .aff file already. */ 5132 do_mapline = spin->si_map.ga_len == 0; 5133 5134 /* 5135 * Allocate and init the afffile_T structure. 5136 */ 5137 aff = (afffile_T *)getroom(spin, sizeof(afffile_T), TRUE); 5138 if (aff == NULL) 5139 { 5140 fclose(fd); 5141 return NULL; 5142 } 5143 hash_init(&aff->af_pref); 5144 hash_init(&aff->af_suff); 5145 hash_init(&aff->af_comp); 5146 5147 /* 5148 * Read all the lines in the file one by one. 5149 */ 5150 while (!vim_fgets(rline, MAXLINELEN, fd) && !got_int) 5151 { 5152 line_breakcheck(); 5153 ++lnum; 5154 5155 /* Skip comment lines. */ 5156 if (*rline == '#') 5157 continue; 5158 5159 /* Convert from "SET" to 'encoding' when needed. */ 5160 vim_free(pc); 5161 #ifdef FEAT_MBYTE 5162 if (spin->si_conv.vc_type != CONV_NONE) 5163 { 5164 pc = string_convert(&spin->si_conv, rline, NULL); 5165 if (pc == NULL) 5166 { 5167 smsg((char_u *)_("Conversion failure for word in %s line %d: %s"), 5168 fname, lnum, rline); 5169 continue; 5170 } 5171 line = pc; 5172 } 5173 else 5174 #endif 5175 { 5176 pc = NULL; 5177 line = rline; 5178 } 5179 5180 /* Split the line up in white separated items. Put a NUL after each 5181 * item. */ 5182 itemcnt = 0; 5183 for (p = line; ; ) 5184 { 5185 while (*p != NUL && *p <= ' ') /* skip white space and CR/NL */ 5186 ++p; 5187 if (*p == NUL) 5188 break; 5189 if (itemcnt == MAXITEMCNT) /* too many items */ 5190 break; 5191 items[itemcnt++] = p; 5192 /* A few items have arbitrary text argument, don't split them. */ 5193 if (itemcnt == 2 && spell_info_item(items[0])) 5194 while (*p >= ' ' || *p == TAB) /* skip until CR/NL */ 5195 ++p; 5196 else 5197 while (*p > ' ') /* skip until white space or CR/NL */ 5198 ++p; 5199 if (*p == NUL) 5200 break; 5201 *p++ = NUL; 5202 } 5203 5204 /* Handle non-empty lines. */ 5205 if (itemcnt > 0) 5206 { 5207 if (STRCMP(items[0], "SET") == 0 && itemcnt == 2 5208 && aff->af_enc == NULL) 5209 { 5210 #ifdef FEAT_MBYTE 5211 /* Setup for conversion from "ENC" to 'encoding'. */ 5212 aff->af_enc = enc_canonize(items[1]); 5213 if (aff->af_enc != NULL && !spin->si_ascii 5214 && convert_setup(&spin->si_conv, aff->af_enc, 5215 p_enc) == FAIL) 5216 smsg((char_u *)_("Conversion in %s not supported: from %s to %s"), 5217 fname, aff->af_enc, p_enc); 5218 spin->si_conv.vc_fail = TRUE; 5219 #else 5220 smsg((char_u *)_("Conversion in %s not supported"), fname); 5221 #endif 5222 } 5223 else if (STRCMP(items[0], "FLAG") == 0 && itemcnt == 2 5224 && aff->af_flagtype == AFT_CHAR) 5225 { 5226 if (STRCMP(items[1], "long") == 0) 5227 aff->af_flagtype = AFT_LONG; 5228 else if (STRCMP(items[1], "num") == 0) 5229 aff->af_flagtype = AFT_NUM; 5230 else if (STRCMP(items[1], "caplong") == 0) 5231 aff->af_flagtype = AFT_CAPLONG; 5232 else 5233 smsg((char_u *)_("Invalid value for FLAG in %s line %d: %s"), 5234 fname, lnum, items[1]); 5235 if (aff->af_rare != 0 5236 || aff->af_keepcase != 0 5237 || aff->af_bad != 0 5238 || aff->af_needaffix != 0 5239 || aff->af_circumfix != 0 5240 || aff->af_needcomp != 0 5241 || aff->af_comproot != 0 5242 || aff->af_nosuggest != 0 5243 || compflags != NULL 5244 || aff->af_suff.ht_used > 0 5245 || aff->af_pref.ht_used > 0) 5246 smsg((char_u *)_("FLAG after using flags in %s line %d: %s"), 5247 fname, lnum, items[1]); 5248 } 5249 else if (spell_info_item(items[0])) 5250 { 5251 p = (char_u *)getroom(spin, 5252 (spin->si_info == NULL ? 0 : STRLEN(spin->si_info)) 5253 + STRLEN(items[0]) 5254 + STRLEN(items[1]) + 3, FALSE); 5255 if (p != NULL) 5256 { 5257 if (spin->si_info != NULL) 5258 { 5259 STRCPY(p, spin->si_info); 5260 STRCAT(p, "\n"); 5261 } 5262 STRCAT(p, items[0]); 5263 STRCAT(p, " "); 5264 STRCAT(p, items[1]); 5265 spin->si_info = p; 5266 } 5267 } 5268 else if (STRCMP(items[0], "MIDWORD") == 0 && itemcnt == 2 5269 && midword == NULL) 5270 { 5271 midword = getroom_save(spin, items[1]); 5272 } 5273 else if (STRCMP(items[0], "TRY") == 0 && itemcnt == 2) 5274 { 5275 /* ignored, we look in the tree for what chars may appear */ 5276 } 5277 /* TODO: remove "RAR" later */ 5278 else if ((STRCMP(items[0], "RAR") == 0 5279 || STRCMP(items[0], "RARE") == 0) && itemcnt == 2 5280 && aff->af_rare == 0) 5281 { 5282 aff->af_rare = affitem2flag(aff->af_flagtype, items[1], 5283 fname, lnum); 5284 } 5285 /* TODO: remove "KEP" later */ 5286 else if ((STRCMP(items[0], "KEP") == 0 5287 || STRCMP(items[0], "KEEPCASE") == 0) && itemcnt == 2 5288 && aff->af_keepcase == 0) 5289 { 5290 aff->af_keepcase = affitem2flag(aff->af_flagtype, items[1], 5291 fname, lnum); 5292 } 5293 else if (STRCMP(items[0], "BAD") == 0 && itemcnt == 2 5294 && aff->af_bad == 0) 5295 { 5296 aff->af_bad = affitem2flag(aff->af_flagtype, items[1], 5297 fname, lnum); 5298 } 5299 else if (STRCMP(items[0], "NEEDAFFIX") == 0 && itemcnt == 2 5300 && aff->af_needaffix == 0) 5301 { 5302 aff->af_needaffix = affitem2flag(aff->af_flagtype, items[1], 5303 fname, lnum); 5304 } 5305 else if (STRCMP(items[0], "CIRCUMFIX") == 0 && itemcnt == 2 5306 && aff->af_circumfix == 0) 5307 { 5308 aff->af_circumfix = affitem2flag(aff->af_flagtype, items[1], 5309 fname, lnum); 5310 } 5311 else if (STRCMP(items[0], "NOSUGGEST") == 0 && itemcnt == 2 5312 && aff->af_nosuggest == 0) 5313 { 5314 aff->af_nosuggest = affitem2flag(aff->af_flagtype, items[1], 5315 fname, lnum); 5316 } 5317 else if (STRCMP(items[0], "NEEDCOMPOUND") == 0 && itemcnt == 2 5318 && aff->af_needcomp == 0) 5319 { 5320 aff->af_needcomp = affitem2flag(aff->af_flagtype, items[1], 5321 fname, lnum); 5322 } 5323 else if (STRCMP(items[0], "COMPOUNDROOT") == 0 && itemcnt == 2 5324 && aff->af_comproot == 0) 5325 { 5326 aff->af_comproot = affitem2flag(aff->af_flagtype, items[1], 5327 fname, lnum); 5328 } 5329 else if (STRCMP(items[0], "COMPOUNDFORBIDFLAG") == 0 5330 && itemcnt == 2 && aff->af_compforbid == 0) 5331 { 5332 aff->af_compforbid = affitem2flag(aff->af_flagtype, items[1], 5333 fname, lnum); 5334 if (aff->af_pref.ht_used > 0) 5335 smsg((char_u *)_("Defining COMPOUNDFORBIDFLAG after PFX item may give wrong results in %s line %d"), 5336 fname, lnum); 5337 } 5338 else if (STRCMP(items[0], "COMPOUNDPERMITFLAG") == 0 5339 && itemcnt == 2 && aff->af_comppermit == 0) 5340 { 5341 aff->af_comppermit = affitem2flag(aff->af_flagtype, items[1], 5342 fname, lnum); 5343 if (aff->af_pref.ht_used > 0) 5344 smsg((char_u *)_("Defining COMPOUNDPERMITFLAG after PFX item may give wrong results in %s line %d"), 5345 fname, lnum); 5346 } 5347 else if (STRCMP(items[0], "COMPOUNDFLAG") == 0 && itemcnt == 2 5348 && compflags == NULL) 5349 { 5350 /* Turn flag "c" into COMPOUNDRULE compatible string "c+", 5351 * "Na" into "Na+", "1234" into "1234+". */ 5352 p = getroom(spin, STRLEN(items[1]) + 2, FALSE); 5353 if (p != NULL) 5354 { 5355 STRCPY(p, items[1]); 5356 STRCAT(p, "+"); 5357 compflags = p; 5358 } 5359 } 5360 else if (STRCMP(items[0], "COMPOUNDRULE") == 0 && itemcnt == 2) 5361 { 5362 /* Concatenate this string to previously defined ones, using a 5363 * slash to separate them. */ 5364 l = (int)STRLEN(items[1]) + 1; 5365 if (compflags != NULL) 5366 l += (int)STRLEN(compflags) + 1; 5367 p = getroom(spin, l, FALSE); 5368 if (p != NULL) 5369 { 5370 if (compflags != NULL) 5371 { 5372 STRCPY(p, compflags); 5373 STRCAT(p, "/"); 5374 } 5375 STRCAT(p, items[1]); 5376 compflags = p; 5377 } 5378 } 5379 else if (STRCMP(items[0], "COMPOUNDWORDMAX") == 0 && itemcnt == 2 5380 && compmax == 0) 5381 { 5382 compmax = atoi((char *)items[1]); 5383 if (compmax == 0) 5384 smsg((char_u *)_("Wrong COMPOUNDWORDMAX value in %s line %d: %s"), 5385 fname, lnum, items[1]); 5386 } 5387 else if (STRCMP(items[0], "COMPOUNDMIN") == 0 && itemcnt == 2 5388 && compminlen == 0) 5389 { 5390 compminlen = atoi((char *)items[1]); 5391 if (compminlen == 0) 5392 smsg((char_u *)_("Wrong COMPOUNDMIN value in %s line %d: %s"), 5393 fname, lnum, items[1]); 5394 } 5395 else if (STRCMP(items[0], "COMPOUNDSYLMAX") == 0 && itemcnt == 2 5396 && compsylmax == 0) 5397 { 5398 compsylmax = atoi((char *)items[1]); 5399 if (compsylmax == 0) 5400 smsg((char_u *)_("Wrong COMPOUNDSYLMAX value in %s line %d: %s"), 5401 fname, lnum, items[1]); 5402 } 5403 else if (STRCMP(items[0], "CHECKCOMPOUNDDUP") == 0 && itemcnt == 1) 5404 { 5405 compoptions |= COMP_CHECKDUP; 5406 } 5407 else if (STRCMP(items[0], "CHECKCOMPOUNDREP") == 0 && itemcnt == 1) 5408 { 5409 compoptions |= COMP_CHECKREP; 5410 } 5411 else if (STRCMP(items[0], "CHECKCOMPOUNDCASE") == 0 && itemcnt == 1) 5412 { 5413 compoptions |= COMP_CHECKCASE; 5414 } 5415 else if (STRCMP(items[0], "CHECKCOMPOUNDTRIPLE") == 0 5416 && itemcnt == 1) 5417 { 5418 compoptions |= COMP_CHECKTRIPLE; 5419 } 5420 else if (STRCMP(items[0], "CHECKCOMPOUNDPATTERN") == 0 5421 && itemcnt == 2) 5422 { 5423 if (atoi((char *)items[1]) == 0) 5424 smsg((char_u *)_("Wrong CHECKCOMPOUNDPATTERN value in %s line %d: %s"), 5425 fname, lnum, items[1]); 5426 } 5427 else if (STRCMP(items[0], "CHECKCOMPOUNDPATTERN") == 0 5428 && itemcnt == 3) 5429 { 5430 garray_T *gap = &spin->si_comppat; 5431 int i; 5432 5433 /* Only add the couple if it isn't already there. */ 5434 for (i = 0; i < gap->ga_len - 1; i += 2) 5435 if (STRCMP(((char_u **)(gap->ga_data))[i], items[1]) == 0 5436 && STRCMP(((char_u **)(gap->ga_data))[i + 1], 5437 items[2]) == 0) 5438 break; 5439 if (i >= gap->ga_len && ga_grow(gap, 2) == OK) 5440 { 5441 ((char_u **)(gap->ga_data))[gap->ga_len++] 5442 = getroom_save(spin, items[1]); 5443 ((char_u **)(gap->ga_data))[gap->ga_len++] 5444 = getroom_save(spin, items[2]); 5445 } 5446 } 5447 else if (STRCMP(items[0], "SYLLABLE") == 0 && itemcnt == 2 5448 && syllable == NULL) 5449 { 5450 syllable = getroom_save(spin, items[1]); 5451 } 5452 else if (STRCMP(items[0], "NOBREAK") == 0 && itemcnt == 1) 5453 { 5454 spin->si_nobreak = TRUE; 5455 } 5456 else if (STRCMP(items[0], "NOSPLITSUGS") == 0 && itemcnt == 1) 5457 { 5458 spin->si_nosplitsugs = TRUE; 5459 } 5460 else if (STRCMP(items[0], "NOSUGFILE") == 0 && itemcnt == 1) 5461 { 5462 spin->si_nosugfile = TRUE; 5463 } 5464 else if (STRCMP(items[0], "PFXPOSTPONE") == 0 && itemcnt == 1) 5465 { 5466 aff->af_pfxpostpone = TRUE; 5467 } 5468 else if ((STRCMP(items[0], "PFX") == 0 5469 || STRCMP(items[0], "SFX") == 0) 5470 && aff_todo == 0 5471 && itemcnt >= 4) 5472 { 5473 int lasti = 4; 5474 char_u key[AH_KEY_LEN]; 5475 5476 if (*items[0] == 'P') 5477 tp = &aff->af_pref; 5478 else 5479 tp = &aff->af_suff; 5480 5481 /* Myspell allows the same affix name to be used multiple 5482 * times. The affix files that do this have an undocumented 5483 * "S" flag on all but the last block, thus we check for that 5484 * and store it in ah_follows. */ 5485 vim_strncpy(key, items[1], AH_KEY_LEN - 1); 5486 hi = hash_find(tp, key); 5487 if (!HASHITEM_EMPTY(hi)) 5488 { 5489 cur_aff = HI2AH(hi); 5490 if (cur_aff->ah_combine != (*items[2] == 'Y')) 5491 smsg((char_u *)_("Different combining flag in continued affix block in %s line %d: %s"), 5492 fname, lnum, items[1]); 5493 if (!cur_aff->ah_follows) 5494 smsg((char_u *)_("Duplicate affix in %s line %d: %s"), 5495 fname, lnum, items[1]); 5496 } 5497 else 5498 { 5499 /* New affix letter. */ 5500 cur_aff = (affheader_T *)getroom(spin, 5501 sizeof(affheader_T), TRUE); 5502 if (cur_aff == NULL) 5503 break; 5504 cur_aff->ah_flag = affitem2flag(aff->af_flagtype, items[1], 5505 fname, lnum); 5506 if (cur_aff->ah_flag == 0 || STRLEN(items[1]) >= AH_KEY_LEN) 5507 break; 5508 if (cur_aff->ah_flag == aff->af_bad 5509 || cur_aff->ah_flag == aff->af_rare 5510 || cur_aff->ah_flag == aff->af_keepcase 5511 || cur_aff->ah_flag == aff->af_needaffix 5512 || cur_aff->ah_flag == aff->af_circumfix 5513 || cur_aff->ah_flag == aff->af_nosuggest 5514 || cur_aff->ah_flag == aff->af_needcomp 5515 || cur_aff->ah_flag == aff->af_comproot) 5516 smsg((char_u *)_("Affix also used for BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST in %s line %d: %s"), 5517 fname, lnum, items[1]); 5518 STRCPY(cur_aff->ah_key, items[1]); 5519 hash_add(tp, cur_aff->ah_key); 5520 5521 cur_aff->ah_combine = (*items[2] == 'Y'); 5522 } 5523 5524 /* Check for the "S" flag, which apparently means that another 5525 * block with the same affix name is following. */ 5526 if (itemcnt > lasti && STRCMP(items[lasti], "S") == 0) 5527 { 5528 ++lasti; 5529 cur_aff->ah_follows = TRUE; 5530 } 5531 else 5532 cur_aff->ah_follows = FALSE; 5533 5534 /* Myspell allows extra text after the item, but that might 5535 * mean mistakes go unnoticed. Require a comment-starter. */ 5536 if (itemcnt > lasti && *items[lasti] != '#') 5537 smsg((char_u *)_(e_afftrailing), fname, lnum, items[lasti]); 5538 5539 if (STRCMP(items[2], "Y") != 0 && STRCMP(items[2], "N") != 0) 5540 smsg((char_u *)_("Expected Y or N in %s line %d: %s"), 5541 fname, lnum, items[2]); 5542 5543 if (*items[0] == 'P' && aff->af_pfxpostpone) 5544 { 5545 if (cur_aff->ah_newID == 0) 5546 { 5547 /* Use a new number in the .spl file later, to be able 5548 * to handle multiple .aff files. */ 5549 check_renumber(spin); 5550 cur_aff->ah_newID = ++spin->si_newprefID; 5551 5552 /* We only really use ah_newID if the prefix is 5553 * postponed. We know that only after handling all 5554 * the items. */ 5555 did_postpone_prefix = FALSE; 5556 } 5557 else 5558 /* Did use the ID in a previous block. */ 5559 did_postpone_prefix = TRUE; 5560 } 5561 5562 aff_todo = atoi((char *)items[3]); 5563 } 5564 else if ((STRCMP(items[0], "PFX") == 0 5565 || STRCMP(items[0], "SFX") == 0) 5566 && aff_todo > 0 5567 && STRCMP(cur_aff->ah_key, items[1]) == 0 5568 && itemcnt >= 5) 5569 { 5570 affentry_T *aff_entry; 5571 int upper = FALSE; 5572 int lasti = 5; 5573 5574 /* Myspell allows extra text after the item, but that might 5575 * mean mistakes go unnoticed. Require a comment-starter. 5576 * Hunspell uses a "-" item. */ 5577 if (itemcnt > lasti && *items[lasti] != '#' 5578 && (STRCMP(items[lasti], "-") != 0 5579 || itemcnt != lasti + 1)) 5580 smsg((char_u *)_(e_afftrailing), fname, lnum, items[lasti]); 5581 5582 /* New item for an affix letter. */ 5583 --aff_todo; 5584 aff_entry = (affentry_T *)getroom(spin, 5585 sizeof(affentry_T), TRUE); 5586 if (aff_entry == NULL) 5587 break; 5588 5589 if (STRCMP(items[2], "0") != 0) 5590 aff_entry->ae_chop = getroom_save(spin, items[2]); 5591 if (STRCMP(items[3], "0") != 0) 5592 { 5593 aff_entry->ae_add = getroom_save(spin, items[3]); 5594 5595 /* Recognize flags on the affix: abcd/XYZ */ 5596 aff_entry->ae_flags = vim_strchr(aff_entry->ae_add, '/'); 5597 if (aff_entry->ae_flags != NULL) 5598 { 5599 *aff_entry->ae_flags++ = NUL; 5600 aff_process_flags(aff, aff_entry); 5601 } 5602 } 5603 5604 /* Don't use an affix entry with non-ASCII characters when 5605 * "spin->si_ascii" is TRUE. */ 5606 if (!spin->si_ascii || !(has_non_ascii(aff_entry->ae_chop) 5607 || has_non_ascii(aff_entry->ae_add))) 5608 { 5609 aff_entry->ae_next = cur_aff->ah_first; 5610 cur_aff->ah_first = aff_entry; 5611 5612 if (STRCMP(items[4], ".") != 0) 5613 { 5614 char_u buf[MAXLINELEN]; 5615 5616 aff_entry->ae_cond = getroom_save(spin, items[4]); 5617 if (*items[0] == 'P') 5618 sprintf((char *)buf, "^%s", items[4]); 5619 else 5620 sprintf((char *)buf, "%s$", items[4]); 5621 aff_entry->ae_prog = vim_regcomp(buf, 5622 RE_MAGIC + RE_STRING + RE_STRICT); 5623 if (aff_entry->ae_prog == NULL) 5624 smsg((char_u *)_("Broken condition in %s line %d: %s"), 5625 fname, lnum, items[4]); 5626 } 5627 5628 /* For postponed prefixes we need an entry in si_prefcond 5629 * for the condition. Use an existing one if possible. 5630 * Can't be done for an affix with flags, ignoring 5631 * COMPOUNDFORBIDFLAG and COMPOUNDPERMITFLAG. */ 5632 if (*items[0] == 'P' && aff->af_pfxpostpone 5633 && aff_entry->ae_flags == NULL) 5634 { 5635 /* When the chop string is one lower-case letter and 5636 * the add string ends in the upper-case letter we set 5637 * the "upper" flag, clear "ae_chop" and remove the 5638 * letters from "ae_add". The condition must either 5639 * be empty or start with the same letter. */ 5640 if (aff_entry->ae_chop != NULL 5641 && aff_entry->ae_add != NULL 5642 #ifdef FEAT_MBYTE 5643 && aff_entry->ae_chop[(*mb_ptr2len)( 5644 aff_entry->ae_chop)] == NUL 5645 #else 5646 && aff_entry->ae_chop[1] == NUL 5647 #endif 5648 ) 5649 { 5650 int c, c_up; 5651 5652 c = PTR2CHAR(aff_entry->ae_chop); 5653 c_up = SPELL_TOUPPER(c); 5654 if (c_up != c 5655 && (aff_entry->ae_cond == NULL 5656 || PTR2CHAR(aff_entry->ae_cond) == c)) 5657 { 5658 p = aff_entry->ae_add 5659 + STRLEN(aff_entry->ae_add); 5660 mb_ptr_back(aff_entry->ae_add, p); 5661 if (PTR2CHAR(p) == c_up) 5662 { 5663 upper = TRUE; 5664 aff_entry->ae_chop = NULL; 5665 *p = NUL; 5666 5667 /* The condition is matched with the 5668 * actual word, thus must check for the 5669 * upper-case letter. */ 5670 if (aff_entry->ae_cond != NULL) 5671 { 5672 char_u buf[MAXLINELEN]; 5673 #ifdef FEAT_MBYTE 5674 if (has_mbyte) 5675 { 5676 onecap_copy(items[4], buf, TRUE); 5677 aff_entry->ae_cond = getroom_save( 5678 spin, buf); 5679 } 5680 else 5681 #endif 5682 *aff_entry->ae_cond = c_up; 5683 if (aff_entry->ae_cond != NULL) 5684 { 5685 sprintf((char *)buf, "^%s", 5686 aff_entry->ae_cond); 5687 vim_free(aff_entry->ae_prog); 5688 aff_entry->ae_prog = vim_regcomp( 5689 buf, RE_MAGIC + RE_STRING); 5690 } 5691 } 5692 } 5693 } 5694 } 5695 5696 if (aff_entry->ae_chop == NULL 5697 && aff_entry->ae_flags == NULL) 5698 { 5699 int idx; 5700 char_u **pp; 5701 int n; 5702 5703 /* Find a previously used condition. */ 5704 for (idx = spin->si_prefcond.ga_len - 1; idx >= 0; 5705 --idx) 5706 { 5707 p = ((char_u **)spin->si_prefcond.ga_data)[idx]; 5708 if (str_equal(p, aff_entry->ae_cond)) 5709 break; 5710 } 5711 if (idx < 0 && ga_grow(&spin->si_prefcond, 1) == OK) 5712 { 5713 /* Not found, add a new condition. */ 5714 idx = spin->si_prefcond.ga_len++; 5715 pp = ((char_u **)spin->si_prefcond.ga_data) 5716 + idx; 5717 if (aff_entry->ae_cond == NULL) 5718 *pp = NULL; 5719 else 5720 *pp = getroom_save(spin, 5721 aff_entry->ae_cond); 5722 } 5723 5724 /* Add the prefix to the prefix tree. */ 5725 if (aff_entry->ae_add == NULL) 5726 p = (char_u *)""; 5727 else 5728 p = aff_entry->ae_add; 5729 5730 /* PFX_FLAGS is a negative number, so that 5731 * tree_add_word() knows this is the prefix tree. */ 5732 n = PFX_FLAGS; 5733 if (!cur_aff->ah_combine) 5734 n |= WFP_NC; 5735 if (upper) 5736 n |= WFP_UP; 5737 if (aff_entry->ae_comppermit) 5738 n |= WFP_COMPPERMIT; 5739 if (aff_entry->ae_compforbid) 5740 n |= WFP_COMPFORBID; 5741 tree_add_word(spin, p, spin->si_prefroot, n, 5742 idx, cur_aff->ah_newID); 5743 did_postpone_prefix = TRUE; 5744 } 5745 5746 /* Didn't actually use ah_newID, backup si_newprefID. */ 5747 if (aff_todo == 0 && !did_postpone_prefix) 5748 { 5749 --spin->si_newprefID; 5750 cur_aff->ah_newID = 0; 5751 } 5752 } 5753 } 5754 } 5755 else if (STRCMP(items[0], "FOL") == 0 && itemcnt == 2 5756 && fol == NULL) 5757 { 5758 fol = vim_strsave(items[1]); 5759 } 5760 else if (STRCMP(items[0], "LOW") == 0 && itemcnt == 2 5761 && low == NULL) 5762 { 5763 low = vim_strsave(items[1]); 5764 } 5765 else if (STRCMP(items[0], "UPP") == 0 && itemcnt == 2 5766 && upp == NULL) 5767 { 5768 upp = vim_strsave(items[1]); 5769 } 5770 else if ((STRCMP(items[0], "REP") == 0 5771 || STRCMP(items[0], "REPSAL") == 0) 5772 && itemcnt == 2) 5773 { 5774 /* Ignore REP/REPSAL count */; 5775 if (!isdigit(*items[1])) 5776 smsg((char_u *)_("Expected REP(SAL) count in %s line %d"), 5777 fname, lnum); 5778 } 5779 else if ((STRCMP(items[0], "REP") == 0 5780 || STRCMP(items[0], "REPSAL") == 0) 5781 && itemcnt >= 3) 5782 { 5783 /* REP/REPSAL item */ 5784 /* Myspell ignores extra arguments, we require it starts with 5785 * # to detect mistakes. */ 5786 if (itemcnt > 3 && items[3][0] != '#') 5787 smsg((char_u *)_(e_afftrailing), fname, lnum, items[3]); 5788 if (items[0][3] == 'S' ? do_repsal : do_rep) 5789 { 5790 /* Replace underscore with space (can't include a space 5791 * directly). */ 5792 for (p = items[1]; *p != NUL; mb_ptr_adv(p)) 5793 if (*p == '_') 5794 *p = ' '; 5795 for (p = items[2]; *p != NUL; mb_ptr_adv(p)) 5796 if (*p == '_') 5797 *p = ' '; 5798 add_fromto(spin, items[0][3] == 'S' 5799 ? &spin->si_repsal 5800 : &spin->si_rep, items[1], items[2]); 5801 } 5802 } 5803 else if (STRCMP(items[0], "MAP") == 0 && itemcnt == 2) 5804 { 5805 /* MAP item or count */ 5806 if (!found_map) 5807 { 5808 /* First line contains the count. */ 5809 found_map = TRUE; 5810 if (!isdigit(*items[1])) 5811 smsg((char_u *)_("Expected MAP count in %s line %d"), 5812 fname, lnum); 5813 } 5814 else if (do_mapline) 5815 { 5816 int c; 5817 5818 /* Check that every character appears only once. */ 5819 for (p = items[1]; *p != NUL; ) 5820 { 5821 #ifdef FEAT_MBYTE 5822 c = mb_ptr2char_adv(&p); 5823 #else 5824 c = *p++; 5825 #endif 5826 if ((spin->si_map.ga_len > 0 5827 && vim_strchr(spin->si_map.ga_data, c) 5828 != NULL) 5829 || vim_strchr(p, c) != NULL) 5830 smsg((char_u *)_("Duplicate character in MAP in %s line %d"), 5831 fname, lnum); 5832 } 5833 5834 /* We simply concatenate all the MAP strings, separated by 5835 * slashes. */ 5836 ga_concat(&spin->si_map, items[1]); 5837 ga_append(&spin->si_map, '/'); 5838 } 5839 } 5840 /* Accept "SAL from to" and "SAL from to # comment". */ 5841 else if (STRCMP(items[0], "SAL") == 0 5842 && (itemcnt == 3 || (itemcnt > 3 && items[3][0] == '#'))) 5843 { 5844 if (do_sal) 5845 { 5846 /* SAL item (sounds-a-like) 5847 * Either one of the known keys or a from-to pair. */ 5848 if (STRCMP(items[1], "followup") == 0) 5849 spin->si_followup = sal_to_bool(items[2]); 5850 else if (STRCMP(items[1], "collapse_result") == 0) 5851 spin->si_collapse = sal_to_bool(items[2]); 5852 else if (STRCMP(items[1], "remove_accents") == 0) 5853 spin->si_rem_accents = sal_to_bool(items[2]); 5854 else 5855 /* when "to" is "_" it means empty */ 5856 add_fromto(spin, &spin->si_sal, items[1], 5857 STRCMP(items[2], "_") == 0 ? (char_u *)"" 5858 : items[2]); 5859 } 5860 } 5861 else if (STRCMP(items[0], "SOFOFROM") == 0 && itemcnt == 2 5862 && sofofrom == NULL) 5863 { 5864 sofofrom = getroom_save(spin, items[1]); 5865 } 5866 else if (STRCMP(items[0], "SOFOTO") == 0 && itemcnt == 2 5867 && sofoto == NULL) 5868 { 5869 sofoto = getroom_save(spin, items[1]); 5870 } 5871 else if (STRCMP(items[0], "COMMON") == 0) 5872 { 5873 int i; 5874 5875 for (i = 1; i < itemcnt; ++i) 5876 { 5877 if (HASHITEM_EMPTY(hash_find(&spin->si_commonwords, 5878 items[i]))) 5879 { 5880 p = vim_strsave(items[i]); 5881 if (p == NULL) 5882 break; 5883 hash_add(&spin->si_commonwords, p); 5884 } 5885 } 5886 } 5887 else 5888 smsg((char_u *)_("Unrecognized or duplicate item in %s line %d: %s"), 5889 fname, lnum, items[0]); 5890 } 5891 } 5892 5893 if (fol != NULL || low != NULL || upp != NULL) 5894 { 5895 if (spin->si_clear_chartab) 5896 { 5897 /* Clear the char type tables, don't want to use any of the 5898 * currently used spell properties. */ 5899 init_spell_chartab(); 5900 spin->si_clear_chartab = FALSE; 5901 } 5902 5903 /* 5904 * Don't write a word table for an ASCII file, so that we don't check 5905 * for conflicts with a word table that matches 'encoding'. 5906 * Don't write one for utf-8 either, we use utf_*() and 5907 * mb_get_class(), the list of chars in the file will be incomplete. 5908 */ 5909 if (!spin->si_ascii 5910 #ifdef FEAT_MBYTE 5911 && !enc_utf8 5912 #endif 5913 ) 5914 { 5915 if (fol == NULL || low == NULL || upp == NULL) 5916 smsg((char_u *)_("Missing FOL/LOW/UPP line in %s"), fname); 5917 else 5918 (void)set_spell_chartab(fol, low, upp); 5919 } 5920 5921 vim_free(fol); 5922 vim_free(low); 5923 vim_free(upp); 5924 } 5925 5926 /* Use compound specifications of the .aff file for the spell info. */ 5927 if (compmax != 0) 5928 { 5929 aff_check_number(spin->si_compmax, compmax, "COMPOUNDWORDMAX"); 5930 spin->si_compmax = compmax; 5931 } 5932 5933 if (compminlen != 0) 5934 { 5935 aff_check_number(spin->si_compminlen, compminlen, "COMPOUNDMIN"); 5936 spin->si_compminlen = compminlen; 5937 } 5938 5939 if (compsylmax != 0) 5940 { 5941 if (syllable == NULL) 5942 smsg((char_u *)_("COMPOUNDSYLMAX used without SYLLABLE")); 5943 aff_check_number(spin->si_compsylmax, compsylmax, "COMPOUNDSYLMAX"); 5944 spin->si_compsylmax = compsylmax; 5945 } 5946 5947 if (compoptions != 0) 5948 { 5949 aff_check_number(spin->si_compoptions, compoptions, "COMPOUND options"); 5950 spin->si_compoptions |= compoptions; 5951 } 5952 5953 if (compflags != NULL) 5954 process_compflags(spin, aff, compflags); 5955 5956 /* Check that we didn't use too many renumbered flags. */ 5957 if (spin->si_newcompID < spin->si_newprefID) 5958 { 5959 if (spin->si_newcompID == 127 || spin->si_newcompID == 255) 5960 MSG(_("Too many postponed prefixes")); 5961 else if (spin->si_newprefID == 0 || spin->si_newprefID == 127) 5962 MSG(_("Too many compound flags")); 5963 else 5964 MSG(_("Too many posponed prefixes and/or compound flags")); 5965 } 5966 5967 if (syllable != NULL) 5968 { 5969 aff_check_string(spin->si_syllable, syllable, "SYLLABLE"); 5970 spin->si_syllable = syllable; 5971 } 5972 5973 if (sofofrom != NULL || sofoto != NULL) 5974 { 5975 if (sofofrom == NULL || sofoto == NULL) 5976 smsg((char_u *)_("Missing SOFO%s line in %s"), 5977 sofofrom == NULL ? "FROM" : "TO", fname); 5978 else if (spin->si_sal.ga_len > 0) 5979 smsg((char_u *)_("Both SAL and SOFO lines in %s"), fname); 5980 else 5981 { 5982 aff_check_string(spin->si_sofofr, sofofrom, "SOFOFROM"); 5983 aff_check_string(spin->si_sofoto, sofoto, "SOFOTO"); 5984 spin->si_sofofr = sofofrom; 5985 spin->si_sofoto = sofoto; 5986 } 5987 } 5988 5989 if (midword != NULL) 5990 { 5991 aff_check_string(spin->si_midword, midword, "MIDWORD"); 5992 spin->si_midword = midword; 5993 } 5994 5995 vim_free(pc); 5996 fclose(fd); 5997 return aff; 5998 } 5999 6000 /* 6001 * For affix "entry" move COMPOUNDFORBIDFLAG and COMPOUNDPERMITFLAG from 6002 * ae_flags to ae_comppermit and ae_compforbid. 6003 */ 6004 static void 6005 aff_process_flags(affile, entry) 6006 afffile_T *affile; 6007 affentry_T *entry; 6008 { 6009 char_u *p; 6010 char_u *prevp; 6011 unsigned flag; 6012 6013 if (entry->ae_flags != NULL 6014 && (affile->af_compforbid != 0 || affile->af_comppermit != 0)) 6015 { 6016 for (p = entry->ae_flags; *p != NUL; ) 6017 { 6018 prevp = p; 6019 flag = get_affitem(affile->af_flagtype, &p); 6020 if (flag == affile->af_comppermit || flag == affile->af_compforbid) 6021 { 6022 mch_memmove(prevp, p, STRLEN(p) + 1); 6023 p = prevp; 6024 if (flag == affile->af_comppermit) 6025 entry->ae_comppermit = TRUE; 6026 else 6027 entry->ae_compforbid = TRUE; 6028 } 6029 if (affile->af_flagtype == AFT_NUM && *p == ',') 6030 ++p; 6031 } 6032 if (*entry->ae_flags == NUL) 6033 entry->ae_flags = NULL; /* nothing left */ 6034 } 6035 } 6036 6037 /* 6038 * Return TRUE if "s" is the name of an info item in the affix file. 6039 */ 6040 static int 6041 spell_info_item(s) 6042 char_u *s; 6043 { 6044 return STRCMP(s, "NAME") == 0 6045 || STRCMP(s, "HOME") == 0 6046 || STRCMP(s, "VERSION") == 0 6047 || STRCMP(s, "AUTHOR") == 0 6048 || STRCMP(s, "EMAIL") == 0 6049 || STRCMP(s, "COPYRIGHT") == 0; 6050 } 6051 6052 /* 6053 * Turn an affix flag name into a number, according to the FLAG type. 6054 * returns zero for failure. 6055 */ 6056 static unsigned 6057 affitem2flag(flagtype, item, fname, lnum) 6058 int flagtype; 6059 char_u *item; 6060 char_u *fname; 6061 int lnum; 6062 { 6063 unsigned res; 6064 char_u *p = item; 6065 6066 res = get_affitem(flagtype, &p); 6067 if (res == 0) 6068 { 6069 if (flagtype == AFT_NUM) 6070 smsg((char_u *)_("Flag is not a number in %s line %d: %s"), 6071 fname, lnum, item); 6072 else 6073 smsg((char_u *)_("Illegal flag in %s line %d: %s"), 6074 fname, lnum, item); 6075 } 6076 if (*p != NUL) 6077 { 6078 smsg((char_u *)_(e_affname), fname, lnum, item); 6079 return 0; 6080 } 6081 6082 return res; 6083 } 6084 6085 /* 6086 * Get one affix name from "*pp" and advance the pointer. 6087 * Returns zero for an error, still advances the pointer then. 6088 */ 6089 static unsigned 6090 get_affitem(flagtype, pp) 6091 int flagtype; 6092 char_u **pp; 6093 { 6094 int res; 6095 6096 if (flagtype == AFT_NUM) 6097 { 6098 if (!VIM_ISDIGIT(**pp)) 6099 { 6100 ++*pp; /* always advance, avoid getting stuck */ 6101 return 0; 6102 } 6103 res = getdigits(pp); 6104 } 6105 else 6106 { 6107 #ifdef FEAT_MBYTE 6108 res = mb_ptr2char_adv(pp); 6109 #else 6110 res = *(*pp)++; 6111 #endif 6112 if (flagtype == AFT_LONG || (flagtype == AFT_CAPLONG 6113 && res >= 'A' && res <= 'Z')) 6114 { 6115 if (**pp == NUL) 6116 return 0; 6117 #ifdef FEAT_MBYTE 6118 res = mb_ptr2char_adv(pp) + (res << 16); 6119 #else 6120 res = *(*pp)++ + (res << 16); 6121 #endif 6122 } 6123 } 6124 return res; 6125 } 6126 6127 /* 6128 * Process the "compflags" string used in an affix file and append it to 6129 * spin->si_compflags. 6130 * The processing involves changing the affix names to ID numbers, so that 6131 * they fit in one byte. 6132 */ 6133 static void 6134 process_compflags(spin, aff, compflags) 6135 spellinfo_T *spin; 6136 afffile_T *aff; 6137 char_u *compflags; 6138 { 6139 char_u *p; 6140 char_u *prevp; 6141 unsigned flag; 6142 compitem_T *ci; 6143 int id; 6144 int len; 6145 char_u *tp; 6146 char_u key[AH_KEY_LEN]; 6147 hashitem_T *hi; 6148 6149 /* Make room for the old and the new compflags, concatenated with a / in 6150 * between. Processing it makes it shorter, but we don't know by how 6151 * much, thus allocate the maximum. */ 6152 len = (int)STRLEN(compflags) + 1; 6153 if (spin->si_compflags != NULL) 6154 len += (int)STRLEN(spin->si_compflags) + 1; 6155 p = getroom(spin, len, FALSE); 6156 if (p == NULL) 6157 return; 6158 if (spin->si_compflags != NULL) 6159 { 6160 STRCPY(p, spin->si_compflags); 6161 STRCAT(p, "/"); 6162 } 6163 spin->si_compflags = p; 6164 tp = p + STRLEN(p); 6165 6166 for (p = compflags; *p != NUL; ) 6167 { 6168 if (vim_strchr((char_u *)"/*+[]", *p) != NULL) 6169 /* Copy non-flag characters directly. */ 6170 *tp++ = *p++; 6171 else 6172 { 6173 /* First get the flag number, also checks validity. */ 6174 prevp = p; 6175 flag = get_affitem(aff->af_flagtype, &p); 6176 if (flag != 0) 6177 { 6178 /* Find the flag in the hashtable. If it was used before, use 6179 * the existing ID. Otherwise add a new entry. */ 6180 vim_strncpy(key, prevp, p - prevp); 6181 hi = hash_find(&aff->af_comp, key); 6182 if (!HASHITEM_EMPTY(hi)) 6183 id = HI2CI(hi)->ci_newID; 6184 else 6185 { 6186 ci = (compitem_T *)getroom(spin, sizeof(compitem_T), TRUE); 6187 if (ci == NULL) 6188 break; 6189 STRCPY(ci->ci_key, key); 6190 ci->ci_flag = flag; 6191 /* Avoid using a flag ID that has a special meaning in a 6192 * regexp (also inside []). */ 6193 do 6194 { 6195 check_renumber(spin); 6196 id = spin->si_newcompID--; 6197 } while (vim_strchr((char_u *)"/+*[]\\-^", id) != NULL); 6198 ci->ci_newID = id; 6199 hash_add(&aff->af_comp, ci->ci_key); 6200 } 6201 *tp++ = id; 6202 } 6203 if (aff->af_flagtype == AFT_NUM && *p == ',') 6204 ++p; 6205 } 6206 } 6207 6208 *tp = NUL; 6209 } 6210 6211 /* 6212 * Check that the new IDs for postponed affixes and compounding don't overrun 6213 * each other. We have almost 255 available, but start at 0-127 to avoid 6214 * using two bytes for utf-8. When the 0-127 range is used up go to 128-255. 6215 * When that is used up an error message is given. 6216 */ 6217 static void 6218 check_renumber(spin) 6219 spellinfo_T *spin; 6220 { 6221 if (spin->si_newprefID == spin->si_newcompID && spin->si_newcompID < 128) 6222 { 6223 spin->si_newprefID = 127; 6224 spin->si_newcompID = 255; 6225 } 6226 } 6227 6228 /* 6229 * Return TRUE if flag "flag" appears in affix list "afflist". 6230 */ 6231 static int 6232 flag_in_afflist(flagtype, afflist, flag) 6233 int flagtype; 6234 char_u *afflist; 6235 unsigned flag; 6236 { 6237 char_u *p; 6238 unsigned n; 6239 6240 switch (flagtype) 6241 { 6242 case AFT_CHAR: 6243 return vim_strchr(afflist, flag) != NULL; 6244 6245 case AFT_CAPLONG: 6246 case AFT_LONG: 6247 for (p = afflist; *p != NUL; ) 6248 { 6249 #ifdef FEAT_MBYTE 6250 n = mb_ptr2char_adv(&p); 6251 #else 6252 n = *p++; 6253 #endif 6254 if ((flagtype == AFT_LONG || (n >= 'A' && n <= 'Z')) 6255 && *p != NUL) 6256 #ifdef FEAT_MBYTE 6257 n = mb_ptr2char_adv(&p) + (n << 16); 6258 #else 6259 n = *p++ + (n << 16); 6260 #endif 6261 if (n == flag) 6262 return TRUE; 6263 } 6264 break; 6265 6266 case AFT_NUM: 6267 for (p = afflist; *p != NUL; ) 6268 { 6269 n = getdigits(&p); 6270 if (n == flag) 6271 return TRUE; 6272 if (*p != NUL) /* skip over comma */ 6273 ++p; 6274 } 6275 break; 6276 } 6277 return FALSE; 6278 } 6279 6280 /* 6281 * Give a warning when "spinval" and "affval" numbers are set and not the same. 6282 */ 6283 static void 6284 aff_check_number(spinval, affval, name) 6285 int spinval; 6286 int affval; 6287 char *name; 6288 { 6289 if (spinval != 0 && spinval != affval) 6290 smsg((char_u *)_("%s value differs from what is used in another .aff file"), name); 6291 } 6292 6293 /* 6294 * Give a warning when "spinval" and "affval" strings are set and not the same. 6295 */ 6296 static void 6297 aff_check_string(spinval, affval, name) 6298 char_u *spinval; 6299 char_u *affval; 6300 char *name; 6301 { 6302 if (spinval != NULL && STRCMP(spinval, affval) != 0) 6303 smsg((char_u *)_("%s value differs from what is used in another .aff file"), name); 6304 } 6305 6306 /* 6307 * Return TRUE if strings "s1" and "s2" are equal. Also consider both being 6308 * NULL as equal. 6309 */ 6310 static int 6311 str_equal(s1, s2) 6312 char_u *s1; 6313 char_u *s2; 6314 { 6315 if (s1 == NULL || s2 == NULL) 6316 return s1 == s2; 6317 return STRCMP(s1, s2) == 0; 6318 } 6319 6320 /* 6321 * Add a from-to item to "gap". Used for REP and SAL items. 6322 * They are stored case-folded. 6323 */ 6324 static void 6325 add_fromto(spin, gap, from, to) 6326 spellinfo_T *spin; 6327 garray_T *gap; 6328 char_u *from; 6329 char_u *to; 6330 { 6331 fromto_T *ftp; 6332 char_u word[MAXWLEN]; 6333 6334 if (ga_grow(gap, 1) == OK) 6335 { 6336 ftp = ((fromto_T *)gap->ga_data) + gap->ga_len; 6337 (void)spell_casefold(from, (int)STRLEN(from), word, MAXWLEN); 6338 ftp->ft_from = getroom_save(spin, word); 6339 (void)spell_casefold(to, (int)STRLEN(to), word, MAXWLEN); 6340 ftp->ft_to = getroom_save(spin, word); 6341 ++gap->ga_len; 6342 } 6343 } 6344 6345 /* 6346 * Convert a boolean argument in a SAL line to TRUE or FALSE; 6347 */ 6348 static int 6349 sal_to_bool(s) 6350 char_u *s; 6351 { 6352 return STRCMP(s, "1") == 0 || STRCMP(s, "true") == 0; 6353 } 6354 6355 /* 6356 * Return TRUE if string "s" contains a non-ASCII character (128 or higher). 6357 * When "s" is NULL FALSE is returned. 6358 */ 6359 static int 6360 has_non_ascii(s) 6361 char_u *s; 6362 { 6363 char_u *p; 6364 6365 if (s != NULL) 6366 for (p = s; *p != NUL; ++p) 6367 if (*p >= 128) 6368 return TRUE; 6369 return FALSE; 6370 } 6371 6372 /* 6373 * Free the structure filled by spell_read_aff(). 6374 */ 6375 static void 6376 spell_free_aff(aff) 6377 afffile_T *aff; 6378 { 6379 hashtab_T *ht; 6380 hashitem_T *hi; 6381 int todo; 6382 affheader_T *ah; 6383 affentry_T *ae; 6384 6385 vim_free(aff->af_enc); 6386 6387 /* All this trouble to free the "ae_prog" items... */ 6388 for (ht = &aff->af_pref; ; ht = &aff->af_suff) 6389 { 6390 todo = (int)ht->ht_used; 6391 for (hi = ht->ht_array; todo > 0; ++hi) 6392 { 6393 if (!HASHITEM_EMPTY(hi)) 6394 { 6395 --todo; 6396 ah = HI2AH(hi); 6397 for (ae = ah->ah_first; ae != NULL; ae = ae->ae_next) 6398 vim_free(ae->ae_prog); 6399 } 6400 } 6401 if (ht == &aff->af_suff) 6402 break; 6403 } 6404 6405 hash_clear(&aff->af_pref); 6406 hash_clear(&aff->af_suff); 6407 hash_clear(&aff->af_comp); 6408 } 6409 6410 /* 6411 * Read dictionary file "fname". 6412 * Returns OK or FAIL; 6413 */ 6414 static int 6415 spell_read_dic(spin, fname, affile) 6416 spellinfo_T *spin; 6417 char_u *fname; 6418 afffile_T *affile; 6419 { 6420 hashtab_T ht; 6421 char_u line[MAXLINELEN]; 6422 char_u *p; 6423 char_u *afflist; 6424 char_u store_afflist[MAXWLEN]; 6425 int pfxlen; 6426 int need_affix; 6427 char_u *dw; 6428 char_u *pc; 6429 char_u *w; 6430 int l; 6431 hash_T hash; 6432 hashitem_T *hi; 6433 FILE *fd; 6434 int lnum = 1; 6435 int non_ascii = 0; 6436 int retval = OK; 6437 char_u message[MAXLINELEN + MAXWLEN]; 6438 int flags; 6439 int duplicate = 0; 6440 6441 /* 6442 * Open the file. 6443 */ 6444 fd = mch_fopen((char *)fname, "r"); 6445 if (fd == NULL) 6446 { 6447 EMSG2(_(e_notopen), fname); 6448 return FAIL; 6449 } 6450 6451 /* The hashtable is only used to detect duplicated words. */ 6452 hash_init(&ht); 6453 6454 vim_snprintf((char *)IObuff, IOSIZE, 6455 _("Reading dictionary file %s ..."), fname); 6456 spell_message(spin, IObuff); 6457 6458 /* start with a message for the first line */ 6459 spin->si_msg_count = 999999; 6460 6461 /* Read and ignore the first line: word count. */ 6462 (void)vim_fgets(line, MAXLINELEN, fd); 6463 if (!vim_isdigit(*skipwhite(line))) 6464 EMSG2(_("E760: No word count in %s"), fname); 6465 6466 /* 6467 * Read all the lines in the file one by one. 6468 * The words are converted to 'encoding' here, before being added to 6469 * the hashtable. 6470 */ 6471 while (!vim_fgets(line, MAXLINELEN, fd) && !got_int) 6472 { 6473 line_breakcheck(); 6474 ++lnum; 6475 if (line[0] == '#' || line[0] == '/') 6476 continue; /* comment line */ 6477 6478 /* Remove CR, LF and white space from the end. White space halfway 6479 * the word is kept to allow e.g., "et al.". */ 6480 l = (int)STRLEN(line); 6481 while (l > 0 && line[l - 1] <= ' ') 6482 --l; 6483 if (l == 0) 6484 continue; /* empty line */ 6485 line[l] = NUL; 6486 6487 #ifdef FEAT_MBYTE 6488 /* Convert from "SET" to 'encoding' when needed. */ 6489 if (spin->si_conv.vc_type != CONV_NONE) 6490 { 6491 pc = string_convert(&spin->si_conv, line, NULL); 6492 if (pc == NULL) 6493 { 6494 smsg((char_u *)_("Conversion failure for word in %s line %d: %s"), 6495 fname, lnum, line); 6496 continue; 6497 } 6498 w = pc; 6499 } 6500 else 6501 #endif 6502 { 6503 pc = NULL; 6504 w = line; 6505 } 6506 6507 /* Truncate the word at the "/", set "afflist" to what follows. 6508 * Replace "\/" by "/" and "\\" by "\". */ 6509 afflist = NULL; 6510 for (p = w; *p != NUL; mb_ptr_adv(p)) 6511 { 6512 if (*p == '\\' && (p[1] == '\\' || p[1] == '/')) 6513 mch_memmove(p, p + 1, STRLEN(p)); 6514 else if (*p == '/') 6515 { 6516 *p = NUL; 6517 afflist = p + 1; 6518 break; 6519 } 6520 } 6521 6522 /* Skip non-ASCII words when "spin->si_ascii" is TRUE. */ 6523 if (spin->si_ascii && has_non_ascii(w)) 6524 { 6525 ++non_ascii; 6526 vim_free(pc); 6527 continue; 6528 } 6529 6530 /* This takes time, print a message every 10000 words. */ 6531 if (spin->si_verbose && spin->si_msg_count > 10000) 6532 { 6533 spin->si_msg_count = 0; 6534 vim_snprintf((char *)message, sizeof(message), 6535 _("line %6d, word %6d - %s"), 6536 lnum, spin->si_foldwcount + spin->si_keepwcount, w); 6537 msg_start(); 6538 msg_puts_long_attr(message, 0); 6539 msg_clr_eos(); 6540 msg_didout = FALSE; 6541 msg_col = 0; 6542 out_flush(); 6543 } 6544 6545 /* Store the word in the hashtable to be able to find duplicates. */ 6546 dw = (char_u *)getroom_save(spin, w); 6547 if (dw == NULL) 6548 { 6549 retval = FAIL; 6550 vim_free(pc); 6551 break; 6552 } 6553 6554 hash = hash_hash(dw); 6555 hi = hash_lookup(&ht, dw, hash); 6556 if (!HASHITEM_EMPTY(hi)) 6557 { 6558 if (p_verbose > 0) 6559 smsg((char_u *)_("Duplicate word in %s line %d: %s"), 6560 fname, lnum, dw); 6561 else if (duplicate == 0) 6562 smsg((char_u *)_("First duplicate word in %s line %d: %s"), 6563 fname, lnum, dw); 6564 ++duplicate; 6565 } 6566 else 6567 hash_add_item(&ht, hi, dw, hash); 6568 6569 flags = 0; 6570 store_afflist[0] = NUL; 6571 pfxlen = 0; 6572 need_affix = FALSE; 6573 if (afflist != NULL) 6574 { 6575 /* Extract flags from the affix list. */ 6576 flags |= get_affix_flags(affile, afflist); 6577 6578 if (affile->af_needaffix != 0 && flag_in_afflist( 6579 affile->af_flagtype, afflist, affile->af_needaffix)) 6580 need_affix = TRUE; 6581 6582 if (affile->af_pfxpostpone) 6583 /* Need to store the list of prefix IDs with the word. */ 6584 pfxlen = get_pfxlist(affile, afflist, store_afflist); 6585 6586 if (spin->si_compflags != NULL) 6587 /* Need to store the list of compound flags with the word. 6588 * Concatenate them to the list of prefix IDs. */ 6589 get_compflags(affile, afflist, store_afflist + pfxlen); 6590 } 6591 6592 /* Add the word to the word tree(s). */ 6593 if (store_word(spin, dw, flags, spin->si_region, 6594 store_afflist, need_affix) == FAIL) 6595 retval = FAIL; 6596 6597 if (afflist != NULL) 6598 { 6599 /* Find all matching suffixes and add the resulting words. 6600 * Additionally do matching prefixes that combine. */ 6601 if (store_aff_word(spin, dw, afflist, affile, 6602 &affile->af_suff, &affile->af_pref, 6603 CONDIT_SUF, flags, store_afflist, pfxlen) == FAIL) 6604 retval = FAIL; 6605 6606 /* Find all matching prefixes and add the resulting words. */ 6607 if (store_aff_word(spin, dw, afflist, affile, 6608 &affile->af_pref, NULL, 6609 CONDIT_SUF, flags, store_afflist, pfxlen) == FAIL) 6610 retval = FAIL; 6611 } 6612 6613 vim_free(pc); 6614 } 6615 6616 if (duplicate > 0) 6617 smsg((char_u *)_("%d duplicate word(s) in %s"), duplicate, fname); 6618 if (spin->si_ascii && non_ascii > 0) 6619 smsg((char_u *)_("Ignored %d word(s) with non-ASCII characters in %s"), 6620 non_ascii, fname); 6621 hash_clear(&ht); 6622 6623 fclose(fd); 6624 return retval; 6625 } 6626 6627 /* 6628 * Check for affix flags in "afflist" that are turned into word flags. 6629 * Return WF_ flags. 6630 */ 6631 static int 6632 get_affix_flags(affile, afflist) 6633 afffile_T *affile; 6634 char_u *afflist; 6635 { 6636 int flags = 0; 6637 6638 if (affile->af_keepcase != 0 && flag_in_afflist( 6639 affile->af_flagtype, afflist, affile->af_keepcase)) 6640 flags |= WF_KEEPCAP | WF_FIXCAP; 6641 if (affile->af_rare != 0 && flag_in_afflist( 6642 affile->af_flagtype, afflist, affile->af_rare)) 6643 flags |= WF_RARE; 6644 if (affile->af_bad != 0 && flag_in_afflist( 6645 affile->af_flagtype, afflist, affile->af_bad)) 6646 flags |= WF_BANNED; 6647 if (affile->af_needcomp != 0 && flag_in_afflist( 6648 affile->af_flagtype, afflist, affile->af_needcomp)) 6649 flags |= WF_NEEDCOMP; 6650 if (affile->af_comproot != 0 && flag_in_afflist( 6651 affile->af_flagtype, afflist, affile->af_comproot)) 6652 flags |= WF_COMPROOT; 6653 if (affile->af_nosuggest != 0 && flag_in_afflist( 6654 affile->af_flagtype, afflist, affile->af_nosuggest)) 6655 flags |= WF_NOSUGGEST; 6656 return flags; 6657 } 6658 6659 /* 6660 * Get the list of prefix IDs from the affix list "afflist". 6661 * Used for PFXPOSTPONE. 6662 * Put the resulting flags in "store_afflist[MAXWLEN]" with a terminating NUL 6663 * and return the number of affixes. 6664 */ 6665 static int 6666 get_pfxlist(affile, afflist, store_afflist) 6667 afffile_T *affile; 6668 char_u *afflist; 6669 char_u *store_afflist; 6670 { 6671 char_u *p; 6672 char_u *prevp; 6673 int cnt = 0; 6674 int id; 6675 char_u key[AH_KEY_LEN]; 6676 hashitem_T *hi; 6677 6678 for (p = afflist; *p != NUL; ) 6679 { 6680 prevp = p; 6681 if (get_affitem(affile->af_flagtype, &p) != 0) 6682 { 6683 /* A flag is a postponed prefix flag if it appears in "af_pref" 6684 * and it's ID is not zero. */ 6685 vim_strncpy(key, prevp, p - prevp); 6686 hi = hash_find(&affile->af_pref, key); 6687 if (!HASHITEM_EMPTY(hi)) 6688 { 6689 id = HI2AH(hi)->ah_newID; 6690 if (id != 0) 6691 store_afflist[cnt++] = id; 6692 } 6693 } 6694 if (affile->af_flagtype == AFT_NUM && *p == ',') 6695 ++p; 6696 } 6697 6698 store_afflist[cnt] = NUL; 6699 return cnt; 6700 } 6701 6702 /* 6703 * Get the list of compound IDs from the affix list "afflist" that are used 6704 * for compound words. 6705 * Puts the flags in "store_afflist[]". 6706 */ 6707 static void 6708 get_compflags(affile, afflist, store_afflist) 6709 afffile_T *affile; 6710 char_u *afflist; 6711 char_u *store_afflist; 6712 { 6713 char_u *p; 6714 char_u *prevp; 6715 int cnt = 0; 6716 char_u key[AH_KEY_LEN]; 6717 hashitem_T *hi; 6718 6719 for (p = afflist; *p != NUL; ) 6720 { 6721 prevp = p; 6722 if (get_affitem(affile->af_flagtype, &p) != 0) 6723 { 6724 /* A flag is a compound flag if it appears in "af_comp". */ 6725 vim_strncpy(key, prevp, p - prevp); 6726 hi = hash_find(&affile->af_comp, key); 6727 if (!HASHITEM_EMPTY(hi)) 6728 store_afflist[cnt++] = HI2CI(hi)->ci_newID; 6729 } 6730 if (affile->af_flagtype == AFT_NUM && *p == ',') 6731 ++p; 6732 } 6733 6734 store_afflist[cnt] = NUL; 6735 } 6736 6737 /* 6738 * Apply affixes to a word and store the resulting words. 6739 * "ht" is the hashtable with affentry_T that need to be applied, either 6740 * prefixes or suffixes. 6741 * "xht", when not NULL, is the prefix hashtable, to be used additionally on 6742 * the resulting words for combining affixes. 6743 * 6744 * Returns FAIL when out of memory. 6745 */ 6746 static int 6747 store_aff_word(spin, word, afflist, affile, ht, xht, condit, flags, 6748 pfxlist, pfxlen) 6749 spellinfo_T *spin; /* spell info */ 6750 char_u *word; /* basic word start */ 6751 char_u *afflist; /* list of names of supported affixes */ 6752 afffile_T *affile; 6753 hashtab_T *ht; 6754 hashtab_T *xht; 6755 int condit; /* CONDIT_SUF et al. */ 6756 int flags; /* flags for the word */ 6757 char_u *pfxlist; /* list of prefix IDs */ 6758 int pfxlen; /* nr of flags in "pfxlist" for prefixes, rest 6759 * is compound flags */ 6760 { 6761 int todo; 6762 hashitem_T *hi; 6763 affheader_T *ah; 6764 affentry_T *ae; 6765 regmatch_T regmatch; 6766 char_u newword[MAXWLEN]; 6767 int retval = OK; 6768 int i, j; 6769 char_u *p; 6770 int use_flags; 6771 char_u *use_pfxlist; 6772 int use_pfxlen; 6773 int need_affix; 6774 char_u store_afflist[MAXWLEN]; 6775 char_u pfx_pfxlist[MAXWLEN]; 6776 size_t wordlen = STRLEN(word); 6777 int use_condit; 6778 6779 todo = (int)ht->ht_used; 6780 for (hi = ht->ht_array; todo > 0 && retval == OK; ++hi) 6781 { 6782 if (!HASHITEM_EMPTY(hi)) 6783 { 6784 --todo; 6785 ah = HI2AH(hi); 6786 6787 /* Check that the affix combines, if required, and that the word 6788 * supports this affix. */ 6789 if (((condit & CONDIT_COMB) == 0 || ah->ah_combine) 6790 && flag_in_afflist(affile->af_flagtype, afflist, 6791 ah->ah_flag)) 6792 { 6793 /* Loop over all affix entries with this name. */ 6794 for (ae = ah->ah_first; ae != NULL; ae = ae->ae_next) 6795 { 6796 /* Check the condition. It's not logical to match case 6797 * here, but it is required for compatibility with 6798 * Myspell. 6799 * Another requirement from Myspell is that the chop 6800 * string is shorter than the word itself. 6801 * For prefixes, when "PFXPOSTPONE" was used, only do 6802 * prefixes with a chop string and/or flags. 6803 * When a previously added affix had CIRCUMFIX this one 6804 * must have it too, if it had not then this one must not 6805 * have one either. */ 6806 regmatch.regprog = ae->ae_prog; 6807 regmatch.rm_ic = FALSE; 6808 if ((xht != NULL || !affile->af_pfxpostpone 6809 || ae->ae_chop != NULL 6810 || ae->ae_flags != NULL) 6811 && (ae->ae_chop == NULL 6812 || STRLEN(ae->ae_chop) < wordlen) 6813 && (ae->ae_prog == NULL 6814 || vim_regexec(®match, word, (colnr_T)0)) 6815 && (((condit & CONDIT_CFIX) == 0) 6816 == ((condit & CONDIT_AFF) == 0 6817 || ae->ae_flags == NULL 6818 || !flag_in_afflist(affile->af_flagtype, 6819 ae->ae_flags, affile->af_circumfix)))) 6820 { 6821 /* Match. Remove the chop and add the affix. */ 6822 if (xht == NULL) 6823 { 6824 /* prefix: chop/add at the start of the word */ 6825 if (ae->ae_add == NULL) 6826 *newword = NUL; 6827 else 6828 STRCPY(newword, ae->ae_add); 6829 p = word; 6830 if (ae->ae_chop != NULL) 6831 { 6832 /* Skip chop string. */ 6833 #ifdef FEAT_MBYTE 6834 if (has_mbyte) 6835 { 6836 i = mb_charlen(ae->ae_chop); 6837 for ( ; i > 0; --i) 6838 mb_ptr_adv(p); 6839 } 6840 else 6841 #endif 6842 p += STRLEN(ae->ae_chop); 6843 } 6844 STRCAT(newword, p); 6845 } 6846 else 6847 { 6848 /* suffix: chop/add at the end of the word */ 6849 STRCPY(newword, word); 6850 if (ae->ae_chop != NULL) 6851 { 6852 /* Remove chop string. */ 6853 p = newword + STRLEN(newword); 6854 i = (int)MB_CHARLEN(ae->ae_chop); 6855 for ( ; i > 0; --i) 6856 mb_ptr_back(newword, p); 6857 *p = NUL; 6858 } 6859 if (ae->ae_add != NULL) 6860 STRCAT(newword, ae->ae_add); 6861 } 6862 6863 use_flags = flags; 6864 use_pfxlist = pfxlist; 6865 use_pfxlen = pfxlen; 6866 need_affix = FALSE; 6867 use_condit = condit | CONDIT_COMB | CONDIT_AFF; 6868 if (ae->ae_flags != NULL) 6869 { 6870 /* Extract flags from the affix list. */ 6871 use_flags |= get_affix_flags(affile, ae->ae_flags); 6872 6873 if (affile->af_needaffix != 0 && flag_in_afflist( 6874 affile->af_flagtype, ae->ae_flags, 6875 affile->af_needaffix)) 6876 need_affix = TRUE; 6877 6878 /* When there is a CIRCUMFIX flag the other affix 6879 * must also have it and we don't add the word 6880 * with one affix. */ 6881 if (affile->af_circumfix != 0 && flag_in_afflist( 6882 affile->af_flagtype, ae->ae_flags, 6883 affile->af_circumfix)) 6884 { 6885 use_condit |= CONDIT_CFIX; 6886 if ((condit & CONDIT_CFIX) == 0) 6887 need_affix = TRUE; 6888 } 6889 6890 if (affile->af_pfxpostpone 6891 || spin->si_compflags != NULL) 6892 { 6893 if (affile->af_pfxpostpone) 6894 /* Get prefix IDS from the affix list. */ 6895 use_pfxlen = get_pfxlist(affile, 6896 ae->ae_flags, store_afflist); 6897 else 6898 use_pfxlen = 0; 6899 use_pfxlist = store_afflist; 6900 6901 /* Combine the prefix IDs. Avoid adding the 6902 * same ID twice. */ 6903 for (i = 0; i < pfxlen; ++i) 6904 { 6905 for (j = 0; j < use_pfxlen; ++j) 6906 if (pfxlist[i] == use_pfxlist[j]) 6907 break; 6908 if (j == use_pfxlen) 6909 use_pfxlist[use_pfxlen++] = pfxlist[i]; 6910 } 6911 6912 if (spin->si_compflags != NULL) 6913 /* Get compound IDS from the affix list. */ 6914 get_compflags(affile, ae->ae_flags, 6915 use_pfxlist + use_pfxlen); 6916 6917 /* Combine the list of compound flags. 6918 * Concatenate them to the prefix IDs list. 6919 * Avoid adding the same ID twice. */ 6920 for (i = pfxlen; pfxlist[i] != NUL; ++i) 6921 { 6922 for (j = use_pfxlen; 6923 use_pfxlist[j] != NUL; ++j) 6924 if (pfxlist[i] == use_pfxlist[j]) 6925 break; 6926 if (use_pfxlist[j] == NUL) 6927 { 6928 use_pfxlist[j++] = pfxlist[i]; 6929 use_pfxlist[j] = NUL; 6930 } 6931 } 6932 } 6933 } 6934 6935 /* Obey a "COMPOUNDFORBIDFLAG" of the affix: don't 6936 * use the compound flags. */ 6937 if (use_pfxlist != NULL && ae->ae_compforbid) 6938 { 6939 vim_strncpy(pfx_pfxlist, use_pfxlist, use_pfxlen); 6940 use_pfxlist = pfx_pfxlist; 6941 } 6942 6943 /* When there are postponed prefixes... */ 6944 if (spin->si_prefroot != NULL 6945 && spin->si_prefroot->wn_sibling != NULL) 6946 { 6947 /* ... add a flag to indicate an affix was used. */ 6948 use_flags |= WF_HAS_AFF; 6949 6950 /* ... don't use a prefix list if combining 6951 * affixes is not allowed. But do use the 6952 * compound flags after them. */ 6953 if (!ah->ah_combine && use_pfxlist != NULL) 6954 use_pfxlist += use_pfxlen; 6955 } 6956 6957 /* When compounding is supported and there is no 6958 * "COMPOUNDPERMITFLAG" then forbid compounding on the 6959 * side where the affix is applied. */ 6960 if (spin->si_compflags != NULL && !ae->ae_comppermit) 6961 { 6962 if (xht != NULL) 6963 use_flags |= WF_NOCOMPAFT; 6964 else 6965 use_flags |= WF_NOCOMPBEF; 6966 } 6967 6968 /* Store the modified word. */ 6969 if (store_word(spin, newword, use_flags, 6970 spin->si_region, use_pfxlist, 6971 need_affix) == FAIL) 6972 retval = FAIL; 6973 6974 /* When added a prefix or a first suffix and the affix 6975 * has flags may add a(nother) suffix. RECURSIVE! */ 6976 if ((condit & CONDIT_SUF) && ae->ae_flags != NULL) 6977 if (store_aff_word(spin, newword, ae->ae_flags, 6978 affile, &affile->af_suff, xht, 6979 use_condit & (xht == NULL 6980 ? ~0 : ~CONDIT_SUF), 6981 use_flags, use_pfxlist, pfxlen) == FAIL) 6982 retval = FAIL; 6983 6984 /* When added a suffix and combining is allowed also 6985 * try adding a prefix additionally. Both for the 6986 * word flags and for the affix flags. RECURSIVE! */ 6987 if (xht != NULL && ah->ah_combine) 6988 { 6989 if (store_aff_word(spin, newword, 6990 afflist, affile, 6991 xht, NULL, use_condit, 6992 use_flags, use_pfxlist, 6993 pfxlen) == FAIL 6994 || (ae->ae_flags != NULL 6995 && store_aff_word(spin, newword, 6996 ae->ae_flags, affile, 6997 xht, NULL, use_condit, 6998 use_flags, use_pfxlist, 6999 pfxlen) == FAIL)) 7000 retval = FAIL; 7001 } 7002 } 7003 } 7004 } 7005 } 7006 } 7007 7008 return retval; 7009 } 7010 7011 /* 7012 * Read a file with a list of words. 7013 */ 7014 static int 7015 spell_read_wordfile(spin, fname) 7016 spellinfo_T *spin; 7017 char_u *fname; 7018 { 7019 FILE *fd; 7020 long lnum = 0; 7021 char_u rline[MAXLINELEN]; 7022 char_u *line; 7023 char_u *pc = NULL; 7024 char_u *p; 7025 int l; 7026 int retval = OK; 7027 int did_word = FALSE; 7028 int non_ascii = 0; 7029 int flags; 7030 int regionmask; 7031 7032 /* 7033 * Open the file. 7034 */ 7035 fd = mch_fopen((char *)fname, "r"); 7036 if (fd == NULL) 7037 { 7038 EMSG2(_(e_notopen), fname); 7039 return FAIL; 7040 } 7041 7042 vim_snprintf((char *)IObuff, IOSIZE, _("Reading word file %s ..."), fname); 7043 spell_message(spin, IObuff); 7044 7045 /* 7046 * Read all the lines in the file one by one. 7047 */ 7048 while (!vim_fgets(rline, MAXLINELEN, fd) && !got_int) 7049 { 7050 line_breakcheck(); 7051 ++lnum; 7052 7053 /* Skip comment lines. */ 7054 if (*rline == '#') 7055 continue; 7056 7057 /* Remove CR, LF and white space from the end. */ 7058 l = (int)STRLEN(rline); 7059 while (l > 0 && rline[l - 1] <= ' ') 7060 --l; 7061 if (l == 0) 7062 continue; /* empty or blank line */ 7063 rline[l] = NUL; 7064 7065 /* Convert from "/encoding={encoding}" to 'encoding' when needed. */ 7066 vim_free(pc); 7067 #ifdef FEAT_MBYTE 7068 if (spin->si_conv.vc_type != CONV_NONE) 7069 { 7070 pc = string_convert(&spin->si_conv, rline, NULL); 7071 if (pc == NULL) 7072 { 7073 smsg((char_u *)_("Conversion failure for word in %s line %d: %s"), 7074 fname, lnum, rline); 7075 continue; 7076 } 7077 line = pc; 7078 } 7079 else 7080 #endif 7081 { 7082 pc = NULL; 7083 line = rline; 7084 } 7085 7086 if (*line == '/') 7087 { 7088 ++line; 7089 if (STRNCMP(line, "encoding=", 9) == 0) 7090 { 7091 if (spin->si_conv.vc_type != CONV_NONE) 7092 smsg((char_u *)_("Duplicate /encoding= line ignored in %s line %d: %s"), 7093 fname, lnum, line - 1); 7094 else if (did_word) 7095 smsg((char_u *)_("/encoding= line after word ignored in %s line %d: %s"), 7096 fname, lnum, line - 1); 7097 else 7098 { 7099 #ifdef FEAT_MBYTE 7100 char_u *enc; 7101 7102 /* Setup for conversion to 'encoding'. */ 7103 line += 9; 7104 enc = enc_canonize(line); 7105 if (enc != NULL && !spin->si_ascii 7106 && convert_setup(&spin->si_conv, enc, 7107 p_enc) == FAIL) 7108 smsg((char_u *)_("Conversion in %s not supported: from %s to %s"), 7109 fname, line, p_enc); 7110 vim_free(enc); 7111 spin->si_conv.vc_fail = TRUE; 7112 #else 7113 smsg((char_u *)_("Conversion in %s not supported"), fname); 7114 #endif 7115 } 7116 continue; 7117 } 7118 7119 if (STRNCMP(line, "regions=", 8) == 0) 7120 { 7121 if (spin->si_region_count > 1) 7122 smsg((char_u *)_("Duplicate /regions= line ignored in %s line %d: %s"), 7123 fname, lnum, line); 7124 else 7125 { 7126 line += 8; 7127 if (STRLEN(line) > 16) 7128 smsg((char_u *)_("Too many regions in %s line %d: %s"), 7129 fname, lnum, line); 7130 else 7131 { 7132 spin->si_region_count = (int)STRLEN(line) / 2; 7133 STRCPY(spin->si_region_name, line); 7134 7135 /* Adjust the mask for a word valid in all regions. */ 7136 spin->si_region = (1 << spin->si_region_count) - 1; 7137 } 7138 } 7139 continue; 7140 } 7141 7142 smsg((char_u *)_("/ line ignored in %s line %d: %s"), 7143 fname, lnum, line - 1); 7144 continue; 7145 } 7146 7147 flags = 0; 7148 regionmask = spin->si_region; 7149 7150 /* Check for flags and region after a slash. */ 7151 p = vim_strchr(line, '/'); 7152 if (p != NULL) 7153 { 7154 *p++ = NUL; 7155 while (*p != NUL) 7156 { 7157 if (*p == '=') /* keep-case word */ 7158 flags |= WF_KEEPCAP | WF_FIXCAP; 7159 else if (*p == '!') /* Bad, bad, wicked word. */ 7160 flags |= WF_BANNED; 7161 else if (*p == '?') /* Rare word. */ 7162 flags |= WF_RARE; 7163 else if (VIM_ISDIGIT(*p)) /* region number(s) */ 7164 { 7165 if ((flags & WF_REGION) == 0) /* first one */ 7166 regionmask = 0; 7167 flags |= WF_REGION; 7168 7169 l = *p - '0'; 7170 if (l > spin->si_region_count) 7171 { 7172 smsg((char_u *)_("Invalid region nr in %s line %d: %s"), 7173 fname, lnum, p); 7174 break; 7175 } 7176 regionmask |= 1 << (l - 1); 7177 } 7178 else 7179 { 7180 smsg((char_u *)_("Unrecognized flags in %s line %d: %s"), 7181 fname, lnum, p); 7182 break; 7183 } 7184 ++p; 7185 } 7186 } 7187 7188 /* Skip non-ASCII words when "spin->si_ascii" is TRUE. */ 7189 if (spin->si_ascii && has_non_ascii(line)) 7190 { 7191 ++non_ascii; 7192 continue; 7193 } 7194 7195 /* Normal word: store it. */ 7196 if (store_word(spin, line, flags, regionmask, NULL, FALSE) == FAIL) 7197 { 7198 retval = FAIL; 7199 break; 7200 } 7201 did_word = TRUE; 7202 } 7203 7204 vim_free(pc); 7205 fclose(fd); 7206 7207 if (spin->si_ascii && non_ascii > 0) 7208 { 7209 vim_snprintf((char *)IObuff, IOSIZE, 7210 _("Ignored %d words with non-ASCII characters"), non_ascii); 7211 spell_message(spin, IObuff); 7212 } 7213 7214 return retval; 7215 } 7216 7217 /* 7218 * Get part of an sblock_T, "len" bytes long. 7219 * This avoids calling free() for every little struct we use (and keeping 7220 * track of them). 7221 * The memory is cleared to all zeros. 7222 * Returns NULL when out of memory. 7223 */ 7224 static void * 7225 getroom(spin, len, align) 7226 spellinfo_T *spin; 7227 size_t len; /* length needed */ 7228 int align; /* align for pointer */ 7229 { 7230 char_u *p; 7231 sblock_T *bl = spin->si_blocks; 7232 7233 if (align && bl != NULL) 7234 /* Round size up for alignment. On some systems structures need to be 7235 * aligned to the size of a pointer (e.g., SPARC). */ 7236 bl->sb_used = (bl->sb_used + sizeof(char *) - 1) 7237 & ~(sizeof(char *) - 1); 7238 7239 if (bl == NULL || bl->sb_used + len > SBLOCKSIZE) 7240 { 7241 /* Allocate a block of memory. This is not freed until much later. */ 7242 bl = (sblock_T *)alloc_clear((unsigned)(sizeof(sblock_T) + SBLOCKSIZE)); 7243 if (bl == NULL) 7244 return NULL; 7245 bl->sb_next = spin->si_blocks; 7246 spin->si_blocks = bl; 7247 bl->sb_used = 0; 7248 ++spin->si_blocks_cnt; 7249 } 7250 7251 p = bl->sb_data + bl->sb_used; 7252 bl->sb_used += (int)len; 7253 7254 return p; 7255 } 7256 7257 /* 7258 * Make a copy of a string into memory allocated with getroom(). 7259 */ 7260 static char_u * 7261 getroom_save(spin, s) 7262 spellinfo_T *spin; 7263 char_u *s; 7264 { 7265 char_u *sc; 7266 7267 sc = (char_u *)getroom(spin, STRLEN(s) + 1, FALSE); 7268 if (sc != NULL) 7269 STRCPY(sc, s); 7270 return sc; 7271 } 7272 7273 7274 /* 7275 * Free the list of allocated sblock_T. 7276 */ 7277 static void 7278 free_blocks(bl) 7279 sblock_T *bl; 7280 { 7281 sblock_T *next; 7282 7283 while (bl != NULL) 7284 { 7285 next = bl->sb_next; 7286 vim_free(bl); 7287 bl = next; 7288 } 7289 } 7290 7291 /* 7292 * Allocate the root of a word tree. 7293 */ 7294 static wordnode_T * 7295 wordtree_alloc(spin) 7296 spellinfo_T *spin; 7297 { 7298 return (wordnode_T *)getroom(spin, sizeof(wordnode_T), TRUE); 7299 } 7300 7301 /* 7302 * Store a word in the tree(s). 7303 * Always store it in the case-folded tree. For a keep-case word this is 7304 * useful when the word can also be used with all caps (no WF_FIXCAP flag) and 7305 * used to find suggestions. 7306 * For a keep-case word also store it in the keep-case tree. 7307 * When "pfxlist" is not NULL store the word for each postponed prefix ID and 7308 * compound flag. 7309 */ 7310 static int 7311 store_word(spin, word, flags, region, pfxlist, need_affix) 7312 spellinfo_T *spin; 7313 char_u *word; 7314 int flags; /* extra flags, WF_BANNED */ 7315 int region; /* supported region(s) */ 7316 char_u *pfxlist; /* list of prefix IDs or NULL */ 7317 int need_affix; /* only store word with affix ID */ 7318 { 7319 int len = (int)STRLEN(word); 7320 int ct = captype(word, word + len); 7321 char_u foldword[MAXWLEN]; 7322 int res = OK; 7323 char_u *p; 7324 7325 (void)spell_casefold(word, len, foldword, MAXWLEN); 7326 for (p = pfxlist; res == OK; ++p) 7327 { 7328 if (!need_affix || (p != NULL && *p != NUL)) 7329 res = tree_add_word(spin, foldword, spin->si_foldroot, ct | flags, 7330 region, p == NULL ? 0 : *p); 7331 if (p == NULL || *p == NUL) 7332 break; 7333 } 7334 ++spin->si_foldwcount; 7335 7336 if (res == OK && (ct == WF_KEEPCAP || (flags & WF_KEEPCAP))) 7337 { 7338 for (p = pfxlist; res == OK; ++p) 7339 { 7340 if (!need_affix || (p != NULL && *p != NUL)) 7341 res = tree_add_word(spin, word, spin->si_keeproot, flags, 7342 region, p == NULL ? 0 : *p); 7343 if (p == NULL || *p == NUL) 7344 break; 7345 } 7346 ++spin->si_keepwcount; 7347 } 7348 return res; 7349 } 7350 7351 /* 7352 * Add word "word" to a word tree at "root". 7353 * When "flags" < 0 we are adding to the prefix tree where "flags" is used for 7354 * "rare" and "region" is the condition nr. 7355 * Returns FAIL when out of memory. 7356 */ 7357 static int 7358 tree_add_word(spin, word, root, flags, region, affixID) 7359 spellinfo_T *spin; 7360 char_u *word; 7361 wordnode_T *root; 7362 int flags; 7363 int region; 7364 int affixID; 7365 { 7366 wordnode_T *node = root; 7367 wordnode_T *np; 7368 wordnode_T *copyp, **copyprev; 7369 wordnode_T **prev = NULL; 7370 int i; 7371 7372 /* Add each byte of the word to the tree, including the NUL at the end. */ 7373 for (i = 0; ; ++i) 7374 { 7375 /* When there is more than one reference to this node we need to make 7376 * a copy, so that we can modify it. Copy the whole list of siblings 7377 * (we don't optimize for a partly shared list of siblings). */ 7378 if (node != NULL && node->wn_refs > 1) 7379 { 7380 --node->wn_refs; 7381 copyprev = prev; 7382 for (copyp = node; copyp != NULL; copyp = copyp->wn_sibling) 7383 { 7384 /* Allocate a new node and copy the info. */ 7385 np = get_wordnode(spin); 7386 if (np == NULL) 7387 return FAIL; 7388 np->wn_child = copyp->wn_child; 7389 if (np->wn_child != NULL) 7390 ++np->wn_child->wn_refs; /* child gets extra ref */ 7391 np->wn_byte = copyp->wn_byte; 7392 if (np->wn_byte == NUL) 7393 { 7394 np->wn_flags = copyp->wn_flags; 7395 np->wn_region = copyp->wn_region; 7396 np->wn_affixID = copyp->wn_affixID; 7397 } 7398 7399 /* Link the new node in the list, there will be one ref. */ 7400 np->wn_refs = 1; 7401 if (copyprev != NULL) 7402 *copyprev = np; 7403 copyprev = &np->wn_sibling; 7404 7405 /* Let "node" point to the head of the copied list. */ 7406 if (copyp == node) 7407 node = np; 7408 } 7409 } 7410 7411 /* Look for the sibling that has the same character. They are sorted 7412 * on byte value, thus stop searching when a sibling is found with a 7413 * higher byte value. For zero bytes (end of word) the sorting is 7414 * done on flags and then on affixID. */ 7415 while (node != NULL 7416 && (node->wn_byte < word[i] 7417 || (node->wn_byte == NUL 7418 && (flags < 0 7419 ? node->wn_affixID < (unsigned)affixID 7420 : (node->wn_flags < (unsigned)(flags & WN_MASK) 7421 || (node->wn_flags == (flags & WN_MASK) 7422 && (spin->si_sugtree 7423 ? (node->wn_region & 0xffff) < region 7424 : node->wn_affixID 7425 < (unsigned)affixID))))))) 7426 { 7427 prev = &node->wn_sibling; 7428 node = *prev; 7429 } 7430 if (node == NULL 7431 || node->wn_byte != word[i] 7432 || (word[i] == NUL 7433 && (flags < 0 7434 || spin->si_sugtree 7435 || node->wn_flags != (flags & WN_MASK) 7436 || node->wn_affixID != affixID))) 7437 { 7438 /* Allocate a new node. */ 7439 np = get_wordnode(spin); 7440 if (np == NULL) 7441 return FAIL; 7442 np->wn_byte = word[i]; 7443 7444 /* If "node" is NULL this is a new child or the end of the sibling 7445 * list: ref count is one. Otherwise use ref count of sibling and 7446 * make ref count of sibling one (matters when inserting in front 7447 * of the list of siblings). */ 7448 if (node == NULL) 7449 np->wn_refs = 1; 7450 else 7451 { 7452 np->wn_refs = node->wn_refs; 7453 node->wn_refs = 1; 7454 } 7455 *prev = np; 7456 np->wn_sibling = node; 7457 node = np; 7458 } 7459 7460 if (word[i] == NUL) 7461 { 7462 node->wn_flags = flags; 7463 node->wn_region |= region; 7464 node->wn_affixID = affixID; 7465 break; 7466 } 7467 prev = &node->wn_child; 7468 node = *prev; 7469 } 7470 #ifdef SPELL_PRINTTREE 7471 smsg("Added \"%s\"", word); 7472 spell_print_tree(root->wn_sibling); 7473 #endif 7474 7475 /* count nr of words added since last message */ 7476 ++spin->si_msg_count; 7477 7478 if (spin->si_compress_cnt > 1) 7479 { 7480 if (--spin->si_compress_cnt == 1) 7481 /* Did enough words to lower the block count limit. */ 7482 spin->si_blocks_cnt += compress_inc; 7483 } 7484 7485 /* 7486 * When we have allocated lots of memory we need to compress the word tree 7487 * to free up some room. But compression is slow, and we might actually 7488 * need that room, thus only compress in the following situations: 7489 * 1. When not compressed before (si_compress_cnt == 0): when using 7490 * "compress_start" blocks. 7491 * 2. When compressed before and used "compress_inc" blocks before 7492 * adding "compress_added" words (si_compress_cnt > 1). 7493 * 3. When compressed before, added "compress_added" words 7494 * (si_compress_cnt == 1) and the number of free nodes drops below the 7495 * maximum word length. 7496 */ 7497 #ifndef SPELL_PRINTTREE 7498 if (spin->si_compress_cnt == 1 7499 ? spin->si_free_count < MAXWLEN 7500 : spin->si_blocks_cnt >= compress_start) 7501 #endif 7502 { 7503 /* Decrement the block counter. The effect is that we compress again 7504 * when the freed up room has been used and another "compress_inc" 7505 * blocks have been allocated. Unless "compress_added" words have 7506 * been added, then the limit is put back again. */ 7507 spin->si_blocks_cnt -= compress_inc; 7508 spin->si_compress_cnt = compress_added; 7509 7510 if (spin->si_verbose) 7511 { 7512 msg_start(); 7513 msg_puts((char_u *)_(msg_compressing)); 7514 msg_clr_eos(); 7515 msg_didout = FALSE; 7516 msg_col = 0; 7517 out_flush(); 7518 } 7519 7520 /* Compress both trees. Either they both have many nodes, which makes 7521 * compression useful, or one of them is small, which means 7522 * compression goes fast. But when filling the souldfold word tree 7523 * there is no keep-case tree. */ 7524 wordtree_compress(spin, spin->si_foldroot); 7525 if (affixID >= 0) 7526 wordtree_compress(spin, spin->si_keeproot); 7527 } 7528 7529 return OK; 7530 } 7531 7532 /* 7533 * Check the 'mkspellmem' option. Return FAIL if it's wrong. 7534 * Sets "sps_flags". 7535 */ 7536 int 7537 spell_check_msm() 7538 { 7539 char_u *p = p_msm; 7540 long start = 0; 7541 long incr = 0; 7542 long added = 0; 7543 7544 if (!VIM_ISDIGIT(*p)) 7545 return FAIL; 7546 /* block count = (value * 1024) / SBLOCKSIZE (but avoid overflow)*/ 7547 start = (getdigits(&p) * 10) / (SBLOCKSIZE / 102); 7548 if (*p != ',') 7549 return FAIL; 7550 ++p; 7551 if (!VIM_ISDIGIT(*p)) 7552 return FAIL; 7553 incr = (getdigits(&p) * 102) / (SBLOCKSIZE / 10); 7554 if (*p != ',') 7555 return FAIL; 7556 ++p; 7557 if (!VIM_ISDIGIT(*p)) 7558 return FAIL; 7559 added = getdigits(&p) * 1024; 7560 if (*p != NUL) 7561 return FAIL; 7562 7563 if (start == 0 || incr == 0 || added == 0 || incr > start) 7564 return FAIL; 7565 7566 compress_start = start; 7567 compress_inc = incr; 7568 compress_added = added; 7569 return OK; 7570 } 7571 7572 7573 /* 7574 * Get a wordnode_T, either from the list of previously freed nodes or 7575 * allocate a new one. 7576 */ 7577 static wordnode_T * 7578 get_wordnode(spin) 7579 spellinfo_T *spin; 7580 { 7581 wordnode_T *n; 7582 7583 if (spin->si_first_free == NULL) 7584 n = (wordnode_T *)getroom(spin, sizeof(wordnode_T), TRUE); 7585 else 7586 { 7587 n = spin->si_first_free; 7588 spin->si_first_free = n->wn_child; 7589 vim_memset(n, 0, sizeof(wordnode_T)); 7590 --spin->si_free_count; 7591 } 7592 #ifdef SPELL_PRINTTREE 7593 n->wn_nr = ++spin->si_wordnode_nr; 7594 #endif 7595 return n; 7596 } 7597 7598 /* 7599 * Decrement the reference count on a node (which is the head of a list of 7600 * siblings). If the reference count becomes zero free the node and its 7601 * siblings. 7602 * Returns the number of nodes actually freed. 7603 */ 7604 static int 7605 deref_wordnode(spin, node) 7606 spellinfo_T *spin; 7607 wordnode_T *node; 7608 { 7609 wordnode_T *np; 7610 int cnt = 0; 7611 7612 if (--node->wn_refs == 0) 7613 { 7614 for (np = node; np != NULL; np = np->wn_sibling) 7615 { 7616 if (np->wn_child != NULL) 7617 cnt += deref_wordnode(spin, np->wn_child); 7618 free_wordnode(spin, np); 7619 ++cnt; 7620 } 7621 ++cnt; /* length field */ 7622 } 7623 return cnt; 7624 } 7625 7626 /* 7627 * Free a wordnode_T for re-use later. 7628 * Only the "wn_child" field becomes invalid. 7629 */ 7630 static void 7631 free_wordnode(spin, n) 7632 spellinfo_T *spin; 7633 wordnode_T *n; 7634 { 7635 n->wn_child = spin->si_first_free; 7636 spin->si_first_free = n; 7637 ++spin->si_free_count; 7638 } 7639 7640 /* 7641 * Compress a tree: find tails that are identical and can be shared. 7642 */ 7643 static void 7644 wordtree_compress(spin, root) 7645 spellinfo_T *spin; 7646 wordnode_T *root; 7647 { 7648 hashtab_T ht; 7649 int n; 7650 int tot = 0; 7651 int perc; 7652 7653 /* Skip the root itself, it's not actually used. The first sibling is the 7654 * start of the tree. */ 7655 if (root->wn_sibling != NULL) 7656 { 7657 hash_init(&ht); 7658 n = node_compress(spin, root->wn_sibling, &ht, &tot); 7659 7660 #ifndef SPELL_PRINTTREE 7661 if (spin->si_verbose || p_verbose > 2) 7662 #endif 7663 { 7664 if (tot > 1000000) 7665 perc = (tot - n) / (tot / 100); 7666 else if (tot == 0) 7667 perc = 0; 7668 else 7669 perc = (tot - n) * 100 / tot; 7670 vim_snprintf((char *)IObuff, IOSIZE, 7671 _("Compressed %d of %d nodes; %d (%d%%) remaining"), 7672 n, tot, tot - n, perc); 7673 spell_message(spin, IObuff); 7674 } 7675 #ifdef SPELL_PRINTTREE 7676 spell_print_tree(root->wn_sibling); 7677 #endif 7678 hash_clear(&ht); 7679 } 7680 } 7681 7682 /* 7683 * Compress a node, its siblings and its children, depth first. 7684 * Returns the number of compressed nodes. 7685 */ 7686 static int 7687 node_compress(spin, node, ht, tot) 7688 spellinfo_T *spin; 7689 wordnode_T *node; 7690 hashtab_T *ht; 7691 int *tot; /* total count of nodes before compressing, 7692 incremented while going through the tree */ 7693 { 7694 wordnode_T *np; 7695 wordnode_T *tp; 7696 wordnode_T *child; 7697 hash_T hash; 7698 hashitem_T *hi; 7699 int len = 0; 7700 unsigned nr, n; 7701 int compressed = 0; 7702 7703 /* 7704 * Go through the list of siblings. Compress each child and then try 7705 * finding an identical child to replace it. 7706 * Note that with "child" we mean not just the node that is pointed to, 7707 * but the whole list of siblings of which the child node is the first. 7708 */ 7709 for (np = node; np != NULL && !got_int; np = np->wn_sibling) 7710 { 7711 ++len; 7712 if ((child = np->wn_child) != NULL) 7713 { 7714 /* Compress the child first. This fills hashkey. */ 7715 compressed += node_compress(spin, child, ht, tot); 7716 7717 /* Try to find an identical child. */ 7718 hash = hash_hash(child->wn_u1.hashkey); 7719 hi = hash_lookup(ht, child->wn_u1.hashkey, hash); 7720 if (!HASHITEM_EMPTY(hi)) 7721 { 7722 /* There are children we encountered before with a hash value 7723 * identical to the current child. Now check if there is one 7724 * that is really identical. */ 7725 for (tp = HI2WN(hi); tp != NULL; tp = tp->wn_u2.next) 7726 if (node_equal(child, tp)) 7727 { 7728 /* Found one! Now use that child in place of the 7729 * current one. This means the current child and all 7730 * its siblings is unlinked from the tree. */ 7731 ++tp->wn_refs; 7732 compressed += deref_wordnode(spin, child); 7733 np->wn_child = tp; 7734 break; 7735 } 7736 if (tp == NULL) 7737 { 7738 /* No other child with this hash value equals the child of 7739 * the node, add it to the linked list after the first 7740 * item. */ 7741 tp = HI2WN(hi); 7742 child->wn_u2.next = tp->wn_u2.next; 7743 tp->wn_u2.next = child; 7744 } 7745 } 7746 else 7747 /* No other child has this hash value, add it to the 7748 * hashtable. */ 7749 hash_add_item(ht, hi, child->wn_u1.hashkey, hash); 7750 } 7751 } 7752 *tot += len + 1; /* add one for the node that stores the length */ 7753 7754 /* 7755 * Make a hash key for the node and its siblings, so that we can quickly 7756 * find a lookalike node. This must be done after compressing the sibling 7757 * list, otherwise the hash key would become invalid by the compression. 7758 */ 7759 node->wn_u1.hashkey[0] = len; 7760 nr = 0; 7761 for (np = node; np != NULL; np = np->wn_sibling) 7762 { 7763 if (np->wn_byte == NUL) 7764 /* end node: use wn_flags, wn_region and wn_affixID */ 7765 n = np->wn_flags + (np->wn_region << 8) + (np->wn_affixID << 16); 7766 else 7767 /* byte node: use the byte value and the child pointer */ 7768 n = (unsigned)(np->wn_byte + ((long_u)np->wn_child << 8)); 7769 nr = nr * 101 + n; 7770 } 7771 7772 /* Avoid NUL bytes, it terminates the hash key. */ 7773 n = nr & 0xff; 7774 node->wn_u1.hashkey[1] = n == 0 ? 1 : n; 7775 n = (nr >> 8) & 0xff; 7776 node->wn_u1.hashkey[2] = n == 0 ? 1 : n; 7777 n = (nr >> 16) & 0xff; 7778 node->wn_u1.hashkey[3] = n == 0 ? 1 : n; 7779 n = (nr >> 24) & 0xff; 7780 node->wn_u1.hashkey[4] = n == 0 ? 1 : n; 7781 node->wn_u1.hashkey[5] = NUL; 7782 7783 /* Check for CTRL-C pressed now and then. */ 7784 fast_breakcheck(); 7785 7786 return compressed; 7787 } 7788 7789 /* 7790 * Return TRUE when two nodes have identical siblings and children. 7791 */ 7792 static int 7793 node_equal(n1, n2) 7794 wordnode_T *n1; 7795 wordnode_T *n2; 7796 { 7797 wordnode_T *p1; 7798 wordnode_T *p2; 7799 7800 for (p1 = n1, p2 = n2; p1 != NULL && p2 != NULL; 7801 p1 = p1->wn_sibling, p2 = p2->wn_sibling) 7802 if (p1->wn_byte != p2->wn_byte 7803 || (p1->wn_byte == NUL 7804 ? (p1->wn_flags != p2->wn_flags 7805 || p1->wn_region != p2->wn_region 7806 || p1->wn_affixID != p2->wn_affixID) 7807 : (p1->wn_child != p2->wn_child))) 7808 break; 7809 7810 return p1 == NULL && p2 == NULL; 7811 } 7812 7813 /* 7814 * Write a number to file "fd", MSB first, in "len" bytes. 7815 */ 7816 void 7817 put_bytes(fd, nr, len) 7818 FILE *fd; 7819 long_u nr; 7820 int len; 7821 { 7822 int i; 7823 7824 for (i = len - 1; i >= 0; --i) 7825 putc((int)(nr >> (i * 8)), fd); 7826 } 7827 7828 #ifdef _MSC_VER 7829 # if (_MSC_VER <= 1200) 7830 /* This line is required for VC6 without the service pack. Also see the 7831 * matching #pragma below. */ 7832 /* # pragma optimize("", off) */ 7833 # endif 7834 #endif 7835 7836 /* 7837 * Write spin->si_sugtime to file "fd". 7838 */ 7839 static void 7840 put_sugtime(spin, fd) 7841 spellinfo_T *spin; 7842 FILE *fd; 7843 { 7844 int c; 7845 int i; 7846 7847 /* time_t can be up to 8 bytes in size, more than long_u, thus we 7848 * can't use put_bytes() here. */ 7849 for (i = 7; i >= 0; --i) 7850 if (i + 1 > sizeof(time_t)) 7851 /* ">>" doesn't work well when shifting more bits than avail */ 7852 putc(0, fd); 7853 else 7854 { 7855 c = (unsigned)spin->si_sugtime >> (i * 8); 7856 putc(c, fd); 7857 } 7858 } 7859 7860 #ifdef _MSC_VER 7861 # if (_MSC_VER <= 1200) 7862 /* # pragma optimize("", on) */ 7863 # endif 7864 #endif 7865 7866 static int 7867 #ifdef __BORLANDC__ 7868 _RTLENTRYF 7869 #endif 7870 rep_compare __ARGS((const void *s1, const void *s2)); 7871 7872 /* 7873 * Function given to qsort() to sort the REP items on "from" string. 7874 */ 7875 static int 7876 #ifdef __BORLANDC__ 7877 _RTLENTRYF 7878 #endif 7879 rep_compare(s1, s2) 7880 const void *s1; 7881 const void *s2; 7882 { 7883 fromto_T *p1 = (fromto_T *)s1; 7884 fromto_T *p2 = (fromto_T *)s2; 7885 7886 return STRCMP(p1->ft_from, p2->ft_from); 7887 } 7888 7889 /* 7890 * Write the Vim .spl file "fname". 7891 * Return FAIL or OK; 7892 */ 7893 static int 7894 write_vim_spell(spin, fname) 7895 spellinfo_T *spin; 7896 char_u *fname; 7897 { 7898 FILE *fd; 7899 int regionmask; 7900 int round; 7901 wordnode_T *tree; 7902 int nodecount; 7903 int i; 7904 int l; 7905 garray_T *gap; 7906 fromto_T *ftp; 7907 char_u *p; 7908 int rr; 7909 int retval = OK; 7910 7911 fd = mch_fopen((char *)fname, "w"); 7912 if (fd == NULL) 7913 { 7914 EMSG2(_(e_notopen), fname); 7915 return FAIL; 7916 } 7917 7918 /* <HEADER>: <fileID> <versionnr> */ 7919 /* <fileID> */ 7920 if (fwrite(VIMSPELLMAGIC, VIMSPELLMAGICL, (size_t)1, fd) != 1) 7921 { 7922 EMSG(_(e_write)); 7923 retval = FAIL; 7924 } 7925 putc(VIMSPELLVERSION, fd); /* <versionnr> */ 7926 7927 /* 7928 * <SECTIONS>: <section> ... <sectionend> 7929 */ 7930 7931 /* SN_INFO: <infotext> */ 7932 if (spin->si_info != NULL) 7933 { 7934 putc(SN_INFO, fd); /* <sectionID> */ 7935 putc(0, fd); /* <sectionflags> */ 7936 7937 i = (int)STRLEN(spin->si_info); 7938 put_bytes(fd, (long_u)i, 4); /* <sectionlen> */ 7939 fwrite(spin->si_info, (size_t)i, (size_t)1, fd); /* <infotext> */ 7940 } 7941 7942 /* SN_REGION: <regionname> ... 7943 * Write the region names only if there is more than one. */ 7944 if (spin->si_region_count > 1) 7945 { 7946 putc(SN_REGION, fd); /* <sectionID> */ 7947 putc(SNF_REQUIRED, fd); /* <sectionflags> */ 7948 l = spin->si_region_count * 2; 7949 put_bytes(fd, (long_u)l, 4); /* <sectionlen> */ 7950 fwrite(spin->si_region_name, (size_t)l, (size_t)1, fd); 7951 /* <regionname> ... */ 7952 regionmask = (1 << spin->si_region_count) - 1; 7953 } 7954 else 7955 regionmask = 0; 7956 7957 /* SN_CHARFLAGS: <charflagslen> <charflags> <folcharslen> <folchars> 7958 * 7959 * The table with character flags and the table for case folding. 7960 * This makes sure the same characters are recognized as word characters 7961 * when generating an when using a spell file. 7962 * Skip this for ASCII, the table may conflict with the one used for 7963 * 'encoding'. 7964 * Also skip this for an .add.spl file, the main spell file must contain 7965 * the table (avoids that it conflicts). File is shorter too. 7966 */ 7967 if (!spin->si_ascii && !spin->si_add) 7968 { 7969 char_u folchars[128 * 8]; 7970 int flags; 7971 7972 putc(SN_CHARFLAGS, fd); /* <sectionID> */ 7973 putc(SNF_REQUIRED, fd); /* <sectionflags> */ 7974 7975 /* Form the <folchars> string first, we need to know its length. */ 7976 l = 0; 7977 for (i = 128; i < 256; ++i) 7978 { 7979 #ifdef FEAT_MBYTE 7980 if (has_mbyte) 7981 l += mb_char2bytes(spelltab.st_fold[i], folchars + l); 7982 else 7983 #endif 7984 folchars[l++] = spelltab.st_fold[i]; 7985 } 7986 put_bytes(fd, (long_u)(1 + 128 + 2 + l), 4); /* <sectionlen> */ 7987 7988 fputc(128, fd); /* <charflagslen> */ 7989 for (i = 128; i < 256; ++i) 7990 { 7991 flags = 0; 7992 if (spelltab.st_isw[i]) 7993 flags |= CF_WORD; 7994 if (spelltab.st_isu[i]) 7995 flags |= CF_UPPER; 7996 fputc(flags, fd); /* <charflags> */ 7997 } 7998 7999 put_bytes(fd, (long_u)l, 2); /* <folcharslen> */ 8000 fwrite(folchars, (size_t)l, (size_t)1, fd); /* <folchars> */ 8001 } 8002 8003 /* SN_MIDWORD: <midword> */ 8004 if (spin->si_midword != NULL) 8005 { 8006 putc(SN_MIDWORD, fd); /* <sectionID> */ 8007 putc(SNF_REQUIRED, fd); /* <sectionflags> */ 8008 8009 i = (int)STRLEN(spin->si_midword); 8010 put_bytes(fd, (long_u)i, 4); /* <sectionlen> */ 8011 fwrite(spin->si_midword, (size_t)i, (size_t)1, fd); /* <midword> */ 8012 } 8013 8014 /* SN_PREFCOND: <prefcondcnt> <prefcond> ... */ 8015 if (spin->si_prefcond.ga_len > 0) 8016 { 8017 putc(SN_PREFCOND, fd); /* <sectionID> */ 8018 putc(SNF_REQUIRED, fd); /* <sectionflags> */ 8019 8020 l = write_spell_prefcond(NULL, &spin->si_prefcond); 8021 put_bytes(fd, (long_u)l, 4); /* <sectionlen> */ 8022 8023 write_spell_prefcond(fd, &spin->si_prefcond); 8024 } 8025 8026 /* SN_REP: <repcount> <rep> ... 8027 * SN_SAL: <salflags> <salcount> <sal> ... 8028 * SN_REPSAL: <repcount> <rep> ... */ 8029 8030 /* round 1: SN_REP section 8031 * round 2: SN_SAL section (unless SN_SOFO is used) 8032 * round 3: SN_REPSAL section */ 8033 for (round = 1; round <= 3; ++round) 8034 { 8035 if (round == 1) 8036 gap = &spin->si_rep; 8037 else if (round == 2) 8038 { 8039 /* Don't write SN_SAL when using a SN_SOFO section */ 8040 if (spin->si_sofofr != NULL && spin->si_sofoto != NULL) 8041 continue; 8042 gap = &spin->si_sal; 8043 } 8044 else 8045 gap = &spin->si_repsal; 8046 8047 /* Don't write the section if there are no items. */ 8048 if (gap->ga_len == 0) 8049 continue; 8050 8051 /* Sort the REP/REPSAL items. */ 8052 if (round != 2) 8053 qsort(gap->ga_data, (size_t)gap->ga_len, 8054 sizeof(fromto_T), rep_compare); 8055 8056 i = round == 1 ? SN_REP : (round == 2 ? SN_SAL : SN_REPSAL); 8057 putc(i, fd); /* <sectionID> */ 8058 8059 /* This is for making suggestions, section is not required. */ 8060 putc(0, fd); /* <sectionflags> */ 8061 8062 /* Compute the length of what follows. */ 8063 l = 2; /* count <repcount> or <salcount> */ 8064 for (i = 0; i < gap->ga_len; ++i) 8065 { 8066 ftp = &((fromto_T *)gap->ga_data)[i]; 8067 l += 1 + (int)STRLEN(ftp->ft_from); /* count <*fromlen> and <*from> */ 8068 l += 1 + (int)STRLEN(ftp->ft_to); /* count <*tolen> and <*to> */ 8069 } 8070 if (round == 2) 8071 ++l; /* count <salflags> */ 8072 put_bytes(fd, (long_u)l, 4); /* <sectionlen> */ 8073 8074 if (round == 2) 8075 { 8076 i = 0; 8077 if (spin->si_followup) 8078 i |= SAL_F0LLOWUP; 8079 if (spin->si_collapse) 8080 i |= SAL_COLLAPSE; 8081 if (spin->si_rem_accents) 8082 i |= SAL_REM_ACCENTS; 8083 putc(i, fd); /* <salflags> */ 8084 } 8085 8086 put_bytes(fd, (long_u)gap->ga_len, 2); /* <repcount> or <salcount> */ 8087 for (i = 0; i < gap->ga_len; ++i) 8088 { 8089 /* <rep> : <repfromlen> <repfrom> <reptolen> <repto> */ 8090 /* <sal> : <salfromlen> <salfrom> <saltolen> <salto> */ 8091 ftp = &((fromto_T *)gap->ga_data)[i]; 8092 for (rr = 1; rr <= 2; ++rr) 8093 { 8094 p = rr == 1 ? ftp->ft_from : ftp->ft_to; 8095 l = (int)STRLEN(p); 8096 putc(l, fd); 8097 fwrite(p, l, (size_t)1, fd); 8098 } 8099 } 8100 8101 } 8102 8103 /* SN_SOFO: <sofofromlen> <sofofrom> <sofotolen> <sofoto> 8104 * This is for making suggestions, section is not required. */ 8105 if (spin->si_sofofr != NULL && spin->si_sofoto != NULL) 8106 { 8107 putc(SN_SOFO, fd); /* <sectionID> */ 8108 putc(0, fd); /* <sectionflags> */ 8109 8110 l = (int)STRLEN(spin->si_sofofr); 8111 put_bytes(fd, (long_u)(l + STRLEN(spin->si_sofoto) + 4), 4); 8112 /* <sectionlen> */ 8113 8114 put_bytes(fd, (long_u)l, 2); /* <sofofromlen> */ 8115 fwrite(spin->si_sofofr, l, (size_t)1, fd); /* <sofofrom> */ 8116 8117 l = (int)STRLEN(spin->si_sofoto); 8118 put_bytes(fd, (long_u)l, 2); /* <sofotolen> */ 8119 fwrite(spin->si_sofoto, l, (size_t)1, fd); /* <sofoto> */ 8120 } 8121 8122 /* SN_WORDS: <word> ... 8123 * This is for making suggestions, section is not required. */ 8124 if (spin->si_commonwords.ht_used > 0) 8125 { 8126 putc(SN_WORDS, fd); /* <sectionID> */ 8127 putc(0, fd); /* <sectionflags> */ 8128 8129 /* round 1: count the bytes 8130 * round 2: write the bytes */ 8131 for (round = 1; round <= 2; ++round) 8132 { 8133 int todo; 8134 int len = 0; 8135 hashitem_T *hi; 8136 8137 todo = (int)spin->si_commonwords.ht_used; 8138 for (hi = spin->si_commonwords.ht_array; todo > 0; ++hi) 8139 if (!HASHITEM_EMPTY(hi)) 8140 { 8141 l = (int)STRLEN(hi->hi_key) + 1; 8142 len += l; 8143 if (round == 2) /* <word> */ 8144 fwrite(hi->hi_key, (size_t)l, (size_t)1, fd); 8145 --todo; 8146 } 8147 if (round == 1) 8148 put_bytes(fd, (long_u)len, 4); /* <sectionlen> */ 8149 } 8150 } 8151 8152 /* SN_MAP: <mapstr> 8153 * This is for making suggestions, section is not required. */ 8154 if (spin->si_map.ga_len > 0) 8155 { 8156 putc(SN_MAP, fd); /* <sectionID> */ 8157 putc(0, fd); /* <sectionflags> */ 8158 l = spin->si_map.ga_len; 8159 put_bytes(fd, (long_u)l, 4); /* <sectionlen> */ 8160 fwrite(spin->si_map.ga_data, (size_t)l, (size_t)1, fd); 8161 /* <mapstr> */ 8162 } 8163 8164 /* SN_SUGFILE: <timestamp> 8165 * This is used to notify that a .sug file may be available and at the 8166 * same time allows for checking that a .sug file that is found matches 8167 * with this .spl file. That's because the word numbers must be exactly 8168 * right. */ 8169 if (!spin->si_nosugfile 8170 && (spin->si_sal.ga_len > 0 8171 || (spin->si_sofofr != NULL && spin->si_sofoto != NULL))) 8172 { 8173 putc(SN_SUGFILE, fd); /* <sectionID> */ 8174 putc(0, fd); /* <sectionflags> */ 8175 put_bytes(fd, (long_u)8, 4); /* <sectionlen> */ 8176 8177 /* Set si_sugtime and write it to the file. */ 8178 spin->si_sugtime = time(NULL); 8179 put_sugtime(spin, fd); /* <timestamp> */ 8180 } 8181 8182 /* SN_NOSPLITSUGS: nothing 8183 * This is used to notify that no suggestions with word splits are to be 8184 * made. */ 8185 if (spin->si_nosplitsugs) 8186 { 8187 putc(SN_NOSPLITSUGS, fd); /* <sectionID> */ 8188 putc(0, fd); /* <sectionflags> */ 8189 put_bytes(fd, (long_u)0, 4); /* <sectionlen> */ 8190 } 8191 8192 /* SN_COMPOUND: compound info. 8193 * We don't mark it required, when not supported all compound words will 8194 * be bad words. */ 8195 if (spin->si_compflags != NULL) 8196 { 8197 putc(SN_COMPOUND, fd); /* <sectionID> */ 8198 putc(0, fd); /* <sectionflags> */ 8199 8200 l = (int)STRLEN(spin->si_compflags); 8201 for (i = 0; i < spin->si_comppat.ga_len; ++i) 8202 l += (int)STRLEN(((char_u **)(spin->si_comppat.ga_data))[i]) + 1; 8203 put_bytes(fd, (long_u)(l + 7), 4); /* <sectionlen> */ 8204 8205 putc(spin->si_compmax, fd); /* <compmax> */ 8206 putc(spin->si_compminlen, fd); /* <compminlen> */ 8207 putc(spin->si_compsylmax, fd); /* <compsylmax> */ 8208 putc(0, fd); /* for Vim 7.0b compatibility */ 8209 putc(spin->si_compoptions, fd); /* <compoptions> */ 8210 put_bytes(fd, (long_u)spin->si_comppat.ga_len, 2); 8211 /* <comppatcount> */ 8212 for (i = 0; i < spin->si_comppat.ga_len; ++i) 8213 { 8214 p = ((char_u **)(spin->si_comppat.ga_data))[i]; 8215 putc((int)STRLEN(p), fd); /* <comppatlen> */ 8216 fwrite(p, (size_t)STRLEN(p), (size_t)1, fd);/* <comppattext> */ 8217 } 8218 /* <compflags> */ 8219 fwrite(spin->si_compflags, (size_t)STRLEN(spin->si_compflags), 8220 (size_t)1, fd); 8221 } 8222 8223 /* SN_NOBREAK: NOBREAK flag */ 8224 if (spin->si_nobreak) 8225 { 8226 putc(SN_NOBREAK, fd); /* <sectionID> */ 8227 putc(0, fd); /* <sectionflags> */ 8228 8229 /* It's empty, the presence of the section flags the feature. */ 8230 put_bytes(fd, (long_u)0, 4); /* <sectionlen> */ 8231 } 8232 8233 /* SN_SYLLABLE: syllable info. 8234 * We don't mark it required, when not supported syllables will not be 8235 * counted. */ 8236 if (spin->si_syllable != NULL) 8237 { 8238 putc(SN_SYLLABLE, fd); /* <sectionID> */ 8239 putc(0, fd); /* <sectionflags> */ 8240 8241 l = (int)STRLEN(spin->si_syllable); 8242 put_bytes(fd, (long_u)l, 4); /* <sectionlen> */ 8243 fwrite(spin->si_syllable, (size_t)l, (size_t)1, fd); /* <syllable> */ 8244 } 8245 8246 /* end of <SECTIONS> */ 8247 putc(SN_END, fd); /* <sectionend> */ 8248 8249 8250 /* 8251 * <LWORDTREE> <KWORDTREE> <PREFIXTREE> 8252 */ 8253 spin->si_memtot = 0; 8254 for (round = 1; round <= 3; ++round) 8255 { 8256 if (round == 1) 8257 tree = spin->si_foldroot->wn_sibling; 8258 else if (round == 2) 8259 tree = spin->si_keeproot->wn_sibling; 8260 else 8261 tree = spin->si_prefroot->wn_sibling; 8262 8263 /* Clear the index and wnode fields in the tree. */ 8264 clear_node(tree); 8265 8266 /* Count the number of nodes. Needed to be able to allocate the 8267 * memory when reading the nodes. Also fills in index for shared 8268 * nodes. */ 8269 nodecount = put_node(NULL, tree, 0, regionmask, round == 3); 8270 8271 /* number of nodes in 4 bytes */ 8272 put_bytes(fd, (long_u)nodecount, 4); /* <nodecount> */ 8273 spin->si_memtot += nodecount + nodecount * sizeof(int); 8274 8275 /* Write the nodes. */ 8276 (void)put_node(fd, tree, 0, regionmask, round == 3); 8277 } 8278 8279 /* Write another byte to check for errors. */ 8280 if (putc(0, fd) == EOF) 8281 retval = FAIL; 8282 8283 if (fclose(fd) == EOF) 8284 retval = FAIL; 8285 8286 return retval; 8287 } 8288 8289 /* 8290 * Clear the index and wnode fields of "node", it siblings and its 8291 * children. This is needed because they are a union with other items to save 8292 * space. 8293 */ 8294 static void 8295 clear_node(node) 8296 wordnode_T *node; 8297 { 8298 wordnode_T *np; 8299 8300 if (node != NULL) 8301 for (np = node; np != NULL; np = np->wn_sibling) 8302 { 8303 np->wn_u1.index = 0; 8304 np->wn_u2.wnode = NULL; 8305 8306 if (np->wn_byte != NUL) 8307 clear_node(np->wn_child); 8308 } 8309 } 8310 8311 8312 /* 8313 * Dump a word tree at node "node". 8314 * 8315 * This first writes the list of possible bytes (siblings). Then for each 8316 * byte recursively write the children. 8317 * 8318 * NOTE: The code here must match the code in read_tree_node(), since 8319 * assumptions are made about the indexes (so that we don't have to write them 8320 * in the file). 8321 * 8322 * Returns the number of nodes used. 8323 */ 8324 static int 8325 put_node(fd, node, idx, regionmask, prefixtree) 8326 FILE *fd; /* NULL when only counting */ 8327 wordnode_T *node; 8328 int idx; 8329 int regionmask; 8330 int prefixtree; /* TRUE for PREFIXTREE */ 8331 { 8332 int newindex = idx; 8333 int siblingcount = 0; 8334 wordnode_T *np; 8335 int flags; 8336 8337 /* If "node" is zero the tree is empty. */ 8338 if (node == NULL) 8339 return 0; 8340 8341 /* Store the index where this node is written. */ 8342 node->wn_u1.index = idx; 8343 8344 /* Count the number of siblings. */ 8345 for (np = node; np != NULL; np = np->wn_sibling) 8346 ++siblingcount; 8347 8348 /* Write the sibling count. */ 8349 if (fd != NULL) 8350 putc(siblingcount, fd); /* <siblingcount> */ 8351 8352 /* Write each sibling byte and optionally extra info. */ 8353 for (np = node; np != NULL; np = np->wn_sibling) 8354 { 8355 if (np->wn_byte == 0) 8356 { 8357 if (fd != NULL) 8358 { 8359 /* For a NUL byte (end of word) write the flags etc. */ 8360 if (prefixtree) 8361 { 8362 /* In PREFIXTREE write the required affixID and the 8363 * associated condition nr (stored in wn_region). The 8364 * byte value is misused to store the "rare" and "not 8365 * combining" flags */ 8366 if (np->wn_flags == (short_u)PFX_FLAGS) 8367 putc(BY_NOFLAGS, fd); /* <byte> */ 8368 else 8369 { 8370 putc(BY_FLAGS, fd); /* <byte> */ 8371 putc(np->wn_flags, fd); /* <pflags> */ 8372 } 8373 putc(np->wn_affixID, fd); /* <affixID> */ 8374 put_bytes(fd, (long_u)np->wn_region, 2); /* <prefcondnr> */ 8375 } 8376 else 8377 { 8378 /* For word trees we write the flag/region items. */ 8379 flags = np->wn_flags; 8380 if (regionmask != 0 && np->wn_region != regionmask) 8381 flags |= WF_REGION; 8382 if (np->wn_affixID != 0) 8383 flags |= WF_AFX; 8384 if (flags == 0) 8385 { 8386 /* word without flags or region */ 8387 putc(BY_NOFLAGS, fd); /* <byte> */ 8388 } 8389 else 8390 { 8391 if (np->wn_flags >= 0x100) 8392 { 8393 putc(BY_FLAGS2, fd); /* <byte> */ 8394 putc(flags, fd); /* <flags> */ 8395 putc((unsigned)flags >> 8, fd); /* <flags2> */ 8396 } 8397 else 8398 { 8399 putc(BY_FLAGS, fd); /* <byte> */ 8400 putc(flags, fd); /* <flags> */ 8401 } 8402 if (flags & WF_REGION) 8403 putc(np->wn_region, fd); /* <region> */ 8404 if (flags & WF_AFX) 8405 putc(np->wn_affixID, fd); /* <affixID> */ 8406 } 8407 } 8408 } 8409 } 8410 else 8411 { 8412 if (np->wn_child->wn_u1.index != 0 8413 && np->wn_child->wn_u2.wnode != node) 8414 { 8415 /* The child is written elsewhere, write the reference. */ 8416 if (fd != NULL) 8417 { 8418 putc(BY_INDEX, fd); /* <byte> */ 8419 /* <nodeidx> */ 8420 put_bytes(fd, (long_u)np->wn_child->wn_u1.index, 3); 8421 } 8422 } 8423 else if (np->wn_child->wn_u2.wnode == NULL) 8424 /* We will write the child below and give it an index. */ 8425 np->wn_child->wn_u2.wnode = node; 8426 8427 if (fd != NULL) 8428 if (putc(np->wn_byte, fd) == EOF) /* <byte> or <xbyte> */ 8429 { 8430 EMSG(_(e_write)); 8431 return 0; 8432 } 8433 } 8434 } 8435 8436 /* Space used in the array when reading: one for each sibling and one for 8437 * the count. */ 8438 newindex += siblingcount + 1; 8439 8440 /* Recursively dump the children of each sibling. */ 8441 for (np = node; np != NULL; np = np->wn_sibling) 8442 if (np->wn_byte != 0 && np->wn_child->wn_u2.wnode == node) 8443 newindex = put_node(fd, np->wn_child, newindex, regionmask, 8444 prefixtree); 8445 8446 return newindex; 8447 } 8448 8449 8450 /* 8451 * ":mkspell [-ascii] outfile infile ..." 8452 * ":mkspell [-ascii] addfile" 8453 */ 8454 void 8455 ex_mkspell(eap) 8456 exarg_T *eap; 8457 { 8458 int fcount; 8459 char_u **fnames; 8460 char_u *arg = eap->arg; 8461 int ascii = FALSE; 8462 8463 if (STRNCMP(arg, "-ascii", 6) == 0) 8464 { 8465 ascii = TRUE; 8466 arg = skipwhite(arg + 6); 8467 } 8468 8469 /* Expand all the remaining arguments (e.g., $VIMRUNTIME). */ 8470 if (get_arglist_exp(arg, &fcount, &fnames) == OK) 8471 { 8472 mkspell(fcount, fnames, ascii, eap->forceit, FALSE); 8473 FreeWild(fcount, fnames); 8474 } 8475 } 8476 8477 /* 8478 * Create the .sug file. 8479 * Uses the soundfold info in "spin". 8480 * Writes the file with the name "wfname", with ".spl" changed to ".sug". 8481 */ 8482 static void 8483 spell_make_sugfile(spin, wfname) 8484 spellinfo_T *spin; 8485 char_u *wfname; 8486 { 8487 char_u fname[MAXPATHL]; 8488 int len; 8489 slang_T *slang; 8490 int free_slang = FALSE; 8491 8492 /* 8493 * Read back the .spl file that was written. This fills the required 8494 * info for soundfolding. This also uses less memory than the 8495 * pointer-linked version of the trie. And it avoids having two versions 8496 * of the code for the soundfolding stuff. 8497 * It might have been done already by spell_reload_one(). 8498 */ 8499 for (slang = first_lang; slang != NULL; slang = slang->sl_next) 8500 if (fullpathcmp(wfname, slang->sl_fname, FALSE) == FPC_SAME) 8501 break; 8502 if (slang == NULL) 8503 { 8504 spell_message(spin, (char_u *)_("Reading back spell file...")); 8505 slang = spell_load_file(wfname, NULL, NULL, FALSE); 8506 if (slang == NULL) 8507 return; 8508 free_slang = TRUE; 8509 } 8510 8511 /* 8512 * Clear the info in "spin" that is used. 8513 */ 8514 spin->si_blocks = NULL; 8515 spin->si_blocks_cnt = 0; 8516 spin->si_compress_cnt = 0; /* will stay at 0 all the time*/ 8517 spin->si_free_count = 0; 8518 spin->si_first_free = NULL; 8519 spin->si_foldwcount = 0; 8520 8521 /* 8522 * Go through the trie of good words, soundfold each word and add it to 8523 * the soundfold trie. 8524 */ 8525 spell_message(spin, (char_u *)_("Performing soundfolding...")); 8526 if (sug_filltree(spin, slang) == FAIL) 8527 goto theend; 8528 8529 /* 8530 * Create the table which links each soundfold word with a list of the 8531 * good words it may come from. Creates buffer "spin->si_spellbuf". 8532 * This also removes the wordnr from the NUL byte entries to make 8533 * compression possible. 8534 */ 8535 if (sug_maketable(spin) == FAIL) 8536 goto theend; 8537 8538 smsg((char_u *)_("Number of words after soundfolding: %ld"), 8539 (long)spin->si_spellbuf->b_ml.ml_line_count); 8540 8541 /* 8542 * Compress the soundfold trie. 8543 */ 8544 spell_message(spin, (char_u *)_(msg_compressing)); 8545 wordtree_compress(spin, spin->si_foldroot); 8546 8547 /* 8548 * Write the .sug file. 8549 * Make the file name by changing ".spl" to ".sug". 8550 */ 8551 STRCPY(fname, wfname); 8552 len = (int)STRLEN(fname); 8553 fname[len - 2] = 'u'; 8554 fname[len - 1] = 'g'; 8555 sug_write(spin, fname); 8556 8557 theend: 8558 if (free_slang) 8559 slang_free(slang); 8560 free_blocks(spin->si_blocks); 8561 close_spellbuf(spin->si_spellbuf); 8562 } 8563 8564 /* 8565 * Build the soundfold trie for language "slang". 8566 */ 8567 static int 8568 sug_filltree(spin, slang) 8569 spellinfo_T *spin; 8570 slang_T *slang; 8571 { 8572 char_u *byts; 8573 idx_T *idxs; 8574 int depth; 8575 idx_T arridx[MAXWLEN]; 8576 int curi[MAXWLEN]; 8577 char_u tword[MAXWLEN]; 8578 char_u tsalword[MAXWLEN]; 8579 int c; 8580 idx_T n; 8581 unsigned words_done = 0; 8582 int wordcount[MAXWLEN]; 8583 8584 /* We use si_foldroot for the souldfolded trie. */ 8585 spin->si_foldroot = wordtree_alloc(spin); 8586 if (spin->si_foldroot == NULL) 8587 return FAIL; 8588 8589 /* let tree_add_word() know we're adding to the soundfolded tree */ 8590 spin->si_sugtree = TRUE; 8591 8592 /* 8593 * Go through the whole case-folded tree, soundfold each word and put it 8594 * in the trie. 8595 */ 8596 byts = slang->sl_fbyts; 8597 idxs = slang->sl_fidxs; 8598 8599 arridx[0] = 0; 8600 curi[0] = 1; 8601 wordcount[0] = 0; 8602 8603 depth = 0; 8604 while (depth >= 0 && !got_int) 8605 { 8606 if (curi[depth] > byts[arridx[depth]]) 8607 { 8608 /* Done all bytes at this node, go up one level. */ 8609 idxs[arridx[depth]] = wordcount[depth]; 8610 if (depth > 0) 8611 wordcount[depth - 1] += wordcount[depth]; 8612 8613 --depth; 8614 line_breakcheck(); 8615 } 8616 else 8617 { 8618 8619 /* Do one more byte at this node. */ 8620 n = arridx[depth] + curi[depth]; 8621 ++curi[depth]; 8622 8623 c = byts[n]; 8624 if (c == 0) 8625 { 8626 /* Sound-fold the word. */ 8627 tword[depth] = NUL; 8628 spell_soundfold(slang, tword, TRUE, tsalword); 8629 8630 /* We use the "flags" field for the MSB of the wordnr, 8631 * "region" for the LSB of the wordnr. */ 8632 if (tree_add_word(spin, tsalword, spin->si_foldroot, 8633 words_done >> 16, words_done & 0xffff, 8634 0) == FAIL) 8635 return FAIL; 8636 8637 ++words_done; 8638 ++wordcount[depth]; 8639 8640 /* Reset the block count each time to avoid compression 8641 * kicking in. */ 8642 spin->si_blocks_cnt = 0; 8643 8644 /* Skip over any other NUL bytes (same word with different 8645 * flags). */ 8646 while (byts[n + 1] == 0) 8647 { 8648 ++n; 8649 ++curi[depth]; 8650 } 8651 } 8652 else 8653 { 8654 /* Normal char, go one level deeper. */ 8655 tword[depth++] = c; 8656 arridx[depth] = idxs[n]; 8657 curi[depth] = 1; 8658 wordcount[depth] = 0; 8659 } 8660 } 8661 } 8662 8663 smsg((char_u *)_("Total number of words: %d"), words_done); 8664 8665 return OK; 8666 } 8667 8668 /* 8669 * Make the table that links each word in the soundfold trie to the words it 8670 * can be produced from. 8671 * This is not unlike lines in a file, thus use a memfile to be able to access 8672 * the table efficiently. 8673 * Returns FAIL when out of memory. 8674 */ 8675 static int 8676 sug_maketable(spin) 8677 spellinfo_T *spin; 8678 { 8679 garray_T ga; 8680 int res = OK; 8681 8682 /* Allocate a buffer, open a memline for it and create the swap file 8683 * (uses a temp file, not a .swp file). */ 8684 spin->si_spellbuf = open_spellbuf(); 8685 if (spin->si_spellbuf == NULL) 8686 return FAIL; 8687 8688 /* Use a buffer to store the line info, avoids allocating many small 8689 * pieces of memory. */ 8690 ga_init2(&ga, 1, 100); 8691 8692 /* recursively go through the tree */ 8693 if (sug_filltable(spin, spin->si_foldroot->wn_sibling, 0, &ga) == -1) 8694 res = FAIL; 8695 8696 ga_clear(&ga); 8697 return res; 8698 } 8699 8700 /* 8701 * Fill the table for one node and its children. 8702 * Returns the wordnr at the start of the node. 8703 * Returns -1 when out of memory. 8704 */ 8705 static int 8706 sug_filltable(spin, node, startwordnr, gap) 8707 spellinfo_T *spin; 8708 wordnode_T *node; 8709 int startwordnr; 8710 garray_T *gap; /* place to store line of numbers */ 8711 { 8712 wordnode_T *p, *np; 8713 int wordnr = startwordnr; 8714 int nr; 8715 int prev_nr; 8716 8717 for (p = node; p != NULL; p = p->wn_sibling) 8718 { 8719 if (p->wn_byte == NUL) 8720 { 8721 gap->ga_len = 0; 8722 prev_nr = 0; 8723 for (np = p; np != NULL && np->wn_byte == NUL; np = np->wn_sibling) 8724 { 8725 if (ga_grow(gap, 10) == FAIL) 8726 return -1; 8727 8728 nr = (np->wn_flags << 16) + (np->wn_region & 0xffff); 8729 /* Compute the offset from the previous nr and store the 8730 * offset in a way that it takes a minimum number of bytes. 8731 * It's a bit like utf-8, but without the need to mark 8732 * following bytes. */ 8733 nr -= prev_nr; 8734 prev_nr += nr; 8735 gap->ga_len += offset2bytes(nr, 8736 (char_u *)gap->ga_data + gap->ga_len); 8737 } 8738 8739 /* add the NUL byte */ 8740 ((char_u *)gap->ga_data)[gap->ga_len++] = NUL; 8741 8742 if (ml_append_buf(spin->si_spellbuf, (linenr_T)wordnr, 8743 gap->ga_data, gap->ga_len, TRUE) == FAIL) 8744 return -1; 8745 ++wordnr; 8746 8747 /* Remove extra NUL entries, we no longer need them. We don't 8748 * bother freeing the nodes, the won't be reused anyway. */ 8749 while (p->wn_sibling != NULL && p->wn_sibling->wn_byte == NUL) 8750 p->wn_sibling = p->wn_sibling->wn_sibling; 8751 8752 /* Clear the flags on the remaining NUL node, so that compression 8753 * works a lot better. */ 8754 p->wn_flags = 0; 8755 p->wn_region = 0; 8756 } 8757 else 8758 { 8759 wordnr = sug_filltable(spin, p->wn_child, wordnr, gap); 8760 if (wordnr == -1) 8761 return -1; 8762 } 8763 } 8764 return wordnr; 8765 } 8766 8767 /* 8768 * Convert an offset into a minimal number of bytes. 8769 * Similar to utf_char2byters, but use 8 bits in followup bytes and avoid NUL 8770 * bytes. 8771 */ 8772 static int 8773 offset2bytes(nr, buf) 8774 int nr; 8775 char_u *buf; 8776 { 8777 int rem; 8778 int b1, b2, b3, b4; 8779 8780 /* Split the number in parts of base 255. We need to avoid NUL bytes. */ 8781 b1 = nr % 255 + 1; 8782 rem = nr / 255; 8783 b2 = rem % 255 + 1; 8784 rem = rem / 255; 8785 b3 = rem % 255 + 1; 8786 b4 = rem / 255 + 1; 8787 8788 if (b4 > 1 || b3 > 0x1f) /* 4 bytes */ 8789 { 8790 buf[0] = 0xe0 + b4; 8791 buf[1] = b3; 8792 buf[2] = b2; 8793 buf[3] = b1; 8794 return 4; 8795 } 8796 if (b3 > 1 || b2 > 0x3f ) /* 3 bytes */ 8797 { 8798 buf[0] = 0xc0 + b3; 8799 buf[1] = b2; 8800 buf[2] = b1; 8801 return 3; 8802 } 8803 if (b2 > 1 || b1 > 0x7f ) /* 2 bytes */ 8804 { 8805 buf[0] = 0x80 + b2; 8806 buf[1] = b1; 8807 return 2; 8808 } 8809 /* 1 byte */ 8810 buf[0] = b1; 8811 return 1; 8812 } 8813 8814 /* 8815 * Opposite of offset2bytes(). 8816 * "pp" points to the bytes and is advanced over it. 8817 * Returns the offset. 8818 */ 8819 static int 8820 bytes2offset(pp) 8821 char_u **pp; 8822 { 8823 char_u *p = *pp; 8824 int nr; 8825 int c; 8826 8827 c = *p++; 8828 if ((c & 0x80) == 0x00) /* 1 byte */ 8829 { 8830 nr = c - 1; 8831 } 8832 else if ((c & 0xc0) == 0x80) /* 2 bytes */ 8833 { 8834 nr = (c & 0x3f) - 1; 8835 nr = nr * 255 + (*p++ - 1); 8836 } 8837 else if ((c & 0xe0) == 0xc0) /* 3 bytes */ 8838 { 8839 nr = (c & 0x1f) - 1; 8840 nr = nr * 255 + (*p++ - 1); 8841 nr = nr * 255 + (*p++ - 1); 8842 } 8843 else /* 4 bytes */ 8844 { 8845 nr = (c & 0x0f) - 1; 8846 nr = nr * 255 + (*p++ - 1); 8847 nr = nr * 255 + (*p++ - 1); 8848 nr = nr * 255 + (*p++ - 1); 8849 } 8850 8851 *pp = p; 8852 return nr; 8853 } 8854 8855 /* 8856 * Write the .sug file in "fname". 8857 */ 8858 static void 8859 sug_write(spin, fname) 8860 spellinfo_T *spin; 8861 char_u *fname; 8862 { 8863 FILE *fd; 8864 wordnode_T *tree; 8865 int nodecount; 8866 int wcount; 8867 char_u *line; 8868 linenr_T lnum; 8869 int len; 8870 8871 /* Create the file. Note that an existing file is silently overwritten! */ 8872 fd = mch_fopen((char *)fname, "w"); 8873 if (fd == NULL) 8874 { 8875 EMSG2(_(e_notopen), fname); 8876 return; 8877 } 8878 8879 vim_snprintf((char *)IObuff, IOSIZE, 8880 _("Writing suggestion file %s ..."), fname); 8881 spell_message(spin, IObuff); 8882 8883 /* 8884 * <SUGHEADER>: <fileID> <versionnr> <timestamp> 8885 */ 8886 if (fwrite(VIMSUGMAGIC, VIMSUGMAGICL, (size_t)1, fd) != 1) /* <fileID> */ 8887 { 8888 EMSG(_(e_write)); 8889 goto theend; 8890 } 8891 putc(VIMSUGVERSION, fd); /* <versionnr> */ 8892 8893 /* Write si_sugtime to the file. */ 8894 put_sugtime(spin, fd); /* <timestamp> */ 8895 8896 /* 8897 * <SUGWORDTREE> 8898 */ 8899 spin->si_memtot = 0; 8900 tree = spin->si_foldroot->wn_sibling; 8901 8902 /* Clear the index and wnode fields in the tree. */ 8903 clear_node(tree); 8904 8905 /* Count the number of nodes. Needed to be able to allocate the 8906 * memory when reading the nodes. Also fills in index for shared 8907 * nodes. */ 8908 nodecount = put_node(NULL, tree, 0, 0, FALSE); 8909 8910 /* number of nodes in 4 bytes */ 8911 put_bytes(fd, (long_u)nodecount, 4); /* <nodecount> */ 8912 spin->si_memtot += nodecount + nodecount * sizeof(int); 8913 8914 /* Write the nodes. */ 8915 (void)put_node(fd, tree, 0, 0, FALSE); 8916 8917 /* 8918 * <SUGTABLE>: <sugwcount> <sugline> ... 8919 */ 8920 wcount = spin->si_spellbuf->b_ml.ml_line_count; 8921 put_bytes(fd, (long_u)wcount, 4); /* <sugwcount> */ 8922 8923 for (lnum = 1; lnum <= (linenr_T)wcount; ++lnum) 8924 { 8925 /* <sugline>: <sugnr> ... NUL */ 8926 line = ml_get_buf(spin->si_spellbuf, lnum, FALSE); 8927 len = (int)STRLEN(line) + 1; 8928 if (fwrite(line, (size_t)len, (size_t)1, fd) == 0) 8929 { 8930 EMSG(_(e_write)); 8931 goto theend; 8932 } 8933 spin->si_memtot += len; 8934 } 8935 8936 /* Write another byte to check for errors. */ 8937 if (putc(0, fd) == EOF) 8938 EMSG(_(e_write)); 8939 8940 vim_snprintf((char *)IObuff, IOSIZE, 8941 _("Estimated runtime memory use: %d bytes"), spin->si_memtot); 8942 spell_message(spin, IObuff); 8943 8944 theend: 8945 /* close the file */ 8946 fclose(fd); 8947 } 8948 8949 /* 8950 * Open a spell buffer. This is a nameless buffer that is not in the buffer 8951 * list and only contains text lines. Can use a swapfile to reduce memory 8952 * use. 8953 * Most other fields are invalid! Esp. watch out for string options being 8954 * NULL and there is no undo info. 8955 * Returns NULL when out of memory. 8956 */ 8957 static buf_T * 8958 open_spellbuf() 8959 { 8960 buf_T *buf; 8961 8962 buf = (buf_T *)alloc_clear(sizeof(buf_T)); 8963 if (buf != NULL) 8964 { 8965 buf->b_spell = TRUE; 8966 buf->b_p_swf = TRUE; /* may create a swap file */ 8967 ml_open(buf); 8968 ml_open_file(buf); /* create swap file now */ 8969 } 8970 return buf; 8971 } 8972 8973 /* 8974 * Close the buffer used for spell info. 8975 */ 8976 static void 8977 close_spellbuf(buf) 8978 buf_T *buf; 8979 { 8980 if (buf != NULL) 8981 { 8982 ml_close(buf, TRUE); 8983 vim_free(buf); 8984 } 8985 } 8986 8987 8988 /* 8989 * Create a Vim spell file from one or more word lists. 8990 * "fnames[0]" is the output file name. 8991 * "fnames[fcount - 1]" is the last input file name. 8992 * Exception: when "fnames[0]" ends in ".add" it's used as the input file name 8993 * and ".spl" is appended to make the output file name. 8994 */ 8995 static void 8996 mkspell(fcount, fnames, ascii, overwrite, added_word) 8997 int fcount; 8998 char_u **fnames; 8999 int ascii; /* -ascii argument given */ 9000 int overwrite; /* overwrite existing output file */ 9001 int added_word; /* invoked through "zg" */ 9002 { 9003 char_u fname[MAXPATHL]; 9004 char_u wfname[MAXPATHL]; 9005 char_u **innames; 9006 int incount; 9007 afffile_T *(afile[8]); 9008 int i; 9009 int len; 9010 struct stat st; 9011 int error = FALSE; 9012 spellinfo_T spin; 9013 9014 vim_memset(&spin, 0, sizeof(spin)); 9015 spin.si_verbose = !added_word; 9016 spin.si_ascii = ascii; 9017 spin.si_followup = TRUE; 9018 spin.si_rem_accents = TRUE; 9019 ga_init2(&spin.si_rep, (int)sizeof(fromto_T), 20); 9020 ga_init2(&spin.si_repsal, (int)sizeof(fromto_T), 20); 9021 ga_init2(&spin.si_sal, (int)sizeof(fromto_T), 20); 9022 ga_init2(&spin.si_map, (int)sizeof(char_u), 100); 9023 ga_init2(&spin.si_comppat, (int)sizeof(char_u *), 20); 9024 ga_init2(&spin.si_prefcond, (int)sizeof(char_u *), 50); 9025 hash_init(&spin.si_commonwords); 9026 spin.si_newcompID = 127; /* start compound ID at first maximum */ 9027 9028 /* default: fnames[0] is output file, following are input files */ 9029 innames = &fnames[1]; 9030 incount = fcount - 1; 9031 9032 if (fcount >= 1) 9033 { 9034 len = (int)STRLEN(fnames[0]); 9035 if (fcount == 1 && len > 4 && STRCMP(fnames[0] + len - 4, ".add") == 0) 9036 { 9037 /* For ":mkspell path/en.latin1.add" output file is 9038 * "path/en.latin1.add.spl". */ 9039 innames = &fnames[0]; 9040 incount = 1; 9041 vim_snprintf((char *)wfname, sizeof(wfname), "%s.spl", fnames[0]); 9042 } 9043 else if (fcount == 1) 9044 { 9045 /* For ":mkspell path/vim" output file is "path/vim.latin1.spl". */ 9046 innames = &fnames[0]; 9047 incount = 1; 9048 vim_snprintf((char *)wfname, sizeof(wfname), "%s.%s.spl", fnames[0], 9049 spin.si_ascii ? (char_u *)"ascii" : spell_enc()); 9050 } 9051 else if (len > 4 && STRCMP(fnames[0] + len - 4, ".spl") == 0) 9052 { 9053 /* Name ends in ".spl", use as the file name. */ 9054 vim_strncpy(wfname, fnames[0], sizeof(wfname) - 1); 9055 } 9056 else 9057 /* Name should be language, make the file name from it. */ 9058 vim_snprintf((char *)wfname, sizeof(wfname), "%s.%s.spl", fnames[0], 9059 spin.si_ascii ? (char_u *)"ascii" : spell_enc()); 9060 9061 /* Check for .ascii.spl. */ 9062 if (strstr((char *)gettail(wfname), ".ascii.") != NULL) 9063 spin.si_ascii = TRUE; 9064 9065 /* Check for .add.spl. */ 9066 if (strstr((char *)gettail(wfname), ".add.") != NULL) 9067 spin.si_add = TRUE; 9068 } 9069 9070 if (incount <= 0) 9071 EMSG(_(e_invarg)); /* need at least output and input names */ 9072 else if (vim_strchr(gettail(wfname), '_') != NULL) 9073 EMSG(_("E751: Output file name must not have region name")); 9074 else if (incount > 8) 9075 EMSG(_("E754: Only up to 8 regions supported")); 9076 else 9077 { 9078 /* Check for overwriting before doing things that may take a lot of 9079 * time. */ 9080 if (!overwrite && mch_stat((char *)wfname, &st) >= 0) 9081 { 9082 EMSG(_(e_exists)); 9083 return; 9084 } 9085 if (mch_isdir(wfname)) 9086 { 9087 EMSG2(_(e_isadir2), wfname); 9088 return; 9089 } 9090 9091 /* 9092 * Init the aff and dic pointers. 9093 * Get the region names if there are more than 2 arguments. 9094 */ 9095 for (i = 0; i < incount; ++i) 9096 { 9097 afile[i] = NULL; 9098 9099 if (incount > 1) 9100 { 9101 len = (int)STRLEN(innames[i]); 9102 if (STRLEN(gettail(innames[i])) < 5 9103 || innames[i][len - 3] != '_') 9104 { 9105 EMSG2(_("E755: Invalid region in %s"), innames[i]); 9106 return; 9107 } 9108 spin.si_region_name[i * 2] = TOLOWER_ASC(innames[i][len - 2]); 9109 spin.si_region_name[i * 2 + 1] = 9110 TOLOWER_ASC(innames[i][len - 1]); 9111 } 9112 } 9113 spin.si_region_count = incount; 9114 9115 spin.si_foldroot = wordtree_alloc(&spin); 9116 spin.si_keeproot = wordtree_alloc(&spin); 9117 spin.si_prefroot = wordtree_alloc(&spin); 9118 if (spin.si_foldroot == NULL 9119 || spin.si_keeproot == NULL 9120 || spin.si_prefroot == NULL) 9121 { 9122 free_blocks(spin.si_blocks); 9123 return; 9124 } 9125 9126 /* When not producing a .add.spl file clear the character table when 9127 * we encounter one in the .aff file. This means we dump the current 9128 * one in the .spl file if the .aff file doesn't define one. That's 9129 * better than guessing the contents, the table will match a 9130 * previously loaded spell file. */ 9131 if (!spin.si_add) 9132 spin.si_clear_chartab = TRUE; 9133 9134 /* 9135 * Read all the .aff and .dic files. 9136 * Text is converted to 'encoding'. 9137 * Words are stored in the case-folded and keep-case trees. 9138 */ 9139 for (i = 0; i < incount && !error; ++i) 9140 { 9141 spin.si_conv.vc_type = CONV_NONE; 9142 spin.si_region = 1 << i; 9143 9144 vim_snprintf((char *)fname, sizeof(fname), "%s.aff", innames[i]); 9145 if (mch_stat((char *)fname, &st) >= 0) 9146 { 9147 /* Read the .aff file. Will init "spin->si_conv" based on the 9148 * "SET" line. */ 9149 afile[i] = spell_read_aff(&spin, fname); 9150 if (afile[i] == NULL) 9151 error = TRUE; 9152 else 9153 { 9154 /* Read the .dic file and store the words in the trees. */ 9155 vim_snprintf((char *)fname, sizeof(fname), "%s.dic", 9156 innames[i]); 9157 if (spell_read_dic(&spin, fname, afile[i]) == FAIL) 9158 error = TRUE; 9159 } 9160 } 9161 else 9162 { 9163 /* No .aff file, try reading the file as a word list. Store 9164 * the words in the trees. */ 9165 if (spell_read_wordfile(&spin, innames[i]) == FAIL) 9166 error = TRUE; 9167 } 9168 9169 #ifdef FEAT_MBYTE 9170 /* Free any conversion stuff. */ 9171 convert_setup(&spin.si_conv, NULL, NULL); 9172 #endif 9173 } 9174 9175 if (spin.si_compflags != NULL && spin.si_nobreak) 9176 MSG(_("Warning: both compounding and NOBREAK specified")); 9177 9178 if (!error && !got_int) 9179 { 9180 /* 9181 * Combine tails in the tree. 9182 */ 9183 spell_message(&spin, (char_u *)_(msg_compressing)); 9184 wordtree_compress(&spin, spin.si_foldroot); 9185 wordtree_compress(&spin, spin.si_keeproot); 9186 wordtree_compress(&spin, spin.si_prefroot); 9187 } 9188 9189 if (!error && !got_int) 9190 { 9191 /* 9192 * Write the info in the spell file. 9193 */ 9194 vim_snprintf((char *)IObuff, IOSIZE, 9195 _("Writing spell file %s ..."), wfname); 9196 spell_message(&spin, IObuff); 9197 9198 error = write_vim_spell(&spin, wfname) == FAIL; 9199 9200 spell_message(&spin, (char_u *)_("Done!")); 9201 vim_snprintf((char *)IObuff, IOSIZE, 9202 _("Estimated runtime memory use: %d bytes"), spin.si_memtot); 9203 spell_message(&spin, IObuff); 9204 9205 /* 9206 * If the file is loaded need to reload it. 9207 */ 9208 if (!error) 9209 spell_reload_one(wfname, added_word); 9210 } 9211 9212 /* Free the allocated memory. */ 9213 ga_clear(&spin.si_rep); 9214 ga_clear(&spin.si_repsal); 9215 ga_clear(&spin.si_sal); 9216 ga_clear(&spin.si_map); 9217 ga_clear(&spin.si_comppat); 9218 ga_clear(&spin.si_prefcond); 9219 hash_clear_all(&spin.si_commonwords, 0); 9220 9221 /* Free the .aff file structures. */ 9222 for (i = 0; i < incount; ++i) 9223 if (afile[i] != NULL) 9224 spell_free_aff(afile[i]); 9225 9226 /* Free all the bits and pieces at once. */ 9227 free_blocks(spin.si_blocks); 9228 9229 /* 9230 * If there is soundfolding info and no NOSUGFILE item create the 9231 * .sug file with the soundfolded word trie. 9232 */ 9233 if (spin.si_sugtime != 0 && !error && !got_int) 9234 spell_make_sugfile(&spin, wfname); 9235 9236 } 9237 } 9238 9239 /* 9240 * Display a message for spell file processing when 'verbose' is set or using 9241 * ":mkspell". "str" can be IObuff. 9242 */ 9243 static void 9244 spell_message(spin, str) 9245 spellinfo_T *spin; 9246 char_u *str; 9247 { 9248 if (spin->si_verbose || p_verbose > 2) 9249 { 9250 if (!spin->si_verbose) 9251 verbose_enter(); 9252 MSG(str); 9253 out_flush(); 9254 if (!spin->si_verbose) 9255 verbose_leave(); 9256 } 9257 } 9258 9259 /* 9260 * ":[count]spellgood {word}" 9261 * ":[count]spellwrong {word}" 9262 * ":[count]spellundo {word}" 9263 */ 9264 void 9265 ex_spell(eap) 9266 exarg_T *eap; 9267 { 9268 spell_add_word(eap->arg, (int)STRLEN(eap->arg), eap->cmdidx == CMD_spellwrong, 9269 eap->forceit ? 0 : (int)eap->line2, 9270 eap->cmdidx == CMD_spellundo); 9271 } 9272 9273 /* 9274 * Add "word[len]" to 'spellfile' as a good or bad word. 9275 */ 9276 void 9277 spell_add_word(word, len, bad, idx, undo) 9278 char_u *word; 9279 int len; 9280 int bad; 9281 int idx; /* "zG" and "zW": zero, otherwise index in 9282 'spellfile' */ 9283 int undo; /* TRUE for "zug", "zuG", "zuw" and "zuW" */ 9284 { 9285 FILE *fd = NULL; 9286 buf_T *buf = NULL; 9287 int new_spf = FALSE; 9288 char_u *fname; 9289 char_u fnamebuf[MAXPATHL]; 9290 char_u line[MAXWLEN * 2]; 9291 long fpos, fpos_next = 0; 9292 int i; 9293 char_u *spf; 9294 9295 if (idx == 0) /* use internal wordlist */ 9296 { 9297 if (int_wordlist == NULL) 9298 { 9299 int_wordlist = vim_tempname('s'); 9300 if (int_wordlist == NULL) 9301 return; 9302 } 9303 fname = int_wordlist; 9304 } 9305 else 9306 { 9307 /* If 'spellfile' isn't set figure out a good default value. */ 9308 if (*curbuf->b_p_spf == NUL) 9309 { 9310 init_spellfile(); 9311 new_spf = TRUE; 9312 } 9313 9314 if (*curbuf->b_p_spf == NUL) 9315 { 9316 EMSG2(_(e_notset), "spellfile"); 9317 return; 9318 } 9319 9320 for (spf = curbuf->b_p_spf, i = 1; *spf != NUL; ++i) 9321 { 9322 copy_option_part(&spf, fnamebuf, MAXPATHL, ","); 9323 if (i == idx) 9324 break; 9325 if (*spf == NUL) 9326 { 9327 EMSGN(_("E765: 'spellfile' does not have %ld entries"), idx); 9328 return; 9329 } 9330 } 9331 9332 /* Check that the user isn't editing the .add file somewhere. */ 9333 buf = buflist_findname_exp(fnamebuf); 9334 if (buf != NULL && buf->b_ml.ml_mfp == NULL) 9335 buf = NULL; 9336 if (buf != NULL && bufIsChanged(buf)) 9337 { 9338 EMSG(_(e_bufloaded)); 9339 return; 9340 } 9341 9342 fname = fnamebuf; 9343 } 9344 9345 if (bad || undo) 9346 { 9347 /* When the word appears as good word we need to remove that one, 9348 * since its flags sort before the one with WF_BANNED. */ 9349 fd = mch_fopen((char *)fname, "r"); 9350 if (fd != NULL) 9351 { 9352 while (!vim_fgets(line, MAXWLEN * 2, fd)) 9353 { 9354 fpos = fpos_next; 9355 fpos_next = ftell(fd); 9356 if (STRNCMP(word, line, len) == 0 9357 && (line[len] == '/' || line[len] < ' ')) 9358 { 9359 /* Found duplicate word. Remove it by writing a '#' at 9360 * the start of the line. Mixing reading and writing 9361 * doesn't work for all systems, close the file first. */ 9362 fclose(fd); 9363 fd = mch_fopen((char *)fname, "r+"); 9364 if (fd == NULL) 9365 break; 9366 if (fseek(fd, fpos, SEEK_SET) == 0) 9367 { 9368 fputc('#', fd); 9369 if (undo) 9370 { 9371 home_replace(NULL, fname, NameBuff, MAXPATHL, TRUE); 9372 smsg((char_u *)_("Word removed from %s"), NameBuff); 9373 } 9374 } 9375 fseek(fd, fpos_next, SEEK_SET); 9376 } 9377 } 9378 fclose(fd); 9379 } 9380 } 9381 9382 if (!undo) 9383 { 9384 fd = mch_fopen((char *)fname, "a"); 9385 if (fd == NULL && new_spf) 9386 { 9387 char_u *p; 9388 9389 /* We just initialized the 'spellfile' option and can't open the 9390 * file. We may need to create the "spell" directory first. We 9391 * already checked the runtime directory is writable in 9392 * init_spellfile(). */ 9393 if (!dir_of_file_exists(fname) && (p = gettail_sep(fname)) != fname) 9394 { 9395 int c = *p; 9396 9397 /* The directory doesn't exist. Try creating it and opening 9398 * the file again. */ 9399 *p = NUL; 9400 vim_mkdir(fname, 0755); 9401 *p = c; 9402 fd = mch_fopen((char *)fname, "a"); 9403 } 9404 } 9405 9406 if (fd == NULL) 9407 EMSG2(_(e_notopen), fname); 9408 else 9409 { 9410 if (bad) 9411 fprintf(fd, "%.*s/!\n", len, word); 9412 else 9413 fprintf(fd, "%.*s\n", len, word); 9414 fclose(fd); 9415 9416 home_replace(NULL, fname, NameBuff, MAXPATHL, TRUE); 9417 smsg((char_u *)_("Word added to %s"), NameBuff); 9418 } 9419 } 9420 9421 if (fd != NULL) 9422 { 9423 /* Update the .add.spl file. */ 9424 mkspell(1, &fname, FALSE, TRUE, TRUE); 9425 9426 /* If the .add file is edited somewhere, reload it. */ 9427 if (buf != NULL) 9428 buf_reload(buf, buf->b_orig_mode); 9429 9430 redraw_all_later(SOME_VALID); 9431 } 9432 } 9433 9434 /* 9435 * Initialize 'spellfile' for the current buffer. 9436 */ 9437 static void 9438 init_spellfile() 9439 { 9440 char_u buf[MAXPATHL]; 9441 int l; 9442 char_u *fname; 9443 char_u *rtp; 9444 char_u *lend; 9445 int aspath = FALSE; 9446 char_u *lstart = curbuf->b_p_spl; 9447 9448 if (*curbuf->b_p_spl != NUL && curbuf->b_langp.ga_len > 0) 9449 { 9450 /* Find the end of the language name. Exclude the region. If there 9451 * is a path separator remember the start of the tail. */ 9452 for (lend = curbuf->b_p_spl; *lend != NUL 9453 && vim_strchr((char_u *)",._", *lend) == NULL; ++lend) 9454 if (vim_ispathsep(*lend)) 9455 { 9456 aspath = TRUE; 9457 lstart = lend + 1; 9458 } 9459 9460 /* Loop over all entries in 'runtimepath'. Use the first one where we 9461 * are allowed to write. */ 9462 rtp = p_rtp; 9463 while (*rtp != NUL) 9464 { 9465 if (aspath) 9466 /* Use directory of an entry with path, e.g., for 9467 * "/dir/lg.utf-8.spl" use "/dir". */ 9468 vim_strncpy(buf, curbuf->b_p_spl, lstart - curbuf->b_p_spl - 1); 9469 else 9470 /* Copy the path from 'runtimepath' to buf[]. */ 9471 copy_option_part(&rtp, buf, MAXPATHL, ","); 9472 if (filewritable(buf) == 2) 9473 { 9474 /* Use the first language name from 'spelllang' and the 9475 * encoding used in the first loaded .spl file. */ 9476 if (aspath) 9477 vim_strncpy(buf, curbuf->b_p_spl, lend - curbuf->b_p_spl); 9478 else 9479 { 9480 /* Create the "spell" directory if it doesn't exist yet. */ 9481 l = (int)STRLEN(buf); 9482 vim_snprintf((char *)buf + l, MAXPATHL - l, "/spell"); 9483 if (!filewritable(buf) != 2) 9484 vim_mkdir(buf, 0755); 9485 9486 l = (int)STRLEN(buf); 9487 vim_snprintf((char *)buf + l, MAXPATHL - l, 9488 "/%.*s", (int)(lend - lstart), lstart); 9489 } 9490 l = (int)STRLEN(buf); 9491 fname = LANGP_ENTRY(curbuf->b_langp, 0)->lp_slang->sl_fname; 9492 vim_snprintf((char *)buf + l, MAXPATHL - l, ".%s.add", 9493 fname != NULL 9494 && strstr((char *)gettail(fname), ".ascii.") != NULL 9495 ? (char_u *)"ascii" : spell_enc()); 9496 set_option_value((char_u *)"spellfile", 0L, buf, OPT_LOCAL); 9497 break; 9498 } 9499 aspath = FALSE; 9500 } 9501 } 9502 } 9503 9504 9505 /* 9506 * Init the chartab used for spelling for ASCII. 9507 * EBCDIC is not supported! 9508 */ 9509 static void 9510 clear_spell_chartab(sp) 9511 spelltab_T *sp; 9512 { 9513 int i; 9514 9515 /* Init everything to FALSE. */ 9516 vim_memset(sp->st_isw, FALSE, sizeof(sp->st_isw)); 9517 vim_memset(sp->st_isu, FALSE, sizeof(sp->st_isu)); 9518 for (i = 0; i < 256; ++i) 9519 { 9520 sp->st_fold[i] = i; 9521 sp->st_upper[i] = i; 9522 } 9523 9524 /* We include digits. A word shouldn't start with a digit, but handling 9525 * that is done separately. */ 9526 for (i = '0'; i <= '9'; ++i) 9527 sp->st_isw[i] = TRUE; 9528 for (i = 'A'; i <= 'Z'; ++i) 9529 { 9530 sp->st_isw[i] = TRUE; 9531 sp->st_isu[i] = TRUE; 9532 sp->st_fold[i] = i + 0x20; 9533 } 9534 for (i = 'a'; i <= 'z'; ++i) 9535 { 9536 sp->st_isw[i] = TRUE; 9537 sp->st_upper[i] = i - 0x20; 9538 } 9539 } 9540 9541 /* 9542 * Init the chartab used for spelling. Only depends on 'encoding'. 9543 * Called once while starting up and when 'encoding' changes. 9544 * The default is to use isalpha(), but the spell file should define the word 9545 * characters to make it possible that 'encoding' differs from the current 9546 * locale. For utf-8 we don't use isalpha() but our own functions. 9547 */ 9548 void 9549 init_spell_chartab() 9550 { 9551 int i; 9552 9553 did_set_spelltab = FALSE; 9554 clear_spell_chartab(&spelltab); 9555 #ifdef FEAT_MBYTE 9556 if (enc_dbcs) 9557 { 9558 /* DBCS: assume double-wide characters are word characters. */ 9559 for (i = 128; i <= 255; ++i) 9560 if (MB_BYTE2LEN(i) == 2) 9561 spelltab.st_isw[i] = TRUE; 9562 } 9563 else if (enc_utf8) 9564 { 9565 for (i = 128; i < 256; ++i) 9566 { 9567 spelltab.st_isu[i] = utf_isupper(i); 9568 spelltab.st_isw[i] = spelltab.st_isu[i] || utf_islower(i); 9569 spelltab.st_fold[i] = utf_fold(i); 9570 spelltab.st_upper[i] = utf_toupper(i); 9571 } 9572 } 9573 else 9574 #endif 9575 { 9576 /* Rough guess: use locale-dependent library functions. */ 9577 for (i = 128; i < 256; ++i) 9578 { 9579 if (MB_ISUPPER(i)) 9580 { 9581 spelltab.st_isw[i] = TRUE; 9582 spelltab.st_isu[i] = TRUE; 9583 spelltab.st_fold[i] = MB_TOLOWER(i); 9584 } 9585 else if (MB_ISLOWER(i)) 9586 { 9587 spelltab.st_isw[i] = TRUE; 9588 spelltab.st_upper[i] = MB_TOUPPER(i); 9589 } 9590 } 9591 } 9592 } 9593 9594 /* 9595 * Set the spell character tables from strings in the affix file. 9596 */ 9597 static int 9598 set_spell_chartab(fol, low, upp) 9599 char_u *fol; 9600 char_u *low; 9601 char_u *upp; 9602 { 9603 /* We build the new tables here first, so that we can compare with the 9604 * previous one. */ 9605 spelltab_T new_st; 9606 char_u *pf = fol, *pl = low, *pu = upp; 9607 int f, l, u; 9608 9609 clear_spell_chartab(&new_st); 9610 9611 while (*pf != NUL) 9612 { 9613 if (*pl == NUL || *pu == NUL) 9614 { 9615 EMSG(_(e_affform)); 9616 return FAIL; 9617 } 9618 #ifdef FEAT_MBYTE 9619 f = mb_ptr2char_adv(&pf); 9620 l = mb_ptr2char_adv(&pl); 9621 u = mb_ptr2char_adv(&pu); 9622 #else 9623 f = *pf++; 9624 l = *pl++; 9625 u = *pu++; 9626 #endif 9627 /* Every character that appears is a word character. */ 9628 if (f < 256) 9629 new_st.st_isw[f] = TRUE; 9630 if (l < 256) 9631 new_st.st_isw[l] = TRUE; 9632 if (u < 256) 9633 new_st.st_isw[u] = TRUE; 9634 9635 /* if "LOW" and "FOL" are not the same the "LOW" char needs 9636 * case-folding */ 9637 if (l < 256 && l != f) 9638 { 9639 if (f >= 256) 9640 { 9641 EMSG(_(e_affrange)); 9642 return FAIL; 9643 } 9644 new_st.st_fold[l] = f; 9645 } 9646 9647 /* if "UPP" and "FOL" are not the same the "UPP" char needs 9648 * case-folding, it's upper case and the "UPP" is the upper case of 9649 * "FOL" . */ 9650 if (u < 256 && u != f) 9651 { 9652 if (f >= 256) 9653 { 9654 EMSG(_(e_affrange)); 9655 return FAIL; 9656 } 9657 new_st.st_fold[u] = f; 9658 new_st.st_isu[u] = TRUE; 9659 new_st.st_upper[f] = u; 9660 } 9661 } 9662 9663 if (*pl != NUL || *pu != NUL) 9664 { 9665 EMSG(_(e_affform)); 9666 return FAIL; 9667 } 9668 9669 return set_spell_finish(&new_st); 9670 } 9671 9672 /* 9673 * Set the spell character tables from strings in the .spl file. 9674 */ 9675 static void 9676 set_spell_charflags(flags, cnt, fol) 9677 char_u *flags; 9678 int cnt; /* length of "flags" */ 9679 char_u *fol; 9680 { 9681 /* We build the new tables here first, so that we can compare with the 9682 * previous one. */ 9683 spelltab_T new_st; 9684 int i; 9685 char_u *p = fol; 9686 int c; 9687 9688 clear_spell_chartab(&new_st); 9689 9690 for (i = 0; i < 128; ++i) 9691 { 9692 if (i < cnt) 9693 { 9694 new_st.st_isw[i + 128] = (flags[i] & CF_WORD) != 0; 9695 new_st.st_isu[i + 128] = (flags[i] & CF_UPPER) != 0; 9696 } 9697 9698 if (*p != NUL) 9699 { 9700 #ifdef FEAT_MBYTE 9701 c = mb_ptr2char_adv(&p); 9702 #else 9703 c = *p++; 9704 #endif 9705 new_st.st_fold[i + 128] = c; 9706 if (i + 128 != c && new_st.st_isu[i + 128] && c < 256) 9707 new_st.st_upper[c] = i + 128; 9708 } 9709 } 9710 9711 (void)set_spell_finish(&new_st); 9712 } 9713 9714 static int 9715 set_spell_finish(new_st) 9716 spelltab_T *new_st; 9717 { 9718 int i; 9719 9720 if (did_set_spelltab) 9721 { 9722 /* check that it's the same table */ 9723 for (i = 0; i < 256; ++i) 9724 { 9725 if (spelltab.st_isw[i] != new_st->st_isw[i] 9726 || spelltab.st_isu[i] != new_st->st_isu[i] 9727 || spelltab.st_fold[i] != new_st->st_fold[i] 9728 || spelltab.st_upper[i] != new_st->st_upper[i]) 9729 { 9730 EMSG(_("E763: Word characters differ between spell files")); 9731 return FAIL; 9732 } 9733 } 9734 } 9735 else 9736 { 9737 /* copy the new spelltab into the one being used */ 9738 spelltab = *new_st; 9739 did_set_spelltab = TRUE; 9740 } 9741 9742 return OK; 9743 } 9744 9745 /* 9746 * Return TRUE if "p" points to a word character. 9747 * As a special case we see "midword" characters as word character when it is 9748 * followed by a word character. This finds they'there but not 'they there'. 9749 * Thus this only works properly when past the first character of the word. 9750 */ 9751 static int 9752 spell_iswordp(p, buf) 9753 char_u *p; 9754 buf_T *buf; /* buffer used */ 9755 { 9756 #ifdef FEAT_MBYTE 9757 char_u *s; 9758 int l; 9759 int c; 9760 9761 if (has_mbyte) 9762 { 9763 l = MB_BYTE2LEN(*p); 9764 s = p; 9765 if (l == 1) 9766 { 9767 /* be quick for ASCII */ 9768 if (buf->b_spell_ismw[*p]) 9769 { 9770 s = p + 1; /* skip a mid-word character */ 9771 l = MB_BYTE2LEN(*s); 9772 } 9773 } 9774 else 9775 { 9776 c = mb_ptr2char(p); 9777 if (c < 256 ? buf->b_spell_ismw[c] 9778 : (buf->b_spell_ismw_mb != NULL 9779 && vim_strchr(buf->b_spell_ismw_mb, c) != NULL)) 9780 { 9781 s = p + l; 9782 l = MB_BYTE2LEN(*s); 9783 } 9784 } 9785 9786 c = mb_ptr2char(s); 9787 if (c > 255) 9788 return mb_get_class(s) >= 2; 9789 return spelltab.st_isw[c]; 9790 } 9791 #endif 9792 9793 return spelltab.st_isw[buf->b_spell_ismw[*p] ? p[1] : p[0]]; 9794 } 9795 9796 /* 9797 * Return TRUE if "p" points to a word character. 9798 * Unlike spell_iswordp() this doesn't check for "midword" characters. 9799 */ 9800 static int 9801 spell_iswordp_nmw(p) 9802 char_u *p; 9803 { 9804 #ifdef FEAT_MBYTE 9805 int c; 9806 9807 if (has_mbyte) 9808 { 9809 c = mb_ptr2char(p); 9810 if (c > 255) 9811 return mb_get_class(p) >= 2; 9812 return spelltab.st_isw[c]; 9813 } 9814 #endif 9815 return spelltab.st_isw[*p]; 9816 } 9817 9818 #ifdef FEAT_MBYTE 9819 /* 9820 * Return TRUE if "p" points to a word character. 9821 * Wide version of spell_iswordp(). 9822 */ 9823 static int 9824 spell_iswordp_w(p, buf) 9825 int *p; 9826 buf_T *buf; 9827 { 9828 int *s; 9829 9830 if (*p < 256 ? buf->b_spell_ismw[*p] 9831 : (buf->b_spell_ismw_mb != NULL 9832 && vim_strchr(buf->b_spell_ismw_mb, *p) != NULL)) 9833 s = p + 1; 9834 else 9835 s = p; 9836 9837 if (*s > 255) 9838 { 9839 if (enc_utf8) 9840 return utf_class(*s) >= 2; 9841 if (enc_dbcs) 9842 return dbcs_class((unsigned)*s >> 8, *s & 0xff) >= 2; 9843 return 0; 9844 } 9845 return spelltab.st_isw[*s]; 9846 } 9847 #endif 9848 9849 /* 9850 * Write the table with prefix conditions to the .spl file. 9851 * When "fd" is NULL only count the length of what is written. 9852 */ 9853 static int 9854 write_spell_prefcond(fd, gap) 9855 FILE *fd; 9856 garray_T *gap; 9857 { 9858 int i; 9859 char_u *p; 9860 int len; 9861 int totlen; 9862 9863 if (fd != NULL) 9864 put_bytes(fd, (long_u)gap->ga_len, 2); /* <prefcondcnt> */ 9865 9866 totlen = 2 + gap->ga_len; /* length of <prefcondcnt> and <condlen> bytes */ 9867 9868 for (i = 0; i < gap->ga_len; ++i) 9869 { 9870 /* <prefcond> : <condlen> <condstr> */ 9871 p = ((char_u **)gap->ga_data)[i]; 9872 if (p != NULL) 9873 { 9874 len = (int)STRLEN(p); 9875 if (fd != NULL) 9876 { 9877 fputc(len, fd); 9878 fwrite(p, (size_t)len, (size_t)1, fd); 9879 } 9880 totlen += len; 9881 } 9882 else if (fd != NULL) 9883 fputc(0, fd); 9884 } 9885 9886 return totlen; 9887 } 9888 9889 /* 9890 * Case-fold "str[len]" into "buf[buflen]". The result is NUL terminated. 9891 * Uses the character definitions from the .spl file. 9892 * When using a multi-byte 'encoding' the length may change! 9893 * Returns FAIL when something wrong. 9894 */ 9895 static int 9896 spell_casefold(str, len, buf, buflen) 9897 char_u *str; 9898 int len; 9899 char_u *buf; 9900 int buflen; 9901 { 9902 int i; 9903 9904 if (len >= buflen) 9905 { 9906 buf[0] = NUL; 9907 return FAIL; /* result will not fit */ 9908 } 9909 9910 #ifdef FEAT_MBYTE 9911 if (has_mbyte) 9912 { 9913 int outi = 0; 9914 char_u *p; 9915 int c; 9916 9917 /* Fold one character at a time. */ 9918 for (p = str; p < str + len; ) 9919 { 9920 if (outi + MB_MAXBYTES > buflen) 9921 { 9922 buf[outi] = NUL; 9923 return FAIL; 9924 } 9925 c = mb_cptr2char_adv(&p); 9926 outi += mb_char2bytes(SPELL_TOFOLD(c), buf + outi); 9927 } 9928 buf[outi] = NUL; 9929 } 9930 else 9931 #endif 9932 { 9933 /* Be quick for non-multibyte encodings. */ 9934 for (i = 0; i < len; ++i) 9935 buf[i] = spelltab.st_fold[str[i]]; 9936 buf[i] = NUL; 9937 } 9938 9939 return OK; 9940 } 9941 9942 /* values for sps_flags */ 9943 #define SPS_BEST 1 9944 #define SPS_FAST 2 9945 #define SPS_DOUBLE 4 9946 9947 static int sps_flags = SPS_BEST; /* flags from 'spellsuggest' */ 9948 static int sps_limit = 9999; /* max nr of suggestions given */ 9949 9950 /* 9951 * Check the 'spellsuggest' option. Return FAIL if it's wrong. 9952 * Sets "sps_flags" and "sps_limit". 9953 */ 9954 int 9955 spell_check_sps() 9956 { 9957 char_u *p; 9958 char_u *s; 9959 char_u buf[MAXPATHL]; 9960 int f; 9961 9962 sps_flags = 0; 9963 sps_limit = 9999; 9964 9965 for (p = p_sps; *p != NUL; ) 9966 { 9967 copy_option_part(&p, buf, MAXPATHL, ","); 9968 9969 f = 0; 9970 if (VIM_ISDIGIT(*buf)) 9971 { 9972 s = buf; 9973 sps_limit = getdigits(&s); 9974 if (*s != NUL && !VIM_ISDIGIT(*s)) 9975 f = -1; 9976 } 9977 else if (STRCMP(buf, "best") == 0) 9978 f = SPS_BEST; 9979 else if (STRCMP(buf, "fast") == 0) 9980 f = SPS_FAST; 9981 else if (STRCMP(buf, "double") == 0) 9982 f = SPS_DOUBLE; 9983 else if (STRNCMP(buf, "expr:", 5) != 0 9984 && STRNCMP(buf, "file:", 5) != 0) 9985 f = -1; 9986 9987 if (f == -1 || (sps_flags != 0 && f != 0)) 9988 { 9989 sps_flags = SPS_BEST; 9990 sps_limit = 9999; 9991 return FAIL; 9992 } 9993 if (f != 0) 9994 sps_flags = f; 9995 } 9996 9997 if (sps_flags == 0) 9998 sps_flags = SPS_BEST; 9999 10000 return OK; 10001 } 10002 10003 /* 10004 * "z?": Find badly spelled word under or after the cursor. 10005 * Give suggestions for the properly spelled word. 10006 * In Visual mode use the highlighted word as the bad word. 10007 * When "count" is non-zero use that suggestion. 10008 */ 10009 void 10010 spell_suggest(count) 10011 int count; 10012 { 10013 char_u *line; 10014 pos_T prev_cursor = curwin->w_cursor; 10015 char_u wcopy[MAXWLEN + 2]; 10016 char_u *p; 10017 int i; 10018 int c; 10019 suginfo_T sug; 10020 suggest_T *stp; 10021 int mouse_used; 10022 int need_cap; 10023 int limit; 10024 int selected = count; 10025 int badlen = 0; 10026 10027 if (no_spell_checking(curwin)) 10028 return; 10029 10030 #ifdef FEAT_VISUAL 10031 if (VIsual_active) 10032 { 10033 /* Use the Visually selected text as the bad word. But reject 10034 * a multi-line selection. */ 10035 if (curwin->w_cursor.lnum != VIsual.lnum) 10036 { 10037 vim_beep(); 10038 return; 10039 } 10040 badlen = (int)curwin->w_cursor.col - (int)VIsual.col; 10041 if (badlen < 0) 10042 badlen = -badlen; 10043 else 10044 curwin->w_cursor.col = VIsual.col; 10045 ++badlen; 10046 end_visual_mode(); 10047 } 10048 else 10049 #endif 10050 /* Find the start of the badly spelled word. */ 10051 if (spell_move_to(curwin, FORWARD, TRUE, TRUE, NULL) == 0 10052 || curwin->w_cursor.col > prev_cursor.col) 10053 { 10054 /* No bad word or it starts after the cursor: use the word under the 10055 * cursor. */ 10056 curwin->w_cursor = prev_cursor; 10057 line = ml_get_curline(); 10058 p = line + curwin->w_cursor.col; 10059 /* Backup to before start of word. */ 10060 while (p > line && spell_iswordp_nmw(p)) 10061 mb_ptr_back(line, p); 10062 /* Forward to start of word. */ 10063 while (*p != NUL && !spell_iswordp_nmw(p)) 10064 mb_ptr_adv(p); 10065 10066 if (!spell_iswordp_nmw(p)) /* No word found. */ 10067 { 10068 beep_flush(); 10069 return; 10070 } 10071 curwin->w_cursor.col = (colnr_T)(p - line); 10072 } 10073 10074 /* Get the word and its length. */ 10075 10076 /* Figure out if the word should be capitalised. */ 10077 need_cap = check_need_cap(curwin->w_cursor.lnum, curwin->w_cursor.col); 10078 10079 line = ml_get_curline(); 10080 10081 /* Get the list of suggestions. Limit to 'lines' - 2 or the number in 10082 * 'spellsuggest', whatever is smaller. */ 10083 if (sps_limit > (int)Rows - 2) 10084 limit = (int)Rows - 2; 10085 else 10086 limit = sps_limit; 10087 spell_find_suggest(line + curwin->w_cursor.col, badlen, &sug, limit, 10088 TRUE, need_cap, TRUE); 10089 10090 if (sug.su_ga.ga_len == 0) 10091 MSG(_("Sorry, no suggestions")); 10092 else if (count > 0) 10093 { 10094 if (count > sug.su_ga.ga_len) 10095 smsg((char_u *)_("Sorry, only %ld suggestions"), 10096 (long)sug.su_ga.ga_len); 10097 } 10098 else 10099 { 10100 vim_free(repl_from); 10101 repl_from = NULL; 10102 vim_free(repl_to); 10103 repl_to = NULL; 10104 10105 #ifdef FEAT_RIGHTLEFT 10106 /* When 'rightleft' is set the list is drawn right-left. */ 10107 cmdmsg_rl = curwin->w_p_rl; 10108 if (cmdmsg_rl) 10109 msg_col = Columns - 1; 10110 #endif 10111 10112 /* List the suggestions. */ 10113 msg_start(); 10114 msg_row = Rows - 1; /* for when 'cmdheight' > 1 */ 10115 lines_left = Rows; /* avoid more prompt */ 10116 vim_snprintf((char *)IObuff, IOSIZE, _("Change \"%.*s\" to:"), 10117 sug.su_badlen, sug.su_badptr); 10118 #ifdef FEAT_RIGHTLEFT 10119 if (cmdmsg_rl && STRNCMP(IObuff, "Change", 6) == 0) 10120 { 10121 /* And now the rabbit from the high hat: Avoid showing the 10122 * untranslated message rightleft. */ 10123 vim_snprintf((char *)IObuff, IOSIZE, ":ot \"%.*s\" egnahC", 10124 sug.su_badlen, sug.su_badptr); 10125 } 10126 #endif 10127 msg_puts(IObuff); 10128 msg_clr_eos(); 10129 msg_putchar('\n'); 10130 10131 msg_scroll = TRUE; 10132 for (i = 0; i < sug.su_ga.ga_len; ++i) 10133 { 10134 stp = &SUG(sug.su_ga, i); 10135 10136 /* The suggested word may replace only part of the bad word, add 10137 * the not replaced part. */ 10138 STRCPY(wcopy, stp->st_word); 10139 if (sug.su_badlen > stp->st_orglen) 10140 vim_strncpy(wcopy + stp->st_wordlen, 10141 sug.su_badptr + stp->st_orglen, 10142 sug.su_badlen - stp->st_orglen); 10143 vim_snprintf((char *)IObuff, IOSIZE, "%2d", i + 1); 10144 #ifdef FEAT_RIGHTLEFT 10145 if (cmdmsg_rl) 10146 rl_mirror(IObuff); 10147 #endif 10148 msg_puts(IObuff); 10149 10150 vim_snprintf((char *)IObuff, IOSIZE, " \"%s\"", wcopy); 10151 msg_puts(IObuff); 10152 10153 /* The word may replace more than "su_badlen". */ 10154 if (sug.su_badlen < stp->st_orglen) 10155 { 10156 vim_snprintf((char *)IObuff, IOSIZE, _(" < \"%.*s\""), 10157 stp->st_orglen, sug.su_badptr); 10158 msg_puts(IObuff); 10159 } 10160 10161 if (p_verbose > 0) 10162 { 10163 /* Add the score. */ 10164 if (sps_flags & (SPS_DOUBLE | SPS_BEST)) 10165 vim_snprintf((char *)IObuff, IOSIZE, " (%s%d - %d)", 10166 stp->st_salscore ? "s " : "", 10167 stp->st_score, stp->st_altscore); 10168 else 10169 vim_snprintf((char *)IObuff, IOSIZE, " (%d)", 10170 stp->st_score); 10171 #ifdef FEAT_RIGHTLEFT 10172 if (cmdmsg_rl) 10173 /* Mirror the numbers, but keep the leading space. */ 10174 rl_mirror(IObuff + 1); 10175 #endif 10176 msg_advance(30); 10177 msg_puts(IObuff); 10178 } 10179 msg_putchar('\n'); 10180 } 10181 10182 #ifdef FEAT_RIGHTLEFT 10183 cmdmsg_rl = FALSE; 10184 msg_col = 0; 10185 #endif 10186 /* Ask for choice. */ 10187 selected = prompt_for_number(&mouse_used); 10188 if (mouse_used) 10189 selected -= lines_left; 10190 lines_left = Rows; /* avoid more prompt */ 10191 } 10192 10193 if (selected > 0 && selected <= sug.su_ga.ga_len && u_save_cursor() == OK) 10194 { 10195 /* Save the from and to text for :spellrepall. */ 10196 stp = &SUG(sug.su_ga, selected - 1); 10197 if (sug.su_badlen > stp->st_orglen) 10198 { 10199 /* Replacing less than "su_badlen", append the remainder to 10200 * repl_to. */ 10201 repl_from = vim_strnsave(sug.su_badptr, sug.su_badlen); 10202 vim_snprintf((char *)IObuff, IOSIZE, "%s%.*s", stp->st_word, 10203 sug.su_badlen - stp->st_orglen, 10204 sug.su_badptr + stp->st_orglen); 10205 repl_to = vim_strsave(IObuff); 10206 } 10207 else 10208 { 10209 /* Replacing su_badlen or more, use the whole word. */ 10210 repl_from = vim_strnsave(sug.su_badptr, stp->st_orglen); 10211 repl_to = vim_strsave(stp->st_word); 10212 } 10213 10214 /* Replace the word. */ 10215 p = alloc((unsigned)STRLEN(line) - stp->st_orglen + stp->st_wordlen + 1); 10216 if (p != NULL) 10217 { 10218 c = (int)(sug.su_badptr - line); 10219 mch_memmove(p, line, c); 10220 STRCPY(p + c, stp->st_word); 10221 STRCAT(p, sug.su_badptr + stp->st_orglen); 10222 ml_replace(curwin->w_cursor.lnum, p, FALSE); 10223 curwin->w_cursor.col = c; 10224 10225 /* For redo we use a change-word command. */ 10226 ResetRedobuff(); 10227 AppendToRedobuff((char_u *)"ciw"); 10228 AppendToRedobuffLit(p + c, 10229 stp->st_wordlen + sug.su_badlen - stp->st_orglen); 10230 AppendCharToRedobuff(ESC); 10231 10232 /* After this "p" may be invalid. */ 10233 changed_bytes(curwin->w_cursor.lnum, c); 10234 } 10235 } 10236 else 10237 curwin->w_cursor = prev_cursor; 10238 10239 spell_find_cleanup(&sug); 10240 } 10241 10242 /* 10243 * Check if the word at line "lnum" column "col" is required to start with a 10244 * capital. This uses 'spellcapcheck' of the current buffer. 10245 */ 10246 static int 10247 check_need_cap(lnum, col) 10248 linenr_T lnum; 10249 colnr_T col; 10250 { 10251 int need_cap = FALSE; 10252 char_u *line; 10253 char_u *line_copy = NULL; 10254 char_u *p; 10255 colnr_T endcol; 10256 regmatch_T regmatch; 10257 10258 if (curbuf->b_cap_prog == NULL) 10259 return FALSE; 10260 10261 line = ml_get_curline(); 10262 endcol = 0; 10263 if ((int)(skipwhite(line) - line) >= (int)col) 10264 { 10265 /* At start of line, check if previous line is empty or sentence 10266 * ends there. */ 10267 if (lnum == 1) 10268 need_cap = TRUE; 10269 else 10270 { 10271 line = ml_get(lnum - 1); 10272 if (*skipwhite(line) == NUL) 10273 need_cap = TRUE; 10274 else 10275 { 10276 /* Append a space in place of the line break. */ 10277 line_copy = concat_str(line, (char_u *)" "); 10278 line = line_copy; 10279 endcol = (colnr_T)STRLEN(line); 10280 } 10281 } 10282 } 10283 else 10284 endcol = col; 10285 10286 if (endcol > 0) 10287 { 10288 /* Check if sentence ends before the bad word. */ 10289 regmatch.regprog = curbuf->b_cap_prog; 10290 regmatch.rm_ic = FALSE; 10291 p = line + endcol; 10292 for (;;) 10293 { 10294 mb_ptr_back(line, p); 10295 if (p == line || spell_iswordp_nmw(p)) 10296 break; 10297 if (vim_regexec(®match, p, 0) 10298 && regmatch.endp[0] == line + endcol) 10299 { 10300 need_cap = TRUE; 10301 break; 10302 } 10303 } 10304 } 10305 10306 vim_free(line_copy); 10307 10308 return need_cap; 10309 } 10310 10311 10312 /* 10313 * ":spellrepall" 10314 */ 10315 /*ARGSUSED*/ 10316 void 10317 ex_spellrepall(eap) 10318 exarg_T *eap; 10319 { 10320 pos_T pos = curwin->w_cursor; 10321 char_u *frompat; 10322 int addlen; 10323 char_u *line; 10324 char_u *p; 10325 int save_ws = p_ws; 10326 linenr_T prev_lnum = 0; 10327 10328 if (repl_from == NULL || repl_to == NULL) 10329 { 10330 EMSG(_("E752: No previous spell replacement")); 10331 return; 10332 } 10333 addlen = (int)(STRLEN(repl_to) - STRLEN(repl_from)); 10334 10335 frompat = alloc((unsigned)STRLEN(repl_from) + 7); 10336 if (frompat == NULL) 10337 return; 10338 sprintf((char *)frompat, "\\V\\<%s\\>", repl_from); 10339 p_ws = FALSE; 10340 10341 sub_nsubs = 0; 10342 sub_nlines = 0; 10343 curwin->w_cursor.lnum = 0; 10344 while (!got_int) 10345 { 10346 if (do_search(NULL, '/', frompat, 1L, SEARCH_KEEP) == 0 10347 || u_save_cursor() == FAIL) 10348 break; 10349 10350 /* Only replace when the right word isn't there yet. This happens 10351 * when changing "etc" to "etc.". */ 10352 line = ml_get_curline(); 10353 if (addlen <= 0 || STRNCMP(line + curwin->w_cursor.col, 10354 repl_to, STRLEN(repl_to)) != 0) 10355 { 10356 p = alloc((unsigned)STRLEN(line) + addlen + 1); 10357 if (p == NULL) 10358 break; 10359 mch_memmove(p, line, curwin->w_cursor.col); 10360 STRCPY(p + curwin->w_cursor.col, repl_to); 10361 STRCAT(p, line + curwin->w_cursor.col + STRLEN(repl_from)); 10362 ml_replace(curwin->w_cursor.lnum, p, FALSE); 10363 changed_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col); 10364 10365 if (curwin->w_cursor.lnum != prev_lnum) 10366 { 10367 ++sub_nlines; 10368 prev_lnum = curwin->w_cursor.lnum; 10369 } 10370 ++sub_nsubs; 10371 } 10372 curwin->w_cursor.col += (colnr_T)STRLEN(repl_to); 10373 } 10374 10375 p_ws = save_ws; 10376 curwin->w_cursor = pos; 10377 vim_free(frompat); 10378 10379 if (sub_nsubs == 0) 10380 EMSG2(_("E753: Not found: %s"), repl_from); 10381 else 10382 do_sub_msg(FALSE); 10383 } 10384 10385 /* 10386 * Find spell suggestions for "word". Return them in the growarray "*gap" as 10387 * a list of allocated strings. 10388 */ 10389 void 10390 spell_suggest_list(gap, word, maxcount, need_cap, interactive) 10391 garray_T *gap; 10392 char_u *word; 10393 int maxcount; /* maximum nr of suggestions */ 10394 int need_cap; /* 'spellcapcheck' matched */ 10395 int interactive; 10396 { 10397 suginfo_T sug; 10398 int i; 10399 suggest_T *stp; 10400 char_u *wcopy; 10401 10402 spell_find_suggest(word, 0, &sug, maxcount, FALSE, need_cap, interactive); 10403 10404 /* Make room in "gap". */ 10405 ga_init2(gap, sizeof(char_u *), sug.su_ga.ga_len + 1); 10406 if (ga_grow(gap, sug.su_ga.ga_len) == OK) 10407 { 10408 for (i = 0; i < sug.su_ga.ga_len; ++i) 10409 { 10410 stp = &SUG(sug.su_ga, i); 10411 10412 /* The suggested word may replace only part of "word", add the not 10413 * replaced part. */ 10414 wcopy = alloc(stp->st_wordlen 10415 + (unsigned)STRLEN(sug.su_badptr + stp->st_orglen) + 1); 10416 if (wcopy == NULL) 10417 break; 10418 STRCPY(wcopy, stp->st_word); 10419 STRCPY(wcopy + stp->st_wordlen, sug.su_badptr + stp->st_orglen); 10420 ((char_u **)gap->ga_data)[gap->ga_len++] = wcopy; 10421 } 10422 } 10423 10424 spell_find_cleanup(&sug); 10425 } 10426 10427 /* 10428 * Find spell suggestions for the word at the start of "badptr". 10429 * Return the suggestions in "su->su_ga". 10430 * The maximum number of suggestions is "maxcount". 10431 * Note: does use info for the current window. 10432 * This is based on the mechanisms of Aspell, but completely reimplemented. 10433 */ 10434 static void 10435 spell_find_suggest(badptr, badlen, su, maxcount, banbadword, need_cap, interactive) 10436 char_u *badptr; 10437 int badlen; /* length of bad word or 0 if unknown */ 10438 suginfo_T *su; 10439 int maxcount; 10440 int banbadword; /* don't include badword in suggestions */ 10441 int need_cap; /* word should start with capital */ 10442 int interactive; 10443 { 10444 hlf_T attr = HLF_COUNT; 10445 char_u buf[MAXPATHL]; 10446 char_u *p; 10447 int do_combine = FALSE; 10448 char_u *sps_copy; 10449 #ifdef FEAT_EVAL 10450 static int expr_busy = FALSE; 10451 #endif 10452 int c; 10453 int i; 10454 langp_T *lp; 10455 10456 /* 10457 * Set the info in "*su". 10458 */ 10459 vim_memset(su, 0, sizeof(suginfo_T)); 10460 ga_init2(&su->su_ga, (int)sizeof(suggest_T), 10); 10461 ga_init2(&su->su_sga, (int)sizeof(suggest_T), 10); 10462 if (*badptr == NUL) 10463 return; 10464 hash_init(&su->su_banned); 10465 10466 su->su_badptr = badptr; 10467 if (badlen != 0) 10468 su->su_badlen = badlen; 10469 else 10470 su->su_badlen = spell_check(curwin, su->su_badptr, &attr, NULL, FALSE); 10471 su->su_maxcount = maxcount; 10472 su->su_maxscore = SCORE_MAXINIT; 10473 10474 if (su->su_badlen >= MAXWLEN) 10475 su->su_badlen = MAXWLEN - 1; /* just in case */ 10476 vim_strncpy(su->su_badword, su->su_badptr, su->su_badlen); 10477 (void)spell_casefold(su->su_badptr, su->su_badlen, 10478 su->su_fbadword, MAXWLEN); 10479 /* get caps flags for bad word */ 10480 su->su_badflags = badword_captype(su->su_badptr, 10481 su->su_badptr + su->su_badlen); 10482 if (need_cap) 10483 su->su_badflags |= WF_ONECAP; 10484 10485 /* Find the default language for sound folding. We simply use the first 10486 * one in 'spelllang' that supports sound folding. That's good for when 10487 * using multiple files for one language, it's not that bad when mixing 10488 * languages (e.g., "pl,en"). */ 10489 for (i = 0; i < curbuf->b_langp.ga_len; ++i) 10490 { 10491 lp = LANGP_ENTRY(curbuf->b_langp, i); 10492 if (lp->lp_sallang != NULL) 10493 { 10494 su->su_sallang = lp->lp_sallang; 10495 break; 10496 } 10497 } 10498 10499 /* Soundfold the bad word with the default sound folding, so that we don't 10500 * have to do this many times. */ 10501 if (su->su_sallang != NULL) 10502 spell_soundfold(su->su_sallang, su->su_fbadword, TRUE, 10503 su->su_sal_badword); 10504 10505 /* If the word is not capitalised and spell_check() doesn't consider the 10506 * word to be bad then it might need to be capitalised. Add a suggestion 10507 * for that. */ 10508 c = PTR2CHAR(su->su_badptr); 10509 if (!SPELL_ISUPPER(c) && attr == HLF_COUNT) 10510 { 10511 make_case_word(su->su_badword, buf, WF_ONECAP); 10512 add_suggestion(su, &su->su_ga, buf, su->su_badlen, SCORE_ICASE, 10513 0, TRUE, su->su_sallang, FALSE); 10514 } 10515 10516 /* Ban the bad word itself. It may appear in another region. */ 10517 if (banbadword) 10518 add_banned(su, su->su_badword); 10519 10520 /* Make a copy of 'spellsuggest', because the expression may change it. */ 10521 sps_copy = vim_strsave(p_sps); 10522 if (sps_copy == NULL) 10523 return; 10524 10525 /* Loop over the items in 'spellsuggest'. */ 10526 for (p = sps_copy; *p != NUL; ) 10527 { 10528 copy_option_part(&p, buf, MAXPATHL, ","); 10529 10530 if (STRNCMP(buf, "expr:", 5) == 0) 10531 { 10532 #ifdef FEAT_EVAL 10533 /* Evaluate an expression. Skip this when called recursively, 10534 * when using spellsuggest() in the expression. */ 10535 if (!expr_busy) 10536 { 10537 expr_busy = TRUE; 10538 spell_suggest_expr(su, buf + 5); 10539 expr_busy = FALSE; 10540 } 10541 #endif 10542 } 10543 else if (STRNCMP(buf, "file:", 5) == 0) 10544 /* Use list of suggestions in a file. */ 10545 spell_suggest_file(su, buf + 5); 10546 else 10547 { 10548 /* Use internal method. */ 10549 spell_suggest_intern(su, interactive); 10550 if (sps_flags & SPS_DOUBLE) 10551 do_combine = TRUE; 10552 } 10553 } 10554 10555 vim_free(sps_copy); 10556 10557 if (do_combine) 10558 /* Combine the two list of suggestions. This must be done last, 10559 * because sorting changes the order again. */ 10560 score_combine(su); 10561 } 10562 10563 #ifdef FEAT_EVAL 10564 /* 10565 * Find suggestions by evaluating expression "expr". 10566 */ 10567 static void 10568 spell_suggest_expr(su, expr) 10569 suginfo_T *su; 10570 char_u *expr; 10571 { 10572 list_T *list; 10573 listitem_T *li; 10574 int score; 10575 char_u *p; 10576 10577 /* The work is split up in a few parts to avoid having to export 10578 * suginfo_T. 10579 * First evaluate the expression and get the resulting list. */ 10580 list = eval_spell_expr(su->su_badword, expr); 10581 if (list != NULL) 10582 { 10583 /* Loop over the items in the list. */ 10584 for (li = list->lv_first; li != NULL; li = li->li_next) 10585 if (li->li_tv.v_type == VAR_LIST) 10586 { 10587 /* Get the word and the score from the items. */ 10588 score = get_spellword(li->li_tv.vval.v_list, &p); 10589 if (score >= 0 && score <= su->su_maxscore) 10590 add_suggestion(su, &su->su_ga, p, su->su_badlen, 10591 score, 0, TRUE, su->su_sallang, FALSE); 10592 } 10593 list_unref(list); 10594 } 10595 10596 /* Remove bogus suggestions, sort and truncate at "maxcount". */ 10597 check_suggestions(su, &su->su_ga); 10598 (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount); 10599 } 10600 #endif 10601 10602 /* 10603 * Find suggestions in file "fname". Used for "file:" in 'spellsuggest'. 10604 */ 10605 static void 10606 spell_suggest_file(su, fname) 10607 suginfo_T *su; 10608 char_u *fname; 10609 { 10610 FILE *fd; 10611 char_u line[MAXWLEN * 2]; 10612 char_u *p; 10613 int len; 10614 char_u cword[MAXWLEN]; 10615 10616 /* Open the file. */ 10617 fd = mch_fopen((char *)fname, "r"); 10618 if (fd == NULL) 10619 { 10620 EMSG2(_(e_notopen), fname); 10621 return; 10622 } 10623 10624 /* Read it line by line. */ 10625 while (!vim_fgets(line, MAXWLEN * 2, fd) && !got_int) 10626 { 10627 line_breakcheck(); 10628 10629 p = vim_strchr(line, '/'); 10630 if (p == NULL) 10631 continue; /* No Tab found, just skip the line. */ 10632 *p++ = NUL; 10633 if (STRICMP(su->su_badword, line) == 0) 10634 { 10635 /* Match! Isolate the good word, until CR or NL. */ 10636 for (len = 0; p[len] >= ' '; ++len) 10637 ; 10638 p[len] = NUL; 10639 10640 /* If the suggestion doesn't have specific case duplicate the case 10641 * of the bad word. */ 10642 if (captype(p, NULL) == 0) 10643 { 10644 make_case_word(p, cword, su->su_badflags); 10645 p = cword; 10646 } 10647 10648 add_suggestion(su, &su->su_ga, p, su->su_badlen, 10649 SCORE_FILE, 0, TRUE, su->su_sallang, FALSE); 10650 } 10651 } 10652 10653 fclose(fd); 10654 10655 /* Remove bogus suggestions, sort and truncate at "maxcount". */ 10656 check_suggestions(su, &su->su_ga); 10657 (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount); 10658 } 10659 10660 /* 10661 * Find suggestions for the internal method indicated by "sps_flags". 10662 */ 10663 static void 10664 spell_suggest_intern(su, interactive) 10665 suginfo_T *su; 10666 int interactive; 10667 { 10668 /* 10669 * Load the .sug file(s) that are available and not done yet. 10670 */ 10671 suggest_load_files(); 10672 10673 /* 10674 * 1. Try special cases, such as repeating a word: "the the" -> "the". 10675 * 10676 * Set a maximum score to limit the combination of operations that is 10677 * tried. 10678 */ 10679 suggest_try_special(su); 10680 10681 /* 10682 * 2. Try inserting/deleting/swapping/changing a letter, use REP entries 10683 * from the .aff file and inserting a space (split the word). 10684 */ 10685 suggest_try_change(su); 10686 10687 /* For the resulting top-scorers compute the sound-a-like score. */ 10688 if (sps_flags & SPS_DOUBLE) 10689 score_comp_sal(su); 10690 10691 /* 10692 * 3. Try finding sound-a-like words. 10693 */ 10694 if ((sps_flags & SPS_FAST) == 0) 10695 { 10696 if (sps_flags & SPS_BEST) 10697 /* Adjust the word score for the suggestions found so far for how 10698 * they sounds like. */ 10699 rescore_suggestions(su); 10700 10701 /* 10702 * While going throught the soundfold tree "su_maxscore" is the score 10703 * for the soundfold word, limits the changes that are being tried, 10704 * and "su_sfmaxscore" the rescored score, which is set by 10705 * cleanup_suggestions(). 10706 * First find words with a small edit distance, because this is much 10707 * faster and often already finds the top-N suggestions. If we didn't 10708 * find many suggestions try again with a higher edit distance. 10709 * "sl_sounddone" is used to avoid doing the same word twice. 10710 */ 10711 suggest_try_soundalike_prep(); 10712 su->su_maxscore = SCORE_SFMAX1; 10713 su->su_sfmaxscore = SCORE_MAXINIT * 3; 10714 suggest_try_soundalike(su); 10715 if (su->su_ga.ga_len < SUG_CLEAN_COUNT(su)) 10716 { 10717 /* We didn't find enough matches, try again, allowing more 10718 * changes to the soundfold word. */ 10719 su->su_maxscore = SCORE_SFMAX2; 10720 suggest_try_soundalike(su); 10721 if (su->su_ga.ga_len < SUG_CLEAN_COUNT(su)) 10722 { 10723 /* Still didn't find enough matches, try again, allowing even 10724 * more changes to the soundfold word. */ 10725 su->su_maxscore = SCORE_SFMAX3; 10726 suggest_try_soundalike(su); 10727 } 10728 } 10729 su->su_maxscore = su->su_sfmaxscore; 10730 suggest_try_soundalike_finish(); 10731 } 10732 10733 /* When CTRL-C was hit while searching do show the results. Only clear 10734 * got_int when using a command, not for spellsuggest(). */ 10735 ui_breakcheck(); 10736 if (interactive && got_int) 10737 { 10738 (void)vgetc(); 10739 got_int = FALSE; 10740 } 10741 10742 if ((sps_flags & SPS_DOUBLE) == 0 && su->su_ga.ga_len != 0) 10743 { 10744 if (sps_flags & SPS_BEST) 10745 /* Adjust the word score for how it sounds like. */ 10746 rescore_suggestions(su); 10747 10748 /* Remove bogus suggestions, sort and truncate at "maxcount". */ 10749 check_suggestions(su, &su->su_ga); 10750 (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount); 10751 } 10752 } 10753 10754 /* 10755 * Load the .sug files for languages that have one and weren't loaded yet. 10756 */ 10757 static void 10758 suggest_load_files() 10759 { 10760 langp_T *lp; 10761 int lpi; 10762 slang_T *slang; 10763 char_u *dotp; 10764 FILE *fd; 10765 char_u buf[MAXWLEN]; 10766 int i; 10767 time_t timestamp; 10768 int wcount; 10769 int wordnr; 10770 garray_T ga; 10771 int c; 10772 10773 /* Do this for all languages that support sound folding. */ 10774 for (lpi = 0; lpi < curbuf->b_langp.ga_len; ++lpi) 10775 { 10776 lp = LANGP_ENTRY(curbuf->b_langp, lpi); 10777 slang = lp->lp_slang; 10778 if (slang->sl_sugtime != 0 && !slang->sl_sugloaded) 10779 { 10780 /* Change ".spl" to ".sug" and open the file. When the file isn't 10781 * found silently skip it. Do set "sl_sugloaded" so that we 10782 * don't try again and again. */ 10783 slang->sl_sugloaded = TRUE; 10784 10785 dotp = vim_strrchr(slang->sl_fname, '.'); 10786 if (dotp == NULL || fnamecmp(dotp, ".spl") != 0) 10787 continue; 10788 STRCPY(dotp, ".sug"); 10789 fd = mch_fopen((char *)slang->sl_fname, "r"); 10790 if (fd == NULL) 10791 goto nextone; 10792 10793 /* 10794 * <SUGHEADER>: <fileID> <versionnr> <timestamp> 10795 */ 10796 for (i = 0; i < VIMSUGMAGICL; ++i) 10797 buf[i] = getc(fd); /* <fileID> */ 10798 if (STRNCMP(buf, VIMSUGMAGIC, VIMSUGMAGICL) != 0) 10799 { 10800 EMSG2(_("E778: This does not look like a .sug file: %s"), 10801 slang->sl_fname); 10802 goto nextone; 10803 } 10804 c = getc(fd); /* <versionnr> */ 10805 if (c < VIMSUGVERSION) 10806 { 10807 EMSG2(_("E779: Old .sug file, needs to be updated: %s"), 10808 slang->sl_fname); 10809 goto nextone; 10810 } 10811 else if (c > VIMSUGVERSION) 10812 { 10813 EMSG2(_("E780: .sug file is for newer version of Vim: %s"), 10814 slang->sl_fname); 10815 goto nextone; 10816 } 10817 10818 /* Check the timestamp, it must be exactly the same as the one in 10819 * the .spl file. Otherwise the word numbers won't match. */ 10820 timestamp = get8c(fd); /* <timestamp> */ 10821 if (timestamp != slang->sl_sugtime) 10822 { 10823 EMSG2(_("E781: .sug file doesn't match .spl file: %s"), 10824 slang->sl_fname); 10825 goto nextone; 10826 } 10827 10828 /* 10829 * <SUGWORDTREE>: <wordtree> 10830 * Read the trie with the soundfolded words. 10831 */ 10832 if (spell_read_tree(fd, &slang->sl_sbyts, &slang->sl_sidxs, 10833 FALSE, 0) != 0) 10834 { 10835 someerror: 10836 EMSG2(_("E782: error while reading .sug file: %s"), 10837 slang->sl_fname); 10838 slang_clear_sug(slang); 10839 goto nextone; 10840 } 10841 10842 /* 10843 * <SUGTABLE>: <sugwcount> <sugline> ... 10844 * 10845 * Read the table with word numbers. We use a file buffer for 10846 * this, because it's so much like a file with lines. Makes it 10847 * possible to swap the info and save on memory use. 10848 */ 10849 slang->sl_sugbuf = open_spellbuf(); 10850 if (slang->sl_sugbuf == NULL) 10851 goto someerror; 10852 /* <sugwcount> */ 10853 wcount = get4c(fd); 10854 if (wcount < 0) 10855 goto someerror; 10856 10857 /* Read all the wordnr lists into the buffer, one NUL terminated 10858 * list per line. */ 10859 ga_init2(&ga, 1, 100); 10860 for (wordnr = 0; wordnr < wcount; ++wordnr) 10861 { 10862 ga.ga_len = 0; 10863 for (;;) 10864 { 10865 c = getc(fd); /* <sugline> */ 10866 if (c < 0 || ga_grow(&ga, 1) == FAIL) 10867 goto someerror; 10868 ((char_u *)ga.ga_data)[ga.ga_len++] = c; 10869 if (c == NUL) 10870 break; 10871 } 10872 if (ml_append_buf(slang->sl_sugbuf, (linenr_T)wordnr, 10873 ga.ga_data, ga.ga_len, TRUE) == FAIL) 10874 goto someerror; 10875 } 10876 ga_clear(&ga); 10877 10878 /* 10879 * Need to put word counts in the word tries, so that we can find 10880 * a word by its number. 10881 */ 10882 tree_count_words(slang->sl_fbyts, slang->sl_fidxs); 10883 tree_count_words(slang->sl_sbyts, slang->sl_sidxs); 10884 10885 nextone: 10886 if (fd != NULL) 10887 fclose(fd); 10888 STRCPY(dotp, ".spl"); 10889 } 10890 } 10891 } 10892 10893 10894 /* 10895 * Fill in the wordcount fields for a trie. 10896 * Returns the total number of words. 10897 */ 10898 static void 10899 tree_count_words(byts, idxs) 10900 char_u *byts; 10901 idx_T *idxs; 10902 { 10903 int depth; 10904 idx_T arridx[MAXWLEN]; 10905 int curi[MAXWLEN]; 10906 int c; 10907 idx_T n; 10908 int wordcount[MAXWLEN]; 10909 10910 arridx[0] = 0; 10911 curi[0] = 1; 10912 wordcount[0] = 0; 10913 depth = 0; 10914 while (depth >= 0 && !got_int) 10915 { 10916 if (curi[depth] > byts[arridx[depth]]) 10917 { 10918 /* Done all bytes at this node, go up one level. */ 10919 idxs[arridx[depth]] = wordcount[depth]; 10920 if (depth > 0) 10921 wordcount[depth - 1] += wordcount[depth]; 10922 10923 --depth; 10924 fast_breakcheck(); 10925 } 10926 else 10927 { 10928 /* Do one more byte at this node. */ 10929 n = arridx[depth] + curi[depth]; 10930 ++curi[depth]; 10931 10932 c = byts[n]; 10933 if (c == 0) 10934 { 10935 /* End of word, count it. */ 10936 ++wordcount[depth]; 10937 10938 /* Skip over any other NUL bytes (same word with different 10939 * flags). */ 10940 while (byts[n + 1] == 0) 10941 { 10942 ++n; 10943 ++curi[depth]; 10944 } 10945 } 10946 else 10947 { 10948 /* Normal char, go one level deeper to count the words. */ 10949 ++depth; 10950 arridx[depth] = idxs[n]; 10951 curi[depth] = 1; 10952 wordcount[depth] = 0; 10953 } 10954 } 10955 } 10956 } 10957 10958 /* 10959 * Free the info put in "*su" by spell_find_suggest(). 10960 */ 10961 static void 10962 spell_find_cleanup(su) 10963 suginfo_T *su; 10964 { 10965 int i; 10966 10967 /* Free the suggestions. */ 10968 for (i = 0; i < su->su_ga.ga_len; ++i) 10969 vim_free(SUG(su->su_ga, i).st_word); 10970 ga_clear(&su->su_ga); 10971 for (i = 0; i < su->su_sga.ga_len; ++i) 10972 vim_free(SUG(su->su_sga, i).st_word); 10973 ga_clear(&su->su_sga); 10974 10975 /* Free the banned words. */ 10976 hash_clear_all(&su->su_banned, 0); 10977 } 10978 10979 /* 10980 * Make a copy of "word", with the first letter upper or lower cased, to 10981 * "wcopy[MAXWLEN]". "word" must not be empty. 10982 * The result is NUL terminated. 10983 */ 10984 static void 10985 onecap_copy(word, wcopy, upper) 10986 char_u *word; 10987 char_u *wcopy; 10988 int upper; /* TRUE: first letter made upper case */ 10989 { 10990 char_u *p; 10991 int c; 10992 int l; 10993 10994 p = word; 10995 #ifdef FEAT_MBYTE 10996 if (has_mbyte) 10997 c = mb_cptr2char_adv(&p); 10998 else 10999 #endif 11000 c = *p++; 11001 if (upper) 11002 c = SPELL_TOUPPER(c); 11003 else 11004 c = SPELL_TOFOLD(c); 11005 #ifdef FEAT_MBYTE 11006 if (has_mbyte) 11007 l = mb_char2bytes(c, wcopy); 11008 else 11009 #endif 11010 { 11011 l = 1; 11012 wcopy[0] = c; 11013 } 11014 vim_strncpy(wcopy + l, p, MAXWLEN - l - 1); 11015 } 11016 11017 /* 11018 * Make a copy of "word" with all the letters upper cased into 11019 * "wcopy[MAXWLEN]". The result is NUL terminated. 11020 */ 11021 static void 11022 allcap_copy(word, wcopy) 11023 char_u *word; 11024 char_u *wcopy; 11025 { 11026 char_u *s; 11027 char_u *d; 11028 int c; 11029 11030 d = wcopy; 11031 for (s = word; *s != NUL; ) 11032 { 11033 #ifdef FEAT_MBYTE 11034 if (has_mbyte) 11035 c = mb_cptr2char_adv(&s); 11036 else 11037 #endif 11038 c = *s++; 11039 11040 #ifdef FEAT_MBYTE 11041 /* We only change � to SS when we are certain latin1 is used. It 11042 * would cause weird errors in other 8-bit encodings. */ 11043 if (enc_latin1like && c == 0xdf) 11044 { 11045 c = 'S'; 11046 if (d - wcopy >= MAXWLEN - 1) 11047 break; 11048 *d++ = c; 11049 } 11050 else 11051 #endif 11052 c = SPELL_TOUPPER(c); 11053 11054 #ifdef FEAT_MBYTE 11055 if (has_mbyte) 11056 { 11057 if (d - wcopy >= MAXWLEN - MB_MAXBYTES) 11058 break; 11059 d += mb_char2bytes(c, d); 11060 } 11061 else 11062 #endif 11063 { 11064 if (d - wcopy >= MAXWLEN - 1) 11065 break; 11066 *d++ = c; 11067 } 11068 } 11069 *d = NUL; 11070 } 11071 11072 /* 11073 * Try finding suggestions by recognizing specific situations. 11074 */ 11075 static void 11076 suggest_try_special(su) 11077 suginfo_T *su; 11078 { 11079 char_u *p; 11080 size_t len; 11081 int c; 11082 char_u word[MAXWLEN]; 11083 11084 /* 11085 * Recognize a word that is repeated: "the the". 11086 */ 11087 p = skiptowhite(su->su_fbadword); 11088 len = p - su->su_fbadword; 11089 p = skipwhite(p); 11090 if (STRLEN(p) == len && STRNCMP(su->su_fbadword, p, len) == 0) 11091 { 11092 /* Include badflags: if the badword is onecap or allcap 11093 * use that for the goodword too: "The the" -> "The". */ 11094 c = su->su_fbadword[len]; 11095 su->su_fbadword[len] = NUL; 11096 make_case_word(su->su_fbadword, word, su->su_badflags); 11097 su->su_fbadword[len] = c; 11098 11099 /* Give a soundalike score of 0, compute the score as if deleting one 11100 * character. */ 11101 add_suggestion(su, &su->su_ga, word, su->su_badlen, 11102 RESCORE(SCORE_REP, 0), 0, TRUE, su->su_sallang, FALSE); 11103 } 11104 } 11105 11106 /* 11107 * Try finding suggestions by adding/removing/swapping letters. 11108 */ 11109 static void 11110 suggest_try_change(su) 11111 suginfo_T *su; 11112 { 11113 char_u fword[MAXWLEN]; /* copy of the bad word, case-folded */ 11114 int n; 11115 char_u *p; 11116 int lpi; 11117 langp_T *lp; 11118 11119 /* We make a copy of the case-folded bad word, so that we can modify it 11120 * to find matches (esp. REP items). Append some more text, changing 11121 * chars after the bad word may help. */ 11122 STRCPY(fword, su->su_fbadword); 11123 n = (int)STRLEN(fword); 11124 p = su->su_badptr + su->su_badlen; 11125 (void)spell_casefold(p, (int)STRLEN(p), fword + n, MAXWLEN - n); 11126 11127 for (lpi = 0; lpi < curbuf->b_langp.ga_len; ++lpi) 11128 { 11129 lp = LANGP_ENTRY(curbuf->b_langp, lpi); 11130 11131 /* If reloading a spell file fails it's still in the list but 11132 * everything has been cleared. */ 11133 if (lp->lp_slang->sl_fbyts == NULL) 11134 continue; 11135 11136 /* Try it for this language. Will add possible suggestions. */ 11137 suggest_trie_walk(su, lp, fword, FALSE); 11138 } 11139 } 11140 11141 /* Check the maximum score, if we go over it we won't try this change. */ 11142 #define TRY_DEEPER(su, stack, depth, add) \ 11143 (stack[depth].ts_score + (add) < su->su_maxscore) 11144 11145 /* 11146 * Try finding suggestions by adding/removing/swapping letters. 11147 * 11148 * This uses a state machine. At each node in the tree we try various 11149 * operations. When trying if an operation works "depth" is increased and the 11150 * stack[] is used to store info. This allows combinations, thus insert one 11151 * character, replace one and delete another. The number of changes is 11152 * limited by su->su_maxscore. 11153 * 11154 * After implementing this I noticed an article by Kemal Oflazer that 11155 * describes something similar: "Error-tolerant Finite State Recognition with 11156 * Applications to Morphological Analysis and Spelling Correction" (1996). 11157 * The implementation in the article is simplified and requires a stack of 11158 * unknown depth. The implementation here only needs a stack depth equal to 11159 * the length of the word. 11160 * 11161 * This is also used for the sound-folded word, "soundfold" is TRUE then. 11162 * The mechanism is the same, but we find a match with a sound-folded word 11163 * that comes from one or more original words. Each of these words may be 11164 * added, this is done by add_sound_suggest(). 11165 * Don't use: 11166 * the prefix tree or the keep-case tree 11167 * "su->su_badlen" 11168 * anything to do with upper and lower case 11169 * anything to do with word or non-word characters ("spell_iswordp()") 11170 * banned words 11171 * word flags (rare, region, compounding) 11172 * word splitting for now 11173 * "similar_chars()" 11174 * use "slang->sl_repsal" instead of "lp->lp_replang->sl_rep" 11175 */ 11176 static void 11177 suggest_trie_walk(su, lp, fword, soundfold) 11178 suginfo_T *su; 11179 langp_T *lp; 11180 char_u *fword; 11181 int soundfold; 11182 { 11183 char_u tword[MAXWLEN]; /* good word collected so far */ 11184 trystate_T stack[MAXWLEN]; 11185 char_u preword[MAXWLEN * 3]; /* word found with proper case; 11186 * concatanation of prefix compound 11187 * words and split word. NUL terminated 11188 * when going deeper but not when coming 11189 * back. */ 11190 char_u compflags[MAXWLEN]; /* compound flags, one for each word */ 11191 trystate_T *sp; 11192 int newscore; 11193 int score; 11194 char_u *byts, *fbyts, *pbyts; 11195 idx_T *idxs, *fidxs, *pidxs; 11196 int depth; 11197 int c, c2, c3; 11198 int n = 0; 11199 int flags; 11200 garray_T *gap; 11201 idx_T arridx; 11202 int len; 11203 char_u *p; 11204 fromto_T *ftp; 11205 int fl = 0, tl; 11206 int repextra = 0; /* extra bytes in fword[] from REP item */ 11207 slang_T *slang = lp->lp_slang; 11208 int fword_ends; 11209 int goodword_ends; 11210 #ifdef DEBUG_TRIEWALK 11211 /* Stores the name of the change made at each level. */ 11212 char_u changename[MAXWLEN][80]; 11213 #endif 11214 int breakcheckcount = 1000; 11215 int compound_ok; 11216 11217 /* 11218 * Go through the whole case-fold tree, try changes at each node. 11219 * "tword[]" contains the word collected from nodes in the tree. 11220 * "fword[]" the word we are trying to match with (initially the bad 11221 * word). 11222 */ 11223 depth = 0; 11224 sp = &stack[0]; 11225 vim_memset(sp, 0, sizeof(trystate_T)); 11226 sp->ts_curi = 1; 11227 11228 if (soundfold) 11229 { 11230 /* Going through the soundfold tree. */ 11231 byts = fbyts = slang->sl_sbyts; 11232 idxs = fidxs = slang->sl_sidxs; 11233 pbyts = NULL; 11234 pidxs = NULL; 11235 sp->ts_prefixdepth = PFD_NOPREFIX; 11236 sp->ts_state = STATE_START; 11237 } 11238 else 11239 { 11240 /* 11241 * When there are postponed prefixes we need to use these first. At 11242 * the end of the prefix we continue in the case-fold tree. 11243 */ 11244 fbyts = slang->sl_fbyts; 11245 fidxs = slang->sl_fidxs; 11246 pbyts = slang->sl_pbyts; 11247 pidxs = slang->sl_pidxs; 11248 if (pbyts != NULL) 11249 { 11250 byts = pbyts; 11251 idxs = pidxs; 11252 sp->ts_prefixdepth = PFD_PREFIXTREE; 11253 sp->ts_state = STATE_NOPREFIX; /* try without prefix first */ 11254 } 11255 else 11256 { 11257 byts = fbyts; 11258 idxs = fidxs; 11259 sp->ts_prefixdepth = PFD_NOPREFIX; 11260 sp->ts_state = STATE_START; 11261 } 11262 } 11263 11264 /* 11265 * Loop to find all suggestions. At each round we either: 11266 * - For the current state try one operation, advance "ts_curi", 11267 * increase "depth". 11268 * - When a state is done go to the next, set "ts_state". 11269 * - When all states are tried decrease "depth". 11270 */ 11271 while (depth >= 0 && !got_int) 11272 { 11273 sp = &stack[depth]; 11274 switch (sp->ts_state) 11275 { 11276 case STATE_START: 11277 case STATE_NOPREFIX: 11278 /* 11279 * Start of node: Deal with NUL bytes, which means 11280 * tword[] may end here. 11281 */ 11282 arridx = sp->ts_arridx; /* current node in the tree */ 11283 len = byts[arridx]; /* bytes in this node */ 11284 arridx += sp->ts_curi; /* index of current byte */ 11285 11286 if (sp->ts_prefixdepth == PFD_PREFIXTREE) 11287 { 11288 /* Skip over the NUL bytes, we use them later. */ 11289 for (n = 0; n < len && byts[arridx + n] == 0; ++n) 11290 ; 11291 sp->ts_curi += n; 11292 11293 /* Always past NUL bytes now. */ 11294 n = (int)sp->ts_state; 11295 sp->ts_state = STATE_ENDNUL; 11296 sp->ts_save_badflags = su->su_badflags; 11297 11298 /* At end of a prefix or at start of prefixtree: check for 11299 * following word. */ 11300 if (byts[arridx] == 0 || n == (int)STATE_NOPREFIX) 11301 { 11302 /* Set su->su_badflags to the caps type at this position. 11303 * Use the caps type until here for the prefix itself. */ 11304 #ifdef FEAT_MBYTE 11305 if (has_mbyte) 11306 n = nofold_len(fword, sp->ts_fidx, su->su_badptr); 11307 else 11308 #endif 11309 n = sp->ts_fidx; 11310 flags = badword_captype(su->su_badptr, su->su_badptr + n); 11311 su->su_badflags = badword_captype(su->su_badptr + n, 11312 su->su_badptr + su->su_badlen); 11313 #ifdef DEBUG_TRIEWALK 11314 sprintf(changename[depth], "prefix"); 11315 #endif 11316 go_deeper(stack, depth, 0); 11317 ++depth; 11318 sp = &stack[depth]; 11319 sp->ts_prefixdepth = depth - 1; 11320 byts = fbyts; 11321 idxs = fidxs; 11322 sp->ts_arridx = 0; 11323 11324 /* Move the prefix to preword[] with the right case 11325 * and make find_keepcap_word() works. */ 11326 tword[sp->ts_twordlen] = NUL; 11327 make_case_word(tword + sp->ts_splitoff, 11328 preword + sp->ts_prewordlen, flags); 11329 sp->ts_prewordlen = (char_u)STRLEN(preword); 11330 sp->ts_splitoff = sp->ts_twordlen; 11331 } 11332 break; 11333 } 11334 11335 if (sp->ts_curi > len || byts[arridx] != 0) 11336 { 11337 /* Past bytes in node and/or past NUL bytes. */ 11338 sp->ts_state = STATE_ENDNUL; 11339 sp->ts_save_badflags = su->su_badflags; 11340 break; 11341 } 11342 11343 /* 11344 * End of word in tree. 11345 */ 11346 ++sp->ts_curi; /* eat one NUL byte */ 11347 11348 flags = (int)idxs[arridx]; 11349 11350 /* Skip words with the NOSUGGEST flag. */ 11351 if (flags & WF_NOSUGGEST) 11352 break; 11353 11354 fword_ends = (fword[sp->ts_fidx] == NUL 11355 || (soundfold 11356 ? vim_iswhite(fword[sp->ts_fidx]) 11357 : !spell_iswordp(fword + sp->ts_fidx, curbuf))); 11358 tword[sp->ts_twordlen] = NUL; 11359 11360 if (sp->ts_prefixdepth <= PFD_NOTSPECIAL 11361 && (sp->ts_flags & TSF_PREFIXOK) == 0) 11362 { 11363 /* There was a prefix before the word. Check that the prefix 11364 * can be used with this word. */ 11365 /* Count the length of the NULs in the prefix. If there are 11366 * none this must be the first try without a prefix. */ 11367 n = stack[sp->ts_prefixdepth].ts_arridx; 11368 len = pbyts[n++]; 11369 for (c = 0; c < len && pbyts[n + c] == 0; ++c) 11370 ; 11371 if (c > 0) 11372 { 11373 c = valid_word_prefix(c, n, flags, 11374 tword + sp->ts_splitoff, slang, FALSE); 11375 if (c == 0) 11376 break; 11377 11378 /* Use the WF_RARE flag for a rare prefix. */ 11379 if (c & WF_RAREPFX) 11380 flags |= WF_RARE; 11381 11382 /* Tricky: when checking for both prefix and compounding 11383 * we run into the prefix flag first. 11384 * Remember that it's OK, so that we accept the prefix 11385 * when arriving at a compound flag. */ 11386 sp->ts_flags |= TSF_PREFIXOK; 11387 } 11388 } 11389 11390 /* Check NEEDCOMPOUND: can't use word without compounding. Do try 11391 * appending another compound word below. */ 11392 if (sp->ts_complen == sp->ts_compsplit && fword_ends 11393 && (flags & WF_NEEDCOMP)) 11394 goodword_ends = FALSE; 11395 else 11396 goodword_ends = TRUE; 11397 11398 p = NULL; 11399 compound_ok = TRUE; 11400 if (sp->ts_complen > sp->ts_compsplit) 11401 { 11402 if (slang->sl_nobreak) 11403 { 11404 /* There was a word before this word. When there was no 11405 * change in this word (it was correct) add the first word 11406 * as a suggestion. If this word was corrected too, we 11407 * need to check if a correct word follows. */ 11408 if (sp->ts_fidx - sp->ts_splitfidx 11409 == sp->ts_twordlen - sp->ts_splitoff 11410 && STRNCMP(fword + sp->ts_splitfidx, 11411 tword + sp->ts_splitoff, 11412 sp->ts_fidx - sp->ts_splitfidx) == 0) 11413 { 11414 preword[sp->ts_prewordlen] = NUL; 11415 newscore = score_wordcount_adj(slang, sp->ts_score, 11416 preword + sp->ts_prewordlen, 11417 sp->ts_prewordlen > 0); 11418 /* Add the suggestion if the score isn't too bad. */ 11419 if (newscore <= su->su_maxscore) 11420 add_suggestion(su, &su->su_ga, preword, 11421 sp->ts_splitfidx - repextra, 11422 newscore, 0, FALSE, 11423 lp->lp_sallang, FALSE); 11424 break; 11425 } 11426 } 11427 else 11428 { 11429 /* There was a compound word before this word. If this 11430 * word does not support compounding then give up 11431 * (splitting is tried for the word without compound 11432 * flag). */ 11433 if (((unsigned)flags >> 24) == 0 11434 || sp->ts_twordlen - sp->ts_splitoff 11435 < slang->sl_compminlen) 11436 break; 11437 #ifdef FEAT_MBYTE 11438 /* For multi-byte chars check character length against 11439 * COMPOUNDMIN. */ 11440 if (has_mbyte 11441 && slang->sl_compminlen > 0 11442 && mb_charlen(tword + sp->ts_splitoff) 11443 < slang->sl_compminlen) 11444 break; 11445 #endif 11446 11447 compflags[sp->ts_complen] = ((unsigned)flags >> 24); 11448 compflags[sp->ts_complen + 1] = NUL; 11449 vim_strncpy(preword + sp->ts_prewordlen, 11450 tword + sp->ts_splitoff, 11451 sp->ts_twordlen - sp->ts_splitoff); 11452 p = preword; 11453 while (*skiptowhite(p) != NUL) 11454 p = skipwhite(skiptowhite(p)); 11455 if (fword_ends && !can_compound(slang, p, 11456 compflags + sp->ts_compsplit)) 11457 /* Compound is not allowed. But it may still be 11458 * possible if we add another (short) word. */ 11459 compound_ok = FALSE; 11460 11461 /* Get pointer to last char of previous word. */ 11462 p = preword + sp->ts_prewordlen; 11463 mb_ptr_back(preword, p); 11464 } 11465 } 11466 11467 /* 11468 * Form the word with proper case in preword. 11469 * If there is a word from a previous split, append. 11470 * For the soundfold tree don't change the case, simply append. 11471 */ 11472 if (soundfold) 11473 STRCPY(preword + sp->ts_prewordlen, tword + sp->ts_splitoff); 11474 else if (flags & WF_KEEPCAP) 11475 /* Must find the word in the keep-case tree. */ 11476 find_keepcap_word(slang, tword + sp->ts_splitoff, 11477 preword + sp->ts_prewordlen); 11478 else 11479 { 11480 /* Include badflags: If the badword is onecap or allcap 11481 * use that for the goodword too. But if the badword is 11482 * allcap and it's only one char long use onecap. */ 11483 c = su->su_badflags; 11484 if ((c & WF_ALLCAP) 11485 #ifdef FEAT_MBYTE 11486 && su->su_badlen == (*mb_ptr2len)(su->su_badptr) 11487 #else 11488 && su->su_badlen == 1 11489 #endif 11490 ) 11491 c = WF_ONECAP; 11492 c |= flags; 11493 11494 /* When appending a compound word after a word character don't 11495 * use Onecap. */ 11496 if (p != NULL && spell_iswordp_nmw(p)) 11497 c &= ~WF_ONECAP; 11498 make_case_word(tword + sp->ts_splitoff, 11499 preword + sp->ts_prewordlen, c); 11500 } 11501 11502 if (!soundfold) 11503 { 11504 /* Don't use a banned word. It may appear again as a good 11505 * word, thus remember it. */ 11506 if (flags & WF_BANNED) 11507 { 11508 add_banned(su, preword + sp->ts_prewordlen); 11509 break; 11510 } 11511 if ((sp->ts_complen == sp->ts_compsplit 11512 && WAS_BANNED(su, preword + sp->ts_prewordlen)) 11513 || WAS_BANNED(su, preword)) 11514 { 11515 if (slang->sl_compprog == NULL) 11516 break; 11517 /* the word so far was banned but we may try compounding */ 11518 goodword_ends = FALSE; 11519 } 11520 } 11521 11522 newscore = 0; 11523 if (!soundfold) /* soundfold words don't have flags */ 11524 { 11525 if ((flags & WF_REGION) 11526 && (((unsigned)flags >> 16) & lp->lp_region) == 0) 11527 newscore += SCORE_REGION; 11528 if (flags & WF_RARE) 11529 newscore += SCORE_RARE; 11530 11531 if (!spell_valid_case(su->su_badflags, 11532 captype(preword + sp->ts_prewordlen, NULL))) 11533 newscore += SCORE_ICASE; 11534 } 11535 11536 /* TODO: how about splitting in the soundfold tree? */ 11537 if (fword_ends 11538 && goodword_ends 11539 && sp->ts_fidx >= sp->ts_fidxtry 11540 && compound_ok) 11541 { 11542 /* The badword also ends: add suggestions. */ 11543 #ifdef DEBUG_TRIEWALK 11544 if (soundfold && STRCMP(preword, "smwrd") == 0) 11545 { 11546 int j; 11547 11548 /* print the stack of changes that brought us here */ 11549 smsg("------ %s -------", fword); 11550 for (j = 0; j < depth; ++j) 11551 smsg("%s", changename[j]); 11552 } 11553 #endif 11554 if (soundfold) 11555 { 11556 /* For soundfolded words we need to find the original 11557 * words, the edit distance and then add them. */ 11558 add_sound_suggest(su, preword, sp->ts_score, lp); 11559 } 11560 else 11561 { 11562 /* Give a penalty when changing non-word char to word 11563 * char, e.g., "thes," -> "these". */ 11564 p = fword + sp->ts_fidx; 11565 mb_ptr_back(fword, p); 11566 if (!spell_iswordp(p, curbuf)) 11567 { 11568 p = preword + STRLEN(preword); 11569 mb_ptr_back(preword, p); 11570 if (spell_iswordp(p, curbuf)) 11571 newscore += SCORE_NONWORD; 11572 } 11573 11574 /* Give a bonus to words seen before. */ 11575 score = score_wordcount_adj(slang, 11576 sp->ts_score + newscore, 11577 preword + sp->ts_prewordlen, 11578 sp->ts_prewordlen > 0); 11579 11580 /* Add the suggestion if the score isn't too bad. */ 11581 if (score <= su->su_maxscore) 11582 { 11583 add_suggestion(su, &su->su_ga, preword, 11584 sp->ts_fidx - repextra, 11585 score, 0, FALSE, lp->lp_sallang, FALSE); 11586 11587 if (su->su_badflags & WF_MIXCAP) 11588 { 11589 /* We really don't know if the word should be 11590 * upper or lower case, add both. */ 11591 c = captype(preword, NULL); 11592 if (c == 0 || c == WF_ALLCAP) 11593 { 11594 make_case_word(tword + sp->ts_splitoff, 11595 preword + sp->ts_prewordlen, 11596 c == 0 ? WF_ALLCAP : 0); 11597 11598 add_suggestion(su, &su->su_ga, preword, 11599 sp->ts_fidx - repextra, 11600 score + SCORE_ICASE, 0, FALSE, 11601 lp->lp_sallang, FALSE); 11602 } 11603 } 11604 } 11605 } 11606 } 11607 11608 /* 11609 * Try word split and/or compounding. 11610 */ 11611 if ((sp->ts_fidx >= sp->ts_fidxtry || fword_ends) 11612 #ifdef FEAT_MBYTE 11613 /* Don't split halfway a character. */ 11614 && (!has_mbyte || sp->ts_tcharlen == 0) 11615 #endif 11616 ) 11617 { 11618 int try_compound; 11619 int try_split; 11620 11621 /* If past the end of the bad word don't try a split. 11622 * Otherwise try changing the next word. E.g., find 11623 * suggestions for "the the" where the second "the" is 11624 * different. It's done like a split. 11625 * TODO: word split for soundfold words */ 11626 try_split = (sp->ts_fidx - repextra < su->su_badlen) 11627 && !soundfold; 11628 11629 /* Get here in several situations: 11630 * 1. The word in the tree ends: 11631 * If the word allows compounding try that. Otherwise try 11632 * a split by inserting a space. For both check that a 11633 * valid words starts at fword[sp->ts_fidx]. 11634 * For NOBREAK do like compounding to be able to check if 11635 * the next word is valid. 11636 * 2. The badword does end, but it was due to a change (e.g., 11637 * a swap). No need to split, but do check that the 11638 * following word is valid. 11639 * 3. The badword and the word in the tree end. It may still 11640 * be possible to compound another (short) word. 11641 */ 11642 try_compound = FALSE; 11643 if (!soundfold 11644 && slang->sl_compprog != NULL 11645 && ((unsigned)flags >> 24) != 0 11646 && sp->ts_twordlen - sp->ts_splitoff 11647 >= slang->sl_compminlen 11648 #ifdef FEAT_MBYTE 11649 && (!has_mbyte 11650 || slang->sl_compminlen == 0 11651 || mb_charlen(tword + sp->ts_splitoff) 11652 >= slang->sl_compminlen) 11653 #endif 11654 && (slang->sl_compsylmax < MAXWLEN 11655 || sp->ts_complen + 1 - sp->ts_compsplit 11656 < slang->sl_compmax) 11657 && (byte_in_str(sp->ts_complen == sp->ts_compsplit 11658 ? slang->sl_compstartflags 11659 : slang->sl_compallflags, 11660 ((unsigned)flags >> 24)))) 11661 { 11662 try_compound = TRUE; 11663 compflags[sp->ts_complen] = ((unsigned)flags >> 24); 11664 compflags[sp->ts_complen + 1] = NUL; 11665 } 11666 11667 /* For NOBREAK we never try splitting, it won't make any word 11668 * valid. */ 11669 if (slang->sl_nobreak) 11670 try_compound = TRUE; 11671 11672 /* If we could add a compound word, and it's also possible to 11673 * split at this point, do the split first and set 11674 * TSF_DIDSPLIT to avoid doing it again. */ 11675 else if (!fword_ends 11676 && try_compound 11677 && (sp->ts_flags & TSF_DIDSPLIT) == 0) 11678 { 11679 try_compound = FALSE; 11680 sp->ts_flags |= TSF_DIDSPLIT; 11681 --sp->ts_curi; /* do the same NUL again */ 11682 compflags[sp->ts_complen] = NUL; 11683 } 11684 else 11685 sp->ts_flags &= ~TSF_DIDSPLIT; 11686 11687 if (try_split || try_compound) 11688 { 11689 if (!try_compound && (!fword_ends || !goodword_ends)) 11690 { 11691 /* If we're going to split need to check that the 11692 * words so far are valid for compounding. If there 11693 * is only one word it must not have the NEEDCOMPOUND 11694 * flag. */ 11695 if (sp->ts_complen == sp->ts_compsplit 11696 && (flags & WF_NEEDCOMP)) 11697 break; 11698 p = preword; 11699 while (*skiptowhite(p) != NUL) 11700 p = skipwhite(skiptowhite(p)); 11701 if (sp->ts_complen > sp->ts_compsplit 11702 && !can_compound(slang, p, 11703 compflags + sp->ts_compsplit)) 11704 break; 11705 11706 if (slang->sl_nosplitsugs) 11707 newscore += SCORE_SPLIT_NO; 11708 else 11709 newscore += SCORE_SPLIT; 11710 11711 /* Give a bonus to words seen before. */ 11712 newscore = score_wordcount_adj(slang, newscore, 11713 preword + sp->ts_prewordlen, TRUE); 11714 } 11715 11716 if (TRY_DEEPER(su, stack, depth, newscore)) 11717 { 11718 go_deeper(stack, depth, newscore); 11719 #ifdef DEBUG_TRIEWALK 11720 if (!try_compound && !fword_ends) 11721 sprintf(changename[depth], "%.*s-%s: split", 11722 sp->ts_twordlen, tword, fword + sp->ts_fidx); 11723 else 11724 sprintf(changename[depth], "%.*s-%s: compound", 11725 sp->ts_twordlen, tword, fword + sp->ts_fidx); 11726 #endif 11727 /* Save things to be restored at STATE_SPLITUNDO. */ 11728 sp->ts_save_badflags = su->su_badflags; 11729 sp->ts_state = STATE_SPLITUNDO; 11730 11731 ++depth; 11732 sp = &stack[depth]; 11733 11734 /* Append a space to preword when splitting. */ 11735 if (!try_compound && !fword_ends) 11736 STRCAT(preword, " "); 11737 sp->ts_prewordlen = (char_u)STRLEN(preword); 11738 sp->ts_splitoff = sp->ts_twordlen; 11739 sp->ts_splitfidx = sp->ts_fidx; 11740 11741 /* If the badword has a non-word character at this 11742 * position skip it. That means replacing the 11743 * non-word character with a space. Always skip a 11744 * character when the word ends. But only when the 11745 * good word can end. */ 11746 if (((!try_compound && !spell_iswordp_nmw(fword 11747 + sp->ts_fidx)) 11748 || fword_ends) 11749 && fword[sp->ts_fidx] != NUL 11750 && goodword_ends) 11751 { 11752 int l; 11753 11754 #ifdef FEAT_MBYTE 11755 if (has_mbyte) 11756 l = MB_BYTE2LEN(fword[sp->ts_fidx]); 11757 else 11758 #endif 11759 l = 1; 11760 if (fword_ends) 11761 { 11762 /* Copy the skipped character to preword. */ 11763 mch_memmove(preword + sp->ts_prewordlen, 11764 fword + sp->ts_fidx, l); 11765 sp->ts_prewordlen += l; 11766 preword[sp->ts_prewordlen] = NUL; 11767 } 11768 else 11769 sp->ts_score -= SCORE_SPLIT - SCORE_SUBST; 11770 sp->ts_fidx += l; 11771 } 11772 11773 /* When compounding include compound flag in 11774 * compflags[] (already set above). When splitting we 11775 * may start compounding over again. */ 11776 if (try_compound) 11777 ++sp->ts_complen; 11778 else 11779 sp->ts_compsplit = sp->ts_complen; 11780 sp->ts_prefixdepth = PFD_NOPREFIX; 11781 11782 /* set su->su_badflags to the caps type at this 11783 * position */ 11784 #ifdef FEAT_MBYTE 11785 if (has_mbyte) 11786 n = nofold_len(fword, sp->ts_fidx, su->su_badptr); 11787 else 11788 #endif 11789 n = sp->ts_fidx; 11790 su->su_badflags = badword_captype(su->su_badptr + n, 11791 su->su_badptr + su->su_badlen); 11792 11793 /* Restart at top of the tree. */ 11794 sp->ts_arridx = 0; 11795 11796 /* If there are postponed prefixes, try these too. */ 11797 if (pbyts != NULL) 11798 { 11799 byts = pbyts; 11800 idxs = pidxs; 11801 sp->ts_prefixdepth = PFD_PREFIXTREE; 11802 sp->ts_state = STATE_NOPREFIX; 11803 } 11804 } 11805 } 11806 } 11807 break; 11808 11809 case STATE_SPLITUNDO: 11810 /* Undo the changes done for word split or compound word. */ 11811 su->su_badflags = sp->ts_save_badflags; 11812 11813 /* Continue looking for NUL bytes. */ 11814 sp->ts_state = STATE_START; 11815 11816 /* In case we went into the prefix tree. */ 11817 byts = fbyts; 11818 idxs = fidxs; 11819 break; 11820 11821 case STATE_ENDNUL: 11822 /* Past the NUL bytes in the node. */ 11823 su->su_badflags = sp->ts_save_badflags; 11824 if (fword[sp->ts_fidx] == NUL 11825 #ifdef FEAT_MBYTE 11826 && sp->ts_tcharlen == 0 11827 #endif 11828 ) 11829 { 11830 /* The badword ends, can't use STATE_PLAIN. */ 11831 sp->ts_state = STATE_DEL; 11832 break; 11833 } 11834 sp->ts_state = STATE_PLAIN; 11835 /*FALLTHROUGH*/ 11836 11837 case STATE_PLAIN: 11838 /* 11839 * Go over all possible bytes at this node, add each to tword[] 11840 * and use child node. "ts_curi" is the index. 11841 */ 11842 arridx = sp->ts_arridx; 11843 if (sp->ts_curi > byts[arridx]) 11844 { 11845 /* Done all bytes at this node, do next state. When still at 11846 * already changed bytes skip the other tricks. */ 11847 if (sp->ts_fidx >= sp->ts_fidxtry) 11848 sp->ts_state = STATE_DEL; 11849 else 11850 sp->ts_state = STATE_FINAL; 11851 } 11852 else 11853 { 11854 arridx += sp->ts_curi++; 11855 c = byts[arridx]; 11856 11857 /* Normal byte, go one level deeper. If it's not equal to the 11858 * byte in the bad word adjust the score. But don't even try 11859 * when the byte was already changed. And don't try when we 11860 * just deleted this byte, accepting it is always cheaper then 11861 * delete + substitute. */ 11862 if (c == fword[sp->ts_fidx] 11863 #ifdef FEAT_MBYTE 11864 || (sp->ts_tcharlen > 0 && sp->ts_isdiff != DIFF_NONE) 11865 #endif 11866 ) 11867 newscore = 0; 11868 else 11869 newscore = SCORE_SUBST; 11870 if ((newscore == 0 11871 || (sp->ts_fidx >= sp->ts_fidxtry 11872 && ((sp->ts_flags & TSF_DIDDEL) == 0 11873 || c != fword[sp->ts_delidx]))) 11874 && TRY_DEEPER(su, stack, depth, newscore)) 11875 { 11876 go_deeper(stack, depth, newscore); 11877 #ifdef DEBUG_TRIEWALK 11878 if (newscore > 0) 11879 sprintf(changename[depth], "%.*s-%s: subst %c to %c", 11880 sp->ts_twordlen, tword, fword + sp->ts_fidx, 11881 fword[sp->ts_fidx], c); 11882 else 11883 sprintf(changename[depth], "%.*s-%s: accept %c", 11884 sp->ts_twordlen, tword, fword + sp->ts_fidx, 11885 fword[sp->ts_fidx]); 11886 #endif 11887 ++depth; 11888 sp = &stack[depth]; 11889 ++sp->ts_fidx; 11890 tword[sp->ts_twordlen++] = c; 11891 sp->ts_arridx = idxs[arridx]; 11892 #ifdef FEAT_MBYTE 11893 if (newscore == SCORE_SUBST) 11894 sp->ts_isdiff = DIFF_YES; 11895 if (has_mbyte) 11896 { 11897 /* Multi-byte characters are a bit complicated to 11898 * handle: They differ when any of the bytes differ 11899 * and then their length may also differ. */ 11900 if (sp->ts_tcharlen == 0) 11901 { 11902 /* First byte. */ 11903 sp->ts_tcharidx = 0; 11904 sp->ts_tcharlen = MB_BYTE2LEN(c); 11905 sp->ts_fcharstart = sp->ts_fidx - 1; 11906 sp->ts_isdiff = (newscore != 0) 11907 ? DIFF_YES : DIFF_NONE; 11908 } 11909 else if (sp->ts_isdiff == DIFF_INSERT) 11910 /* When inserting trail bytes don't advance in the 11911 * bad word. */ 11912 --sp->ts_fidx; 11913 if (++sp->ts_tcharidx == sp->ts_tcharlen) 11914 { 11915 /* Last byte of character. */ 11916 if (sp->ts_isdiff == DIFF_YES) 11917 { 11918 /* Correct ts_fidx for the byte length of the 11919 * character (we didn't check that before). */ 11920 sp->ts_fidx = sp->ts_fcharstart 11921 + MB_BYTE2LEN( 11922 fword[sp->ts_fcharstart]); 11923 11924 /* For changing a composing character adjust 11925 * the score from SCORE_SUBST to 11926 * SCORE_SUBCOMP. */ 11927 if (enc_utf8 11928 && utf_iscomposing( 11929 mb_ptr2char(tword 11930 + sp->ts_twordlen 11931 - sp->ts_tcharlen)) 11932 && utf_iscomposing( 11933 mb_ptr2char(fword 11934 + sp->ts_fcharstart))) 11935 sp->ts_score -= 11936 SCORE_SUBST - SCORE_SUBCOMP; 11937 11938 /* For a similar character adjust score from 11939 * SCORE_SUBST to SCORE_SIMILAR. */ 11940 else if (!soundfold 11941 && slang->sl_has_map 11942 && similar_chars(slang, 11943 mb_ptr2char(tword 11944 + sp->ts_twordlen 11945 - sp->ts_tcharlen), 11946 mb_ptr2char(fword 11947 + sp->ts_fcharstart))) 11948 sp->ts_score -= 11949 SCORE_SUBST - SCORE_SIMILAR; 11950 } 11951 else if (sp->ts_isdiff == DIFF_INSERT 11952 && sp->ts_twordlen > sp->ts_tcharlen) 11953 { 11954 p = tword + sp->ts_twordlen - sp->ts_tcharlen; 11955 c = mb_ptr2char(p); 11956 if (enc_utf8 && utf_iscomposing(c)) 11957 { 11958 /* Inserting a composing char doesn't 11959 * count that much. */ 11960 sp->ts_score -= SCORE_INS - SCORE_INSCOMP; 11961 } 11962 else 11963 { 11964 /* If the previous character was the same, 11965 * thus doubling a character, give a bonus 11966 * to the score. Also for the soundfold 11967 * tree (might seem illogical but does 11968 * give better scores). */ 11969 mb_ptr_back(tword, p); 11970 if (c == mb_ptr2char(p)) 11971 sp->ts_score -= SCORE_INS 11972 - SCORE_INSDUP; 11973 } 11974 } 11975 11976 /* Starting a new char, reset the length. */ 11977 sp->ts_tcharlen = 0; 11978 } 11979 } 11980 else 11981 #endif 11982 { 11983 /* If we found a similar char adjust the score. 11984 * We do this after calling go_deeper() because 11985 * it's slow. */ 11986 if (newscore != 0 11987 && !soundfold 11988 && slang->sl_has_map 11989 && similar_chars(slang, 11990 c, fword[sp->ts_fidx - 1])) 11991 sp->ts_score -= SCORE_SUBST - SCORE_SIMILAR; 11992 } 11993 } 11994 } 11995 break; 11996 11997 case STATE_DEL: 11998 #ifdef FEAT_MBYTE 11999 /* When past the first byte of a multi-byte char don't try 12000 * delete/insert/swap a character. */ 12001 if (has_mbyte && sp->ts_tcharlen > 0) 12002 { 12003 sp->ts_state = STATE_FINAL; 12004 break; 12005 } 12006 #endif 12007 /* 12008 * Try skipping one character in the bad word (delete it). 12009 */ 12010 sp->ts_state = STATE_INS_PREP; 12011 sp->ts_curi = 1; 12012 if (soundfold && sp->ts_fidx == 0 && fword[sp->ts_fidx] == '*') 12013 /* Deleting a vowel at the start of a word counts less, see 12014 * soundalike_score(). */ 12015 newscore = 2 * SCORE_DEL / 3; 12016 else 12017 newscore = SCORE_DEL; 12018 if (fword[sp->ts_fidx] != NUL 12019 && TRY_DEEPER(su, stack, depth, newscore)) 12020 { 12021 go_deeper(stack, depth, newscore); 12022 #ifdef DEBUG_TRIEWALK 12023 sprintf(changename[depth], "%.*s-%s: delete %c", 12024 sp->ts_twordlen, tword, fword + sp->ts_fidx, 12025 fword[sp->ts_fidx]); 12026 #endif 12027 ++depth; 12028 12029 /* Remember what character we deleted, so that we can avoid 12030 * inserting it again. */ 12031 stack[depth].ts_flags |= TSF_DIDDEL; 12032 stack[depth].ts_delidx = sp->ts_fidx; 12033 12034 /* Advance over the character in fword[]. Give a bonus to the 12035 * score if the same character is following "nn" -> "n". It's 12036 * a bit illogical for soundfold tree but it does give better 12037 * results. */ 12038 #ifdef FEAT_MBYTE 12039 if (has_mbyte) 12040 { 12041 c = mb_ptr2char(fword + sp->ts_fidx); 12042 stack[depth].ts_fidx += MB_BYTE2LEN(fword[sp->ts_fidx]); 12043 if (enc_utf8 && utf_iscomposing(c)) 12044 stack[depth].ts_score -= SCORE_DEL - SCORE_DELCOMP; 12045 else if (c == mb_ptr2char(fword + stack[depth].ts_fidx)) 12046 stack[depth].ts_score -= SCORE_DEL - SCORE_DELDUP; 12047 } 12048 else 12049 #endif 12050 { 12051 ++stack[depth].ts_fidx; 12052 if (fword[sp->ts_fidx] == fword[sp->ts_fidx + 1]) 12053 stack[depth].ts_score -= SCORE_DEL - SCORE_DELDUP; 12054 } 12055 break; 12056 } 12057 /*FALLTHROUGH*/ 12058 12059 case STATE_INS_PREP: 12060 if (sp->ts_flags & TSF_DIDDEL) 12061 { 12062 /* If we just deleted a byte then inserting won't make sense, 12063 * a substitute is always cheaper. */ 12064 sp->ts_state = STATE_SWAP; 12065 break; 12066 } 12067 12068 /* skip over NUL bytes */ 12069 n = sp->ts_arridx; 12070 for (;;) 12071 { 12072 if (sp->ts_curi > byts[n]) 12073 { 12074 /* Only NUL bytes at this node, go to next state. */ 12075 sp->ts_state = STATE_SWAP; 12076 break; 12077 } 12078 if (byts[n + sp->ts_curi] != NUL) 12079 { 12080 /* Found a byte to insert. */ 12081 sp->ts_state = STATE_INS; 12082 break; 12083 } 12084 ++sp->ts_curi; 12085 } 12086 break; 12087 12088 /*FALLTHROUGH*/ 12089 12090 case STATE_INS: 12091 /* Insert one byte. Repeat this for each possible byte at this 12092 * node. */ 12093 n = sp->ts_arridx; 12094 if (sp->ts_curi > byts[n]) 12095 { 12096 /* Done all bytes at this node, go to next state. */ 12097 sp->ts_state = STATE_SWAP; 12098 break; 12099 } 12100 12101 /* Do one more byte at this node, but: 12102 * - Skip NUL bytes. 12103 * - Skip the byte if it's equal to the byte in the word, 12104 * accepting that byte is always better. 12105 */ 12106 n += sp->ts_curi++; 12107 c = byts[n]; 12108 if (soundfold && sp->ts_twordlen == 0 && c == '*') 12109 /* Inserting a vowel at the start of a word counts less, 12110 * see soundalike_score(). */ 12111 newscore = 2 * SCORE_INS / 3; 12112 else 12113 newscore = SCORE_INS; 12114 if (c != fword[sp->ts_fidx] 12115 && TRY_DEEPER(su, stack, depth, newscore)) 12116 { 12117 go_deeper(stack, depth, newscore); 12118 #ifdef DEBUG_TRIEWALK 12119 sprintf(changename[depth], "%.*s-%s: insert %c", 12120 sp->ts_twordlen, tword, fword + sp->ts_fidx, 12121 c); 12122 #endif 12123 ++depth; 12124 sp = &stack[depth]; 12125 tword[sp->ts_twordlen++] = c; 12126 sp->ts_arridx = idxs[n]; 12127 #ifdef FEAT_MBYTE 12128 if (has_mbyte) 12129 { 12130 fl = MB_BYTE2LEN(c); 12131 if (fl > 1) 12132 { 12133 /* There are following bytes for the same character. 12134 * We must find all bytes before trying 12135 * delete/insert/swap/etc. */ 12136 sp->ts_tcharlen = fl; 12137 sp->ts_tcharidx = 1; 12138 sp->ts_isdiff = DIFF_INSERT; 12139 } 12140 } 12141 else 12142 fl = 1; 12143 if (fl == 1) 12144 #endif 12145 { 12146 /* If the previous character was the same, thus doubling a 12147 * character, give a bonus to the score. Also for 12148 * soundfold words (illogical but does give a better 12149 * score). */ 12150 if (sp->ts_twordlen >= 2 12151 && tword[sp->ts_twordlen - 2] == c) 12152 sp->ts_score -= SCORE_INS - SCORE_INSDUP; 12153 } 12154 } 12155 break; 12156 12157 case STATE_SWAP: 12158 /* 12159 * Swap two bytes in the bad word: "12" -> "21". 12160 * We change "fword" here, it's changed back afterwards at 12161 * STATE_UNSWAP. 12162 */ 12163 p = fword + sp->ts_fidx; 12164 c = *p; 12165 if (c == NUL) 12166 { 12167 /* End of word, can't swap or replace. */ 12168 sp->ts_state = STATE_FINAL; 12169 break; 12170 } 12171 12172 /* Don't swap if the first character is not a word character. 12173 * SWAP3 etc. also don't make sense then. */ 12174 if (!soundfold && !spell_iswordp(p, curbuf)) 12175 { 12176 sp->ts_state = STATE_REP_INI; 12177 break; 12178 } 12179 12180 #ifdef FEAT_MBYTE 12181 if (has_mbyte) 12182 { 12183 n = mb_cptr2len(p); 12184 c = mb_ptr2char(p); 12185 if (!soundfold && !spell_iswordp(p + n, curbuf)) 12186 c2 = c; /* don't swap non-word char */ 12187 else 12188 c2 = mb_ptr2char(p + n); 12189 } 12190 else 12191 #endif 12192 { 12193 if (!soundfold && !spell_iswordp(p + 1, curbuf)) 12194 c2 = c; /* don't swap non-word char */ 12195 else 12196 c2 = p[1]; 12197 } 12198 12199 /* When characters are identical, swap won't do anything. 12200 * Also get here if the second char is not a word character. */ 12201 if (c == c2) 12202 { 12203 sp->ts_state = STATE_SWAP3; 12204 break; 12205 } 12206 if (c2 != NUL && TRY_DEEPER(su, stack, depth, SCORE_SWAP)) 12207 { 12208 go_deeper(stack, depth, SCORE_SWAP); 12209 #ifdef DEBUG_TRIEWALK 12210 sprintf(changename[depth], "%.*s-%s: swap %c and %c", 12211 sp->ts_twordlen, tword, fword + sp->ts_fidx, 12212 c, c2); 12213 #endif 12214 sp->ts_state = STATE_UNSWAP; 12215 ++depth; 12216 #ifdef FEAT_MBYTE 12217 if (has_mbyte) 12218 { 12219 fl = mb_char2len(c2); 12220 mch_memmove(p, p + n, fl); 12221 mb_char2bytes(c, p + fl); 12222 stack[depth].ts_fidxtry = sp->ts_fidx + n + fl; 12223 } 12224 else 12225 #endif 12226 { 12227 p[0] = c2; 12228 p[1] = c; 12229 stack[depth].ts_fidxtry = sp->ts_fidx + 2; 12230 } 12231 } 12232 else 12233 /* If this swap doesn't work then SWAP3 won't either. */ 12234 sp->ts_state = STATE_REP_INI; 12235 break; 12236 12237 case STATE_UNSWAP: 12238 /* Undo the STATE_SWAP swap: "21" -> "12". */ 12239 p = fword + sp->ts_fidx; 12240 #ifdef FEAT_MBYTE 12241 if (has_mbyte) 12242 { 12243 n = MB_BYTE2LEN(*p); 12244 c = mb_ptr2char(p + n); 12245 mch_memmove(p + MB_BYTE2LEN(p[n]), p, n); 12246 mb_char2bytes(c, p); 12247 } 12248 else 12249 #endif 12250 { 12251 c = *p; 12252 *p = p[1]; 12253 p[1] = c; 12254 } 12255 /*FALLTHROUGH*/ 12256 12257 case STATE_SWAP3: 12258 /* Swap two bytes, skipping one: "123" -> "321". We change 12259 * "fword" here, it's changed back afterwards at STATE_UNSWAP3. */ 12260 p = fword + sp->ts_fidx; 12261 #ifdef FEAT_MBYTE 12262 if (has_mbyte) 12263 { 12264 n = mb_cptr2len(p); 12265 c = mb_ptr2char(p); 12266 fl = mb_cptr2len(p + n); 12267 c2 = mb_ptr2char(p + n); 12268 if (!soundfold && !spell_iswordp(p + n + fl, curbuf)) 12269 c3 = c; /* don't swap non-word char */ 12270 else 12271 c3 = mb_ptr2char(p + n + fl); 12272 } 12273 else 12274 #endif 12275 { 12276 c = *p; 12277 c2 = p[1]; 12278 if (!soundfold && !spell_iswordp(p + 2, curbuf)) 12279 c3 = c; /* don't swap non-word char */ 12280 else 12281 c3 = p[2]; 12282 } 12283 12284 /* When characters are identical: "121" then SWAP3 result is 12285 * identical, ROT3L result is same as SWAP: "211", ROT3L result is 12286 * same as SWAP on next char: "112". Thus skip all swapping. 12287 * Also skip when c3 is NUL. 12288 * Also get here when the third character is not a word character. 12289 * Second character may any char: "a.b" -> "b.a" */ 12290 if (c == c3 || c3 == NUL) 12291 { 12292 sp->ts_state = STATE_REP_INI; 12293 break; 12294 } 12295 if (TRY_DEEPER(su, stack, depth, SCORE_SWAP3)) 12296 { 12297 go_deeper(stack, depth, SCORE_SWAP3); 12298 #ifdef DEBUG_TRIEWALK 12299 sprintf(changename[depth], "%.*s-%s: swap3 %c and %c", 12300 sp->ts_twordlen, tword, fword + sp->ts_fidx, 12301 c, c3); 12302 #endif 12303 sp->ts_state = STATE_UNSWAP3; 12304 ++depth; 12305 #ifdef FEAT_MBYTE 12306 if (has_mbyte) 12307 { 12308 tl = mb_char2len(c3); 12309 mch_memmove(p, p + n + fl, tl); 12310 mb_char2bytes(c2, p + tl); 12311 mb_char2bytes(c, p + fl + tl); 12312 stack[depth].ts_fidxtry = sp->ts_fidx + n + fl + tl; 12313 } 12314 else 12315 #endif 12316 { 12317 p[0] = p[2]; 12318 p[2] = c; 12319 stack[depth].ts_fidxtry = sp->ts_fidx + 3; 12320 } 12321 } 12322 else 12323 sp->ts_state = STATE_REP_INI; 12324 break; 12325 12326 case STATE_UNSWAP3: 12327 /* Undo STATE_SWAP3: "321" -> "123" */ 12328 p = fword + sp->ts_fidx; 12329 #ifdef FEAT_MBYTE 12330 if (has_mbyte) 12331 { 12332 n = MB_BYTE2LEN(*p); 12333 c2 = mb_ptr2char(p + n); 12334 fl = MB_BYTE2LEN(p[n]); 12335 c = mb_ptr2char(p + n + fl); 12336 tl = MB_BYTE2LEN(p[n + fl]); 12337 mch_memmove(p + fl + tl, p, n); 12338 mb_char2bytes(c, p); 12339 mb_char2bytes(c2, p + tl); 12340 p = p + tl; 12341 } 12342 else 12343 #endif 12344 { 12345 c = *p; 12346 *p = p[2]; 12347 p[2] = c; 12348 ++p; 12349 } 12350 12351 if (!soundfold && !spell_iswordp(p, curbuf)) 12352 { 12353 /* Middle char is not a word char, skip the rotate. First and 12354 * third char were already checked at swap and swap3. */ 12355 sp->ts_state = STATE_REP_INI; 12356 break; 12357 } 12358 12359 /* Rotate three characters left: "123" -> "231". We change 12360 * "fword" here, it's changed back afterwards at STATE_UNROT3L. */ 12361 if (TRY_DEEPER(su, stack, depth, SCORE_SWAP3)) 12362 { 12363 go_deeper(stack, depth, SCORE_SWAP3); 12364 #ifdef DEBUG_TRIEWALK 12365 p = fword + sp->ts_fidx; 12366 sprintf(changename[depth], "%.*s-%s: rotate left %c%c%c", 12367 sp->ts_twordlen, tword, fword + sp->ts_fidx, 12368 p[0], p[1], p[2]); 12369 #endif 12370 sp->ts_state = STATE_UNROT3L; 12371 ++depth; 12372 p = fword + sp->ts_fidx; 12373 #ifdef FEAT_MBYTE 12374 if (has_mbyte) 12375 { 12376 n = mb_cptr2len(p); 12377 c = mb_ptr2char(p); 12378 fl = mb_cptr2len(p + n); 12379 fl += mb_cptr2len(p + n + fl); 12380 mch_memmove(p, p + n, fl); 12381 mb_char2bytes(c, p + fl); 12382 stack[depth].ts_fidxtry = sp->ts_fidx + n + fl; 12383 } 12384 else 12385 #endif 12386 { 12387 c = *p; 12388 *p = p[1]; 12389 p[1] = p[2]; 12390 p[2] = c; 12391 stack[depth].ts_fidxtry = sp->ts_fidx + 3; 12392 } 12393 } 12394 else 12395 sp->ts_state = STATE_REP_INI; 12396 break; 12397 12398 case STATE_UNROT3L: 12399 /* Undo ROT3L: "231" -> "123" */ 12400 p = fword + sp->ts_fidx; 12401 #ifdef FEAT_MBYTE 12402 if (has_mbyte) 12403 { 12404 n = MB_BYTE2LEN(*p); 12405 n += MB_BYTE2LEN(p[n]); 12406 c = mb_ptr2char(p + n); 12407 tl = MB_BYTE2LEN(p[n]); 12408 mch_memmove(p + tl, p, n); 12409 mb_char2bytes(c, p); 12410 } 12411 else 12412 #endif 12413 { 12414 c = p[2]; 12415 p[2] = p[1]; 12416 p[1] = *p; 12417 *p = c; 12418 } 12419 12420 /* Rotate three bytes right: "123" -> "312". We change "fword" 12421 * here, it's changed back afterwards at STATE_UNROT3R. */ 12422 if (TRY_DEEPER(su, stack, depth, SCORE_SWAP3)) 12423 { 12424 go_deeper(stack, depth, SCORE_SWAP3); 12425 #ifdef DEBUG_TRIEWALK 12426 p = fword + sp->ts_fidx; 12427 sprintf(changename[depth], "%.*s-%s: rotate right %c%c%c", 12428 sp->ts_twordlen, tword, fword + sp->ts_fidx, 12429 p[0], p[1], p[2]); 12430 #endif 12431 sp->ts_state = STATE_UNROT3R; 12432 ++depth; 12433 p = fword + sp->ts_fidx; 12434 #ifdef FEAT_MBYTE 12435 if (has_mbyte) 12436 { 12437 n = mb_cptr2len(p); 12438 n += mb_cptr2len(p + n); 12439 c = mb_ptr2char(p + n); 12440 tl = mb_cptr2len(p + n); 12441 mch_memmove(p + tl, p, n); 12442 mb_char2bytes(c, p); 12443 stack[depth].ts_fidxtry = sp->ts_fidx + n + tl; 12444 } 12445 else 12446 #endif 12447 { 12448 c = p[2]; 12449 p[2] = p[1]; 12450 p[1] = *p; 12451 *p = c; 12452 stack[depth].ts_fidxtry = sp->ts_fidx + 3; 12453 } 12454 } 12455 else 12456 sp->ts_state = STATE_REP_INI; 12457 break; 12458 12459 case STATE_UNROT3R: 12460 /* Undo ROT3R: "312" -> "123" */ 12461 p = fword + sp->ts_fidx; 12462 #ifdef FEAT_MBYTE 12463 if (has_mbyte) 12464 { 12465 c = mb_ptr2char(p); 12466 tl = MB_BYTE2LEN(*p); 12467 n = MB_BYTE2LEN(p[tl]); 12468 n += MB_BYTE2LEN(p[tl + n]); 12469 mch_memmove(p, p + tl, n); 12470 mb_char2bytes(c, p + n); 12471 } 12472 else 12473 #endif 12474 { 12475 c = *p; 12476 *p = p[1]; 12477 p[1] = p[2]; 12478 p[2] = c; 12479 } 12480 /*FALLTHROUGH*/ 12481 12482 case STATE_REP_INI: 12483 /* Check if matching with REP items from the .aff file would work. 12484 * Quickly skip if: 12485 * - there are no REP items and we are not in the soundfold trie 12486 * - the score is going to be too high anyway 12487 * - already applied a REP item or swapped here */ 12488 if ((lp->lp_replang == NULL && !soundfold) 12489 || sp->ts_score + SCORE_REP >= su->su_maxscore 12490 || sp->ts_fidx < sp->ts_fidxtry) 12491 { 12492 sp->ts_state = STATE_FINAL; 12493 break; 12494 } 12495 12496 /* Use the first byte to quickly find the first entry that may 12497 * match. If the index is -1 there is none. */ 12498 if (soundfold) 12499 sp->ts_curi = slang->sl_repsal_first[fword[sp->ts_fidx]]; 12500 else 12501 sp->ts_curi = lp->lp_replang->sl_rep_first[fword[sp->ts_fidx]]; 12502 12503 if (sp->ts_curi < 0) 12504 { 12505 sp->ts_state = STATE_FINAL; 12506 break; 12507 } 12508 12509 sp->ts_state = STATE_REP; 12510 /*FALLTHROUGH*/ 12511 12512 case STATE_REP: 12513 /* Try matching with REP items from the .aff file. For each match 12514 * replace the characters and check if the resulting word is 12515 * valid. */ 12516 p = fword + sp->ts_fidx; 12517 12518 if (soundfold) 12519 gap = &slang->sl_repsal; 12520 else 12521 gap = &lp->lp_replang->sl_rep; 12522 while (sp->ts_curi < gap->ga_len) 12523 { 12524 ftp = (fromto_T *)gap->ga_data + sp->ts_curi++; 12525 if (*ftp->ft_from != *p) 12526 { 12527 /* past possible matching entries */ 12528 sp->ts_curi = gap->ga_len; 12529 break; 12530 } 12531 if (STRNCMP(ftp->ft_from, p, STRLEN(ftp->ft_from)) == 0 12532 && TRY_DEEPER(su, stack, depth, SCORE_REP)) 12533 { 12534 go_deeper(stack, depth, SCORE_REP); 12535 #ifdef DEBUG_TRIEWALK 12536 sprintf(changename[depth], "%.*s-%s: replace %s with %s", 12537 sp->ts_twordlen, tword, fword + sp->ts_fidx, 12538 ftp->ft_from, ftp->ft_to); 12539 #endif 12540 /* Need to undo this afterwards. */ 12541 sp->ts_state = STATE_REP_UNDO; 12542 12543 /* Change the "from" to the "to" string. */ 12544 ++depth; 12545 fl = (int)STRLEN(ftp->ft_from); 12546 tl = (int)STRLEN(ftp->ft_to); 12547 if (fl != tl) 12548 { 12549 mch_memmove(p + tl, p + fl, STRLEN(p + fl) + 1); 12550 repextra += tl - fl; 12551 } 12552 mch_memmove(p, ftp->ft_to, tl); 12553 stack[depth].ts_fidxtry = sp->ts_fidx + tl; 12554 #ifdef FEAT_MBYTE 12555 stack[depth].ts_tcharlen = 0; 12556 #endif 12557 break; 12558 } 12559 } 12560 12561 if (sp->ts_curi >= gap->ga_len && sp->ts_state == STATE_REP) 12562 /* No (more) matches. */ 12563 sp->ts_state = STATE_FINAL; 12564 12565 break; 12566 12567 case STATE_REP_UNDO: 12568 /* Undo a REP replacement and continue with the next one. */ 12569 if (soundfold) 12570 gap = &slang->sl_repsal; 12571 else 12572 gap = &lp->lp_replang->sl_rep; 12573 ftp = (fromto_T *)gap->ga_data + sp->ts_curi - 1; 12574 fl = (int)STRLEN(ftp->ft_from); 12575 tl = (int)STRLEN(ftp->ft_to); 12576 p = fword + sp->ts_fidx; 12577 if (fl != tl) 12578 { 12579 mch_memmove(p + fl, p + tl, STRLEN(p + tl) + 1); 12580 repextra -= tl - fl; 12581 } 12582 mch_memmove(p, ftp->ft_from, fl); 12583 sp->ts_state = STATE_REP; 12584 break; 12585 12586 default: 12587 /* Did all possible states at this level, go up one level. */ 12588 --depth; 12589 12590 if (depth >= 0 && stack[depth].ts_prefixdepth == PFD_PREFIXTREE) 12591 { 12592 /* Continue in or go back to the prefix tree. */ 12593 byts = pbyts; 12594 idxs = pidxs; 12595 } 12596 12597 /* Don't check for CTRL-C too often, it takes time. */ 12598 if (--breakcheckcount == 0) 12599 { 12600 ui_breakcheck(); 12601 breakcheckcount = 1000; 12602 } 12603 } 12604 } 12605 } 12606 12607 12608 /* 12609 * Go one level deeper in the tree. 12610 */ 12611 static void 12612 go_deeper(stack, depth, score_add) 12613 trystate_T *stack; 12614 int depth; 12615 int score_add; 12616 { 12617 stack[depth + 1] = stack[depth]; 12618 stack[depth + 1].ts_state = STATE_START; 12619 stack[depth + 1].ts_score = stack[depth].ts_score + score_add; 12620 stack[depth + 1].ts_curi = 1; /* start just after length byte */ 12621 stack[depth + 1].ts_flags = 0; 12622 } 12623 12624 #ifdef FEAT_MBYTE 12625 /* 12626 * Case-folding may change the number of bytes: Count nr of chars in 12627 * fword[flen] and return the byte length of that many chars in "word". 12628 */ 12629 static int 12630 nofold_len(fword, flen, word) 12631 char_u *fword; 12632 int flen; 12633 char_u *word; 12634 { 12635 char_u *p; 12636 int i = 0; 12637 12638 for (p = fword; p < fword + flen; mb_ptr_adv(p)) 12639 ++i; 12640 for (p = word; i > 0; mb_ptr_adv(p)) 12641 --i; 12642 return (int)(p - word); 12643 } 12644 #endif 12645 12646 /* 12647 * "fword" is a good word with case folded. Find the matching keep-case 12648 * words and put it in "kword". 12649 * Theoretically there could be several keep-case words that result in the 12650 * same case-folded word, but we only find one... 12651 */ 12652 static void 12653 find_keepcap_word(slang, fword, kword) 12654 slang_T *slang; 12655 char_u *fword; 12656 char_u *kword; 12657 { 12658 char_u uword[MAXWLEN]; /* "fword" in upper-case */ 12659 int depth; 12660 idx_T tryidx; 12661 12662 /* The following arrays are used at each depth in the tree. */ 12663 idx_T arridx[MAXWLEN]; 12664 int round[MAXWLEN]; 12665 int fwordidx[MAXWLEN]; 12666 int uwordidx[MAXWLEN]; 12667 int kwordlen[MAXWLEN]; 12668 12669 int flen, ulen; 12670 int l; 12671 int len; 12672 int c; 12673 idx_T lo, hi, m; 12674 char_u *p; 12675 char_u *byts = slang->sl_kbyts; /* array with bytes of the words */ 12676 idx_T *idxs = slang->sl_kidxs; /* array with indexes */ 12677 12678 if (byts == NULL) 12679 { 12680 /* array is empty: "cannot happen" */ 12681 *kword = NUL; 12682 return; 12683 } 12684 12685 /* Make an all-cap version of "fword". */ 12686 allcap_copy(fword, uword); 12687 12688 /* 12689 * Each character needs to be tried both case-folded and upper-case. 12690 * All this gets very complicated if we keep in mind that changing case 12691 * may change the byte length of a multi-byte character... 12692 */ 12693 depth = 0; 12694 arridx[0] = 0; 12695 round[0] = 0; 12696 fwordidx[0] = 0; 12697 uwordidx[0] = 0; 12698 kwordlen[0] = 0; 12699 while (depth >= 0) 12700 { 12701 if (fword[fwordidx[depth]] == NUL) 12702 { 12703 /* We are at the end of "fword". If the tree allows a word to end 12704 * here we have found a match. */ 12705 if (byts[arridx[depth] + 1] == 0) 12706 { 12707 kword[kwordlen[depth]] = NUL; 12708 return; 12709 } 12710 12711 /* kword is getting too long, continue one level up */ 12712 --depth; 12713 } 12714 else if (++round[depth] > 2) 12715 { 12716 /* tried both fold-case and upper-case character, continue one 12717 * level up */ 12718 --depth; 12719 } 12720 else 12721 { 12722 /* 12723 * round[depth] == 1: Try using the folded-case character. 12724 * round[depth] == 2: Try using the upper-case character. 12725 */ 12726 #ifdef FEAT_MBYTE 12727 if (has_mbyte) 12728 { 12729 flen = mb_cptr2len(fword + fwordidx[depth]); 12730 ulen = mb_cptr2len(uword + uwordidx[depth]); 12731 } 12732 else 12733 #endif 12734 ulen = flen = 1; 12735 if (round[depth] == 1) 12736 { 12737 p = fword + fwordidx[depth]; 12738 l = flen; 12739 } 12740 else 12741 { 12742 p = uword + uwordidx[depth]; 12743 l = ulen; 12744 } 12745 12746 for (tryidx = arridx[depth]; l > 0; --l) 12747 { 12748 /* Perform a binary search in the list of accepted bytes. */ 12749 len = byts[tryidx++]; 12750 c = *p++; 12751 lo = tryidx; 12752 hi = tryidx + len - 1; 12753 while (lo < hi) 12754 { 12755 m = (lo + hi) / 2; 12756 if (byts[m] > c) 12757 hi = m - 1; 12758 else if (byts[m] < c) 12759 lo = m + 1; 12760 else 12761 { 12762 lo = hi = m; 12763 break; 12764 } 12765 } 12766 12767 /* Stop if there is no matching byte. */ 12768 if (hi < lo || byts[lo] != c) 12769 break; 12770 12771 /* Continue at the child (if there is one). */ 12772 tryidx = idxs[lo]; 12773 } 12774 12775 if (l == 0) 12776 { 12777 /* 12778 * Found the matching char. Copy it to "kword" and go a 12779 * level deeper. 12780 */ 12781 if (round[depth] == 1) 12782 { 12783 STRNCPY(kword + kwordlen[depth], fword + fwordidx[depth], 12784 flen); 12785 kwordlen[depth + 1] = kwordlen[depth] + flen; 12786 } 12787 else 12788 { 12789 STRNCPY(kword + kwordlen[depth], uword + uwordidx[depth], 12790 ulen); 12791 kwordlen[depth + 1] = kwordlen[depth] + ulen; 12792 } 12793 fwordidx[depth + 1] = fwordidx[depth] + flen; 12794 uwordidx[depth + 1] = uwordidx[depth] + ulen; 12795 12796 ++depth; 12797 arridx[depth] = tryidx; 12798 round[depth] = 0; 12799 } 12800 } 12801 } 12802 12803 /* Didn't find it: "cannot happen". */ 12804 *kword = NUL; 12805 } 12806 12807 /* 12808 * Compute the sound-a-like score for suggestions in su->su_ga and add them to 12809 * su->su_sga. 12810 */ 12811 static void 12812 score_comp_sal(su) 12813 suginfo_T *su; 12814 { 12815 langp_T *lp; 12816 char_u badsound[MAXWLEN]; 12817 int i; 12818 suggest_T *stp; 12819 suggest_T *sstp; 12820 int score; 12821 int lpi; 12822 12823 if (ga_grow(&su->su_sga, su->su_ga.ga_len) == FAIL) 12824 return; 12825 12826 /* Use the sound-folding of the first language that supports it. */ 12827 for (lpi = 0; lpi < curbuf->b_langp.ga_len; ++lpi) 12828 { 12829 lp = LANGP_ENTRY(curbuf->b_langp, lpi); 12830 if (lp->lp_slang->sl_sal.ga_len > 0) 12831 { 12832 /* soundfold the bad word */ 12833 spell_soundfold(lp->lp_slang, su->su_fbadword, TRUE, badsound); 12834 12835 for (i = 0; i < su->su_ga.ga_len; ++i) 12836 { 12837 stp = &SUG(su->su_ga, i); 12838 12839 /* Case-fold the suggested word, sound-fold it and compute the 12840 * sound-a-like score. */ 12841 score = stp_sal_score(stp, su, lp->lp_slang, badsound); 12842 if (score < SCORE_MAXMAX) 12843 { 12844 /* Add the suggestion. */ 12845 sstp = &SUG(su->su_sga, su->su_sga.ga_len); 12846 sstp->st_word = vim_strsave(stp->st_word); 12847 if (sstp->st_word != NULL) 12848 { 12849 sstp->st_wordlen = stp->st_wordlen; 12850 sstp->st_score = score; 12851 sstp->st_altscore = 0; 12852 sstp->st_orglen = stp->st_orglen; 12853 ++su->su_sga.ga_len; 12854 } 12855 } 12856 } 12857 break; 12858 } 12859 } 12860 } 12861 12862 /* 12863 * Combine the list of suggestions in su->su_ga and su->su_sga. 12864 * They are intwined. 12865 */ 12866 static void 12867 score_combine(su) 12868 suginfo_T *su; 12869 { 12870 int i; 12871 int j; 12872 garray_T ga; 12873 garray_T *gap; 12874 langp_T *lp; 12875 suggest_T *stp; 12876 char_u *p; 12877 char_u badsound[MAXWLEN]; 12878 int round; 12879 int lpi; 12880 slang_T *slang = NULL; 12881 12882 /* Add the alternate score to su_ga. */ 12883 for (lpi = 0; lpi < curbuf->b_langp.ga_len; ++lpi) 12884 { 12885 lp = LANGP_ENTRY(curbuf->b_langp, lpi); 12886 if (lp->lp_slang->sl_sal.ga_len > 0) 12887 { 12888 /* soundfold the bad word */ 12889 slang = lp->lp_slang; 12890 spell_soundfold(slang, su->su_fbadword, TRUE, badsound); 12891 12892 for (i = 0; i < su->su_ga.ga_len; ++i) 12893 { 12894 stp = &SUG(su->su_ga, i); 12895 stp->st_altscore = stp_sal_score(stp, su, slang, badsound); 12896 if (stp->st_altscore == SCORE_MAXMAX) 12897 stp->st_score = (stp->st_score * 3 + SCORE_BIG) / 4; 12898 else 12899 stp->st_score = (stp->st_score * 3 12900 + stp->st_altscore) / 4; 12901 stp->st_salscore = FALSE; 12902 } 12903 break; 12904 } 12905 } 12906 12907 if (slang == NULL) /* Using "double" without sound folding. */ 12908 { 12909 (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, 12910 su->su_maxcount); 12911 return; 12912 } 12913 12914 /* Add the alternate score to su_sga. */ 12915 for (i = 0; i < su->su_sga.ga_len; ++i) 12916 { 12917 stp = &SUG(su->su_sga, i); 12918 stp->st_altscore = spell_edit_score(slang, 12919 su->su_badword, stp->st_word); 12920 if (stp->st_score == SCORE_MAXMAX) 12921 stp->st_score = (SCORE_BIG * 7 + stp->st_altscore) / 8; 12922 else 12923 stp->st_score = (stp->st_score * 7 + stp->st_altscore) / 8; 12924 stp->st_salscore = TRUE; 12925 } 12926 12927 /* Remove bad suggestions, sort the suggestions and truncate at "maxcount" 12928 * for both lists. */ 12929 check_suggestions(su, &su->su_ga); 12930 (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount); 12931 check_suggestions(su, &su->su_sga); 12932 (void)cleanup_suggestions(&su->su_sga, su->su_maxscore, su->su_maxcount); 12933 12934 ga_init2(&ga, (int)sizeof(suginfo_T), 1); 12935 if (ga_grow(&ga, su->su_ga.ga_len + su->su_sga.ga_len) == FAIL) 12936 return; 12937 12938 stp = &SUG(ga, 0); 12939 for (i = 0; i < su->su_ga.ga_len || i < su->su_sga.ga_len; ++i) 12940 { 12941 /* round 1: get a suggestion from su_ga 12942 * round 2: get a suggestion from su_sga */ 12943 for (round = 1; round <= 2; ++round) 12944 { 12945 gap = round == 1 ? &su->su_ga : &su->su_sga; 12946 if (i < gap->ga_len) 12947 { 12948 /* Don't add a word if it's already there. */ 12949 p = SUG(*gap, i).st_word; 12950 for (j = 0; j < ga.ga_len; ++j) 12951 if (STRCMP(stp[j].st_word, p) == 0) 12952 break; 12953 if (j == ga.ga_len) 12954 stp[ga.ga_len++] = SUG(*gap, i); 12955 else 12956 vim_free(p); 12957 } 12958 } 12959 } 12960 12961 ga_clear(&su->su_ga); 12962 ga_clear(&su->su_sga); 12963 12964 /* Truncate the list to the number of suggestions that will be displayed. */ 12965 if (ga.ga_len > su->su_maxcount) 12966 { 12967 for (i = su->su_maxcount; i < ga.ga_len; ++i) 12968 vim_free(stp[i].st_word); 12969 ga.ga_len = su->su_maxcount; 12970 } 12971 12972 su->su_ga = ga; 12973 } 12974 12975 /* 12976 * For the goodword in "stp" compute the soundalike score compared to the 12977 * badword. 12978 */ 12979 static int 12980 stp_sal_score(stp, su, slang, badsound) 12981 suggest_T *stp; 12982 suginfo_T *su; 12983 slang_T *slang; 12984 char_u *badsound; /* sound-folded badword */ 12985 { 12986 char_u *p; 12987 char_u *pbad; 12988 char_u *pgood; 12989 char_u badsound2[MAXWLEN]; 12990 char_u fword[MAXWLEN]; 12991 char_u goodsound[MAXWLEN]; 12992 char_u goodword[MAXWLEN]; 12993 int lendiff; 12994 12995 lendiff = (int)(su->su_badlen - stp->st_orglen); 12996 if (lendiff >= 0) 12997 pbad = badsound; 12998 else 12999 { 13000 /* soundfold the bad word with more characters following */ 13001 (void)spell_casefold(su->su_badptr, stp->st_orglen, fword, MAXWLEN); 13002 13003 /* When joining two words the sound often changes a lot. E.g., "t he" 13004 * sounds like "t h" while "the" sounds like "@". Avoid that by 13005 * removing the space. Don't do it when the good word also contains a 13006 * space. */ 13007 if (vim_iswhite(su->su_badptr[su->su_badlen]) 13008 && *skiptowhite(stp->st_word) == NUL) 13009 for (p = fword; *(p = skiptowhite(p)) != NUL; ) 13010 mch_memmove(p, p + 1, STRLEN(p)); 13011 13012 spell_soundfold(slang, fword, TRUE, badsound2); 13013 pbad = badsound2; 13014 } 13015 13016 if (lendiff > 0) 13017 { 13018 /* Add part of the bad word to the good word, so that we soundfold 13019 * what replaces the bad word. */ 13020 STRCPY(goodword, stp->st_word); 13021 vim_strncpy(goodword + stp->st_wordlen, 13022 su->su_badptr + su->su_badlen - lendiff, lendiff); 13023 pgood = goodword; 13024 } 13025 else 13026 pgood = stp->st_word; 13027 13028 /* Sound-fold the word and compute the score for the difference. */ 13029 spell_soundfold(slang, pgood, FALSE, goodsound); 13030 13031 return soundalike_score(goodsound, pbad); 13032 } 13033 13034 /* structure used to store soundfolded words that add_sound_suggest() has 13035 * handled already. */ 13036 typedef struct 13037 { 13038 short sft_score; /* lowest score used */ 13039 char_u sft_word[1]; /* soundfolded word, actually longer */ 13040 } sftword_T; 13041 13042 static sftword_T dumsft; 13043 #define HIKEY2SFT(p) ((sftword_T *)(p - (dumsft.sft_word - (char_u *)&dumsft))) 13044 #define HI2SFT(hi) HIKEY2SFT((hi)->hi_key) 13045 13046 /* 13047 * Prepare for calling suggest_try_soundalike(). 13048 */ 13049 static void 13050 suggest_try_soundalike_prep() 13051 { 13052 langp_T *lp; 13053 int lpi; 13054 slang_T *slang; 13055 13056 /* Do this for all languages that support sound folding and for which a 13057 * .sug file has been loaded. */ 13058 for (lpi = 0; lpi < curbuf->b_langp.ga_len; ++lpi) 13059 { 13060 lp = LANGP_ENTRY(curbuf->b_langp, lpi); 13061 slang = lp->lp_slang; 13062 if (slang->sl_sal.ga_len > 0 && slang->sl_sbyts != NULL) 13063 /* prepare the hashtable used by add_sound_suggest() */ 13064 hash_init(&slang->sl_sounddone); 13065 } 13066 } 13067 13068 /* 13069 * Find suggestions by comparing the word in a sound-a-like form. 13070 * Note: This doesn't support postponed prefixes. 13071 */ 13072 static void 13073 suggest_try_soundalike(su) 13074 suginfo_T *su; 13075 { 13076 char_u salword[MAXWLEN]; 13077 langp_T *lp; 13078 int lpi; 13079 slang_T *slang; 13080 13081 /* Do this for all languages that support sound folding and for which a 13082 * .sug file has been loaded. */ 13083 for (lpi = 0; lpi < curbuf->b_langp.ga_len; ++lpi) 13084 { 13085 lp = LANGP_ENTRY(curbuf->b_langp, lpi); 13086 slang = lp->lp_slang; 13087 if (slang->sl_sal.ga_len > 0 && slang->sl_sbyts != NULL) 13088 { 13089 /* soundfold the bad word */ 13090 spell_soundfold(slang, su->su_fbadword, TRUE, salword); 13091 13092 /* try all kinds of inserts/deletes/swaps/etc. */ 13093 /* TODO: also soundfold the next words, so that we can try joining 13094 * and splitting */ 13095 suggest_trie_walk(su, lp, salword, TRUE); 13096 } 13097 } 13098 } 13099 13100 /* 13101 * Finish up after calling suggest_try_soundalike(). 13102 */ 13103 static void 13104 suggest_try_soundalike_finish() 13105 { 13106 langp_T *lp; 13107 int lpi; 13108 slang_T *slang; 13109 int todo; 13110 hashitem_T *hi; 13111 13112 /* Do this for all languages that support sound folding and for which a 13113 * .sug file has been loaded. */ 13114 for (lpi = 0; lpi < curbuf->b_langp.ga_len; ++lpi) 13115 { 13116 lp = LANGP_ENTRY(curbuf->b_langp, lpi); 13117 slang = lp->lp_slang; 13118 if (slang->sl_sal.ga_len > 0 && slang->sl_sbyts != NULL) 13119 { 13120 /* Free the info about handled words. */ 13121 todo = (int)slang->sl_sounddone.ht_used; 13122 for (hi = slang->sl_sounddone.ht_array; todo > 0; ++hi) 13123 if (!HASHITEM_EMPTY(hi)) 13124 { 13125 vim_free(HI2SFT(hi)); 13126 --todo; 13127 } 13128 13129 /* Clear the hashtable, it may also be used by another region. */ 13130 hash_clear(&slang->sl_sounddone); 13131 hash_init(&slang->sl_sounddone); 13132 } 13133 } 13134 } 13135 13136 /* 13137 * A match with a soundfolded word is found. Add the good word(s) that 13138 * produce this soundfolded word. 13139 */ 13140 static void 13141 add_sound_suggest(su, goodword, score, lp) 13142 suginfo_T *su; 13143 char_u *goodword; 13144 int score; /* soundfold score */ 13145 langp_T *lp; 13146 { 13147 slang_T *slang = lp->lp_slang; /* language for sound folding */ 13148 int sfwordnr; 13149 char_u *nrline; 13150 int orgnr; 13151 char_u theword[MAXWLEN]; 13152 int i; 13153 int wlen; 13154 char_u *byts; 13155 idx_T *idxs; 13156 int n; 13157 int wordcount; 13158 int wc; 13159 int goodscore; 13160 hash_T hash; 13161 hashitem_T *hi; 13162 sftword_T *sft; 13163 int bc, gc; 13164 int limit; 13165 13166 /* 13167 * It's very well possible that the same soundfold word is found several 13168 * times with different scores. Since the following is quite slow only do 13169 * the words that have a better score than before. Use a hashtable to 13170 * remember the words that have been done. 13171 */ 13172 hash = hash_hash(goodword); 13173 hi = hash_lookup(&slang->sl_sounddone, goodword, hash); 13174 if (HASHITEM_EMPTY(hi)) 13175 { 13176 sft = (sftword_T *)alloc((unsigned)(sizeof(sftword_T) 13177 + STRLEN(goodword))); 13178 if (sft != NULL) 13179 { 13180 sft->sft_score = score; 13181 STRCPY(sft->sft_word, goodword); 13182 hash_add_item(&slang->sl_sounddone, hi, sft->sft_word, hash); 13183 } 13184 } 13185 else 13186 { 13187 sft = HI2SFT(hi); 13188 if (score >= sft->sft_score) 13189 return; 13190 sft->sft_score = score; 13191 } 13192 13193 /* 13194 * Find the word nr in the soundfold tree. 13195 */ 13196 sfwordnr = soundfold_find(slang, goodword); 13197 if (sfwordnr < 0) 13198 { 13199 EMSG2(_(e_intern2), "add_sound_suggest()"); 13200 return; 13201 } 13202 13203 /* 13204 * go over the list of good words that produce this soundfold word 13205 */ 13206 nrline = ml_get_buf(slang->sl_sugbuf, (linenr_T)(sfwordnr + 1), FALSE); 13207 orgnr = 0; 13208 while (*nrline != NUL) 13209 { 13210 /* The wordnr was stored in a minimal nr of bytes as an offset to the 13211 * previous wordnr. */ 13212 orgnr += bytes2offset(&nrline); 13213 13214 byts = slang->sl_fbyts; 13215 idxs = slang->sl_fidxs; 13216 13217 /* Lookup the word "orgnr" one of the two tries. */ 13218 n = 0; 13219 wlen = 0; 13220 wordcount = 0; 13221 for (;;) 13222 { 13223 i = 1; 13224 if (wordcount == orgnr && byts[n + 1] == NUL) 13225 break; /* found end of word */ 13226 13227 if (byts[n + 1] == NUL) 13228 ++wordcount; 13229 13230 /* skip over the NUL bytes */ 13231 for ( ; byts[n + i] == NUL; ++i) 13232 if (i > byts[n]) /* safety check */ 13233 { 13234 STRCPY(theword + wlen, "BAD"); 13235 goto badword; 13236 } 13237 13238 /* One of the siblings must have the word. */ 13239 for ( ; i < byts[n]; ++i) 13240 { 13241 wc = idxs[idxs[n + i]]; /* nr of words under this byte */ 13242 if (wordcount + wc > orgnr) 13243 break; 13244 wordcount += wc; 13245 } 13246 13247 theword[wlen++] = byts[n + i]; 13248 n = idxs[n + i]; 13249 } 13250 badword: 13251 theword[wlen] = NUL; 13252 13253 /* Go over the possible flags and regions. */ 13254 for (; i <= byts[n] && byts[n + i] == NUL; ++i) 13255 { 13256 char_u cword[MAXWLEN]; 13257 char_u *p; 13258 int flags = (int)idxs[n + i]; 13259 13260 /* Skip words with the NOSUGGEST flag */ 13261 if (flags & WF_NOSUGGEST) 13262 continue; 13263 13264 if (flags & WF_KEEPCAP) 13265 { 13266 /* Must find the word in the keep-case tree. */ 13267 find_keepcap_word(slang, theword, cword); 13268 p = cword; 13269 } 13270 else 13271 { 13272 flags |= su->su_badflags; 13273 if ((flags & WF_CAPMASK) != 0) 13274 { 13275 /* Need to fix case according to "flags". */ 13276 make_case_word(theword, cword, flags); 13277 p = cword; 13278 } 13279 else 13280 p = theword; 13281 } 13282 13283 /* Add the suggestion. */ 13284 if (sps_flags & SPS_DOUBLE) 13285 { 13286 /* Add the suggestion if the score isn't too bad. */ 13287 if (score <= su->su_maxscore) 13288 add_suggestion(su, &su->su_sga, p, su->su_badlen, 13289 score, 0, FALSE, slang, FALSE); 13290 } 13291 else 13292 { 13293 /* Add a penalty for words in another region. */ 13294 if ((flags & WF_REGION) 13295 && (((unsigned)flags >> 16) & lp->lp_region) == 0) 13296 goodscore = SCORE_REGION; 13297 else 13298 goodscore = 0; 13299 13300 /* Add a small penalty for changing the first letter from 13301 * lower to upper case. Helps for "tath" -> "Kath", which is 13302 * less common thatn "tath" -> "path". Don't do it when the 13303 * letter is the same, that has already been counted. */ 13304 gc = PTR2CHAR(p); 13305 if (SPELL_ISUPPER(gc)) 13306 { 13307 bc = PTR2CHAR(su->su_badword); 13308 if (!SPELL_ISUPPER(bc) 13309 && SPELL_TOFOLD(bc) != SPELL_TOFOLD(gc)) 13310 goodscore += SCORE_ICASE / 2; 13311 } 13312 13313 /* Compute the score for the good word. This only does letter 13314 * insert/delete/swap/replace. REP items are not considered, 13315 * which may make the score a bit higher. 13316 * Use a limit for the score to make it work faster. Use 13317 * MAXSCORE(), because RESCORE() will change the score. 13318 * If the limit is very high then the iterative method is 13319 * inefficient, using an array is quicker. */ 13320 limit = MAXSCORE(su->su_sfmaxscore - goodscore, score); 13321 if (limit > SCORE_LIMITMAX) 13322 goodscore += spell_edit_score(slang, su->su_badword, p); 13323 else 13324 goodscore += spell_edit_score_limit(slang, su->su_badword, 13325 p, limit); 13326 13327 /* When going over the limit don't bother to do the rest. */ 13328 if (goodscore < SCORE_MAXMAX) 13329 { 13330 /* Give a bonus to words seen before. */ 13331 goodscore = score_wordcount_adj(slang, goodscore, p, FALSE); 13332 13333 /* Add the suggestion if the score isn't too bad. */ 13334 goodscore = RESCORE(goodscore, score); 13335 if (goodscore <= su->su_sfmaxscore) 13336 add_suggestion(su, &su->su_ga, p, su->su_badlen, 13337 goodscore, score, TRUE, slang, TRUE); 13338 } 13339 } 13340 } 13341 /* smsg("word %s (%d): %s (%d)", sftword, sftnr, theword, orgnr); */ 13342 } 13343 } 13344 13345 /* 13346 * Find word "word" in fold-case tree for "slang" and return the word number. 13347 */ 13348 static int 13349 soundfold_find(slang, word) 13350 slang_T *slang; 13351 char_u *word; 13352 { 13353 idx_T arridx = 0; 13354 int len; 13355 int wlen = 0; 13356 int c; 13357 char_u *ptr = word; 13358 char_u *byts; 13359 idx_T *idxs; 13360 int wordnr = 0; 13361 13362 byts = slang->sl_sbyts; 13363 idxs = slang->sl_sidxs; 13364 13365 for (;;) 13366 { 13367 /* First byte is the number of possible bytes. */ 13368 len = byts[arridx++]; 13369 13370 /* If the first possible byte is a zero the word could end here. 13371 * If the word ends we found the word. If not skip the NUL bytes. */ 13372 c = ptr[wlen]; 13373 if (byts[arridx] == NUL) 13374 { 13375 if (c == NUL) 13376 break; 13377 13378 /* Skip over the zeros, there can be several. */ 13379 while (len > 0 && byts[arridx] == NUL) 13380 { 13381 ++arridx; 13382 --len; 13383 } 13384 if (len == 0) 13385 return -1; /* no children, word should have ended here */ 13386 ++wordnr; 13387 } 13388 13389 /* If the word ends we didn't find it. */ 13390 if (c == NUL) 13391 return -1; 13392 13393 /* Perform a binary search in the list of accepted bytes. */ 13394 if (c == TAB) /* <Tab> is handled like <Space> */ 13395 c = ' '; 13396 while (byts[arridx] < c) 13397 { 13398 /* The word count is in the first idxs[] entry of the child. */ 13399 wordnr += idxs[idxs[arridx]]; 13400 ++arridx; 13401 if (--len == 0) /* end of the bytes, didn't find it */ 13402 return -1; 13403 } 13404 if (byts[arridx] != c) /* didn't find the byte */ 13405 return -1; 13406 13407 /* Continue at the child (if there is one). */ 13408 arridx = idxs[arridx]; 13409 ++wlen; 13410 13411 /* One space in the good word may stand for several spaces in the 13412 * checked word. */ 13413 if (c == ' ') 13414 while (ptr[wlen] == ' ' || ptr[wlen] == TAB) 13415 ++wlen; 13416 } 13417 13418 return wordnr; 13419 } 13420 13421 /* 13422 * Copy "fword" to "cword", fixing case according to "flags". 13423 */ 13424 static void 13425 make_case_word(fword, cword, flags) 13426 char_u *fword; 13427 char_u *cword; 13428 int flags; 13429 { 13430 if (flags & WF_ALLCAP) 13431 /* Make it all upper-case */ 13432 allcap_copy(fword, cword); 13433 else if (flags & WF_ONECAP) 13434 /* Make the first letter upper-case */ 13435 onecap_copy(fword, cword, TRUE); 13436 else 13437 /* Use goodword as-is. */ 13438 STRCPY(cword, fword); 13439 } 13440 13441 /* 13442 * Use map string "map" for languages "lp". 13443 */ 13444 static void 13445 set_map_str(lp, map) 13446 slang_T *lp; 13447 char_u *map; 13448 { 13449 char_u *p; 13450 int headc = 0; 13451 int c; 13452 int i; 13453 13454 if (*map == NUL) 13455 { 13456 lp->sl_has_map = FALSE; 13457 return; 13458 } 13459 lp->sl_has_map = TRUE; 13460 13461 /* Init the array and hash tables empty. */ 13462 for (i = 0; i < 256; ++i) 13463 lp->sl_map_array[i] = 0; 13464 #ifdef FEAT_MBYTE 13465 hash_init(&lp->sl_map_hash); 13466 #endif 13467 13468 /* 13469 * The similar characters are stored separated with slashes: 13470 * "aaa/bbb/ccc/". Fill sl_map_array[c] with the character before c and 13471 * before the same slash. For characters above 255 sl_map_hash is used. 13472 */ 13473 for (p = map; *p != NUL; ) 13474 { 13475 #ifdef FEAT_MBYTE 13476 c = mb_cptr2char_adv(&p); 13477 #else 13478 c = *p++; 13479 #endif 13480 if (c == '/') 13481 headc = 0; 13482 else 13483 { 13484 if (headc == 0) 13485 headc = c; 13486 13487 #ifdef FEAT_MBYTE 13488 /* Characters above 255 don't fit in sl_map_array[], put them in 13489 * the hash table. Each entry is the char, a NUL the headchar and 13490 * a NUL. */ 13491 if (c >= 256) 13492 { 13493 int cl = mb_char2len(c); 13494 int headcl = mb_char2len(headc); 13495 char_u *b; 13496 hash_T hash; 13497 hashitem_T *hi; 13498 13499 b = alloc((unsigned)(cl + headcl + 2)); 13500 if (b == NULL) 13501 return; 13502 mb_char2bytes(c, b); 13503 b[cl] = NUL; 13504 mb_char2bytes(headc, b + cl + 1); 13505 b[cl + 1 + headcl] = NUL; 13506 hash = hash_hash(b); 13507 hi = hash_lookup(&lp->sl_map_hash, b, hash); 13508 if (HASHITEM_EMPTY(hi)) 13509 hash_add_item(&lp->sl_map_hash, hi, b, hash); 13510 else 13511 { 13512 /* This should have been checked when generating the .spl 13513 * file. */ 13514 EMSG(_("E783: duplicate char in MAP entry")); 13515 vim_free(b); 13516 } 13517 } 13518 else 13519 #endif 13520 lp->sl_map_array[c] = headc; 13521 } 13522 } 13523 } 13524 13525 /* 13526 * Return TRUE if "c1" and "c2" are similar characters according to the MAP 13527 * lines in the .aff file. 13528 */ 13529 static int 13530 similar_chars(slang, c1, c2) 13531 slang_T *slang; 13532 int c1; 13533 int c2; 13534 { 13535 int m1, m2; 13536 #ifdef FEAT_MBYTE 13537 char_u buf[MB_MAXBYTES]; 13538 hashitem_T *hi; 13539 13540 if (c1 >= 256) 13541 { 13542 buf[mb_char2bytes(c1, buf)] = 0; 13543 hi = hash_find(&slang->sl_map_hash, buf); 13544 if (HASHITEM_EMPTY(hi)) 13545 m1 = 0; 13546 else 13547 m1 = mb_ptr2char(hi->hi_key + STRLEN(hi->hi_key) + 1); 13548 } 13549 else 13550 #endif 13551 m1 = slang->sl_map_array[c1]; 13552 if (m1 == 0) 13553 return FALSE; 13554 13555 13556 #ifdef FEAT_MBYTE 13557 if (c2 >= 256) 13558 { 13559 buf[mb_char2bytes(c2, buf)] = 0; 13560 hi = hash_find(&slang->sl_map_hash, buf); 13561 if (HASHITEM_EMPTY(hi)) 13562 m2 = 0; 13563 else 13564 m2 = mb_ptr2char(hi->hi_key + STRLEN(hi->hi_key) + 1); 13565 } 13566 else 13567 #endif 13568 m2 = slang->sl_map_array[c2]; 13569 13570 return m1 == m2; 13571 } 13572 13573 /* 13574 * Add a suggestion to the list of suggestions. 13575 * For a suggestion that is already in the list the lowest score is remembered. 13576 */ 13577 static void 13578 add_suggestion(su, gap, goodword, badlenarg, score, altscore, had_bonus, 13579 slang, maxsf) 13580 suginfo_T *su; 13581 garray_T *gap; /* either su_ga or su_sga */ 13582 char_u *goodword; 13583 int badlenarg; /* len of bad word replaced with "goodword" */ 13584 int score; 13585 int altscore; 13586 int had_bonus; /* value for st_had_bonus */ 13587 slang_T *slang; /* language for sound folding */ 13588 int maxsf; /* su_maxscore applies to soundfold score, 13589 su_sfmaxscore to the total score. */ 13590 { 13591 int goodlen; /* len of goodword changed */ 13592 int badlen; /* len of bad word changed */ 13593 suggest_T *stp; 13594 suggest_T new_sug; 13595 int i; 13596 char_u *pgood, *pbad; 13597 13598 /* Minimize "badlen" for consistency. Avoids that changing "the the" to 13599 * "thee the" is added next to changing the first "the" the "thee". */ 13600 pgood = goodword + STRLEN(goodword); 13601 pbad = su->su_badptr + badlenarg; 13602 for (;;) 13603 { 13604 goodlen = (int)(pgood - goodword); 13605 badlen = (int)(pbad - su->su_badptr); 13606 if (goodlen <= 0 || badlen <= 0) 13607 break; 13608 mb_ptr_back(goodword, pgood); 13609 mb_ptr_back(su->su_badptr, pbad); 13610 #ifdef FEAT_MBYTE 13611 if (has_mbyte) 13612 { 13613 if (mb_ptr2char(pgood) != mb_ptr2char(pbad)) 13614 break; 13615 } 13616 else 13617 #endif 13618 if (*pgood != *pbad) 13619 break; 13620 } 13621 13622 if (badlen == 0 && goodlen == 0) 13623 /* goodword doesn't change anything; may happen for "the the" changing 13624 * the first "the" to itself. */ 13625 return; 13626 13627 if (gap->ga_len == 0) 13628 i = -1; 13629 else 13630 { 13631 /* Check if the word is already there. Also check the length that is 13632 * being replaced "thes," -> "these" is a different suggestion from 13633 * "thes" -> "these". */ 13634 stp = &SUG(*gap, 0); 13635 for (i = gap->ga_len; --i >= 0; ++stp) 13636 if (stp->st_wordlen == goodlen 13637 && stp->st_orglen == badlen 13638 && STRNCMP(stp->st_word, goodword, goodlen) == 0) 13639 { 13640 /* 13641 * Found it. Remember the word with the lowest score. 13642 */ 13643 if (stp->st_slang == NULL) 13644 stp->st_slang = slang; 13645 13646 new_sug.st_score = score; 13647 new_sug.st_altscore = altscore; 13648 new_sug.st_had_bonus = had_bonus; 13649 13650 if (stp->st_had_bonus != had_bonus) 13651 { 13652 /* Only one of the two had the soundalike score computed. 13653 * Need to do that for the other one now, otherwise the 13654 * scores can't be compared. This happens because 13655 * suggest_try_change() doesn't compute the soundalike 13656 * word to keep it fast, while some special methods set 13657 * the soundalike score to zero. */ 13658 if (had_bonus) 13659 rescore_one(su, stp); 13660 else 13661 { 13662 new_sug.st_word = stp->st_word; 13663 new_sug.st_wordlen = stp->st_wordlen; 13664 new_sug.st_slang = stp->st_slang; 13665 new_sug.st_orglen = badlen; 13666 rescore_one(su, &new_sug); 13667 } 13668 } 13669 13670 if (stp->st_score > new_sug.st_score) 13671 { 13672 stp->st_score = new_sug.st_score; 13673 stp->st_altscore = new_sug.st_altscore; 13674 stp->st_had_bonus = new_sug.st_had_bonus; 13675 } 13676 break; 13677 } 13678 } 13679 13680 if (i < 0 && ga_grow(gap, 1) == OK) 13681 { 13682 /* Add a suggestion. */ 13683 stp = &SUG(*gap, gap->ga_len); 13684 stp->st_word = vim_strnsave(goodword, goodlen); 13685 if (stp->st_word != NULL) 13686 { 13687 stp->st_wordlen = goodlen; 13688 stp->st_score = score; 13689 stp->st_altscore = altscore; 13690 stp->st_had_bonus = had_bonus; 13691 stp->st_orglen = badlen; 13692 stp->st_slang = slang; 13693 ++gap->ga_len; 13694 13695 /* If we have too many suggestions now, sort the list and keep 13696 * the best suggestions. */ 13697 if (gap->ga_len > SUG_MAX_COUNT(su)) 13698 { 13699 if (maxsf) 13700 su->su_sfmaxscore = cleanup_suggestions(gap, 13701 su->su_sfmaxscore, SUG_CLEAN_COUNT(su)); 13702 else 13703 { 13704 i = su->su_maxscore; 13705 su->su_maxscore = cleanup_suggestions(gap, 13706 su->su_maxscore, SUG_CLEAN_COUNT(su)); 13707 } 13708 } 13709 } 13710 } 13711 } 13712 13713 /* 13714 * Suggestions may in fact be flagged as errors. Esp. for banned words and 13715 * for split words, such as "the the". Remove these from the list here. 13716 */ 13717 static void 13718 check_suggestions(su, gap) 13719 suginfo_T *su; 13720 garray_T *gap; /* either su_ga or su_sga */ 13721 { 13722 suggest_T *stp; 13723 int i; 13724 char_u longword[MAXWLEN + 1]; 13725 int len; 13726 hlf_T attr; 13727 13728 stp = &SUG(*gap, 0); 13729 for (i = gap->ga_len - 1; i >= 0; --i) 13730 { 13731 /* Need to append what follows to check for "the the". */ 13732 STRCPY(longword, stp[i].st_word); 13733 len = stp[i].st_wordlen; 13734 vim_strncpy(longword + len, su->su_badptr + stp[i].st_orglen, 13735 MAXWLEN - len); 13736 attr = HLF_COUNT; 13737 (void)spell_check(curwin, longword, &attr, NULL, FALSE); 13738 if (attr != HLF_COUNT) 13739 { 13740 /* Remove this entry. */ 13741 vim_free(stp[i].st_word); 13742 --gap->ga_len; 13743 if (i < gap->ga_len) 13744 mch_memmove(stp + i, stp + i + 1, 13745 sizeof(suggest_T) * (gap->ga_len - i)); 13746 } 13747 } 13748 } 13749 13750 13751 /* 13752 * Add a word to be banned. 13753 */ 13754 static void 13755 add_banned(su, word) 13756 suginfo_T *su; 13757 char_u *word; 13758 { 13759 char_u *s; 13760 hash_T hash; 13761 hashitem_T *hi; 13762 13763 hash = hash_hash(word); 13764 hi = hash_lookup(&su->su_banned, word, hash); 13765 if (HASHITEM_EMPTY(hi)) 13766 { 13767 s = vim_strsave(word); 13768 if (s != NULL) 13769 hash_add_item(&su->su_banned, hi, s, hash); 13770 } 13771 } 13772 13773 /* 13774 * Recompute the score for all suggestions if sound-folding is possible. This 13775 * is slow, thus only done for the final results. 13776 */ 13777 static void 13778 rescore_suggestions(su) 13779 suginfo_T *su; 13780 { 13781 int i; 13782 13783 if (su->su_sallang != NULL) 13784 for (i = 0; i < su->su_ga.ga_len; ++i) 13785 rescore_one(su, &SUG(su->su_ga, i)); 13786 } 13787 13788 /* 13789 * Recompute the score for one suggestion if sound-folding is possible. 13790 */ 13791 static void 13792 rescore_one(su, stp) 13793 suginfo_T *su; 13794 suggest_T *stp; 13795 { 13796 slang_T *slang = stp->st_slang; 13797 char_u sal_badword[MAXWLEN]; 13798 char_u *p; 13799 13800 /* Only rescore suggestions that have no sal score yet and do have a 13801 * language. */ 13802 if (slang != NULL && slang->sl_sal.ga_len > 0 && !stp->st_had_bonus) 13803 { 13804 if (slang == su->su_sallang) 13805 p = su->su_sal_badword; 13806 else 13807 { 13808 spell_soundfold(slang, su->su_fbadword, TRUE, sal_badword); 13809 p = sal_badword; 13810 } 13811 13812 stp->st_altscore = stp_sal_score(stp, su, slang, p); 13813 if (stp->st_altscore == SCORE_MAXMAX) 13814 stp->st_altscore = SCORE_BIG; 13815 stp->st_score = RESCORE(stp->st_score, stp->st_altscore); 13816 stp->st_had_bonus = TRUE; 13817 } 13818 } 13819 13820 static int 13821 #ifdef __BORLANDC__ 13822 _RTLENTRYF 13823 #endif 13824 sug_compare __ARGS((const void *s1, const void *s2)); 13825 13826 /* 13827 * Function given to qsort() to sort the suggestions on st_score. 13828 * First on "st_score", then "st_altscore" then alphabetically. 13829 */ 13830 static int 13831 #ifdef __BORLANDC__ 13832 _RTLENTRYF 13833 #endif 13834 sug_compare(s1, s2) 13835 const void *s1; 13836 const void *s2; 13837 { 13838 suggest_T *p1 = (suggest_T *)s1; 13839 suggest_T *p2 = (suggest_T *)s2; 13840 int n = p1->st_score - p2->st_score; 13841 13842 if (n == 0) 13843 { 13844 n = p1->st_altscore - p2->st_altscore; 13845 if (n == 0) 13846 n = STRICMP(p1->st_word, p2->st_word); 13847 } 13848 return n; 13849 } 13850 13851 /* 13852 * Cleanup the suggestions: 13853 * - Sort on score. 13854 * - Remove words that won't be displayed. 13855 * Returns the maximum score in the list or "maxscore" unmodified. 13856 */ 13857 static int 13858 cleanup_suggestions(gap, maxscore, keep) 13859 garray_T *gap; 13860 int maxscore; 13861 int keep; /* nr of suggestions to keep */ 13862 { 13863 suggest_T *stp = &SUG(*gap, 0); 13864 int i; 13865 13866 /* Sort the list. */ 13867 qsort(gap->ga_data, (size_t)gap->ga_len, sizeof(suggest_T), sug_compare); 13868 13869 /* Truncate the list to the number of suggestions that will be displayed. */ 13870 if (gap->ga_len > keep) 13871 { 13872 for (i = keep; i < gap->ga_len; ++i) 13873 vim_free(stp[i].st_word); 13874 gap->ga_len = keep; 13875 return stp[keep - 1].st_score; 13876 } 13877 return maxscore; 13878 } 13879 13880 #if defined(FEAT_EVAL) || defined(PROTO) 13881 /* 13882 * Soundfold a string, for soundfold(). 13883 * Result is in allocated memory, NULL for an error. 13884 */ 13885 char_u * 13886 eval_soundfold(word) 13887 char_u *word; 13888 { 13889 langp_T *lp; 13890 char_u sound[MAXWLEN]; 13891 int lpi; 13892 13893 if (curwin->w_p_spell && *curbuf->b_p_spl != NUL) 13894 /* Use the sound-folding of the first language that supports it. */ 13895 for (lpi = 0; lpi < curbuf->b_langp.ga_len; ++lpi) 13896 { 13897 lp = LANGP_ENTRY(curbuf->b_langp, lpi); 13898 if (lp->lp_slang->sl_sal.ga_len > 0) 13899 { 13900 /* soundfold the word */ 13901 spell_soundfold(lp->lp_slang, word, FALSE, sound); 13902 return vim_strsave(sound); 13903 } 13904 } 13905 13906 /* No language with sound folding, return word as-is. */ 13907 return vim_strsave(word); 13908 } 13909 #endif 13910 13911 /* 13912 * Turn "inword" into its sound-a-like equivalent in "res[MAXWLEN]". 13913 * 13914 * There are many ways to turn a word into a sound-a-like representation. The 13915 * oldest is Soundex (1918!). A nice overview can be found in "Approximate 13916 * swedish name matching - survey and test of different algorithms" by Klas 13917 * Erikson. 13918 * 13919 * We support two methods: 13920 * 1. SOFOFROM/SOFOTO do a simple character mapping. 13921 * 2. SAL items define a more advanced sound-folding (and much slower). 13922 */ 13923 static void 13924 spell_soundfold(slang, inword, folded, res) 13925 slang_T *slang; 13926 char_u *inword; 13927 int folded; /* "inword" is already case-folded */ 13928 char_u *res; 13929 { 13930 char_u fword[MAXWLEN]; 13931 char_u *word; 13932 13933 if (slang->sl_sofo) 13934 /* SOFOFROM and SOFOTO used */ 13935 spell_soundfold_sofo(slang, inword, res); 13936 else 13937 { 13938 /* SAL items used. Requires the word to be case-folded. */ 13939 if (folded) 13940 word = inword; 13941 else 13942 { 13943 (void)spell_casefold(inword, (int)STRLEN(inword), fword, MAXWLEN); 13944 word = fword; 13945 } 13946 13947 #ifdef FEAT_MBYTE 13948 if (has_mbyte) 13949 spell_soundfold_wsal(slang, word, res); 13950 else 13951 #endif 13952 spell_soundfold_sal(slang, word, res); 13953 } 13954 } 13955 13956 /* 13957 * Perform sound folding of "inword" into "res" according to SOFOFROM and 13958 * SOFOTO lines. 13959 */ 13960 static void 13961 spell_soundfold_sofo(slang, inword, res) 13962 slang_T *slang; 13963 char_u *inword; 13964 char_u *res; 13965 { 13966 char_u *s; 13967 int ri = 0; 13968 int c; 13969 13970 #ifdef FEAT_MBYTE 13971 if (has_mbyte) 13972 { 13973 int prevc = 0; 13974 int *ip; 13975 13976 /* The sl_sal_first[] table contains the translation for chars up to 13977 * 255, sl_sal the rest. */ 13978 for (s = inword; *s != NUL; ) 13979 { 13980 c = mb_cptr2char_adv(&s); 13981 if (enc_utf8 ? utf_class(c) == 0 : vim_iswhite(c)) 13982 c = ' '; 13983 else if (c < 256) 13984 c = slang->sl_sal_first[c]; 13985 else 13986 { 13987 ip = ((int **)slang->sl_sal.ga_data)[c & 0xff]; 13988 if (ip == NULL) /* empty list, can't match */ 13989 c = NUL; 13990 else 13991 for (;;) /* find "c" in the list */ 13992 { 13993 if (*ip == 0) /* not found */ 13994 { 13995 c = NUL; 13996 break; 13997 } 13998 if (*ip == c) /* match! */ 13999 { 14000 c = ip[1]; 14001 break; 14002 } 14003 ip += 2; 14004 } 14005 } 14006 14007 if (c != NUL && c != prevc) 14008 { 14009 ri += mb_char2bytes(c, res + ri); 14010 if (ri + MB_MAXBYTES > MAXWLEN) 14011 break; 14012 prevc = c; 14013 } 14014 } 14015 } 14016 else 14017 #endif 14018 { 14019 /* The sl_sal_first[] table contains the translation. */ 14020 for (s = inword; (c = *s) != NUL; ++s) 14021 { 14022 if (vim_iswhite(c)) 14023 c = ' '; 14024 else 14025 c = slang->sl_sal_first[c]; 14026 if (c != NUL && (ri == 0 || res[ri - 1] != c)) 14027 res[ri++] = c; 14028 } 14029 } 14030 14031 res[ri] = NUL; 14032 } 14033 14034 static void 14035 spell_soundfold_sal(slang, inword, res) 14036 slang_T *slang; 14037 char_u *inword; 14038 char_u *res; 14039 { 14040 salitem_T *smp; 14041 char_u word[MAXWLEN]; 14042 char_u *s = inword; 14043 char_u *t; 14044 char_u *pf; 14045 int i, j, z; 14046 int reslen; 14047 int n, k = 0; 14048 int z0; 14049 int k0; 14050 int n0; 14051 int c; 14052 int pri; 14053 int p0 = -333; 14054 int c0; 14055 14056 /* Remove accents, if wanted. We actually remove all non-word characters. 14057 * But keep white space. We need a copy, the word may be changed here. */ 14058 if (slang->sl_rem_accents) 14059 { 14060 t = word; 14061 while (*s != NUL) 14062 { 14063 if (vim_iswhite(*s)) 14064 { 14065 *t++ = ' '; 14066 s = skipwhite(s); 14067 } 14068 else 14069 { 14070 if (spell_iswordp_nmw(s)) 14071 *t++ = *s; 14072 ++s; 14073 } 14074 } 14075 *t = NUL; 14076 } 14077 else 14078 STRCPY(word, s); 14079 14080 smp = (salitem_T *)slang->sl_sal.ga_data; 14081 14082 /* 14083 * This comes from Aspell phonet.cpp. Converted from C++ to C. 14084 * Changed to keep spaces. 14085 */ 14086 i = reslen = z = 0; 14087 while ((c = word[i]) != NUL) 14088 { 14089 /* Start with the first rule that has the character in the word. */ 14090 n = slang->sl_sal_first[c]; 14091 z0 = 0; 14092 14093 if (n >= 0) 14094 { 14095 /* check all rules for the same letter */ 14096 for (; (s = smp[n].sm_lead)[0] == c; ++n) 14097 { 14098 /* Quickly skip entries that don't match the word. Most 14099 * entries are less then three chars, optimize for that. */ 14100 k = smp[n].sm_leadlen; 14101 if (k > 1) 14102 { 14103 if (word[i + 1] != s[1]) 14104 continue; 14105 if (k > 2) 14106 { 14107 for (j = 2; j < k; ++j) 14108 if (word[i + j] != s[j]) 14109 break; 14110 if (j < k) 14111 continue; 14112 } 14113 } 14114 14115 if ((pf = smp[n].sm_oneof) != NULL) 14116 { 14117 /* Check for match with one of the chars in "sm_oneof". */ 14118 while (*pf != NUL && *pf != word[i + k]) 14119 ++pf; 14120 if (*pf == NUL) 14121 continue; 14122 ++k; 14123 } 14124 s = smp[n].sm_rules; 14125 pri = 5; /* default priority */ 14126 14127 p0 = *s; 14128 k0 = k; 14129 while (*s == '-' && k > 1) 14130 { 14131 k--; 14132 s++; 14133 } 14134 if (*s == '<') 14135 s++; 14136 if (VIM_ISDIGIT(*s)) 14137 { 14138 /* determine priority */ 14139 pri = *s - '0'; 14140 s++; 14141 } 14142 if (*s == '^' && *(s + 1) == '^') 14143 s++; 14144 14145 if (*s == NUL 14146 || (*s == '^' 14147 && (i == 0 || !(word[i - 1] == ' ' 14148 || spell_iswordp(word + i - 1, curbuf))) 14149 && (*(s + 1) != '$' 14150 || (!spell_iswordp(word + i + k0, curbuf)))) 14151 || (*s == '$' && i > 0 14152 && spell_iswordp(word + i - 1, curbuf) 14153 && (!spell_iswordp(word + i + k0, curbuf)))) 14154 { 14155 /* search for followup rules, if: */ 14156 /* followup and k > 1 and NO '-' in searchstring */ 14157 c0 = word[i + k - 1]; 14158 n0 = slang->sl_sal_first[c0]; 14159 14160 if (slang->sl_followup && k > 1 && n0 >= 0 14161 && p0 != '-' && word[i + k] != NUL) 14162 { 14163 /* test follow-up rule for "word[i + k]" */ 14164 for ( ; (s = smp[n0].sm_lead)[0] == c0; ++n0) 14165 { 14166 /* Quickly skip entries that don't match the word. 14167 * */ 14168 k0 = smp[n0].sm_leadlen; 14169 if (k0 > 1) 14170 { 14171 if (word[i + k] != s[1]) 14172 continue; 14173 if (k0 > 2) 14174 { 14175 pf = word + i + k + 1; 14176 for (j = 2; j < k0; ++j) 14177 if (*pf++ != s[j]) 14178 break; 14179 if (j < k0) 14180 continue; 14181 } 14182 } 14183 k0 += k - 1; 14184 14185 if ((pf = smp[n0].sm_oneof) != NULL) 14186 { 14187 /* Check for match with one of the chars in 14188 * "sm_oneof". */ 14189 while (*pf != NUL && *pf != word[i + k0]) 14190 ++pf; 14191 if (*pf == NUL) 14192 continue; 14193 ++k0; 14194 } 14195 14196 p0 = 5; 14197 s = smp[n0].sm_rules; 14198 while (*s == '-') 14199 { 14200 /* "k0" gets NOT reduced because 14201 * "if (k0 == k)" */ 14202 s++; 14203 } 14204 if (*s == '<') 14205 s++; 14206 if (VIM_ISDIGIT(*s)) 14207 { 14208 p0 = *s - '0'; 14209 s++; 14210 } 14211 14212 if (*s == NUL 14213 /* *s == '^' cuts */ 14214 || (*s == '$' 14215 && !spell_iswordp(word + i + k0, 14216 curbuf))) 14217 { 14218 if (k0 == k) 14219 /* this is just a piece of the string */ 14220 continue; 14221 14222 if (p0 < pri) 14223 /* priority too low */ 14224 continue; 14225 /* rule fits; stop search */ 14226 break; 14227 } 14228 } 14229 14230 if (p0 >= pri && smp[n0].sm_lead[0] == c0) 14231 continue; 14232 } 14233 14234 /* replace string */ 14235 s = smp[n].sm_to; 14236 if (s == NULL) 14237 s = (char_u *)""; 14238 pf = smp[n].sm_rules; 14239 p0 = (vim_strchr(pf, '<') != NULL) ? 1 : 0; 14240 if (p0 == 1 && z == 0) 14241 { 14242 /* rule with '<' is used */ 14243 if (reslen > 0 && *s != NUL && (res[reslen - 1] == c 14244 || res[reslen - 1] == *s)) 14245 reslen--; 14246 z0 = 1; 14247 z = 1; 14248 k0 = 0; 14249 while (*s != NUL && word[i + k0] != NUL) 14250 { 14251 word[i + k0] = *s; 14252 k0++; 14253 s++; 14254 } 14255 if (k > k0) 14256 mch_memmove(word + i + k0, word + i + k, 14257 STRLEN(word + i + k) + 1); 14258 14259 /* new "actual letter" */ 14260 c = word[i]; 14261 } 14262 else 14263 { 14264 /* no '<' rule used */ 14265 i += k - 1; 14266 z = 0; 14267 while (*s != NUL && s[1] != NUL && reslen < MAXWLEN) 14268 { 14269 if (reslen == 0 || res[reslen - 1] != *s) 14270 res[reslen++] = *s; 14271 s++; 14272 } 14273 /* new "actual letter" */ 14274 c = *s; 14275 if (strstr((char *)pf, "^^") != NULL) 14276 { 14277 if (c != NUL) 14278 res[reslen++] = c; 14279 mch_memmove(word, word + i + 1, 14280 STRLEN(word + i + 1) + 1); 14281 i = 0; 14282 z0 = 1; 14283 } 14284 } 14285 break; 14286 } 14287 } 14288 } 14289 else if (vim_iswhite(c)) 14290 { 14291 c = ' '; 14292 k = 1; 14293 } 14294 14295 if (z0 == 0) 14296 { 14297 if (k && !p0 && reslen < MAXWLEN && c != NUL 14298 && (!slang->sl_collapse || reslen == 0 14299 || res[reslen - 1] != c)) 14300 /* condense only double letters */ 14301 res[reslen++] = c; 14302 14303 i++; 14304 z = 0; 14305 k = 0; 14306 } 14307 } 14308 14309 res[reslen] = NUL; 14310 } 14311 14312 #ifdef FEAT_MBYTE 14313 /* 14314 * Turn "inword" into its sound-a-like equivalent in "res[MAXWLEN]". 14315 * Multi-byte version of spell_soundfold(). 14316 */ 14317 static void 14318 spell_soundfold_wsal(slang, inword, res) 14319 slang_T *slang; 14320 char_u *inword; 14321 char_u *res; 14322 { 14323 salitem_T *smp = (salitem_T *)slang->sl_sal.ga_data; 14324 int word[MAXWLEN]; 14325 int wres[MAXWLEN]; 14326 int l; 14327 char_u *s; 14328 int *ws; 14329 char_u *t; 14330 int *pf; 14331 int i, j, z; 14332 int reslen; 14333 int n, k = 0; 14334 int z0; 14335 int k0; 14336 int n0; 14337 int c; 14338 int pri; 14339 int p0 = -333; 14340 int c0; 14341 int did_white = FALSE; 14342 14343 /* 14344 * Convert the multi-byte string to a wide-character string. 14345 * Remove accents, if wanted. We actually remove all non-word characters. 14346 * But keep white space. 14347 */ 14348 n = 0; 14349 for (s = inword; *s != NUL; ) 14350 { 14351 t = s; 14352 c = mb_cptr2char_adv(&s); 14353 if (slang->sl_rem_accents) 14354 { 14355 if (enc_utf8 ? utf_class(c) == 0 : vim_iswhite(c)) 14356 { 14357 if (did_white) 14358 continue; 14359 c = ' '; 14360 did_white = TRUE; 14361 } 14362 else 14363 { 14364 did_white = FALSE; 14365 if (!spell_iswordp_nmw(t)) 14366 continue; 14367 } 14368 } 14369 word[n++] = c; 14370 } 14371 word[n] = NUL; 14372 14373 /* 14374 * This comes from Aspell phonet.cpp. 14375 * Converted from C++ to C. Added support for multi-byte chars. 14376 * Changed to keep spaces. 14377 */ 14378 i = reslen = z = 0; 14379 while ((c = word[i]) != NUL) 14380 { 14381 /* Start with the first rule that has the character in the word. */ 14382 n = slang->sl_sal_first[c & 0xff]; 14383 z0 = 0; 14384 14385 if (n >= 0) 14386 { 14387 /* check all rules for the same index byte */ 14388 for (; ((ws = smp[n].sm_lead_w)[0] & 0xff) == (c & 0xff); ++n) 14389 { 14390 /* Quickly skip entries that don't match the word. Most 14391 * entries are less then three chars, optimize for that. */ 14392 if (c != ws[0]) 14393 continue; 14394 k = smp[n].sm_leadlen; 14395 if (k > 1) 14396 { 14397 if (word[i + 1] != ws[1]) 14398 continue; 14399 if (k > 2) 14400 { 14401 for (j = 2; j < k; ++j) 14402 if (word[i + j] != ws[j]) 14403 break; 14404 if (j < k) 14405 continue; 14406 } 14407 } 14408 14409 if ((pf = smp[n].sm_oneof_w) != NULL) 14410 { 14411 /* Check for match with one of the chars in "sm_oneof". */ 14412 while (*pf != NUL && *pf != word[i + k]) 14413 ++pf; 14414 if (*pf == NUL) 14415 continue; 14416 ++k; 14417 } 14418 s = smp[n].sm_rules; 14419 pri = 5; /* default priority */ 14420 14421 p0 = *s; 14422 k0 = k; 14423 while (*s == '-' && k > 1) 14424 { 14425 k--; 14426 s++; 14427 } 14428 if (*s == '<') 14429 s++; 14430 if (VIM_ISDIGIT(*s)) 14431 { 14432 /* determine priority */ 14433 pri = *s - '0'; 14434 s++; 14435 } 14436 if (*s == '^' && *(s + 1) == '^') 14437 s++; 14438 14439 if (*s == NUL 14440 || (*s == '^' 14441 && (i == 0 || !(word[i - 1] == ' ' 14442 || spell_iswordp_w(word + i - 1, curbuf))) 14443 && (*(s + 1) != '$' 14444 || (!spell_iswordp_w(word + i + k0, curbuf)))) 14445 || (*s == '$' && i > 0 14446 && spell_iswordp_w(word + i - 1, curbuf) 14447 && (!spell_iswordp_w(word + i + k0, curbuf)))) 14448 { 14449 /* search for followup rules, if: */ 14450 /* followup and k > 1 and NO '-' in searchstring */ 14451 c0 = word[i + k - 1]; 14452 n0 = slang->sl_sal_first[c0 & 0xff]; 14453 14454 if (slang->sl_followup && k > 1 && n0 >= 0 14455 && p0 != '-' && word[i + k] != NUL) 14456 { 14457 /* Test follow-up rule for "word[i + k]"; loop over 14458 * all entries with the same index byte. */ 14459 for ( ; ((ws = smp[n0].sm_lead_w)[0] & 0xff) 14460 == (c0 & 0xff); ++n0) 14461 { 14462 /* Quickly skip entries that don't match the word. 14463 */ 14464 if (c0 != ws[0]) 14465 continue; 14466 k0 = smp[n0].sm_leadlen; 14467 if (k0 > 1) 14468 { 14469 if (word[i + k] != ws[1]) 14470 continue; 14471 if (k0 > 2) 14472 { 14473 pf = word + i + k + 1; 14474 for (j = 2; j < k0; ++j) 14475 if (*pf++ != ws[j]) 14476 break; 14477 if (j < k0) 14478 continue; 14479 } 14480 } 14481 k0 += k - 1; 14482 14483 if ((pf = smp[n0].sm_oneof_w) != NULL) 14484 { 14485 /* Check for match with one of the chars in 14486 * "sm_oneof". */ 14487 while (*pf != NUL && *pf != word[i + k0]) 14488 ++pf; 14489 if (*pf == NUL) 14490 continue; 14491 ++k0; 14492 } 14493 14494 p0 = 5; 14495 s = smp[n0].sm_rules; 14496 while (*s == '-') 14497 { 14498 /* "k0" gets NOT reduced because 14499 * "if (k0 == k)" */ 14500 s++; 14501 } 14502 if (*s == '<') 14503 s++; 14504 if (VIM_ISDIGIT(*s)) 14505 { 14506 p0 = *s - '0'; 14507 s++; 14508 } 14509 14510 if (*s == NUL 14511 /* *s == '^' cuts */ 14512 || (*s == '$' 14513 && !spell_iswordp_w(word + i + k0, 14514 curbuf))) 14515 { 14516 if (k0 == k) 14517 /* this is just a piece of the string */ 14518 continue; 14519 14520 if (p0 < pri) 14521 /* priority too low */ 14522 continue; 14523 /* rule fits; stop search */ 14524 break; 14525 } 14526 } 14527 14528 if (p0 >= pri && (smp[n0].sm_lead_w[0] & 0xff) 14529 == (c0 & 0xff)) 14530 continue; 14531 } 14532 14533 /* replace string */ 14534 ws = smp[n].sm_to_w; 14535 s = smp[n].sm_rules; 14536 p0 = (vim_strchr(s, '<') != NULL) ? 1 : 0; 14537 if (p0 == 1 && z == 0) 14538 { 14539 /* rule with '<' is used */ 14540 if (reslen > 0 && ws != NULL && *ws != NUL 14541 && (wres[reslen - 1] == c 14542 || wres[reslen - 1] == *ws)) 14543 reslen--; 14544 z0 = 1; 14545 z = 1; 14546 k0 = 0; 14547 if (ws != NULL) 14548 while (*ws != NUL && word[i + k0] != NUL) 14549 { 14550 word[i + k0] = *ws; 14551 k0++; 14552 ws++; 14553 } 14554 if (k > k0) 14555 mch_memmove(word + i + k0, word + i + k, 14556 sizeof(int) * (STRLEN(word + i + k) + 1)); 14557 14558 /* new "actual letter" */ 14559 c = word[i]; 14560 } 14561 else 14562 { 14563 /* no '<' rule used */ 14564 i += k - 1; 14565 z = 0; 14566 if (ws != NULL) 14567 while (*ws != NUL && ws[1] != NUL 14568 && reslen < MAXWLEN) 14569 { 14570 if (reslen == 0 || wres[reslen - 1] != *ws) 14571 wres[reslen++] = *ws; 14572 ws++; 14573 } 14574 /* new "actual letter" */ 14575 if (ws == NULL) 14576 c = NUL; 14577 else 14578 c = *ws; 14579 if (strstr((char *)s, "^^") != NULL) 14580 { 14581 if (c != NUL) 14582 wres[reslen++] = c; 14583 mch_memmove(word, word + i + 1, 14584 sizeof(int) * (STRLEN(word + i + 1) + 1)); 14585 i = 0; 14586 z0 = 1; 14587 } 14588 } 14589 break; 14590 } 14591 } 14592 } 14593 else if (vim_iswhite(c)) 14594 { 14595 c = ' '; 14596 k = 1; 14597 } 14598 14599 if (z0 == 0) 14600 { 14601 if (k && !p0 && reslen < MAXWLEN && c != NUL 14602 && (!slang->sl_collapse || reslen == 0 14603 || wres[reslen - 1] != c)) 14604 /* condense only double letters */ 14605 wres[reslen++] = c; 14606 14607 i++; 14608 z = 0; 14609 k = 0; 14610 } 14611 } 14612 14613 /* Convert wide characters in "wres" to a multi-byte string in "res". */ 14614 l = 0; 14615 for (n = 0; n < reslen; ++n) 14616 { 14617 l += mb_char2bytes(wres[n], res + l); 14618 if (l + MB_MAXBYTES > MAXWLEN) 14619 break; 14620 } 14621 res[l] = NUL; 14622 } 14623 #endif 14624 14625 /* 14626 * Compute a score for two sound-a-like words. 14627 * This permits up to two inserts/deletes/swaps/etc. to keep things fast. 14628 * Instead of a generic loop we write out the code. That keeps it fast by 14629 * avoiding checks that will not be possible. 14630 */ 14631 static int 14632 soundalike_score(goodstart, badstart) 14633 char_u *goodstart; /* sound-folded good word */ 14634 char_u *badstart; /* sound-folded bad word */ 14635 { 14636 char_u *goodsound = goodstart; 14637 char_u *badsound = badstart; 14638 int goodlen; 14639 int badlen; 14640 int n; 14641 char_u *pl, *ps; 14642 char_u *pl2, *ps2; 14643 int score = 0; 14644 14645 /* adding/inserting "*" at the start (word starts with vowel) shouldn't be 14646 * counted so much, vowels halfway the word aren't counted at all. */ 14647 if ((*badsound == '*' || *goodsound == '*') && *badsound != *goodsound) 14648 { 14649 if (badsound[1] == goodsound[1] 14650 || (badsound[1] != NUL 14651 && goodsound[1] != NUL 14652 && badsound[2] == goodsound[2])) 14653 { 14654 /* handle like a substitute */ 14655 } 14656 else 14657 { 14658 score = 2 * SCORE_DEL / 3; 14659 if (*badsound == '*') 14660 ++badsound; 14661 else 14662 ++goodsound; 14663 } 14664 } 14665 14666 goodlen = (int)STRLEN(goodsound); 14667 badlen = (int)STRLEN(badsound); 14668 14669 /* Return quickly if the lengths are too different to be fixed by two 14670 * changes. */ 14671 n = goodlen - badlen; 14672 if (n < -2 || n > 2) 14673 return SCORE_MAXMAX; 14674 14675 if (n > 0) 14676 { 14677 pl = goodsound; /* goodsound is longest */ 14678 ps = badsound; 14679 } 14680 else 14681 { 14682 pl = badsound; /* badsound is longest */ 14683 ps = goodsound; 14684 } 14685 14686 /* Skip over the identical part. */ 14687 while (*pl == *ps && *pl != NUL) 14688 { 14689 ++pl; 14690 ++ps; 14691 } 14692 14693 switch (n) 14694 { 14695 case -2: 14696 case 2: 14697 /* 14698 * Must delete two characters from "pl". 14699 */ 14700 ++pl; /* first delete */ 14701 while (*pl == *ps) 14702 { 14703 ++pl; 14704 ++ps; 14705 } 14706 /* strings must be equal after second delete */ 14707 if (STRCMP(pl + 1, ps) == 0) 14708 return score + SCORE_DEL * 2; 14709 14710 /* Failed to compare. */ 14711 break; 14712 14713 case -1: 14714 case 1: 14715 /* 14716 * Minimal one delete from "pl" required. 14717 */ 14718 14719 /* 1: delete */ 14720 pl2 = pl + 1; 14721 ps2 = ps; 14722 while (*pl2 == *ps2) 14723 { 14724 if (*pl2 == NUL) /* reached the end */ 14725 return score + SCORE_DEL; 14726 ++pl2; 14727 ++ps2; 14728 } 14729 14730 /* 2: delete then swap, then rest must be equal */ 14731 if (pl2[0] == ps2[1] && pl2[1] == ps2[0] 14732 && STRCMP(pl2 + 2, ps2 + 2) == 0) 14733 return score + SCORE_DEL + SCORE_SWAP; 14734 14735 /* 3: delete then substitute, then the rest must be equal */ 14736 if (STRCMP(pl2 + 1, ps2 + 1) == 0) 14737 return score + SCORE_DEL + SCORE_SUBST; 14738 14739 /* 4: first swap then delete */ 14740 if (pl[0] == ps[1] && pl[1] == ps[0]) 14741 { 14742 pl2 = pl + 2; /* swap, skip two chars */ 14743 ps2 = ps + 2; 14744 while (*pl2 == *ps2) 14745 { 14746 ++pl2; 14747 ++ps2; 14748 } 14749 /* delete a char and then strings must be equal */ 14750 if (STRCMP(pl2 + 1, ps2) == 0) 14751 return score + SCORE_SWAP + SCORE_DEL; 14752 } 14753 14754 /* 5: first substitute then delete */ 14755 pl2 = pl + 1; /* substitute, skip one char */ 14756 ps2 = ps + 1; 14757 while (*pl2 == *ps2) 14758 { 14759 ++pl2; 14760 ++ps2; 14761 } 14762 /* delete a char and then strings must be equal */ 14763 if (STRCMP(pl2 + 1, ps2) == 0) 14764 return score + SCORE_SUBST + SCORE_DEL; 14765 14766 /* Failed to compare. */ 14767 break; 14768 14769 case 0: 14770 /* 14771 * Lenghts are equal, thus changes must result in same length: An 14772 * insert is only possible in combination with a delete. 14773 * 1: check if for identical strings 14774 */ 14775 if (*pl == NUL) 14776 return score; 14777 14778 /* 2: swap */ 14779 if (pl[0] == ps[1] && pl[1] == ps[0]) 14780 { 14781 pl2 = pl + 2; /* swap, skip two chars */ 14782 ps2 = ps + 2; 14783 while (*pl2 == *ps2) 14784 { 14785 if (*pl2 == NUL) /* reached the end */ 14786 return score + SCORE_SWAP; 14787 ++pl2; 14788 ++ps2; 14789 } 14790 /* 3: swap and swap again */ 14791 if (pl2[0] == ps2[1] && pl2[1] == ps2[0] 14792 && STRCMP(pl2 + 2, ps2 + 2) == 0) 14793 return score + SCORE_SWAP + SCORE_SWAP; 14794 14795 /* 4: swap and substitute */ 14796 if (STRCMP(pl2 + 1, ps2 + 1) == 0) 14797 return score + SCORE_SWAP + SCORE_SUBST; 14798 } 14799 14800 /* 5: substitute */ 14801 pl2 = pl + 1; 14802 ps2 = ps + 1; 14803 while (*pl2 == *ps2) 14804 { 14805 if (*pl2 == NUL) /* reached the end */ 14806 return score + SCORE_SUBST; 14807 ++pl2; 14808 ++ps2; 14809 } 14810 14811 /* 6: substitute and swap */ 14812 if (pl2[0] == ps2[1] && pl2[1] == ps2[0] 14813 && STRCMP(pl2 + 2, ps2 + 2) == 0) 14814 return score + SCORE_SUBST + SCORE_SWAP; 14815 14816 /* 7: substitute and substitute */ 14817 if (STRCMP(pl2 + 1, ps2 + 1) == 0) 14818 return score + SCORE_SUBST + SCORE_SUBST; 14819 14820 /* 8: insert then delete */ 14821 pl2 = pl; 14822 ps2 = ps + 1; 14823 while (*pl2 == *ps2) 14824 { 14825 ++pl2; 14826 ++ps2; 14827 } 14828 if (STRCMP(pl2 + 1, ps2) == 0) 14829 return score + SCORE_INS + SCORE_DEL; 14830 14831 /* 9: delete then insert */ 14832 pl2 = pl + 1; 14833 ps2 = ps; 14834 while (*pl2 == *ps2) 14835 { 14836 ++pl2; 14837 ++ps2; 14838 } 14839 if (STRCMP(pl2, ps2 + 1) == 0) 14840 return score + SCORE_INS + SCORE_DEL; 14841 14842 /* Failed to compare. */ 14843 break; 14844 } 14845 14846 return SCORE_MAXMAX; 14847 } 14848 14849 /* 14850 * Compute the "edit distance" to turn "badword" into "goodword". The less 14851 * deletes/inserts/substitutes/swaps are required the lower the score. 14852 * 14853 * The algorithm is described by Du and Chang, 1992. 14854 * The implementation of the algorithm comes from Aspell editdist.cpp, 14855 * edit_distance(). It has been converted from C++ to C and modified to 14856 * support multi-byte characters. 14857 */ 14858 static int 14859 spell_edit_score(slang, badword, goodword) 14860 slang_T *slang; 14861 char_u *badword; 14862 char_u *goodword; 14863 { 14864 int *cnt; 14865 int badlen, goodlen; /* lengths including NUL */ 14866 int j, i; 14867 int t; 14868 int bc, gc; 14869 int pbc, pgc; 14870 #ifdef FEAT_MBYTE 14871 char_u *p; 14872 int wbadword[MAXWLEN]; 14873 int wgoodword[MAXWLEN]; 14874 14875 if (has_mbyte) 14876 { 14877 /* Get the characters from the multi-byte strings and put them in an 14878 * int array for easy access. */ 14879 for (p = badword, badlen = 0; *p != NUL; ) 14880 wbadword[badlen++] = mb_cptr2char_adv(&p); 14881 wbadword[badlen++] = 0; 14882 for (p = goodword, goodlen = 0; *p != NUL; ) 14883 wgoodword[goodlen++] = mb_cptr2char_adv(&p); 14884 wgoodword[goodlen++] = 0; 14885 } 14886 else 14887 #endif 14888 { 14889 badlen = (int)STRLEN(badword) + 1; 14890 goodlen = (int)STRLEN(goodword) + 1; 14891 } 14892 14893 /* We use "cnt" as an array: CNT(badword_idx, goodword_idx). */ 14894 #define CNT(a, b) cnt[(a) + (b) * (badlen + 1)] 14895 cnt = (int *)lalloc((long_u)(sizeof(int) * (badlen + 1) * (goodlen + 1)), 14896 TRUE); 14897 if (cnt == NULL) 14898 return 0; /* out of memory */ 14899 14900 CNT(0, 0) = 0; 14901 for (j = 1; j <= goodlen; ++j) 14902 CNT(0, j) = CNT(0, j - 1) + SCORE_INS; 14903 14904 for (i = 1; i <= badlen; ++i) 14905 { 14906 CNT(i, 0) = CNT(i - 1, 0) + SCORE_DEL; 14907 for (j = 1; j <= goodlen; ++j) 14908 { 14909 #ifdef FEAT_MBYTE 14910 if (has_mbyte) 14911 { 14912 bc = wbadword[i - 1]; 14913 gc = wgoodword[j - 1]; 14914 } 14915 else 14916 #endif 14917 { 14918 bc = badword[i - 1]; 14919 gc = goodword[j - 1]; 14920 } 14921 if (bc == gc) 14922 CNT(i, j) = CNT(i - 1, j - 1); 14923 else 14924 { 14925 /* Use a better score when there is only a case difference. */ 14926 if (SPELL_TOFOLD(bc) == SPELL_TOFOLD(gc)) 14927 CNT(i, j) = SCORE_ICASE + CNT(i - 1, j - 1); 14928 else 14929 { 14930 /* For a similar character use SCORE_SIMILAR. */ 14931 if (slang != NULL 14932 && slang->sl_has_map 14933 && similar_chars(slang, gc, bc)) 14934 CNT(i, j) = SCORE_SIMILAR + CNT(i - 1, j - 1); 14935 else 14936 CNT(i, j) = SCORE_SUBST + CNT(i - 1, j - 1); 14937 } 14938 14939 if (i > 1 && j > 1) 14940 { 14941 #ifdef FEAT_MBYTE 14942 if (has_mbyte) 14943 { 14944 pbc = wbadword[i - 2]; 14945 pgc = wgoodword[j - 2]; 14946 } 14947 else 14948 #endif 14949 { 14950 pbc = badword[i - 2]; 14951 pgc = goodword[j - 2]; 14952 } 14953 if (bc == pgc && pbc == gc) 14954 { 14955 t = SCORE_SWAP + CNT(i - 2, j - 2); 14956 if (t < CNT(i, j)) 14957 CNT(i, j) = t; 14958 } 14959 } 14960 t = SCORE_DEL + CNT(i - 1, j); 14961 if (t < CNT(i, j)) 14962 CNT(i, j) = t; 14963 t = SCORE_INS + CNT(i, j - 1); 14964 if (t < CNT(i, j)) 14965 CNT(i, j) = t; 14966 } 14967 } 14968 } 14969 14970 i = CNT(badlen - 1, goodlen - 1); 14971 vim_free(cnt); 14972 return i; 14973 } 14974 14975 typedef struct 14976 { 14977 int badi; 14978 int goodi; 14979 int score; 14980 } limitscore_T; 14981 14982 /* 14983 * Like spell_edit_score(), but with a limit on the score to make it faster. 14984 * May return SCORE_MAXMAX when the score is higher than "limit". 14985 * 14986 * This uses a stack for the edits still to be tried. 14987 * The idea comes from Aspell leditdist.cpp. Rewritten in C and added support 14988 * for multi-byte characters. 14989 */ 14990 static int 14991 spell_edit_score_limit(slang, badword, goodword, limit) 14992 slang_T *slang; 14993 char_u *badword; 14994 char_u *goodword; 14995 int limit; 14996 { 14997 limitscore_T stack[10]; /* allow for over 3 * 2 edits */ 14998 int stackidx; 14999 int bi, gi; 15000 int bi2, gi2; 15001 int bc, gc; 15002 int score; 15003 int score_off; 15004 int minscore; 15005 int round; 15006 15007 #ifdef FEAT_MBYTE 15008 /* Multi-byte characters require a bit more work, use a different function 15009 * to avoid testing "has_mbyte" quite often. */ 15010 if (has_mbyte) 15011 return spell_edit_score_limit_w(slang, badword, goodword, limit); 15012 #endif 15013 15014 /* 15015 * The idea is to go from start to end over the words. So long as 15016 * characters are equal just continue, this always gives the lowest score. 15017 * When there is a difference try several alternatives. Each alternative 15018 * increases "score" for the edit distance. Some of the alternatives are 15019 * pushed unto a stack and tried later, some are tried right away. At the 15020 * end of the word the score for one alternative is known. The lowest 15021 * possible score is stored in "minscore". 15022 */ 15023 stackidx = 0; 15024 bi = 0; 15025 gi = 0; 15026 score = 0; 15027 minscore = limit + 1; 15028 15029 for (;;) 15030 { 15031 /* Skip over an equal part, score remains the same. */ 15032 for (;;) 15033 { 15034 bc = badword[bi]; 15035 gc = goodword[gi]; 15036 if (bc != gc) /* stop at a char that's different */ 15037 break; 15038 if (bc == NUL) /* both words end */ 15039 { 15040 if (score < minscore) 15041 minscore = score; 15042 goto pop; /* do next alternative */ 15043 } 15044 ++bi; 15045 ++gi; 15046 } 15047 15048 if (gc == NUL) /* goodword ends, delete badword chars */ 15049 { 15050 do 15051 { 15052 if ((score += SCORE_DEL) >= minscore) 15053 goto pop; /* do next alternative */ 15054 } while (badword[++bi] != NUL); 15055 minscore = score; 15056 } 15057 else if (bc == NUL) /* badword ends, insert badword chars */ 15058 { 15059 do 15060 { 15061 if ((score += SCORE_INS) >= minscore) 15062 goto pop; /* do next alternative */ 15063 } while (goodword[++gi] != NUL); 15064 minscore = score; 15065 } 15066 else /* both words continue */ 15067 { 15068 /* If not close to the limit, perform a change. Only try changes 15069 * that may lead to a lower score than "minscore". 15070 * round 0: try deleting a char from badword 15071 * round 1: try inserting a char in badword */ 15072 for (round = 0; round <= 1; ++round) 15073 { 15074 score_off = score + (round == 0 ? SCORE_DEL : SCORE_INS); 15075 if (score_off < minscore) 15076 { 15077 if (score_off + SCORE_EDIT_MIN >= minscore) 15078 { 15079 /* Near the limit, rest of the words must match. We 15080 * can check that right now, no need to push an item 15081 * onto the stack. */ 15082 bi2 = bi + 1 - round; 15083 gi2 = gi + round; 15084 while (goodword[gi2] == badword[bi2]) 15085 { 15086 if (goodword[gi2] == NUL) 15087 { 15088 minscore = score_off; 15089 break; 15090 } 15091 ++bi2; 15092 ++gi2; 15093 } 15094 } 15095 else 15096 { 15097 /* try deleting/inserting a character later */ 15098 stack[stackidx].badi = bi + 1 - round; 15099 stack[stackidx].goodi = gi + round; 15100 stack[stackidx].score = score_off; 15101 ++stackidx; 15102 } 15103 } 15104 } 15105 15106 if (score + SCORE_SWAP < minscore) 15107 { 15108 /* If swapping two characters makes a match then the 15109 * substitution is more expensive, thus there is no need to 15110 * try both. */ 15111 if (gc == badword[bi + 1] && bc == goodword[gi + 1]) 15112 { 15113 /* Swap two characters, that is: skip them. */ 15114 gi += 2; 15115 bi += 2; 15116 score += SCORE_SWAP; 15117 continue; 15118 } 15119 } 15120 15121 /* Substitute one character for another which is the same 15122 * thing as deleting a character from both goodword and badword. 15123 * Use a better score when there is only a case difference. */ 15124 if (SPELL_TOFOLD(bc) == SPELL_TOFOLD(gc)) 15125 score += SCORE_ICASE; 15126 else 15127 { 15128 /* For a similar character use SCORE_SIMILAR. */ 15129 if (slang != NULL 15130 && slang->sl_has_map 15131 && similar_chars(slang, gc, bc)) 15132 score += SCORE_SIMILAR; 15133 else 15134 score += SCORE_SUBST; 15135 } 15136 15137 if (score < minscore) 15138 { 15139 /* Do the substitution. */ 15140 ++gi; 15141 ++bi; 15142 continue; 15143 } 15144 } 15145 pop: 15146 /* 15147 * Get here to try the next alternative, pop it from the stack. 15148 */ 15149 if (stackidx == 0) /* stack is empty, finished */ 15150 break; 15151 15152 /* pop an item from the stack */ 15153 --stackidx; 15154 gi = stack[stackidx].goodi; 15155 bi = stack[stackidx].badi; 15156 score = stack[stackidx].score; 15157 } 15158 15159 /* When the score goes over "limit" it may actually be much higher. 15160 * Return a very large number to avoid going below the limit when giving a 15161 * bonus. */ 15162 if (minscore > limit) 15163 return SCORE_MAXMAX; 15164 return minscore; 15165 } 15166 15167 #ifdef FEAT_MBYTE 15168 /* 15169 * Multi-byte version of spell_edit_score_limit(). 15170 * Keep it in sync with the above! 15171 */ 15172 static int 15173 spell_edit_score_limit_w(slang, badword, goodword, limit) 15174 slang_T *slang; 15175 char_u *badword; 15176 char_u *goodword; 15177 int limit; 15178 { 15179 limitscore_T stack[10]; /* allow for over 3 * 2 edits */ 15180 int stackidx; 15181 int bi, gi; 15182 int bi2, gi2; 15183 int bc, gc; 15184 int score; 15185 int score_off; 15186 int minscore; 15187 int round; 15188 char_u *p; 15189 int wbadword[MAXWLEN]; 15190 int wgoodword[MAXWLEN]; 15191 15192 /* Get the characters from the multi-byte strings and put them in an 15193 * int array for easy access. */ 15194 bi = 0; 15195 for (p = badword; *p != NUL; ) 15196 wbadword[bi++] = mb_cptr2char_adv(&p); 15197 wbadword[bi++] = 0; 15198 gi = 0; 15199 for (p = goodword; *p != NUL; ) 15200 wgoodword[gi++] = mb_cptr2char_adv(&p); 15201 wgoodword[gi++] = 0; 15202 15203 /* 15204 * The idea is to go from start to end over the words. So long as 15205 * characters are equal just continue, this always gives the lowest score. 15206 * When there is a difference try several alternatives. Each alternative 15207 * increases "score" for the edit distance. Some of the alternatives are 15208 * pushed unto a stack and tried later, some are tried right away. At the 15209 * end of the word the score for one alternative is known. The lowest 15210 * possible score is stored in "minscore". 15211 */ 15212 stackidx = 0; 15213 bi = 0; 15214 gi = 0; 15215 score = 0; 15216 minscore = limit + 1; 15217 15218 for (;;) 15219 { 15220 /* Skip over an equal part, score remains the same. */ 15221 for (;;) 15222 { 15223 bc = wbadword[bi]; 15224 gc = wgoodword[gi]; 15225 15226 if (bc != gc) /* stop at a char that's different */ 15227 break; 15228 if (bc == NUL) /* both words end */ 15229 { 15230 if (score < minscore) 15231 minscore = score; 15232 goto pop; /* do next alternative */ 15233 } 15234 ++bi; 15235 ++gi; 15236 } 15237 15238 if (gc == NUL) /* goodword ends, delete badword chars */ 15239 { 15240 do 15241 { 15242 if ((score += SCORE_DEL) >= minscore) 15243 goto pop; /* do next alternative */ 15244 } while (wbadword[++bi] != NUL); 15245 minscore = score; 15246 } 15247 else if (bc == NUL) /* badword ends, insert badword chars */ 15248 { 15249 do 15250 { 15251 if ((score += SCORE_INS) >= minscore) 15252 goto pop; /* do next alternative */ 15253 } while (wgoodword[++gi] != NUL); 15254 minscore = score; 15255 } 15256 else /* both words continue */ 15257 { 15258 /* If not close to the limit, perform a change. Only try changes 15259 * that may lead to a lower score than "minscore". 15260 * round 0: try deleting a char from badword 15261 * round 1: try inserting a char in badword */ 15262 for (round = 0; round <= 1; ++round) 15263 { 15264 score_off = score + (round == 0 ? SCORE_DEL : SCORE_INS); 15265 if (score_off < minscore) 15266 { 15267 if (score_off + SCORE_EDIT_MIN >= minscore) 15268 { 15269 /* Near the limit, rest of the words must match. We 15270 * can check that right now, no need to push an item 15271 * onto the stack. */ 15272 bi2 = bi + 1 - round; 15273 gi2 = gi + round; 15274 while (wgoodword[gi2] == wbadword[bi2]) 15275 { 15276 if (wgoodword[gi2] == NUL) 15277 { 15278 minscore = score_off; 15279 break; 15280 } 15281 ++bi2; 15282 ++gi2; 15283 } 15284 } 15285 else 15286 { 15287 /* try deleting a character from badword later */ 15288 stack[stackidx].badi = bi + 1 - round; 15289 stack[stackidx].goodi = gi + round; 15290 stack[stackidx].score = score_off; 15291 ++stackidx; 15292 } 15293 } 15294 } 15295 15296 if (score + SCORE_SWAP < minscore) 15297 { 15298 /* If swapping two characters makes a match then the 15299 * substitution is more expensive, thus there is no need to 15300 * try both. */ 15301 if (gc == wbadword[bi + 1] && bc == wgoodword[gi + 1]) 15302 { 15303 /* Swap two characters, that is: skip them. */ 15304 gi += 2; 15305 bi += 2; 15306 score += SCORE_SWAP; 15307 continue; 15308 } 15309 } 15310 15311 /* Substitute one character for another which is the same 15312 * thing as deleting a character from both goodword and badword. 15313 * Use a better score when there is only a case difference. */ 15314 if (SPELL_TOFOLD(bc) == SPELL_TOFOLD(gc)) 15315 score += SCORE_ICASE; 15316 else 15317 { 15318 /* For a similar character use SCORE_SIMILAR. */ 15319 if (slang != NULL 15320 && slang->sl_has_map 15321 && similar_chars(slang, gc, bc)) 15322 score += SCORE_SIMILAR; 15323 else 15324 score += SCORE_SUBST; 15325 } 15326 15327 if (score < minscore) 15328 { 15329 /* Do the substitution. */ 15330 ++gi; 15331 ++bi; 15332 continue; 15333 } 15334 } 15335 pop: 15336 /* 15337 * Get here to try the next alternative, pop it from the stack. 15338 */ 15339 if (stackidx == 0) /* stack is empty, finished */ 15340 break; 15341 15342 /* pop an item from the stack */ 15343 --stackidx; 15344 gi = stack[stackidx].goodi; 15345 bi = stack[stackidx].badi; 15346 score = stack[stackidx].score; 15347 } 15348 15349 /* When the score goes over "limit" it may actually be much higher. 15350 * Return a very large number to avoid going below the limit when giving a 15351 * bonus. */ 15352 if (minscore > limit) 15353 return SCORE_MAXMAX; 15354 return minscore; 15355 } 15356 #endif 15357 15358 /* 15359 * ":spellinfo" 15360 */ 15361 /*ARGSUSED*/ 15362 void 15363 ex_spellinfo(eap) 15364 exarg_T *eap; 15365 { 15366 int lpi; 15367 langp_T *lp; 15368 char_u *p; 15369 15370 if (no_spell_checking(curwin)) 15371 return; 15372 15373 msg_start(); 15374 for (lpi = 0; lpi < curbuf->b_langp.ga_len && !got_int; ++lpi) 15375 { 15376 lp = LANGP_ENTRY(curbuf->b_langp, lpi); 15377 msg_puts((char_u *)"file: "); 15378 msg_puts(lp->lp_slang->sl_fname); 15379 msg_putchar('\n'); 15380 p = lp->lp_slang->sl_info; 15381 if (p != NULL) 15382 { 15383 msg_puts(p); 15384 msg_putchar('\n'); 15385 } 15386 } 15387 msg_end(); 15388 } 15389 15390 #define DUMPFLAG_KEEPCASE 1 /* round 2: keep-case tree */ 15391 #define DUMPFLAG_COUNT 2 /* include word count */ 15392 #define DUMPFLAG_ICASE 4 /* ignore case when finding matches */ 15393 #define DUMPFLAG_ONECAP 8 /* pattern starts with capital */ 15394 #define DUMPFLAG_ALLCAP 16 /* pattern is all capitals */ 15395 15396 /* 15397 * ":spelldump" 15398 */ 15399 void 15400 ex_spelldump(eap) 15401 exarg_T *eap; 15402 { 15403 buf_T *buf = curbuf; 15404 15405 if (no_spell_checking(curwin)) 15406 return; 15407 15408 /* Create a new empty buffer by splitting the window. */ 15409 do_cmdline_cmd((char_u *)"new"); 15410 if (!bufempty() || !buf_valid(buf)) 15411 return; 15412 15413 spell_dump_compl(buf, NULL, 0, NULL, eap->forceit ? DUMPFLAG_COUNT : 0); 15414 15415 /* Delete the empty line that we started with. */ 15416 if (curbuf->b_ml.ml_line_count > 1) 15417 ml_delete(curbuf->b_ml.ml_line_count, FALSE); 15418 15419 redraw_later(NOT_VALID); 15420 } 15421 15422 /* 15423 * Go through all possible words and: 15424 * 1. When "pat" is NULL: dump a list of all words in the current buffer. 15425 * "ic" and "dir" are not used. 15426 * 2. When "pat" is not NULL: add matching words to insert mode completion. 15427 */ 15428 void 15429 spell_dump_compl(buf, pat, ic, dir, dumpflags_arg) 15430 buf_T *buf; /* buffer with spell checking */ 15431 char_u *pat; /* leading part of the word */ 15432 int ic; /* ignore case */ 15433 int *dir; /* direction for adding matches */ 15434 int dumpflags_arg; /* DUMPFLAG_* */ 15435 { 15436 langp_T *lp; 15437 slang_T *slang; 15438 idx_T arridx[MAXWLEN]; 15439 int curi[MAXWLEN]; 15440 char_u word[MAXWLEN]; 15441 int c; 15442 char_u *byts; 15443 idx_T *idxs; 15444 linenr_T lnum = 0; 15445 int round; 15446 int depth; 15447 int n; 15448 int flags; 15449 char_u *region_names = NULL; /* region names being used */ 15450 int do_region = TRUE; /* dump region names and numbers */ 15451 char_u *p; 15452 int lpi; 15453 int dumpflags = dumpflags_arg; 15454 int patlen; 15455 15456 /* When ignoring case or when the pattern starts with capital pass this on 15457 * to dump_word(). */ 15458 if (pat != NULL) 15459 { 15460 if (ic) 15461 dumpflags |= DUMPFLAG_ICASE; 15462 else 15463 { 15464 n = captype(pat, NULL); 15465 if (n == WF_ONECAP) 15466 dumpflags |= DUMPFLAG_ONECAP; 15467 else if (n == WF_ALLCAP 15468 #ifdef FEAT_MBYTE 15469 && (int)STRLEN(pat) > mb_ptr2len(pat) 15470 #else 15471 && (int)STRLEN(pat) > 1 15472 #endif 15473 ) 15474 dumpflags |= DUMPFLAG_ALLCAP; 15475 } 15476 } 15477 15478 /* Find out if we can support regions: All languages must support the same 15479 * regions or none at all. */ 15480 for (lpi = 0; lpi < buf->b_langp.ga_len; ++lpi) 15481 { 15482 lp = LANGP_ENTRY(buf->b_langp, lpi); 15483 p = lp->lp_slang->sl_regions; 15484 if (p[0] != 0) 15485 { 15486 if (region_names == NULL) /* first language with regions */ 15487 region_names = p; 15488 else if (STRCMP(region_names, p) != 0) 15489 { 15490 do_region = FALSE; /* region names are different */ 15491 break; 15492 } 15493 } 15494 } 15495 15496 if (do_region && region_names != NULL) 15497 { 15498 if (pat == NULL) 15499 { 15500 vim_snprintf((char *)IObuff, IOSIZE, "/regions=%s", region_names); 15501 ml_append(lnum++, IObuff, (colnr_T)0, FALSE); 15502 } 15503 } 15504 else 15505 do_region = FALSE; 15506 15507 /* 15508 * Loop over all files loaded for the entries in 'spelllang'. 15509 */ 15510 for (lpi = 0; lpi < buf->b_langp.ga_len; ++lpi) 15511 { 15512 lp = LANGP_ENTRY(buf->b_langp, lpi); 15513 slang = lp->lp_slang; 15514 if (slang->sl_fbyts == NULL) /* reloading failed */ 15515 continue; 15516 15517 if (pat == NULL) 15518 { 15519 vim_snprintf((char *)IObuff, IOSIZE, "# file: %s", slang->sl_fname); 15520 ml_append(lnum++, IObuff, (colnr_T)0, FALSE); 15521 } 15522 15523 /* When matching with a pattern and there are no prefixes only use 15524 * parts of the tree that match "pat". */ 15525 if (pat != NULL && slang->sl_pbyts == NULL) 15526 patlen = (int)STRLEN(pat); 15527 else 15528 patlen = -1; 15529 15530 /* round 1: case-folded tree 15531 * round 2: keep-case tree */ 15532 for (round = 1; round <= 2; ++round) 15533 { 15534 if (round == 1) 15535 { 15536 dumpflags &= ~DUMPFLAG_KEEPCASE; 15537 byts = slang->sl_fbyts; 15538 idxs = slang->sl_fidxs; 15539 } 15540 else 15541 { 15542 dumpflags |= DUMPFLAG_KEEPCASE; 15543 byts = slang->sl_kbyts; 15544 idxs = slang->sl_kidxs; 15545 } 15546 if (byts == NULL) 15547 continue; /* array is empty */ 15548 15549 depth = 0; 15550 arridx[0] = 0; 15551 curi[0] = 1; 15552 while (depth >= 0 && !got_int 15553 && (pat == NULL || !compl_interrupted)) 15554 { 15555 if (curi[depth] > byts[arridx[depth]]) 15556 { 15557 /* Done all bytes at this node, go up one level. */ 15558 --depth; 15559 line_breakcheck(); 15560 ins_compl_check_keys(50); 15561 } 15562 else 15563 { 15564 /* Do one more byte at this node. */ 15565 n = arridx[depth] + curi[depth]; 15566 ++curi[depth]; 15567 c = byts[n]; 15568 if (c == 0) 15569 { 15570 /* End of word, deal with the word. 15571 * Don't use keep-case words in the fold-case tree, 15572 * they will appear in the keep-case tree. 15573 * Only use the word when the region matches. */ 15574 flags = (int)idxs[n]; 15575 if ((round == 2 || (flags & WF_KEEPCAP) == 0) 15576 && (flags & WF_NEEDCOMP) == 0 15577 && (do_region 15578 || (flags & WF_REGION) == 0 15579 || (((unsigned)flags >> 16) 15580 & lp->lp_region) != 0)) 15581 { 15582 word[depth] = NUL; 15583 if (!do_region) 15584 flags &= ~WF_REGION; 15585 15586 /* Dump the basic word if there is no prefix or 15587 * when it's the first one. */ 15588 c = (unsigned)flags >> 24; 15589 if (c == 0 || curi[depth] == 2) 15590 { 15591 dump_word(slang, word, pat, dir, 15592 dumpflags, flags, lnum); 15593 if (pat == NULL) 15594 ++lnum; 15595 } 15596 15597 /* Apply the prefix, if there is one. */ 15598 if (c != 0) 15599 lnum = dump_prefixes(slang, word, pat, dir, 15600 dumpflags, flags, lnum); 15601 } 15602 } 15603 else 15604 { 15605 /* Normal char, go one level deeper. */ 15606 word[depth++] = c; 15607 arridx[depth] = idxs[n]; 15608 curi[depth] = 1; 15609 15610 /* Check if this characters matches with the pattern. 15611 * If not skip the whole tree below it. 15612 * Always ignore case here, dump_word() will check 15613 * proper case later. This isn't exactly right when 15614 * length changes for multi-byte characters with 15615 * ignore case... */ 15616 if (depth <= patlen 15617 && MB_STRNICMP(word, pat, depth) != 0) 15618 --depth; 15619 } 15620 } 15621 } 15622 } 15623 } 15624 } 15625 15626 /* 15627 * Dump one word: apply case modifications and append a line to the buffer. 15628 * When "lnum" is zero add insert mode completion. 15629 */ 15630 static void 15631 dump_word(slang, word, pat, dir, dumpflags, wordflags, lnum) 15632 slang_T *slang; 15633 char_u *word; 15634 char_u *pat; 15635 int *dir; 15636 int dumpflags; 15637 int wordflags; 15638 linenr_T lnum; 15639 { 15640 int keepcap = FALSE; 15641 char_u *p; 15642 char_u *tw; 15643 char_u cword[MAXWLEN]; 15644 char_u badword[MAXWLEN + 10]; 15645 int i; 15646 int flags = wordflags; 15647 15648 if (dumpflags & DUMPFLAG_ONECAP) 15649 flags |= WF_ONECAP; 15650 if (dumpflags & DUMPFLAG_ALLCAP) 15651 flags |= WF_ALLCAP; 15652 15653 if ((dumpflags & DUMPFLAG_KEEPCASE) == 0 && (flags & WF_CAPMASK) != 0) 15654 { 15655 /* Need to fix case according to "flags". */ 15656 make_case_word(word, cword, flags); 15657 p = cword; 15658 } 15659 else 15660 { 15661 p = word; 15662 if ((dumpflags & DUMPFLAG_KEEPCASE) 15663 && ((captype(word, NULL) & WF_KEEPCAP) == 0 15664 || (flags & WF_FIXCAP) != 0)) 15665 keepcap = TRUE; 15666 } 15667 tw = p; 15668 15669 if (pat == NULL) 15670 { 15671 /* Add flags and regions after a slash. */ 15672 if ((flags & (WF_BANNED | WF_RARE | WF_REGION)) || keepcap) 15673 { 15674 STRCPY(badword, p); 15675 STRCAT(badword, "/"); 15676 if (keepcap) 15677 STRCAT(badword, "="); 15678 if (flags & WF_BANNED) 15679 STRCAT(badword, "!"); 15680 else if (flags & WF_RARE) 15681 STRCAT(badword, "?"); 15682 if (flags & WF_REGION) 15683 for (i = 0; i < 7; ++i) 15684 if (flags & (0x10000 << i)) 15685 sprintf((char *)badword + STRLEN(badword), "%d", i + 1); 15686 p = badword; 15687 } 15688 15689 if (dumpflags & DUMPFLAG_COUNT) 15690 { 15691 hashitem_T *hi; 15692 15693 /* Include the word count for ":spelldump!". */ 15694 hi = hash_find(&slang->sl_wordcount, tw); 15695 if (!HASHITEM_EMPTY(hi)) 15696 { 15697 vim_snprintf((char *)IObuff, IOSIZE, "%s\t%d", 15698 tw, HI2WC(hi)->wc_count); 15699 p = IObuff; 15700 } 15701 } 15702 15703 ml_append(lnum, p, (colnr_T)0, FALSE); 15704 } 15705 else if (((dumpflags & DUMPFLAG_ICASE) 15706 ? MB_STRNICMP(p, pat, STRLEN(pat)) == 0 15707 : STRNCMP(p, pat, STRLEN(pat)) == 0) 15708 && ins_compl_add_infercase(p, (int)STRLEN(p), 15709 p_ic, NULL, *dir, 0) == OK) 15710 /* if dir was BACKWARD then honor it just once */ 15711 *dir = FORWARD; 15712 } 15713 15714 /* 15715 * For ":spelldump": Find matching prefixes for "word". Prepend each to 15716 * "word" and append a line to the buffer. 15717 * When "lnum" is zero add insert mode completion. 15718 * Return the updated line number. 15719 */ 15720 static linenr_T 15721 dump_prefixes(slang, word, pat, dir, dumpflags, flags, startlnum) 15722 slang_T *slang; 15723 char_u *word; /* case-folded word */ 15724 char_u *pat; 15725 int *dir; 15726 int dumpflags; 15727 int flags; /* flags with prefix ID */ 15728 linenr_T startlnum; 15729 { 15730 idx_T arridx[MAXWLEN]; 15731 int curi[MAXWLEN]; 15732 char_u prefix[MAXWLEN]; 15733 char_u word_up[MAXWLEN]; 15734 int has_word_up = FALSE; 15735 int c; 15736 char_u *byts; 15737 idx_T *idxs; 15738 linenr_T lnum = startlnum; 15739 int depth; 15740 int n; 15741 int len; 15742 int i; 15743 15744 /* If the word starts with a lower-case letter make the word with an 15745 * upper-case letter in word_up[]. */ 15746 c = PTR2CHAR(word); 15747 if (SPELL_TOUPPER(c) != c) 15748 { 15749 onecap_copy(word, word_up, TRUE); 15750 has_word_up = TRUE; 15751 } 15752 15753 byts = slang->sl_pbyts; 15754 idxs = slang->sl_pidxs; 15755 if (byts != NULL) /* array not is empty */ 15756 { 15757 /* 15758 * Loop over all prefixes, building them byte-by-byte in prefix[]. 15759 * When at the end of a prefix check that it supports "flags". 15760 */ 15761 depth = 0; 15762 arridx[0] = 0; 15763 curi[0] = 1; 15764 while (depth >= 0 && !got_int) 15765 { 15766 n = arridx[depth]; 15767 len = byts[n]; 15768 if (curi[depth] > len) 15769 { 15770 /* Done all bytes at this node, go up one level. */ 15771 --depth; 15772 line_breakcheck(); 15773 } 15774 else 15775 { 15776 /* Do one more byte at this node. */ 15777 n += curi[depth]; 15778 ++curi[depth]; 15779 c = byts[n]; 15780 if (c == 0) 15781 { 15782 /* End of prefix, find out how many IDs there are. */ 15783 for (i = 1; i < len; ++i) 15784 if (byts[n + i] != 0) 15785 break; 15786 curi[depth] += i - 1; 15787 15788 c = valid_word_prefix(i, n, flags, word, slang, FALSE); 15789 if (c != 0) 15790 { 15791 vim_strncpy(prefix + depth, word, MAXWLEN - depth - 1); 15792 dump_word(slang, prefix, pat, dir, dumpflags, 15793 (c & WF_RAREPFX) ? (flags | WF_RARE) 15794 : flags, lnum); 15795 if (lnum != 0) 15796 ++lnum; 15797 } 15798 15799 /* Check for prefix that matches the word when the 15800 * first letter is upper-case, but only if the prefix has 15801 * a condition. */ 15802 if (has_word_up) 15803 { 15804 c = valid_word_prefix(i, n, flags, word_up, slang, 15805 TRUE); 15806 if (c != 0) 15807 { 15808 vim_strncpy(prefix + depth, word_up, 15809 MAXWLEN - depth - 1); 15810 dump_word(slang, prefix, pat, dir, dumpflags, 15811 (c & WF_RAREPFX) ? (flags | WF_RARE) 15812 : flags, lnum); 15813 if (lnum != 0) 15814 ++lnum; 15815 } 15816 } 15817 } 15818 else 15819 { 15820 /* Normal char, go one level deeper. */ 15821 prefix[depth++] = c; 15822 arridx[depth] = idxs[n]; 15823 curi[depth] = 1; 15824 } 15825 } 15826 } 15827 } 15828 15829 return lnum; 15830 } 15831 15832 /* 15833 * Move "p" to the end of word "start". 15834 * Uses the spell-checking word characters. 15835 */ 15836 char_u * 15837 spell_to_word_end(start, buf) 15838 char_u *start; 15839 buf_T *buf; 15840 { 15841 char_u *p = start; 15842 15843 while (*p != NUL && spell_iswordp(p, buf)) 15844 mb_ptr_adv(p); 15845 return p; 15846 } 15847 15848 #if defined(FEAT_INS_EXPAND) || defined(PROTO) 15849 /* 15850 * For Insert mode completion CTRL-X s: 15851 * Find start of the word in front of column "startcol". 15852 * We don't check if it is badly spelled, with completion we can only change 15853 * the word in front of the cursor. 15854 * Returns the column number of the word. 15855 */ 15856 int 15857 spell_word_start(startcol) 15858 int startcol; 15859 { 15860 char_u *line; 15861 char_u *p; 15862 int col = 0; 15863 15864 if (no_spell_checking(curwin)) 15865 return startcol; 15866 15867 /* Find a word character before "startcol". */ 15868 line = ml_get_curline(); 15869 for (p = line + startcol; p > line; ) 15870 { 15871 mb_ptr_back(line, p); 15872 if (spell_iswordp_nmw(p)) 15873 break; 15874 } 15875 15876 /* Go back to start of the word. */ 15877 while (p > line) 15878 { 15879 col = (int)(p - line); 15880 mb_ptr_back(line, p); 15881 if (!spell_iswordp(p, curbuf)) 15882 break; 15883 col = 0; 15884 } 15885 15886 return col; 15887 } 15888 15889 /* 15890 * Need to check for 'spellcapcheck' now, the word is removed before 15891 * expand_spelling() is called. Therefore the ugly global variable. 15892 */ 15893 static int spell_expand_need_cap; 15894 15895 void 15896 spell_expand_check_cap(col) 15897 colnr_T col; 15898 { 15899 spell_expand_need_cap = check_need_cap(curwin->w_cursor.lnum, col); 15900 } 15901 15902 /* 15903 * Get list of spelling suggestions. 15904 * Used for Insert mode completion CTRL-X ?. 15905 * Returns the number of matches. The matches are in "matchp[]", array of 15906 * allocated strings. 15907 */ 15908 /*ARGSUSED*/ 15909 int 15910 expand_spelling(lnum, col, pat, matchp) 15911 linenr_T lnum; 15912 int col; 15913 char_u *pat; 15914 char_u ***matchp; 15915 { 15916 garray_T ga; 15917 15918 spell_suggest_list(&ga, pat, 100, spell_expand_need_cap, TRUE); 15919 *matchp = ga.ga_data; 15920 return ga.ga_len; 15921 } 15922 #endif 15923 15924 #endif /* FEAT_SPELL */ 15925