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 * 47 * Matching involves checking the caps type: Onecap ALLCAP KeepCap. 48 * 49 * Why doesn't Vim use aspell/ispell/myspell/etc.? 50 * See ":help develop-spell". 51 */ 52 53 /* Use SPELL_PRINTTREE for debugging: dump the word tree after adding a word. 54 * Only use it for small word lists! */ 55 #if 0 56 # define SPELL_PRINTTREE 57 #endif 58 59 /* 60 * Use this to adjust the score after finding suggestions, based on the 61 * suggested word sounding like the bad word. This is much faster than doing 62 * it for every possible suggestion. 63 * Disadvantage: When "the" is typed as "hte" it sounds different and goes 64 * down in the list. 65 * Used when 'spellsuggest' is set to "best". 66 */ 67 #define RESCORE(word_score, sound_score) ((3 * word_score + sound_score) / 4) 68 69 /* 70 * Vim spell file format: <HEADER> 71 * <SECTIONS> 72 * <LWORDTREE> 73 * <KWORDTREE> 74 * <PREFIXTREE> 75 * 76 * <HEADER>: <fileID> <versionnr> 77 * 78 * <fileID> 8 bytes "VIMspell" 79 * <versionnr> 1 byte VIMSPELLVERSION 80 * 81 * 82 * Sections make it possible to add information to the .spl file without 83 * making it incompatible with previous versions. There are two kinds of 84 * sections: 85 * 1. Not essential for correct spell checking. E.g. for making suggestions. 86 * These are skipped when not supported. 87 * 2. Optional information, but essential for spell checking when present. 88 * E.g. conditions for affixes. When this section is present but not 89 * supported an error message is given. 90 * 91 * <SECTIONS>: <section> ... <sectionend> 92 * 93 * <section>: <sectionID> <sectionflags> <sectionlen> (section contents) 94 * 95 * <sectionID> 1 byte number from 0 to 254 identifying the section 96 * 97 * <sectionflags> 1 byte SNF_REQUIRED: this section is required for correct 98 * spell checking 99 * 100 * <sectionlen> 4 bytes length of section contents, MSB first 101 * 102 * <sectionend> 1 byte SN_END 103 * 104 * 105 * sectionID == SN_REGION: <regionname> ... 106 * <regionname> 2 bytes Up to 8 region names: ca, au, etc. Lower case. 107 * First <regionname> is region 1. 108 * 109 * sectionID == SN_CHARFLAGS: <charflagslen> <charflags> 110 * <folcharslen> <folchars> 111 * <charflagslen> 1 byte Number of bytes in <charflags> (should be 128). 112 * <charflags> N bytes List of flags (first one is for character 128): 113 * 0x01 word character CF_WORD 114 * 0x02 upper-case character CF_UPPER 115 * <folcharslen> 2 bytes Number of bytes in <folchars>. 116 * <folchars> N bytes Folded characters, first one is for character 128. 117 * 118 * sectionID == SN_MIDWORD: <midword> 119 * <midword> N bytes Characters that are word characters only when used 120 * in the middle of a word. 121 * 122 * sectionID == SN_PREFCOND: <prefcondcnt> <prefcond> ... 123 * <prefcondcnt> 2 bytes Number of <prefcond> items following. 124 * <prefcond> : <condlen> <condstr> 125 * <condlen> 1 byte Length of <condstr>. 126 * <condstr> N bytes Condition for the prefix. 127 * 128 * sectionID == SN_REP: <repcount> <rep> ... 129 * <repcount> 2 bytes number of <rep> items, MSB first. 130 * <rep> : <repfromlen> <repfrom> <reptolen> <repto> 131 * <repfromlen> 1 byte length of <repfrom> 132 * <repfrom> N bytes "from" part of replacement 133 * <reptolen> 1 byte length of <repto> 134 * <repto> N bytes "to" part of replacement 135 * 136 * sectionID == SN_SAL: <salflags> <salcount> <sal> ... 137 * <salflags> 1 byte flags for soundsalike conversion: 138 * SAL_F0LLOWUP 139 * SAL_COLLAPSE 140 * SAL_REM_ACCENTS 141 * <salcount> 2 bytes number of <sal> items following 142 * <sal> : <salfromlen> <salfrom> <saltolen> <salto> 143 * <salfromlen> 1 byte length of <salfrom> 144 * <salfrom> N bytes "from" part of soundsalike 145 * <saltolen> 1 byte length of <salto> 146 * <salto> N bytes "to" part of soundsalike 147 * 148 * sectionID == SN_SOFO: <sofofromlen> <sofofrom> <sofotolen> <sofoto> 149 * <sofofromlen> 2 bytes length of <sofofrom> 150 * <sofofrom> N bytes "from" part of soundfold 151 * <sofotolen> 2 bytes length of <sofoto> 152 * <sofoto> N bytes "to" part of soundfold 153 * 154 * sectionID == SN_MAP: <mapstr> 155 * <mapstr> N bytes String with sequences of similar characters, 156 * separated by slashes. 157 * 158 * sectionID == SN_COMPOUND: <compmax> <compminlen> <compsylmax> <compflags> 159 * <compmax> 1 byte Maximum nr of words in compound word. 160 * <compminlen> 1 byte Minimal word length for compounding. 161 * <compsylmax> 1 byte Maximum nr of syllables in compound word. 162 * <compflags> N bytes Flags from COMPOUNDFLAGS items, separated by 163 * slashes. 164 * 165 * sectionID == SN_NOBREAK: (empty, its presence is enough) 166 * 167 * sectionID == SN_SYLLABLE: <syllable> 168 * <syllable> N bytes String from SYLLABLE item. 169 * 170 * <LWORDTREE>: <wordtree> 171 * 172 * <KWORDTREE>: <wordtree> 173 * 174 * <PREFIXTREE>: <wordtree> 175 * 176 * 177 * <wordtree>: <nodecount> <nodedata> ... 178 * 179 * <nodecount> 4 bytes Number of nodes following. MSB first. 180 * 181 * <nodedata>: <siblingcount> <sibling> ... 182 * 183 * <siblingcount> 1 byte Number of siblings in this node. The siblings 184 * follow in sorted order. 185 * 186 * <sibling>: <byte> [ <nodeidx> <xbyte> 187 * | <flags> [<flags2>] [<region>] [<affixID>] 188 * | [<pflags>] <affixID> <prefcondnr> ] 189 * 190 * <byte> 1 byte Byte value of the sibling. Special cases: 191 * BY_NOFLAGS: End of word without flags and for all 192 * regions. 193 * For PREFIXTREE <affixID> and 194 * <prefcondnr> follow. 195 * BY_FLAGS: End of word, <flags> follow. 196 * For PREFIXTREE <pflags>, <affixID> 197 * and <prefcondnr> follow. 198 * BY_FLAGS2: End of word, <flags> and <flags2> 199 * follow. Not used in PREFIXTREE. 200 * BY_INDEX: Child of sibling is shared, <nodeidx> 201 * and <xbyte> follow. 202 * 203 * <nodeidx> 3 bytes Index of child for this sibling, MSB first. 204 * 205 * <xbyte> 1 byte byte value of the sibling. 206 * 207 * <flags> 1 byte bitmask of: 208 * WF_ALLCAP word must have only capitals 209 * WF_ONECAP first char of word must be capital 210 * WF_KEEPCAP keep-case word 211 * WF_FIXCAP keep-case word, all caps not allowed 212 * WF_RARE rare word 213 * WF_BANNED bad word 214 * WF_REGION <region> follows 215 * WF_AFX <affixID> follows 216 * 217 * <flags2> 1 byte Bitmask of: 218 * WF_HAS_AFF >> 8 word includes affix 219 * WF_NEEDCOMP >> 8 word only valid in compound 220 * 221 * <pflags> 1 byte bitmask of: 222 * WFP_RARE rare prefix 223 * WFP_NC non-combining prefix 224 * WFP_UP letter after prefix made upper case 225 * 226 * <region> 1 byte Bitmask for regions in which word is valid. When 227 * omitted it's valid in all regions. 228 * Lowest bit is for region 1. 229 * 230 * <affixID> 1 byte ID of affix that can be used with this word. In 231 * PREFIXTREE used for the required prefix ID. 232 * 233 * <prefcondnr> 2 bytes Prefix condition number, index in <prefcond> list 234 * from HEADER. 235 * 236 * All text characters are in 'encoding', but stored as single bytes. 237 */ 238 239 #if defined(MSDOS) || defined(WIN16) || defined(WIN32) || defined(_WIN64) 240 # include <io.h> /* for lseek(), must be before vim.h */ 241 #endif 242 243 #include "vim.h" 244 245 #if defined(FEAT_SYN_HL) || defined(PROTO) 246 247 #ifdef HAVE_FCNTL_H 248 # include <fcntl.h> 249 #endif 250 251 #define MAXWLEN 250 /* Assume max. word len is this many bytes. 252 Some places assume a word length fits in a 253 byte, thus it can't be above 255. */ 254 255 /* Type used for indexes in the word tree need to be at least 4 bytes. If int 256 * is 8 bytes we could use something smaller, but what? */ 257 #if SIZEOF_INT > 3 258 typedef int idx_T; 259 #else 260 typedef long idx_T; 261 #endif 262 263 /* Flags used for a word. Only the lowest byte can be used, the region byte 264 * comes above it. */ 265 #define WF_REGION 0x01 /* region byte follows */ 266 #define WF_ONECAP 0x02 /* word with one capital (or all capitals) */ 267 #define WF_ALLCAP 0x04 /* word must be all capitals */ 268 #define WF_RARE 0x08 /* rare word */ 269 #define WF_BANNED 0x10 /* bad word */ 270 #define WF_AFX 0x20 /* affix ID follows */ 271 #define WF_FIXCAP 0x40 /* keep-case word, allcap not allowed */ 272 #define WF_KEEPCAP 0x80 /* keep-case word */ 273 274 /* for <flags2>, shifted up one byte to be used in wn_flags */ 275 #define WF_HAS_AFF 0x0100 /* word includes affix */ 276 #define WF_NEEDCOMP 0x0200 /* word only valid in compound */ 277 278 #define WF_CAPMASK (WF_ONECAP | WF_ALLCAP | WF_KEEPCAP | WF_FIXCAP) 279 280 /* flags for <pflags> */ 281 #define WFP_RARE 0x01 /* rare prefix */ 282 #define WFP_NC 0x02 /* prefix is not combining */ 283 #define WFP_UP 0x04 /* to-upper prefix */ 284 285 /* Flags for postponed prefixes. Must be above affixID (one byte) 286 * and prefcondnr (two bytes). */ 287 #define WF_RAREPFX (WFP_RARE << 24) /* in sl_pidxs: flag for rare 288 * postponed prefix */ 289 #define WF_PFX_NC (WFP_NC << 24) /* in sl_pidxs: flag for non-combining 290 * postponed prefix */ 291 #define WF_PFX_UP (WFP_UP << 24) /* in sl_pidxs: flag for to-upper 292 * postponed prefix */ 293 294 /* Special byte values for <byte>. Some are only used in the tree for 295 * postponed prefixes, some only in the other trees. This is a bit messy... */ 296 #define BY_NOFLAGS 0 /* end of word without flags or region; for 297 * postponed prefix: no <pflags> */ 298 #define BY_INDEX 1 /* child is shared, index follows */ 299 #define BY_FLAGS 2 /* end of word, <flags> byte follows; for 300 * postponed prefix: <pflags> follows */ 301 #define BY_FLAGS2 3 /* end of word, <flags> and <flags2> bytes 302 * follow; never used in prefix tree */ 303 #define BY_SPECIAL BY_FLAGS2 /* highest special byte value */ 304 305 /* Info from "REP" and "SAL" entries in ".aff" file used in si_rep, sl_rep, 306 * and si_sal. Not for sl_sal! 307 * One replacement: from "ft_from" to "ft_to". */ 308 typedef struct fromto_S 309 { 310 char_u *ft_from; 311 char_u *ft_to; 312 } fromto_T; 313 314 /* Info from "SAL" entries in ".aff" file used in sl_sal. 315 * The info is split for quick processing by spell_soundfold(). 316 * Note that "sm_oneof" and "sm_rules" point into sm_lead. */ 317 typedef struct salitem_S 318 { 319 char_u *sm_lead; /* leading letters */ 320 int sm_leadlen; /* length of "sm_lead" */ 321 char_u *sm_oneof; /* letters from () or NULL */ 322 char_u *sm_rules; /* rules like ^, $, priority */ 323 char_u *sm_to; /* replacement. */ 324 #ifdef FEAT_MBYTE 325 int *sm_lead_w; /* wide character copy of "sm_lead" */ 326 int *sm_oneof_w; /* wide character copy of "sm_oneof" */ 327 int *sm_to_w; /* wide character copy of "sm_to" */ 328 #endif 329 } salitem_T; 330 331 #ifdef FEAT_MBYTE 332 typedef int salfirst_T; 333 #else 334 typedef short salfirst_T; 335 #endif 336 337 /* Values for SP_*ERROR are negative, positive values are used by 338 * read_cnt_string(). */ 339 #define SP_TRUNCERROR -1 /* spell file truncated error */ 340 #define SP_FORMERROR -2 /* format error in spell file */ 341 #define SP_OTHERERROR -3 /* other error while reading spell file */ 342 343 /* 344 * Structure used to store words and other info for one language, loaded from 345 * a .spl file. 346 * The main access is through the tree in "sl_fbyts/sl_fidxs", storing the 347 * case-folded words. "sl_kbyts/sl_kidxs" is for keep-case words. 348 * 349 * The "byts" array stores the possible bytes in each tree node, preceded by 350 * the number of possible bytes, sorted on byte value: 351 * <len> <byte1> <byte2> ... 352 * The "idxs" array stores the index of the child node corresponding to the 353 * byte in "byts". 354 * Exception: when the byte is zero, the word may end here and "idxs" holds 355 * the flags, region mask and affixID for the word. There may be several 356 * zeros in sequence for alternative flag/region/affixID combinations. 357 */ 358 typedef struct slang_S slang_T; 359 struct slang_S 360 { 361 slang_T *sl_next; /* next language */ 362 char_u *sl_name; /* language name "en", "en.rare", "nl", etc. */ 363 char_u *sl_fname; /* name of .spl file */ 364 int sl_add; /* TRUE if it's a .add file. */ 365 366 char_u *sl_fbyts; /* case-folded word bytes */ 367 idx_T *sl_fidxs; /* case-folded word indexes */ 368 char_u *sl_kbyts; /* keep-case word bytes */ 369 idx_T *sl_kidxs; /* keep-case word indexes */ 370 char_u *sl_pbyts; /* prefix tree word bytes */ 371 idx_T *sl_pidxs; /* prefix tree word indexes */ 372 373 char_u sl_regions[17]; /* table with up to 8 region names plus NUL */ 374 375 char_u *sl_midword; /* MIDWORD string or NULL */ 376 377 int sl_compmax; /* COMPOUNDMAX (default: MAXWLEN) */ 378 int sl_compminlen; /* COMPOUNDMIN (default: 0) */ 379 int sl_compsylmax; /* COMPOUNDSYLMAX (default: MAXWLEN) */ 380 regprog_T *sl_compprog; /* COMPOUNDFLAGS turned into a regexp progrm 381 * (NULL when no compounding) */ 382 char_u *sl_compstartflags; /* flags for first compound word */ 383 char_u *sl_compallflags; /* all flags for compound words */ 384 char_u sl_nobreak; /* When TRUE: no spaces between words */ 385 char_u *sl_syllable; /* SYLLABLE repeatable chars or NULL */ 386 garray_T sl_syl_items; /* syllable items */ 387 388 int sl_prefixcnt; /* number of items in "sl_prefprog" */ 389 regprog_T **sl_prefprog; /* table with regprogs for prefixes */ 390 391 garray_T sl_rep; /* list of fromto_T entries from REP lines */ 392 short sl_rep_first[256]; /* indexes where byte first appears, -1 if 393 there is none */ 394 garray_T sl_sal; /* list of salitem_T entries from SAL lines */ 395 salfirst_T sl_sal_first[256]; /* indexes where byte first appears, -1 if 396 there is none */ 397 int sl_sofo; /* SOFOFROM and SOFOTO instead of SAL items: 398 * "sl_sal_first" maps chars, when has_mbyte 399 * "sl_sal" is a list of wide char lists. */ 400 int sl_followup; /* SAL followup */ 401 int sl_collapse; /* SAL collapse_result */ 402 int sl_rem_accents; /* SAL remove_accents */ 403 int sl_has_map; /* TRUE if there is a MAP line */ 404 #ifdef FEAT_MBYTE 405 hashtab_T sl_map_hash; /* MAP for multi-byte chars */ 406 int sl_map_array[256]; /* MAP for first 256 chars */ 407 #else 408 char_u sl_map_array[256]; /* MAP for first 256 chars */ 409 #endif 410 }; 411 412 /* First language that is loaded, start of the linked list of loaded 413 * languages. */ 414 static slang_T *first_lang = NULL; 415 416 /* Flags used in .spl file for soundsalike flags. */ 417 #define SAL_F0LLOWUP 1 418 #define SAL_COLLAPSE 2 419 #define SAL_REM_ACCENTS 4 420 421 /* 422 * Structure used in "b_langp", filled from 'spelllang'. 423 */ 424 typedef struct langp_S 425 { 426 slang_T *lp_slang; /* info for this language */ 427 slang_T *lp_sallang; /* language used for sound folding or NULL */ 428 slang_T *lp_replang; /* language used for REP items or NULL */ 429 int lp_region; /* bitmask for region or REGION_ALL */ 430 } langp_T; 431 432 #define LANGP_ENTRY(ga, i) (((langp_T *)(ga).ga_data) + (i)) 433 434 #define REGION_ALL 0xff /* word valid in all regions */ 435 436 #define VIMSPELLMAGIC "VIMspell" /* string at start of Vim spell file */ 437 #define VIMSPELLMAGICL 8 438 #define VIMSPELLVERSION 50 439 440 /* Section IDs. Only renumber them when VIMSPELLVERSION changes! */ 441 #define SN_REGION 0 /* <regionname> section */ 442 #define SN_CHARFLAGS 1 /* charflags section */ 443 #define SN_MIDWORD 2 /* <midword> section */ 444 #define SN_PREFCOND 3 /* <prefcond> section */ 445 #define SN_REP 4 /* REP items section */ 446 #define SN_SAL 5 /* SAL items section */ 447 #define SN_SOFO 6 /* soundfolding section */ 448 #define SN_MAP 7 /* MAP items section */ 449 #define SN_COMPOUND 8 /* compound words section */ 450 #define SN_SYLLABLE 9 /* syllable section */ 451 #define SN_NOBREAK 10 /* NOBREAK section */ 452 #define SN_END 255 /* end of sections */ 453 454 #define SNF_REQUIRED 1 /* <sectionflags>: required section */ 455 456 /* Result values. Lower number is accepted over higher one. */ 457 #define SP_BANNED -1 458 #define SP_OK 0 459 #define SP_RARE 1 460 #define SP_LOCAL 2 461 #define SP_BAD 3 462 463 /* file used for "zG" and "zW" */ 464 static char_u *int_wordlist = NULL; 465 466 /* 467 * Information used when looking for suggestions. 468 */ 469 typedef struct suginfo_S 470 { 471 garray_T su_ga; /* suggestions, contains "suggest_T" */ 472 int su_maxcount; /* max. number of suggestions displayed */ 473 int su_maxscore; /* maximum score for adding to su_ga */ 474 garray_T su_sga; /* like su_ga, sound-folded scoring */ 475 char_u *su_badptr; /* start of bad word in line */ 476 int su_badlen; /* length of detected bad word in line */ 477 int su_badflags; /* caps flags for bad word */ 478 char_u su_badword[MAXWLEN]; /* bad word truncated at su_badlen */ 479 char_u su_fbadword[MAXWLEN]; /* su_badword case-folded */ 480 char_u su_sal_badword[MAXWLEN]; /* su_badword soundfolded */ 481 slang_T *su_slang_first; /* slang_T used for su_sal_badword */ 482 hashtab_T su_banned; /* table with banned words */ 483 slang_T *su_sallang; /* default language for sound folding */ 484 } suginfo_T; 485 486 /* One word suggestion. Used in "si_ga". */ 487 typedef struct suggest_S 488 { 489 char_u *st_word; /* suggested word, allocated string */ 490 int st_orglen; /* length of replaced text */ 491 int st_score; /* lower is better */ 492 int st_altscore; /* used when st_score compares equal */ 493 int st_salscore; /* st_score is for soundalike */ 494 int st_had_bonus; /* bonus already included in score */ 495 slang_T *st_slang; /* language used for sound folding */ 496 } suggest_T; 497 498 #define SUG(ga, i) (((suggest_T *)(ga).ga_data)[i]) 499 500 /* Number of suggestions kept when cleaning up. When rescore_suggestions() is 501 * called the score may change, thus we need to keep more than what is 502 * displayed. */ 503 #define SUG_CLEAN_COUNT(su) ((su)->su_maxcount < 50 ? 50 : (su)->su_maxcount) 504 505 /* Threshold for sorting and cleaning up suggestions. Don't want to keep lots 506 * of suggestions that are not going to be displayed. */ 507 #define SUG_MAX_COUNT(su) ((su)->su_maxcount + 50) 508 509 /* score for various changes */ 510 #define SCORE_SPLIT 149 /* split bad word */ 511 #define SCORE_ICASE 52 /* slightly different case */ 512 #define SCORE_REGION 200 /* word is for different region */ 513 #define SCORE_RARE 180 /* rare word */ 514 #define SCORE_SWAP 90 /* swap two characters */ 515 #define SCORE_SWAP3 110 /* swap two characters in three */ 516 #define SCORE_REP 65 /* REP replacement */ 517 #define SCORE_SUBST 93 /* substitute a character */ 518 #define SCORE_SIMILAR 33 /* substitute a similar character */ 519 #define SCORE_SUBCOMP 33 /* substitute a composing character */ 520 #define SCORE_DEL 94 /* delete a character */ 521 #define SCORE_DELDUP 66 /* delete a duplicated character */ 522 #define SCORE_DELCOMP 28 /* delete a composing character */ 523 #define SCORE_INS 96 /* insert a character */ 524 #define SCORE_INSDUP 67 /* insert a duplicate character */ 525 #define SCORE_INSCOMP 30 /* insert a composing character */ 526 #define SCORE_NONWORD 103 /* change non-word to word char */ 527 528 #define SCORE_FILE 30 /* suggestion from a file */ 529 #define SCORE_MAXINIT 350 /* Initial maximum score: higher == slower. 530 * 350 allows for about three changes. */ 531 532 #define SCORE_BIG SCORE_INS * 3 /* big difference */ 533 #define SCORE_MAXMAX 999999 /* accept any score */ 534 535 /* 536 * Structure to store info for word matching. 537 */ 538 typedef struct matchinf_S 539 { 540 langp_T *mi_lp; /* info for language and region */ 541 542 /* pointers to original text to be checked */ 543 char_u *mi_word; /* start of word being checked */ 544 char_u *mi_end; /* end of matching word so far */ 545 char_u *mi_fend; /* next char to be added to mi_fword */ 546 char_u *mi_cend; /* char after what was used for 547 mi_capflags */ 548 549 /* case-folded text */ 550 char_u mi_fword[MAXWLEN + 1]; /* mi_word case-folded */ 551 int mi_fwordlen; /* nr of valid bytes in mi_fword */ 552 553 /* for when checking word after a prefix */ 554 int mi_prefarridx; /* index in sl_pidxs with list of 555 affixID/condition */ 556 int mi_prefcnt; /* number of entries at mi_prefarridx */ 557 int mi_prefixlen; /* byte length of prefix */ 558 #ifdef FEAT_MBYTE 559 int mi_cprefixlen; /* byte length of prefix in original 560 case */ 561 #else 562 # define mi_cprefixlen mi_prefixlen /* it's the same value */ 563 #endif 564 565 /* for when checking a compound word */ 566 int mi_compoff; /* start of following word offset */ 567 char_u mi_compflags[MAXWLEN]; /* flags for compound words used */ 568 int mi_complen; /* nr of compound words used */ 569 570 /* others */ 571 int mi_result; /* result so far: SP_BAD, SP_OK, etc. */ 572 int mi_capflags; /* WF_ONECAP WF_ALLCAP WF_KEEPCAP */ 573 buf_T *mi_buf; /* buffer being checked */ 574 575 /* for NOBREAK */ 576 int mi_result2; /* "mi_resul" without following word */ 577 char_u *mi_end2; /* "mi_end" without following word */ 578 } matchinf_T; 579 580 /* 581 * The tables used for recognizing word characters according to spelling. 582 * These are only used for the first 256 characters of 'encoding'. 583 */ 584 typedef struct spelltab_S 585 { 586 char_u st_isw[256]; /* flags: is word char */ 587 char_u st_isu[256]; /* flags: is uppercase char */ 588 char_u st_fold[256]; /* chars: folded case */ 589 char_u st_upper[256]; /* chars: upper case */ 590 } spelltab_T; 591 592 static spelltab_T spelltab; 593 static int did_set_spelltab; 594 595 #define CF_WORD 0x01 596 #define CF_UPPER 0x02 597 598 static void clear_spell_chartab __ARGS((spelltab_T *sp)); 599 static int set_spell_finish __ARGS((spelltab_T *new_st)); 600 static int spell_iswordp __ARGS((char_u *p, buf_T *buf)); 601 static int spell_iswordp_nmw __ARGS((char_u *p)); 602 #ifdef FEAT_MBYTE 603 static int spell_iswordp_w __ARGS((int *p, buf_T *buf)); 604 #endif 605 static int write_spell_prefcond __ARGS((FILE *fd, garray_T *gap)); 606 607 /* 608 * For finding suggestions: At each node in the tree these states are tried: 609 */ 610 typedef enum 611 { 612 STATE_START = 0, /* At start of node check for NUL bytes (goodword 613 * ends); if badword ends there is a match, otherwise 614 * try splitting word. */ 615 STATE_NOPREFIX, /* try without prefix */ 616 STATE_SPLITUNDO, /* Undo splitting. */ 617 STATE_ENDNUL, /* Past NUL bytes at start of the node. */ 618 STATE_PLAIN, /* Use each byte of the node. */ 619 STATE_DEL, /* Delete a byte from the bad word. */ 620 STATE_INS, /* Insert a byte in the bad word. */ 621 STATE_SWAP, /* Swap two bytes. */ 622 STATE_UNSWAP, /* Undo swap two characters. */ 623 STATE_SWAP3, /* Swap two characters over three. */ 624 STATE_UNSWAP3, /* Undo Swap two characters over three. */ 625 STATE_UNROT3L, /* Undo rotate three characters left */ 626 STATE_UNROT3R, /* Undo rotate three characters right */ 627 STATE_REP_INI, /* Prepare for using REP items. */ 628 STATE_REP, /* Use matching REP items from the .aff file. */ 629 STATE_REP_UNDO, /* Undo a REP item replacement. */ 630 STATE_FINAL /* End of this node. */ 631 } state_T; 632 633 /* 634 * Struct to keep the state at each level in suggest_try_change(). 635 */ 636 typedef struct trystate_S 637 { 638 state_T ts_state; /* state at this level, STATE_ */ 639 int ts_score; /* score */ 640 idx_T ts_arridx; /* index in tree array, start of node */ 641 short ts_curi; /* index in list of child nodes */ 642 char_u ts_fidx; /* index in fword[], case-folded bad word */ 643 char_u ts_fidxtry; /* ts_fidx at which bytes may be changed */ 644 char_u ts_twordlen; /* valid length of tword[] */ 645 char_u ts_prefixdepth; /* stack depth for end of prefix or 646 * PFD_PREFIXTREE or PFD_NOPREFIX */ 647 char_u ts_flags; /* TSF_ flags */ 648 #ifdef FEAT_MBYTE 649 char_u ts_tcharlen; /* number of bytes in tword character */ 650 char_u ts_tcharidx; /* current byte index in tword character */ 651 char_u ts_isdiff; /* DIFF_ values */ 652 char_u ts_fcharstart; /* index in fword where badword char started */ 653 #endif 654 char_u ts_prewordlen; /* length of word in "preword[]" */ 655 char_u ts_splitoff; /* index in "tword" after last split */ 656 char_u ts_splitfidx; /* "ts_fidx" at word split */ 657 char_u ts_complen; /* nr of compound words used */ 658 char_u ts_compsplit; /* index for "compflags" where word was spit */ 659 char_u ts_save_badflags; /* su_badflags saved here */ 660 } trystate_T; 661 662 /* values for ts_isdiff */ 663 #define DIFF_NONE 0 /* no different byte (yet) */ 664 #define DIFF_YES 1 /* different byte found */ 665 #define DIFF_INSERT 2 /* inserting character */ 666 667 /* values for ts_flags */ 668 #define TSF_PREFIXOK 1 /* already checked that prefix is OK */ 669 #define TSF_DIDSPLIT 2 /* tried split at this point */ 670 671 /* special values ts_prefixdepth */ 672 #define PFD_NOPREFIX 0xff /* not using prefixes */ 673 #define PFD_PREFIXTREE 0xfe /* walking through the prefix tree */ 674 #define PFD_NOTSPECIAL 0xfd /* first value that's not special */ 675 676 /* mode values for find_word */ 677 #define FIND_FOLDWORD 0 /* find word case-folded */ 678 #define FIND_KEEPWORD 1 /* find keep-case word */ 679 #define FIND_PREFIX 2 /* find word after prefix */ 680 #define FIND_COMPOUND 3 /* find case-folded compound word */ 681 #define FIND_KEEPCOMPOUND 4 /* find keep-case compound word */ 682 683 static slang_T *slang_alloc __ARGS((char_u *lang)); 684 static void slang_free __ARGS((slang_T *lp)); 685 static void slang_clear __ARGS((slang_T *lp)); 686 static void find_word __ARGS((matchinf_T *mip, int mode)); 687 static int can_compound __ARGS((slang_T *slang, char_u *word, char_u *flags)); 688 static int valid_word_prefix __ARGS((int totprefcnt, int arridx, int flags, char_u *word, slang_T *slang, int cond_req)); 689 static void find_prefix __ARGS((matchinf_T *mip, int mode)); 690 static int fold_more __ARGS((matchinf_T *mip)); 691 static int spell_valid_case __ARGS((int wordflags, int treeflags)); 692 static int no_spell_checking __ARGS((win_T *wp)); 693 static void spell_load_lang __ARGS((char_u *lang)); 694 static char_u *spell_enc __ARGS((void)); 695 static void int_wordlist_spl __ARGS((char_u *fname)); 696 static void spell_load_cb __ARGS((char_u *fname, void *cookie)); 697 static slang_T *spell_load_file __ARGS((char_u *fname, char_u *lang, slang_T *old_lp, int silent)); 698 static char_u *read_cnt_string __ARGS((FILE *fd, int cnt_bytes, int *lenp)); 699 static char_u *read_string __ARGS((FILE *fd, int cnt)); 700 static int read_region_section __ARGS((FILE *fd, slang_T *slang, int len)); 701 static int read_charflags_section __ARGS((FILE *fd)); 702 static int read_prefcond_section __ARGS((FILE *fd, slang_T *lp)); 703 static int read_rep_section __ARGS((FILE *fd, slang_T *slang)); 704 static int read_sal_section __ARGS((FILE *fd, slang_T *slang)); 705 static int read_sofo_section __ARGS((FILE *fd, slang_T *slang)); 706 static int read_compound __ARGS((FILE *fd, slang_T *slang, int len)); 707 static int byte_in_str __ARGS((char_u *str, int byte)); 708 static int init_syl_tab __ARGS((slang_T *slang)); 709 static int count_syllables __ARGS((slang_T *slang, char_u *word)); 710 static int set_sofo __ARGS((slang_T *lp, char_u *from, char_u *to)); 711 static void set_sal_first __ARGS((slang_T *lp)); 712 #ifdef FEAT_MBYTE 713 static int *mb_str2wide __ARGS((char_u *s)); 714 #endif 715 static idx_T read_tree __ARGS((FILE *fd, char_u *byts, idx_T *idxs, int maxidx, int startidx, int prefixtree, int maxprefcondnr)); 716 static void clear_midword __ARGS((buf_T *buf)); 717 static void use_midword __ARGS((slang_T *lp, buf_T *buf)); 718 static int find_region __ARGS((char_u *rp, char_u *region)); 719 static int captype __ARGS((char_u *word, char_u *end)); 720 static int badword_captype __ARGS((char_u *word, char_u *end)); 721 static void spell_reload_one __ARGS((char_u *fname, int added_word)); 722 static void set_spell_charflags __ARGS((char_u *flags, int cnt, char_u *upp)); 723 static int set_spell_chartab __ARGS((char_u *fol, char_u *low, char_u *upp)); 724 static int spell_casefold __ARGS((char_u *p, int len, char_u *buf, int buflen)); 725 static int check_need_cap __ARGS((linenr_T lnum, colnr_T col)); 726 static void spell_find_suggest __ARGS((char_u *badptr, suginfo_T *su, int maxcount, int banbadword, int need_cap)); 727 #ifdef FEAT_EVAL 728 static void spell_suggest_expr __ARGS((suginfo_T *su, char_u *expr)); 729 #endif 730 static void spell_suggest_file __ARGS((suginfo_T *su, char_u *fname)); 731 static void spell_suggest_intern __ARGS((suginfo_T *su)); 732 static void spell_find_cleanup __ARGS((suginfo_T *su)); 733 static void onecap_copy __ARGS((char_u *word, char_u *wcopy, int upper)); 734 static void allcap_copy __ARGS((char_u *word, char_u *wcopy)); 735 static void suggest_try_special __ARGS((suginfo_T *su)); 736 static void suggest_try_change __ARGS((suginfo_T *su)); 737 static int try_deeper __ARGS((suginfo_T *su, trystate_T *stack, int depth, int score_add)); 738 #ifdef FEAT_MBYTE 739 static int nofold_len __ARGS((char_u *fword, int flen, char_u *word)); 740 #endif 741 static void find_keepcap_word __ARGS((slang_T *slang, char_u *fword, char_u *kword)); 742 static void score_comp_sal __ARGS((suginfo_T *su)); 743 static void score_combine __ARGS((suginfo_T *su)); 744 static int stp_sal_score __ARGS((suggest_T *stp, suginfo_T *su, slang_T *slang, char_u *badsound)); 745 static void suggest_try_soundalike __ARGS((suginfo_T *su)); 746 static void make_case_word __ARGS((char_u *fword, char_u *cword, int flags)); 747 static void set_map_str __ARGS((slang_T *lp, char_u *map)); 748 static int similar_chars __ARGS((slang_T *slang, int c1, int c2)); 749 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)); 750 static void add_banned __ARGS((suginfo_T *su, char_u *word)); 751 static int was_banned __ARGS((suginfo_T *su, char_u *word)); 752 static void free_banned __ARGS((suginfo_T *su)); 753 static void rescore_suggestions __ARGS((suginfo_T *su)); 754 static void rescore_one __ARGS((suginfo_T *su, suggest_T *stp)); 755 static int cleanup_suggestions __ARGS((garray_T *gap, int maxscore, int keep)); 756 static void spell_soundfold __ARGS((slang_T *slang, char_u *inword, int folded, char_u *res)); 757 static void spell_soundfold_sofo __ARGS((slang_T *slang, char_u *inword, char_u *res)); 758 static void spell_soundfold_sal __ARGS((slang_T *slang, char_u *inword, char_u *res)); 759 #ifdef FEAT_MBYTE 760 static void spell_soundfold_wsal __ARGS((slang_T *slang, char_u *inword, char_u *res)); 761 #endif 762 static int soundalike_score __ARGS((char_u *goodsound, char_u *badsound)); 763 static int spell_edit_score __ARGS((char_u *badword, char_u *goodword)); 764 static void dump_word __ARGS((char_u *word, int round, int flags, linenr_T lnum)); 765 static linenr_T dump_prefixes __ARGS((slang_T *slang, char_u *word, int round, int flags, linenr_T startlnum)); 766 767 /* 768 * Use our own character-case definitions, because the current locale may 769 * differ from what the .spl file uses. 770 * These must not be called with negative number! 771 */ 772 #ifndef FEAT_MBYTE 773 /* Non-multi-byte implementation. */ 774 # define SPELL_TOFOLD(c) ((c) < 256 ? spelltab.st_fold[c] : (c)) 775 # define SPELL_TOUPPER(c) ((c) < 256 ? spelltab.st_upper[c] : (c)) 776 # define SPELL_ISUPPER(c) ((c) < 256 ? spelltab.st_isu[c] : FALSE) 777 #else 778 # if defined(HAVE_WCHAR_H) 779 # include <wchar.h> /* for towupper() and towlower() */ 780 # endif 781 /* Multi-byte implementation. For Unicode we can call utf_*(), but don't do 782 * that for ASCII, because we don't want to use 'casemap' here. Otherwise use 783 * the "w" library function for characters above 255 if available. */ 784 # ifdef HAVE_TOWLOWER 785 # define SPELL_TOFOLD(c) (enc_utf8 && (c) >= 128 ? utf_fold(c) \ 786 : (c) < 256 ? spelltab.st_fold[c] : towlower(c)) 787 # else 788 # define SPELL_TOFOLD(c) (enc_utf8 && (c) >= 128 ? utf_fold(c) \ 789 : (c) < 256 ? spelltab.st_fold[c] : (c)) 790 # endif 791 792 # ifdef HAVE_TOWUPPER 793 # define SPELL_TOUPPER(c) (enc_utf8 && (c) >= 128 ? utf_toupper(c) \ 794 : (c) < 256 ? spelltab.st_upper[c] : towupper(c)) 795 # else 796 # define SPELL_TOUPPER(c) (enc_utf8 && (c) >= 128 ? utf_toupper(c) \ 797 : (c) < 256 ? spelltab.st_upper[c] : (c)) 798 # endif 799 800 # ifdef HAVE_ISWUPPER 801 # define SPELL_ISUPPER(c) (enc_utf8 && (c) >= 128 ? utf_isupper(c) \ 802 : (c) < 256 ? spelltab.st_isu[c] : iswupper(c)) 803 # else 804 # define SPELL_ISUPPER(c) (enc_utf8 && (c) >= 128 ? utf_isupper(c) \ 805 : (c) < 256 ? spelltab.st_isu[c] : (FALSE)) 806 # endif 807 #endif 808 809 810 static char *e_format = N_("E759: Format error in spell file"); 811 static char *e_spell_trunc = N_("E758: Truncated spell file"); 812 static char *e_afftrailing = N_("Trailing text in %s line %d: %s"); 813 static char *e_affname = N_("Affix name too long in %s line %d: %s"); 814 static char *e_affform = N_("E761: Format error in affix file FOL, LOW or UPP"); 815 static char *e_affrange = N_("E762: Character in FOL, LOW or UPP is out of range"); 816 static char *msg_compressing = N_("Compressing word tree..."); 817 818 /* 819 * Main spell-checking function. 820 * "ptr" points to a character that could be the start of a word. 821 * "*attrp" is set to the highlight index for a badly spelled word. For a 822 * non-word or when it's OK it remains unchanged. 823 * This must only be called when 'spelllang' is not empty. 824 * 825 * "capcol" is used to check for a Capitalised word after the end of a 826 * sentence. If it's zero then perform the check. Return the column where to 827 * check next, or -1 when no sentence end was found. If it's NULL then don't 828 * worry. 829 * 830 * Returns the length of the word in bytes, also when it's OK, so that the 831 * caller can skip over the word. 832 */ 833 int 834 spell_check(wp, ptr, attrp, capcol) 835 win_T *wp; /* current window */ 836 char_u *ptr; 837 hlf_T *attrp; 838 int *capcol; /* column to check for Capital */ 839 { 840 matchinf_T mi; /* Most things are put in "mi" so that it can 841 be passed to functions quickly. */ 842 int nrlen = 0; /* found a number first */ 843 int c; 844 int wrongcaplen = 0; 845 int lpi; 846 847 /* A word never starts at a space or a control character. Return quickly 848 * then, skipping over the character. */ 849 if (*ptr <= ' ') 850 return 1; 851 vim_memset(&mi, 0, sizeof(matchinf_T)); 852 853 /* A number is always OK. Also skip hexadecimal numbers 0xFF99 and 854 * 0X99FF. But always do check spelling to find "3GPP" and "11 855 * julifeest". */ 856 if (*ptr >= '0' && *ptr <= '9') 857 { 858 if (*ptr == '0' && (ptr[1] == 'x' || ptr[1] == 'X')) 859 mi.mi_end = skiphex(ptr + 2); 860 else 861 mi.mi_end = skipdigits(ptr); 862 nrlen = mi.mi_end - ptr; 863 } 864 865 /* Find the normal end of the word (until the next non-word character). */ 866 mi.mi_word = ptr; 867 mi.mi_fend = ptr; 868 if (spell_iswordp(mi.mi_fend, wp->w_buffer)) 869 { 870 do 871 { 872 mb_ptr_adv(mi.mi_fend); 873 } while (*mi.mi_fend != NUL && spell_iswordp(mi.mi_fend, wp->w_buffer)); 874 875 if (capcol != NULL && *capcol == 0 && wp->w_buffer->b_cap_prog != NULL) 876 { 877 /* Check word starting with capital letter. */ 878 c = PTR2CHAR(ptr); 879 if (!SPELL_ISUPPER(c)) 880 wrongcaplen = (int)(mi.mi_fend - ptr); 881 } 882 } 883 if (capcol != NULL) 884 *capcol = -1; 885 886 /* We always use the characters up to the next non-word character, 887 * also for bad words. */ 888 mi.mi_end = mi.mi_fend; 889 890 /* Check caps type later. */ 891 mi.mi_buf = wp->w_buffer; 892 893 /* case-fold the word with one non-word character, so that we can check 894 * for the word end. */ 895 if (*mi.mi_fend != NUL) 896 mb_ptr_adv(mi.mi_fend); 897 898 (void)spell_casefold(ptr, (int)(mi.mi_fend - ptr), mi.mi_fword, 899 MAXWLEN + 1); 900 mi.mi_fwordlen = STRLEN(mi.mi_fword); 901 902 /* The word is bad unless we recognize it. */ 903 mi.mi_result = SP_BAD; 904 mi.mi_result2 = SP_BAD; 905 906 /* 907 * Loop over the languages specified in 'spelllang'. 908 * We check them all, because a matching word may be longer than an 909 * already found matching word. 910 */ 911 for (lpi = 0; lpi < wp->w_buffer->b_langp.ga_len; ++lpi) 912 { 913 mi.mi_lp = LANGP_ENTRY(wp->w_buffer->b_langp, lpi); 914 915 /* If reloading fails the language is still in the list but everything 916 * has been cleared. */ 917 if (mi.mi_lp->lp_slang->sl_fidxs == NULL) 918 continue; 919 920 /* Check for a matching word in case-folded words. */ 921 find_word(&mi, FIND_FOLDWORD); 922 923 /* Check for a matching word in keep-case words. */ 924 find_word(&mi, FIND_KEEPWORD); 925 926 /* Check for matching prefixes. */ 927 find_prefix(&mi, FIND_FOLDWORD); 928 929 /* For a NOBREAK language, may want to use a word without a following 930 * word as a backup. */ 931 if (mi.mi_lp->lp_slang->sl_nobreak && mi.mi_result == SP_BAD 932 && mi.mi_result2 != SP_BAD) 933 { 934 mi.mi_result = mi.mi_result2; 935 mi.mi_end = mi.mi_end2; 936 } 937 } 938 939 if (mi.mi_result != SP_OK) 940 { 941 /* If we found a number skip over it. Allows for "42nd". Do flag 942 * rare and local words, e.g., "3GPP". */ 943 if (nrlen > 0) 944 { 945 if (mi.mi_result == SP_BAD || mi.mi_result == SP_BANNED) 946 return nrlen; 947 } 948 949 /* When we are at a non-word character there is no error, just 950 * skip over the character (try looking for a word after it). */ 951 else if (!spell_iswordp_nmw(ptr)) 952 { 953 if (capcol != NULL && wp->w_buffer->b_cap_prog != NULL) 954 { 955 regmatch_T regmatch; 956 957 /* Check for end of sentence. */ 958 regmatch.regprog = wp->w_buffer->b_cap_prog; 959 regmatch.rm_ic = FALSE; 960 if (vim_regexec(®match, ptr, 0)) 961 *capcol = (int)(regmatch.endp[0] - ptr); 962 } 963 964 #ifdef FEAT_MBYTE 965 if (has_mbyte) 966 return (*mb_ptr2len)(ptr); 967 #endif 968 return 1; 969 } 970 else if (mi.mi_end == ptr) 971 /* Always include at least one character. Required for when there 972 * is a mixup in "midword". */ 973 mb_ptr_adv(mi.mi_end); 974 else if (mi.mi_result == SP_BAD 975 && LANGP_ENTRY(wp->w_buffer->b_langp, 0)->lp_slang->sl_nobreak) 976 { 977 char_u *p, *fp; 978 int save_result = mi.mi_result; 979 980 /* First language in 'spelllang' is NOBREAK. Find first position 981 * at which any word would be valid. */ 982 mi.mi_lp = LANGP_ENTRY(wp->w_buffer->b_langp, 0); 983 if (mi.mi_lp->lp_slang->sl_fidxs != NULL) 984 { 985 p = mi.mi_word; 986 fp = mi.mi_fword; 987 for (;;) 988 { 989 mb_ptr_adv(p); 990 mb_ptr_adv(fp); 991 if (p >= mi.mi_end) 992 break; 993 mi.mi_compoff = fp - mi.mi_fword; 994 find_word(&mi, FIND_COMPOUND); 995 if (mi.mi_result != SP_BAD) 996 { 997 mi.mi_end = p; 998 break; 999 } 1000 } 1001 mi.mi_result = save_result; 1002 } 1003 } 1004 1005 if (mi.mi_result == SP_BAD || mi.mi_result == SP_BANNED) 1006 *attrp = HLF_SPB; 1007 else if (mi.mi_result == SP_RARE) 1008 *attrp = HLF_SPR; 1009 else 1010 *attrp = HLF_SPL; 1011 } 1012 1013 if (wrongcaplen > 0 && (mi.mi_result == SP_OK || mi.mi_result == SP_RARE)) 1014 { 1015 /* Report SpellCap only when the word isn't badly spelled. */ 1016 *attrp = HLF_SPC; 1017 return wrongcaplen; 1018 } 1019 1020 return (int)(mi.mi_end - ptr); 1021 } 1022 1023 /* 1024 * Check if the word at "mip->mi_word" is in the tree. 1025 * When "mode" is FIND_FOLDWORD check in fold-case word tree. 1026 * When "mode" is FIND_KEEPWORD check in keep-case word tree. 1027 * When "mode" is FIND_PREFIX check for word after prefix in fold-case word 1028 * tree. 1029 * 1030 * For a match mip->mi_result is updated. 1031 */ 1032 static void 1033 find_word(mip, mode) 1034 matchinf_T *mip; 1035 int mode; 1036 { 1037 idx_T arridx = 0; 1038 int endlen[MAXWLEN]; /* length at possible word endings */ 1039 idx_T endidx[MAXWLEN]; /* possible word endings */ 1040 int endidxcnt = 0; 1041 int len; 1042 int wlen = 0; 1043 int flen; 1044 int c; 1045 char_u *ptr; 1046 idx_T lo, hi, m; 1047 #ifdef FEAT_MBYTE 1048 char_u *s; 1049 #endif 1050 char_u *p; 1051 int res = SP_BAD; 1052 slang_T *slang = mip->mi_lp->lp_slang; 1053 unsigned flags; 1054 char_u *byts; 1055 idx_T *idxs; 1056 int word_ends; 1057 int prefix_found; 1058 int nobreak_result; 1059 1060 if (mode == FIND_KEEPWORD || mode == FIND_KEEPCOMPOUND) 1061 { 1062 /* Check for word with matching case in keep-case tree. */ 1063 ptr = mip->mi_word; 1064 flen = 9999; /* no case folding, always enough bytes */ 1065 byts = slang->sl_kbyts; 1066 idxs = slang->sl_kidxs; 1067 1068 if (mode == FIND_KEEPCOMPOUND) 1069 /* Skip over the previously found word(s). */ 1070 wlen += mip->mi_compoff; 1071 } 1072 else 1073 { 1074 /* Check for case-folded in case-folded tree. */ 1075 ptr = mip->mi_fword; 1076 flen = mip->mi_fwordlen; /* available case-folded bytes */ 1077 byts = slang->sl_fbyts; 1078 idxs = slang->sl_fidxs; 1079 1080 if (mode == FIND_PREFIX) 1081 { 1082 /* Skip over the prefix. */ 1083 wlen = mip->mi_prefixlen; 1084 flen -= mip->mi_prefixlen; 1085 } 1086 else if (mode == FIND_COMPOUND) 1087 { 1088 /* Skip over the previously found word(s). */ 1089 wlen = mip->mi_compoff; 1090 flen -= mip->mi_compoff; 1091 } 1092 1093 } 1094 1095 if (byts == NULL) 1096 return; /* array is empty */ 1097 1098 /* 1099 * Repeat advancing in the tree until: 1100 * - there is a byte that doesn't match, 1101 * - we reach the end of the tree, 1102 * - or we reach the end of the line. 1103 */ 1104 for (;;) 1105 { 1106 if (flen <= 0 && *mip->mi_fend != NUL) 1107 flen = fold_more(mip); 1108 1109 len = byts[arridx++]; 1110 1111 /* If the first possible byte is a zero the word could end here. 1112 * Remember this index, we first check for the longest word. */ 1113 if (byts[arridx] == 0) 1114 { 1115 if (endidxcnt == MAXWLEN) 1116 { 1117 /* Must be a corrupted spell file. */ 1118 EMSG(_(e_format)); 1119 return; 1120 } 1121 endlen[endidxcnt] = wlen; 1122 endidx[endidxcnt++] = arridx++; 1123 --len; 1124 1125 /* Skip over the zeros, there can be several flag/region 1126 * combinations. */ 1127 while (len > 0 && byts[arridx] == 0) 1128 { 1129 ++arridx; 1130 --len; 1131 } 1132 if (len == 0) 1133 break; /* no children, word must end here */ 1134 } 1135 1136 /* Stop looking at end of the line. */ 1137 if (ptr[wlen] == NUL) 1138 break; 1139 1140 /* Perform a binary search in the list of accepted bytes. */ 1141 c = ptr[wlen]; 1142 if (c == TAB) /* <Tab> is handled like <Space> */ 1143 c = ' '; 1144 lo = arridx; 1145 hi = arridx + len - 1; 1146 while (lo < hi) 1147 { 1148 m = (lo + hi) / 2; 1149 if (byts[m] > c) 1150 hi = m - 1; 1151 else if (byts[m] < c) 1152 lo = m + 1; 1153 else 1154 { 1155 lo = hi = m; 1156 break; 1157 } 1158 } 1159 1160 /* Stop if there is no matching byte. */ 1161 if (hi < lo || byts[lo] != c) 1162 break; 1163 1164 /* Continue at the child (if there is one). */ 1165 arridx = idxs[lo]; 1166 ++wlen; 1167 --flen; 1168 1169 /* One space in the good word may stand for several spaces in the 1170 * checked word. */ 1171 if (c == ' ') 1172 { 1173 for (;;) 1174 { 1175 if (flen <= 0 && *mip->mi_fend != NUL) 1176 flen = fold_more(mip); 1177 if (ptr[wlen] != ' ' && ptr[wlen] != TAB) 1178 break; 1179 ++wlen; 1180 --flen; 1181 } 1182 } 1183 } 1184 1185 /* 1186 * Verify that one of the possible endings is valid. Try the longest 1187 * first. 1188 */ 1189 while (endidxcnt > 0) 1190 { 1191 --endidxcnt; 1192 arridx = endidx[endidxcnt]; 1193 wlen = endlen[endidxcnt]; 1194 1195 #ifdef FEAT_MBYTE 1196 if ((*mb_head_off)(ptr, ptr + wlen) > 0) 1197 continue; /* not at first byte of character */ 1198 #endif 1199 if (spell_iswordp(ptr + wlen, mip->mi_buf)) 1200 { 1201 if (slang->sl_compprog == NULL && !slang->sl_nobreak) 1202 continue; /* next char is a word character */ 1203 word_ends = FALSE; 1204 } 1205 else 1206 word_ends = TRUE; 1207 /* The prefix flag is before compound flags. Once a valid prefix flag 1208 * has been found we try compound flags. */ 1209 prefix_found = FALSE; 1210 1211 #ifdef FEAT_MBYTE 1212 if (mode != FIND_KEEPWORD && has_mbyte) 1213 { 1214 /* Compute byte length in original word, length may change 1215 * when folding case. This can be slow, take a shortcut when the 1216 * case-folded word is equal to the keep-case word. */ 1217 p = mip->mi_word; 1218 if (STRNCMP(ptr, p, wlen) != 0) 1219 { 1220 for (s = ptr; s < ptr + wlen; mb_ptr_adv(s)) 1221 mb_ptr_adv(p); 1222 wlen = p - mip->mi_word; 1223 } 1224 } 1225 #endif 1226 1227 /* Check flags and region. For FIND_PREFIX check the condition and 1228 * prefix ID. 1229 * Repeat this if there are more flags/region alternatives until there 1230 * is a match. */ 1231 res = SP_BAD; 1232 for (len = byts[arridx - 1]; len > 0 && byts[arridx] == 0; 1233 --len, ++arridx) 1234 { 1235 flags = idxs[arridx]; 1236 1237 /* For the fold-case tree check that the case of the checked word 1238 * matches with what the word in the tree requires. 1239 * For keep-case tree the case is always right. For prefixes we 1240 * don't bother to check. */ 1241 if (mode == FIND_FOLDWORD) 1242 { 1243 if (mip->mi_cend != mip->mi_word + wlen) 1244 { 1245 /* mi_capflags was set for a different word length, need 1246 * to do it again. */ 1247 mip->mi_cend = mip->mi_word + wlen; 1248 mip->mi_capflags = captype(mip->mi_word, mip->mi_cend); 1249 } 1250 1251 if (mip->mi_capflags == WF_KEEPCAP 1252 || !spell_valid_case(mip->mi_capflags, flags)) 1253 continue; 1254 } 1255 1256 /* When mode is FIND_PREFIX the word must support the prefix: 1257 * check the prefix ID and the condition. Do that for the list at 1258 * mip->mi_prefarridx that find_prefix() filled. */ 1259 else if (mode == FIND_PREFIX && !prefix_found) 1260 { 1261 c = valid_word_prefix(mip->mi_prefcnt, mip->mi_prefarridx, 1262 flags, 1263 mip->mi_word + mip->mi_cprefixlen, slang, 1264 FALSE); 1265 if (c == 0) 1266 continue; 1267 1268 /* Use the WF_RARE flag for a rare prefix. */ 1269 if (c & WF_RAREPFX) 1270 flags |= WF_RARE; 1271 prefix_found = TRUE; 1272 } 1273 1274 if (slang->sl_nobreak) 1275 { 1276 if ((mode == FIND_COMPOUND || mode == FIND_KEEPCOMPOUND) 1277 && (flags & WF_BANNED) == 0) 1278 { 1279 /* NOBREAK: found a valid following word. That's all we 1280 * need to know, so return. */ 1281 mip->mi_result = SP_OK; 1282 break; 1283 } 1284 } 1285 1286 else if ((mode == FIND_COMPOUND || mode == FIND_KEEPCOMPOUND 1287 || !word_ends)) 1288 { 1289 /* If there is no flag or the word is shorter than 1290 * COMPOUNDMIN reject it quickly. 1291 * Makes you wonder why someone puts a compound flag on a word 1292 * that's too short... Myspell compatibility requires this 1293 * anyway. */ 1294 if (((unsigned)flags >> 24) == 0 1295 || wlen - mip->mi_compoff < slang->sl_compminlen) 1296 continue; 1297 #ifdef FEAT_MBYTE 1298 /* For multi-byte chars check character length against 1299 * COMPOUNDMIN. */ 1300 if (has_mbyte 1301 && slang->sl_compminlen > 0 1302 && mb_charlen_len(mip->mi_word + mip->mi_compoff, 1303 wlen - mip->mi_compoff) < slang->sl_compminlen) 1304 continue; 1305 #endif 1306 1307 /* Limit the number of compound words to COMPOUNDMAX if no 1308 * maximum for syllables is specified. */ 1309 if (!word_ends && mip->mi_complen + 2 > slang->sl_compmax 1310 && slang->sl_compsylmax == MAXWLEN) 1311 continue; 1312 1313 /* Quickly check if compounding is possible with this flag. */ 1314 if (!byte_in_str(mip->mi_complen == 0 1315 ? slang->sl_compstartflags 1316 : slang->sl_compallflags, 1317 ((unsigned)flags >> 24))) 1318 continue; 1319 1320 if (mode == FIND_COMPOUND) 1321 { 1322 int capflags; 1323 1324 /* Need to check the caps type of the appended compound 1325 * word. */ 1326 #ifdef FEAT_MBYTE 1327 if (has_mbyte && STRNCMP(ptr, mip->mi_word, 1328 mip->mi_compoff) != 0) 1329 { 1330 /* case folding may have changed the length */ 1331 p = mip->mi_word; 1332 for (s = ptr; s < ptr + mip->mi_compoff; mb_ptr_adv(s)) 1333 mb_ptr_adv(p); 1334 } 1335 else 1336 #endif 1337 p = mip->mi_word + mip->mi_compoff; 1338 capflags = captype(p, mip->mi_word + wlen); 1339 if (capflags == WF_KEEPCAP || (capflags == WF_ALLCAP 1340 && (flags & WF_FIXCAP) != 0)) 1341 continue; 1342 1343 if (capflags != WF_ALLCAP) 1344 { 1345 /* When the character before the word is a word 1346 * character we do not accept a Onecap word. We do 1347 * accept a no-caps word, even when the dictionary 1348 * word specifies ONECAP. */ 1349 mb_ptr_back(mip->mi_word, p); 1350 if (spell_iswordp_nmw(p) 1351 ? capflags == WF_ONECAP 1352 : (flags & WF_ONECAP) != 0 1353 && capflags != WF_ONECAP) 1354 continue; 1355 } 1356 } 1357 1358 /* If the word ends the sequence of compound flags of the 1359 * words must match with one of the COMPOUNDFLAGS items and 1360 * the number of syllables must not be too large. */ 1361 mip->mi_compflags[mip->mi_complen] = ((unsigned)flags >> 24); 1362 mip->mi_compflags[mip->mi_complen + 1] = NUL; 1363 if (word_ends) 1364 { 1365 char_u fword[MAXWLEN]; 1366 1367 if (slang->sl_compsylmax < MAXWLEN) 1368 { 1369 /* "fword" is only needed for checking syllables. */ 1370 if (ptr == mip->mi_word) 1371 (void)spell_casefold(ptr, wlen, fword, MAXWLEN); 1372 else 1373 vim_strncpy(fword, ptr, endlen[endidxcnt]); 1374 } 1375 if (!can_compound(slang, fword, mip->mi_compflags)) 1376 continue; 1377 } 1378 } 1379 1380 /* Check NEEDCOMPOUND: can't use word without compounding. */ 1381 else if (flags & WF_NEEDCOMP) 1382 continue; 1383 1384 nobreak_result = SP_OK; 1385 1386 if (!word_ends) 1387 { 1388 int save_result = mip->mi_result; 1389 char_u *save_end = mip->mi_end; 1390 langp_T *save_lp = mip->mi_lp; 1391 int lpi; 1392 1393 /* Check that a valid word follows. If there is one and we 1394 * are compounding, it will set "mi_result", thus we are 1395 * always finished here. For NOBREAK we only check that a 1396 * valid word follows. 1397 * Recursive! */ 1398 if (slang->sl_nobreak) 1399 mip->mi_result = SP_BAD; 1400 1401 /* Find following word in case-folded tree. */ 1402 mip->mi_compoff = endlen[endidxcnt]; 1403 #ifdef FEAT_MBYTE 1404 if (has_mbyte && mode == FIND_KEEPWORD) 1405 { 1406 /* Compute byte length in case-folded word from "wlen": 1407 * byte length in keep-case word. Length may change when 1408 * folding case. This can be slow, take a shortcut when 1409 * the case-folded word is equal to the keep-case word. */ 1410 p = mip->mi_fword; 1411 if (STRNCMP(ptr, p, wlen) != 0) 1412 { 1413 for (s = ptr; s < ptr + wlen; mb_ptr_adv(s)) 1414 mb_ptr_adv(p); 1415 mip->mi_compoff = p - mip->mi_fword; 1416 } 1417 } 1418 #endif 1419 c = mip->mi_compoff; 1420 ++mip->mi_complen; 1421 1422 /* For NOBREAK we need to try all NOBREAK languages, at least 1423 * to find the ".add" file(s). */ 1424 for (lpi = 0; lpi < mip->mi_buf->b_langp.ga_len; ++lpi) 1425 { 1426 if (slang->sl_nobreak) 1427 { 1428 mip->mi_lp = LANGP_ENTRY(mip->mi_buf->b_langp, lpi); 1429 if (mip->mi_lp->lp_slang->sl_fidxs == NULL 1430 || !mip->mi_lp->lp_slang->sl_nobreak) 1431 continue; 1432 } 1433 1434 find_word(mip, FIND_COMPOUND); 1435 1436 /* When NOBREAK any word that matches is OK. Otherwise we 1437 * need to find the longest match, thus try with keep-case 1438 * and prefix too. */ 1439 if (!slang->sl_nobreak || mip->mi_result == SP_BAD) 1440 { 1441 /* Find following word in keep-case tree. */ 1442 mip->mi_compoff = wlen; 1443 find_word(mip, FIND_KEEPCOMPOUND); 1444 1445 if (!slang->sl_nobreak || mip->mi_result == SP_BAD) 1446 { 1447 /* Check for following word with prefix. */ 1448 mip->mi_compoff = c; 1449 find_prefix(mip, FIND_COMPOUND); 1450 } 1451 } 1452 1453 if (!slang->sl_nobreak) 1454 break; 1455 } 1456 --mip->mi_complen; 1457 mip->mi_lp = save_lp; 1458 1459 if (slang->sl_nobreak) 1460 { 1461 nobreak_result = mip->mi_result; 1462 mip->mi_result = save_result; 1463 mip->mi_end = save_end; 1464 } 1465 else 1466 { 1467 if (mip->mi_result == SP_OK) 1468 break; 1469 continue; 1470 } 1471 } 1472 1473 if (flags & WF_BANNED) 1474 res = SP_BANNED; 1475 else if (flags & WF_REGION) 1476 { 1477 /* Check region. */ 1478 if ((mip->mi_lp->lp_region & (flags >> 16)) != 0) 1479 res = SP_OK; 1480 else 1481 res = SP_LOCAL; 1482 } 1483 else if (flags & WF_RARE) 1484 res = SP_RARE; 1485 else 1486 res = SP_OK; 1487 1488 /* Always use the longest match and the best result. For NOBREAK 1489 * we separately keep the longest match without a following good 1490 * word as a fall-back. */ 1491 if (nobreak_result == SP_BAD) 1492 { 1493 if (mip->mi_result2 > res) 1494 { 1495 mip->mi_result2 = res; 1496 mip->mi_end2 = mip->mi_word + wlen; 1497 } 1498 else if (mip->mi_result2 == res 1499 && mip->mi_end2 < mip->mi_word + wlen) 1500 mip->mi_end2 = mip->mi_word + wlen; 1501 } 1502 else if (mip->mi_result > res) 1503 { 1504 mip->mi_result = res; 1505 mip->mi_end = mip->mi_word + wlen; 1506 } 1507 else if (mip->mi_result == res && mip->mi_end < mip->mi_word + wlen) 1508 mip->mi_end = mip->mi_word + wlen; 1509 1510 if (mip->mi_result == SP_OK) 1511 break; 1512 } 1513 1514 if (mip->mi_result == SP_OK) 1515 break; 1516 } 1517 } 1518 1519 /* 1520 * Return TRUE if "flags" is a valid sequence of compound flags and 1521 * "word[len]" does not have too many syllables. 1522 */ 1523 static int 1524 can_compound(slang, word, flags) 1525 slang_T *slang; 1526 char_u *word; 1527 char_u *flags; 1528 { 1529 regmatch_T regmatch; 1530 #ifdef FEAT_MBYTE 1531 char_u uflags[MAXWLEN * 2]; 1532 int i; 1533 #endif 1534 char_u *p; 1535 1536 if (slang->sl_compprog == NULL) 1537 return FALSE; 1538 #ifdef FEAT_MBYTE 1539 if (enc_utf8) 1540 { 1541 /* Need to convert the single byte flags to utf8 characters. */ 1542 p = uflags; 1543 for (i = 0; flags[i] != NUL; ++i) 1544 p += mb_char2bytes(flags[i], p); 1545 *p = NUL; 1546 p = uflags; 1547 } 1548 else 1549 #endif 1550 p = flags; 1551 regmatch.regprog = slang->sl_compprog; 1552 regmatch.rm_ic = FALSE; 1553 if (!vim_regexec(®match, p, 0)) 1554 return FALSE; 1555 1556 /* Count the number of syllables. This may be slow, do it last. If there 1557 * are too many syllables AND the number of compound words is above 1558 * COMPOUNDMAX then compounding is not allowed. */ 1559 if (slang->sl_compsylmax < MAXWLEN 1560 && count_syllables(slang, word) > slang->sl_compsylmax) 1561 return (int)STRLEN(flags) < slang->sl_compmax; 1562 return TRUE; 1563 } 1564 1565 /* 1566 * Return non-zero if the prefix indicated by "arridx" matches with the prefix 1567 * ID in "flags" for the word "word". 1568 * The WF_RAREPFX flag is included in the return value for a rare prefix. 1569 */ 1570 static int 1571 valid_word_prefix(totprefcnt, arridx, flags, word, slang, cond_req) 1572 int totprefcnt; /* nr of prefix IDs */ 1573 int arridx; /* idx in sl_pidxs[] */ 1574 int flags; 1575 char_u *word; 1576 slang_T *slang; 1577 int cond_req; /* only use prefixes with a condition */ 1578 { 1579 int prefcnt; 1580 int pidx; 1581 regprog_T *rp; 1582 regmatch_T regmatch; 1583 int prefid; 1584 1585 prefid = (unsigned)flags >> 24; 1586 for (prefcnt = totprefcnt - 1; prefcnt >= 0; --prefcnt) 1587 { 1588 pidx = slang->sl_pidxs[arridx + prefcnt]; 1589 1590 /* Check the prefix ID. */ 1591 if (prefid != (pidx & 0xff)) 1592 continue; 1593 1594 /* Check if the prefix doesn't combine and the word already has a 1595 * suffix. */ 1596 if ((flags & WF_HAS_AFF) && (pidx & WF_PFX_NC)) 1597 continue; 1598 1599 /* Check the condition, if there is one. The condition index is 1600 * stored in the two bytes above the prefix ID byte. */ 1601 rp = slang->sl_prefprog[((unsigned)pidx >> 8) & 0xffff]; 1602 if (rp != NULL) 1603 { 1604 regmatch.regprog = rp; 1605 regmatch.rm_ic = FALSE; 1606 if (!vim_regexec(®match, word, 0)) 1607 continue; 1608 } 1609 else if (cond_req) 1610 continue; 1611 1612 /* It's a match! Return the WF_ flags. */ 1613 return pidx; 1614 } 1615 return 0; 1616 } 1617 1618 /* 1619 * Check if the word at "mip->mi_word" has a matching prefix. 1620 * If it does, then check the following word. 1621 * 1622 * If "mode" is "FIND_COMPOUND" then do the same after another word, find a 1623 * prefix in a compound word. 1624 * 1625 * For a match mip->mi_result is updated. 1626 */ 1627 static void 1628 find_prefix(mip, mode) 1629 matchinf_T *mip; 1630 int mode; 1631 { 1632 idx_T arridx = 0; 1633 int len; 1634 int wlen = 0; 1635 int flen; 1636 int c; 1637 char_u *ptr; 1638 idx_T lo, hi, m; 1639 slang_T *slang = mip->mi_lp->lp_slang; 1640 char_u *byts; 1641 idx_T *idxs; 1642 1643 byts = slang->sl_pbyts; 1644 if (byts == NULL) 1645 return; /* array is empty */ 1646 1647 /* We use the case-folded word here, since prefixes are always 1648 * case-folded. */ 1649 ptr = mip->mi_fword; 1650 flen = mip->mi_fwordlen; /* available case-folded bytes */ 1651 if (mode == FIND_COMPOUND) 1652 { 1653 /* Skip over the previously found word(s). */ 1654 ptr += mip->mi_compoff; 1655 flen -= mip->mi_compoff; 1656 } 1657 idxs = slang->sl_pidxs; 1658 1659 /* 1660 * Repeat advancing in the tree until: 1661 * - there is a byte that doesn't match, 1662 * - we reach the end of the tree, 1663 * - or we reach the end of the line. 1664 */ 1665 for (;;) 1666 { 1667 if (flen == 0 && *mip->mi_fend != NUL) 1668 flen = fold_more(mip); 1669 1670 len = byts[arridx++]; 1671 1672 /* If the first possible byte is a zero the prefix could end here. 1673 * Check if the following word matches and supports the prefix. */ 1674 if (byts[arridx] == 0) 1675 { 1676 /* There can be several prefixes with different conditions. We 1677 * try them all, since we don't know which one will give the 1678 * longest match. The word is the same each time, pass the list 1679 * of possible prefixes to find_word(). */ 1680 mip->mi_prefarridx = arridx; 1681 mip->mi_prefcnt = len; 1682 while (len > 0 && byts[arridx] == 0) 1683 { 1684 ++arridx; 1685 --len; 1686 } 1687 mip->mi_prefcnt -= len; 1688 1689 /* Find the word that comes after the prefix. */ 1690 mip->mi_prefixlen = wlen; 1691 if (mode == FIND_COMPOUND) 1692 /* Skip over the previously found word(s). */ 1693 mip->mi_prefixlen += mip->mi_compoff; 1694 1695 #ifdef FEAT_MBYTE 1696 if (has_mbyte) 1697 { 1698 /* Case-folded length may differ from original length. */ 1699 mip->mi_cprefixlen = nofold_len(mip->mi_fword, 1700 mip->mi_prefixlen, mip->mi_word); 1701 } 1702 else 1703 mip->mi_cprefixlen = mip->mi_prefixlen; 1704 #endif 1705 find_word(mip, FIND_PREFIX); 1706 1707 1708 if (len == 0) 1709 break; /* no children, word must end here */ 1710 } 1711 1712 /* Stop looking at end of the line. */ 1713 if (ptr[wlen] == NUL) 1714 break; 1715 1716 /* Perform a binary search in the list of accepted bytes. */ 1717 c = ptr[wlen]; 1718 lo = arridx; 1719 hi = arridx + len - 1; 1720 while (lo < hi) 1721 { 1722 m = (lo + hi) / 2; 1723 if (byts[m] > c) 1724 hi = m - 1; 1725 else if (byts[m] < c) 1726 lo = m + 1; 1727 else 1728 { 1729 lo = hi = m; 1730 break; 1731 } 1732 } 1733 1734 /* Stop if there is no matching byte. */ 1735 if (hi < lo || byts[lo] != c) 1736 break; 1737 1738 /* Continue at the child (if there is one). */ 1739 arridx = idxs[lo]; 1740 ++wlen; 1741 --flen; 1742 } 1743 } 1744 1745 /* 1746 * Need to fold at least one more character. Do until next non-word character 1747 * for efficiency. 1748 * Return the length of the folded chars in bytes. 1749 */ 1750 static int 1751 fold_more(mip) 1752 matchinf_T *mip; 1753 { 1754 int flen; 1755 char_u *p; 1756 1757 p = mip->mi_fend; 1758 do 1759 { 1760 mb_ptr_adv(mip->mi_fend); 1761 } while (*mip->mi_fend != NUL && spell_iswordp(mip->mi_fend, mip->mi_buf)); 1762 1763 /* Include the non-word character so that we can check for the 1764 * word end. */ 1765 if (*mip->mi_fend != NUL) 1766 mb_ptr_adv(mip->mi_fend); 1767 1768 (void)spell_casefold(p, (int)(mip->mi_fend - p), 1769 mip->mi_fword + mip->mi_fwordlen, 1770 MAXWLEN - mip->mi_fwordlen); 1771 flen = STRLEN(mip->mi_fword + mip->mi_fwordlen); 1772 mip->mi_fwordlen += flen; 1773 return flen; 1774 } 1775 1776 /* 1777 * Check case flags for a word. Return TRUE if the word has the requested 1778 * case. 1779 */ 1780 static int 1781 spell_valid_case(wordflags, treeflags) 1782 int wordflags; /* flags for the checked word. */ 1783 int treeflags; /* flags for the word in the spell tree */ 1784 { 1785 return ((wordflags == WF_ALLCAP && (treeflags & WF_FIXCAP) == 0) 1786 || ((treeflags & (WF_ALLCAP | WF_KEEPCAP)) == 0 1787 && ((treeflags & WF_ONECAP) == 0 1788 || (wordflags & WF_ONECAP) != 0))); 1789 } 1790 1791 /* 1792 * Return TRUE if spell checking is not enabled. 1793 */ 1794 static int 1795 no_spell_checking(wp) 1796 win_T *wp; 1797 { 1798 if (!wp->w_p_spell || *wp->w_buffer->b_p_spl == NUL) 1799 { 1800 EMSG(_("E756: Spell checking is not enabled")); 1801 return TRUE; 1802 } 1803 return FALSE; 1804 } 1805 1806 /* 1807 * Move to next spell error. 1808 * "curline" is FALSE for "[s", "]s", "[S" and "]S". 1809 * "curline" is TRUE to find word under/after cursor in the same line. 1810 * For Insert mode completion "dir" is BACKWARD and "curline" is TRUE: move 1811 * to after badly spelled word before the cursor. 1812 * Return 0 if not found, length of the badly spelled word otherwise. 1813 */ 1814 int 1815 spell_move_to(wp, dir, allwords, curline, attrp) 1816 win_T *wp; 1817 int dir; /* FORWARD or BACKWARD */ 1818 int allwords; /* TRUE for "[s"/"]s", FALSE for "[S"/"]S" */ 1819 int curline; 1820 hlf_T *attrp; /* return: attributes of bad word or NULL 1821 (only when "dir" is FORWARD) */ 1822 { 1823 linenr_T lnum; 1824 pos_T found_pos; 1825 int found_len = 0; 1826 char_u *line; 1827 char_u *p; 1828 char_u *endp; 1829 hlf_T attr; 1830 int len; 1831 int has_syntax = syntax_present(wp->w_buffer); 1832 int col; 1833 int can_spell; 1834 char_u *buf = NULL; 1835 int buflen = 0; 1836 int skip = 0; 1837 int capcol = -1; 1838 int found_one = FALSE; 1839 int wrapped = FALSE; 1840 1841 if (no_spell_checking(wp)) 1842 return 0; 1843 1844 /* 1845 * Start looking for bad word at the start of the line, because we can't 1846 * start halfway a word, we don't know where the it starts or ends. 1847 * 1848 * When searching backwards, we continue in the line to find the last 1849 * bad word (in the cursor line: before the cursor). 1850 * 1851 * We concatenate the start of the next line, so that wrapped words work 1852 * (e.g. "et<line-break>cetera"). Doesn't work when searching backwards 1853 * though... 1854 */ 1855 lnum = wp->w_cursor.lnum; 1856 found_pos.lnum = 0; 1857 1858 while (!got_int) 1859 { 1860 line = ml_get_buf(wp->w_buffer, lnum, FALSE); 1861 1862 len = STRLEN(line); 1863 if (buflen < len + MAXWLEN + 2) 1864 { 1865 vim_free(buf); 1866 buflen = len + MAXWLEN + 2; 1867 buf = alloc(buflen); 1868 if (buf == NULL) 1869 break; 1870 } 1871 1872 /* In first line check first word for Capital. */ 1873 if (lnum == 1) 1874 capcol = 0; 1875 1876 /* For checking first word with a capital skip white space. */ 1877 if (capcol == 0) 1878 capcol = skipwhite(line) - line; 1879 1880 /* Copy the line into "buf" and append the start of the next line if 1881 * possible. */ 1882 STRCPY(buf, line); 1883 if (lnum < wp->w_buffer->b_ml.ml_line_count) 1884 spell_cat_line(buf + STRLEN(buf), ml_get(lnum + 1), MAXWLEN); 1885 1886 p = buf + skip; 1887 endp = buf + len; 1888 while (p < endp) 1889 { 1890 /* When searching backward don't search after the cursor. Unless 1891 * we wrapped around the end of the buffer. */ 1892 if (dir == BACKWARD 1893 && lnum == wp->w_cursor.lnum 1894 && !wrapped 1895 && (colnr_T)(p - buf) >= wp->w_cursor.col) 1896 break; 1897 1898 /* start of word */ 1899 attr = HLF_COUNT; 1900 len = spell_check(wp, p, &attr, &capcol); 1901 1902 if (attr != HLF_COUNT) 1903 { 1904 /* We found a bad word. Check the attribute. */ 1905 if (allwords || attr == HLF_SPB) 1906 { 1907 found_one = TRUE; 1908 1909 /* When searching forward only accept a bad word after 1910 * the cursor. */ 1911 if (dir == BACKWARD 1912 || lnum != wp->w_cursor.lnum 1913 || (lnum == wp->w_cursor.lnum 1914 && (wrapped 1915 || (colnr_T)(curline ? p - buf + len 1916 : p - buf) 1917 > wp->w_cursor.col))) 1918 { 1919 if (has_syntax) 1920 { 1921 col = p - buf; 1922 (void)syn_get_id(wp, lnum, (colnr_T)col, 1923 FALSE, &can_spell); 1924 } 1925 else 1926 can_spell = TRUE; 1927 1928 if (can_spell) 1929 { 1930 found_pos.lnum = lnum; 1931 found_pos.col = p - buf; 1932 #ifdef FEAT_VIRTUALEDIT 1933 found_pos.coladd = 0; 1934 #endif 1935 if (dir == FORWARD) 1936 { 1937 /* No need to search further. */ 1938 wp->w_cursor = found_pos; 1939 vim_free(buf); 1940 if (attrp != NULL) 1941 *attrp = attr; 1942 return len; 1943 } 1944 else if (curline) 1945 /* Insert mode completion: put cursor after 1946 * the bad word. */ 1947 found_pos.col += len; 1948 found_len = len; 1949 } 1950 } 1951 } 1952 } 1953 1954 /* advance to character after the word */ 1955 p += len; 1956 capcol -= len; 1957 } 1958 1959 if (dir == BACKWARD && found_pos.lnum != 0) 1960 { 1961 /* Use the last match in the line (before the cursor). */ 1962 wp->w_cursor = found_pos; 1963 vim_free(buf); 1964 return found_len; 1965 } 1966 1967 if (curline) 1968 break; /* only check cursor line */ 1969 1970 /* Advance to next line. */ 1971 if (dir == BACKWARD) 1972 { 1973 /* If we are back at the starting line and searched it again there 1974 * is no match, give up. */ 1975 if (lnum == wp->w_cursor.lnum && wrapped) 1976 break; 1977 1978 if (lnum > 1) 1979 --lnum; 1980 else if (!p_ws) 1981 break; /* at first line and 'nowrapscan' */ 1982 else 1983 { 1984 /* Wrap around to the end of the buffer. May search the 1985 * starting line again and accept the last match. */ 1986 lnum = wp->w_buffer->b_ml.ml_line_count; 1987 wrapped = TRUE; 1988 if (!shortmess(SHM_SEARCH)) 1989 give_warning((char_u *)_(top_bot_msg), TRUE); 1990 } 1991 capcol = -1; 1992 } 1993 else 1994 { 1995 if (lnum < wp->w_buffer->b_ml.ml_line_count) 1996 ++lnum; 1997 else if (!p_ws) 1998 break; /* at first line and 'nowrapscan' */ 1999 else 2000 { 2001 /* Wrap around to the start of the buffer. May search the 2002 * starting line again and accept the first match. */ 2003 lnum = 1; 2004 wrapped = TRUE; 2005 if (!shortmess(SHM_SEARCH)) 2006 give_warning((char_u *)_(bot_top_msg), TRUE); 2007 } 2008 2009 /* If we are back at the starting line and there is no match then 2010 * give up. */ 2011 if (lnum == wp->w_cursor.lnum && !found_one) 2012 break; 2013 2014 /* Skip the characters at the start of the next line that were 2015 * included in a match crossing line boundaries. */ 2016 if (attr == HLF_COUNT) 2017 skip = p - endp; 2018 else 2019 skip = 0; 2020 2021 /* Capscol skips over the inserted space. */ 2022 --capcol; 2023 2024 /* But after empty line check first word in next line */ 2025 if (*skipwhite(line) == NUL) 2026 capcol = 0; 2027 } 2028 2029 line_breakcheck(); 2030 } 2031 2032 vim_free(buf); 2033 return 0; 2034 } 2035 2036 /* 2037 * For spell checking: concatenate the start of the following line "line" into 2038 * "buf", blanking-out special characters. Copy less then "maxlen" bytes. 2039 */ 2040 void 2041 spell_cat_line(buf, line, maxlen) 2042 char_u *buf; 2043 char_u *line; 2044 int maxlen; 2045 { 2046 char_u *p; 2047 int n; 2048 2049 p = skipwhite(line); 2050 while (vim_strchr((char_u *)"*#/\"\t", *p) != NULL) 2051 p = skipwhite(p + 1); 2052 2053 if (*p != NUL) 2054 { 2055 *buf = ' '; 2056 vim_strncpy(buf + 1, line, maxlen - 2); 2057 n = p - line; 2058 if (n >= maxlen) 2059 n = maxlen - 1; 2060 vim_memset(buf + 1, ' ', n); 2061 } 2062 } 2063 2064 typedef struct spelload_S 2065 { 2066 char_u sl_lang[MAXWLEN + 1]; /* language name */ 2067 slang_T *sl_slang; /* resulting slang_T struct */ 2068 int sl_nobreak; /* NOBREAK language found */ 2069 } spelload_T; 2070 2071 /* 2072 * Load word list(s) for "lang" from Vim spell file(s). 2073 * "lang" must be the language without the region: e.g., "en". 2074 */ 2075 static void 2076 spell_load_lang(lang) 2077 char_u *lang; 2078 { 2079 char_u fname_enc[85]; 2080 int r; 2081 spelload_T sl; 2082 2083 /* Copy the language name to pass it to spell_load_cb() as a cookie. 2084 * It's truncated when an error is detected. */ 2085 STRCPY(sl.sl_lang, lang); 2086 sl.sl_slang = NULL; 2087 sl.sl_nobreak = FALSE; 2088 2089 /* 2090 * Find the first spell file for "lang" in 'runtimepath' and load it. 2091 */ 2092 vim_snprintf((char *)fname_enc, sizeof(fname_enc) - 5, 2093 "spell/%s.%s.spl", lang, spell_enc()); 2094 r = do_in_runtimepath(fname_enc, FALSE, spell_load_cb, &sl); 2095 2096 if (r == FAIL && *sl.sl_lang != NUL) 2097 { 2098 /* Try loading the ASCII version. */ 2099 vim_snprintf((char *)fname_enc, sizeof(fname_enc) - 5, 2100 "spell/%s.ascii.spl", lang); 2101 r = do_in_runtimepath(fname_enc, FALSE, spell_load_cb, &sl); 2102 } 2103 2104 if (r == FAIL) 2105 smsg((char_u *)_("Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\""), 2106 lang, spell_enc(), lang); 2107 else if (sl.sl_slang != NULL) 2108 { 2109 /* At least one file was loaded, now load all the additions. */ 2110 STRCPY(fname_enc + STRLEN(fname_enc) - 3, "add.spl"); 2111 do_in_runtimepath(fname_enc, TRUE, spell_load_cb, &sl); 2112 } 2113 } 2114 2115 /* 2116 * Return the encoding used for spell checking: Use 'encoding', except that we 2117 * use "latin1" for "latin9". And limit to 60 characters (just in case). 2118 */ 2119 static char_u * 2120 spell_enc() 2121 { 2122 2123 #ifdef FEAT_MBYTE 2124 if (STRLEN(p_enc) < 60 && STRCMP(p_enc, "iso-8859-15") != 0) 2125 return p_enc; 2126 #endif 2127 return (char_u *)"latin1"; 2128 } 2129 2130 /* 2131 * Get the name of the .spl file for the internal wordlist into 2132 * "fname[MAXPATHL]". 2133 */ 2134 static void 2135 int_wordlist_spl(fname) 2136 char_u *fname; 2137 { 2138 vim_snprintf((char *)fname, MAXPATHL, "%s.%s.spl", 2139 int_wordlist, spell_enc()); 2140 } 2141 2142 /* 2143 * Allocate a new slang_T. 2144 * Caller must fill "sl_next". 2145 */ 2146 static slang_T * 2147 slang_alloc(lang) 2148 char_u *lang; 2149 { 2150 slang_T *lp; 2151 2152 lp = (slang_T *)alloc_clear(sizeof(slang_T)); 2153 if (lp != NULL) 2154 { 2155 lp->sl_name = vim_strsave(lang); 2156 ga_init2(&lp->sl_rep, sizeof(fromto_T), 10); 2157 lp->sl_compmax = MAXWLEN; 2158 lp->sl_compsylmax = MAXWLEN; 2159 } 2160 return lp; 2161 } 2162 2163 /* 2164 * Free the contents of an slang_T and the structure itself. 2165 */ 2166 static void 2167 slang_free(lp) 2168 slang_T *lp; 2169 { 2170 vim_free(lp->sl_name); 2171 vim_free(lp->sl_fname); 2172 slang_clear(lp); 2173 vim_free(lp); 2174 } 2175 2176 /* 2177 * Clear an slang_T so that the file can be reloaded. 2178 */ 2179 static void 2180 slang_clear(lp) 2181 slang_T *lp; 2182 { 2183 garray_T *gap; 2184 fromto_T *ftp; 2185 salitem_T *smp; 2186 int i; 2187 2188 vim_free(lp->sl_fbyts); 2189 lp->sl_fbyts = NULL; 2190 vim_free(lp->sl_kbyts); 2191 lp->sl_kbyts = NULL; 2192 vim_free(lp->sl_pbyts); 2193 lp->sl_pbyts = NULL; 2194 2195 vim_free(lp->sl_fidxs); 2196 lp->sl_fidxs = NULL; 2197 vim_free(lp->sl_kidxs); 2198 lp->sl_kidxs = NULL; 2199 vim_free(lp->sl_pidxs); 2200 lp->sl_pidxs = NULL; 2201 2202 gap = &lp->sl_rep; 2203 while (gap->ga_len > 0) 2204 { 2205 ftp = &((fromto_T *)gap->ga_data)[--gap->ga_len]; 2206 vim_free(ftp->ft_from); 2207 vim_free(ftp->ft_to); 2208 } 2209 ga_clear(gap); 2210 2211 gap = &lp->sl_sal; 2212 if (lp->sl_sofo) 2213 { 2214 /* "ga_len" is set to 1 without adding an item for latin1 */ 2215 if (gap->ga_data != NULL) 2216 /* SOFOFROM and SOFOTO items: free lists of wide characters. */ 2217 for (i = 0; i < gap->ga_len; ++i) 2218 vim_free(((int **)gap->ga_data)[i]); 2219 } 2220 else 2221 /* SAL items: free salitem_T items */ 2222 while (gap->ga_len > 0) 2223 { 2224 smp = &((salitem_T *)gap->ga_data)[--gap->ga_len]; 2225 vim_free(smp->sm_lead); 2226 /* Don't free sm_oneof and sm_rules, they point into sm_lead. */ 2227 vim_free(smp->sm_to); 2228 #ifdef FEAT_MBYTE 2229 vim_free(smp->sm_lead_w); 2230 vim_free(smp->sm_oneof_w); 2231 vim_free(smp->sm_to_w); 2232 #endif 2233 } 2234 ga_clear(gap); 2235 2236 for (i = 0; i < lp->sl_prefixcnt; ++i) 2237 vim_free(lp->sl_prefprog[i]); 2238 lp->sl_prefixcnt = 0; 2239 vim_free(lp->sl_prefprog); 2240 lp->sl_prefprog = NULL; 2241 2242 vim_free(lp->sl_midword); 2243 lp->sl_midword = NULL; 2244 2245 vim_free(lp->sl_compprog); 2246 vim_free(lp->sl_compstartflags); 2247 vim_free(lp->sl_compallflags); 2248 lp->sl_compprog = NULL; 2249 lp->sl_compstartflags = NULL; 2250 lp->sl_compallflags = NULL; 2251 2252 vim_free(lp->sl_syllable); 2253 lp->sl_syllable = NULL; 2254 ga_clear(&lp->sl_syl_items); 2255 2256 #ifdef FEAT_MBYTE 2257 { 2258 int todo = lp->sl_map_hash.ht_used; 2259 hashitem_T *hi; 2260 2261 for (hi = lp->sl_map_hash.ht_array; todo > 0; ++hi) 2262 if (!HASHITEM_EMPTY(hi)) 2263 { 2264 --todo; 2265 vim_free(hi->hi_key); 2266 } 2267 } 2268 hash_clear(&lp->sl_map_hash); 2269 #endif 2270 2271 lp->sl_compmax = MAXWLEN; 2272 lp->sl_compminlen = 0; 2273 lp->sl_compsylmax = MAXWLEN; 2274 lp->sl_regions[0] = NUL; 2275 } 2276 2277 /* 2278 * Load one spell file and store the info into a slang_T. 2279 * Invoked through do_in_runtimepath(). 2280 */ 2281 static void 2282 spell_load_cb(fname, cookie) 2283 char_u *fname; 2284 void *cookie; 2285 { 2286 spelload_T *slp = (spelload_T *)cookie; 2287 slang_T *slang; 2288 2289 slang = spell_load_file(fname, slp->sl_lang, NULL, FALSE); 2290 if (slang != NULL) 2291 { 2292 /* When a previously loaded file has NOBREAK also use it for the 2293 * ".add" files. */ 2294 if (slp->sl_nobreak && slang->sl_add) 2295 slang->sl_nobreak = TRUE; 2296 else if (slang->sl_nobreak) 2297 slp->sl_nobreak = TRUE; 2298 2299 slp->sl_slang = slang; 2300 } 2301 } 2302 2303 /* 2304 * Load one spell file and store the info into a slang_T. 2305 * 2306 * This is invoked in two ways: 2307 * - From spell_load_cb() to load a spell file for the first time. "lang" is 2308 * the language name, "old_lp" is NULL. Will allocate an slang_T. 2309 * - To reload a spell file that was changed. "lang" is NULL and "old_lp" 2310 * points to the existing slang_T. 2311 * Returns the slang_T the spell file was loaded into. NULL for error. 2312 */ 2313 static slang_T * 2314 spell_load_file(fname, lang, old_lp, silent) 2315 char_u *fname; 2316 char_u *lang; 2317 slang_T *old_lp; 2318 int silent; /* no error if file doesn't exist */ 2319 { 2320 FILE *fd; 2321 char_u buf[VIMSPELLMAGICL]; 2322 char_u *p; 2323 char_u *bp; 2324 idx_T *ip; 2325 int i; 2326 int n; 2327 int len; 2328 int round; 2329 char_u *save_sourcing_name = sourcing_name; 2330 linenr_T save_sourcing_lnum = sourcing_lnum; 2331 slang_T *lp = NULL; 2332 idx_T idx; 2333 int c = 0; 2334 int res; 2335 2336 fd = mch_fopen((char *)fname, "r"); 2337 if (fd == NULL) 2338 { 2339 if (!silent) 2340 EMSG2(_(e_notopen), fname); 2341 else if (p_verbose > 2) 2342 { 2343 verbose_enter(); 2344 smsg((char_u *)e_notopen, fname); 2345 verbose_leave(); 2346 } 2347 goto endFAIL; 2348 } 2349 if (p_verbose > 2) 2350 { 2351 verbose_enter(); 2352 smsg((char_u *)_("Reading spell file \"%s\""), fname); 2353 verbose_leave(); 2354 } 2355 2356 if (old_lp == NULL) 2357 { 2358 lp = slang_alloc(lang); 2359 if (lp == NULL) 2360 goto endFAIL; 2361 2362 /* Remember the file name, used to reload the file when it's updated. */ 2363 lp->sl_fname = vim_strsave(fname); 2364 if (lp->sl_fname == NULL) 2365 goto endFAIL; 2366 2367 /* Check for .add.spl. */ 2368 lp->sl_add = strstr((char *)gettail(fname), ".add.") != NULL; 2369 } 2370 else 2371 lp = old_lp; 2372 2373 /* Set sourcing_name, so that error messages mention the file name. */ 2374 sourcing_name = fname; 2375 sourcing_lnum = 0; 2376 2377 /* <HEADER>: <fileID> 2378 */ 2379 for (i = 0; i < VIMSPELLMAGICL; ++i) 2380 buf[i] = getc(fd); /* <fileID> */ 2381 if (STRNCMP(buf, VIMSPELLMAGIC, VIMSPELLMAGICL) != 0) 2382 { 2383 EMSG(_("E757: This does not look like a spell file")); 2384 goto endFAIL; 2385 } 2386 c = getc(fd); /* <versionnr> */ 2387 if (c < VIMSPELLVERSION) 2388 { 2389 EMSG(_("E771: Old spell file, needs to be updated")); 2390 goto endFAIL; 2391 } 2392 else if (c > VIMSPELLVERSION) 2393 { 2394 EMSG(_("E772: Spell file is for newer version of Vim")); 2395 goto endFAIL; 2396 } 2397 2398 2399 /* 2400 * <SECTIONS>: <section> ... <sectionend> 2401 * <section>: <sectionID> <sectionflags> <sectionlen> (section contents) 2402 */ 2403 for (;;) 2404 { 2405 n = getc(fd); /* <sectionID> or <sectionend> */ 2406 if (n == SN_END) 2407 break; 2408 c = getc(fd); /* <sectionflags> */ 2409 len = (getc(fd) << 24) + (getc(fd) << 16) + (getc(fd) << 8) + getc(fd); 2410 /* <sectionlen> */ 2411 if (len < 0) 2412 goto truncerr; 2413 2414 res = 0; 2415 switch (n) 2416 { 2417 case SN_REGION: 2418 res = read_region_section(fd, lp, len); 2419 break; 2420 2421 case SN_CHARFLAGS: 2422 res = read_charflags_section(fd); 2423 break; 2424 2425 case SN_MIDWORD: 2426 lp->sl_midword = read_string(fd, len); /* <midword> */ 2427 if (lp->sl_midword == NULL) 2428 goto endFAIL; 2429 break; 2430 2431 case SN_PREFCOND: 2432 res = read_prefcond_section(fd, lp); 2433 break; 2434 2435 case SN_REP: 2436 res = read_rep_section(fd, lp); 2437 break; 2438 2439 case SN_SAL: 2440 res = read_sal_section(fd, lp); 2441 break; 2442 2443 case SN_SOFO: 2444 res = read_sofo_section(fd, lp); 2445 break; 2446 2447 case SN_MAP: 2448 p = read_string(fd, len); /* <mapstr> */ 2449 if (p == NULL) 2450 goto endFAIL; 2451 set_map_str(lp, p); 2452 vim_free(p); 2453 break; 2454 2455 case SN_COMPOUND: 2456 res = read_compound(fd, lp, len); 2457 break; 2458 2459 case SN_NOBREAK: 2460 lp->sl_nobreak = TRUE; 2461 break; 2462 2463 case SN_SYLLABLE: 2464 lp->sl_syllable = read_string(fd, len); /* <syllable> */ 2465 if (lp->sl_syllable == NULL) 2466 goto endFAIL; 2467 if (init_syl_tab(lp) == FAIL) 2468 goto endFAIL; 2469 break; 2470 2471 default: 2472 /* Unsupported section. When it's required give an error 2473 * message. When it's not required skip the contents. */ 2474 if (c & SNF_REQUIRED) 2475 { 2476 EMSG(_("E770: Unsupported section in spell file")); 2477 goto endFAIL; 2478 } 2479 while (--len >= 0) 2480 if (getc(fd) < 0) 2481 goto truncerr; 2482 break; 2483 } 2484 if (res == SP_FORMERROR) 2485 { 2486 formerr: 2487 EMSG(_(e_format)); 2488 goto endFAIL; 2489 } 2490 if (res == SP_TRUNCERROR) 2491 { 2492 truncerr: 2493 EMSG(_(e_spell_trunc)); 2494 goto endFAIL; 2495 } 2496 if (res == SP_OTHERERROR) 2497 goto endFAIL; 2498 } 2499 2500 /* round 1: <LWORDTREE> 2501 * round 2: <KWORDTREE> 2502 * round 3: <PREFIXTREE> */ 2503 for (round = 1; round <= 3; ++round) 2504 { 2505 /* The tree size was computed when writing the file, so that we can 2506 * allocate it as one long block. <nodecount> */ 2507 len = (getc(fd) << 24) + (getc(fd) << 16) + (getc(fd) << 8) + getc(fd); 2508 if (len < 0) 2509 goto truncerr; 2510 if (len > 0) 2511 { 2512 /* Allocate the byte array. */ 2513 bp = lalloc((long_u)len, TRUE); 2514 if (bp == NULL) 2515 goto endFAIL; 2516 if (round == 1) 2517 lp->sl_fbyts = bp; 2518 else if (round == 2) 2519 lp->sl_kbyts = bp; 2520 else 2521 lp->sl_pbyts = bp; 2522 2523 /* Allocate the index array. */ 2524 ip = (idx_T *)lalloc_clear((long_u)(len * sizeof(int)), TRUE); 2525 if (ip == NULL) 2526 goto endFAIL; 2527 if (round == 1) 2528 lp->sl_fidxs = ip; 2529 else if (round == 2) 2530 lp->sl_kidxs = ip; 2531 else 2532 lp->sl_pidxs = ip; 2533 2534 /* Read the tree and store it in the array. */ 2535 idx = read_tree(fd, bp, ip, len, 0, round == 3, lp->sl_prefixcnt); 2536 if (idx == -1) 2537 goto truncerr; 2538 if (idx < 0) 2539 goto formerr; 2540 } 2541 } 2542 2543 /* For a new file link it in the list of spell files. */ 2544 if (old_lp == NULL) 2545 { 2546 lp->sl_next = first_lang; 2547 first_lang = lp; 2548 } 2549 2550 goto endOK; 2551 2552 endFAIL: 2553 if (lang != NULL) 2554 /* truncating the name signals the error to spell_load_lang() */ 2555 *lang = NUL; 2556 if (lp != NULL && old_lp == NULL) 2557 slang_free(lp); 2558 lp = NULL; 2559 2560 endOK: 2561 if (fd != NULL) 2562 fclose(fd); 2563 sourcing_name = save_sourcing_name; 2564 sourcing_lnum = save_sourcing_lnum; 2565 2566 return lp; 2567 } 2568 2569 /* 2570 * Read a length field from "fd" in "cnt_bytes" bytes. 2571 * Allocate memory, read the string into it and add a NUL at the end. 2572 * Returns NULL when the count is zero. 2573 * Sets "*cntp" to SP_*ERROR when there is an error, length of the result 2574 * otherwise. 2575 */ 2576 static char_u * 2577 read_cnt_string(fd, cnt_bytes, cntp) 2578 FILE *fd; 2579 int cnt_bytes; 2580 int *cntp; 2581 { 2582 int cnt = 0; 2583 int i; 2584 char_u *str; 2585 2586 /* read the length bytes, MSB first */ 2587 for (i = 0; i < cnt_bytes; ++i) 2588 cnt = (cnt << 8) + getc(fd); 2589 if (cnt < 0) 2590 { 2591 *cntp = SP_TRUNCERROR; 2592 return NULL; 2593 } 2594 *cntp = cnt; 2595 if (cnt == 0) 2596 return NULL; /* nothing to read, return NULL */ 2597 2598 str = read_string(fd, cnt); 2599 if (str == NULL) 2600 *cntp = SP_OTHERERROR; 2601 return str; 2602 } 2603 2604 /* 2605 * Read a string of length "cnt" from "fd" into allocated memory. 2606 * Returns NULL when out of memory. 2607 */ 2608 static char_u * 2609 read_string(fd, cnt) 2610 FILE *fd; 2611 int cnt; 2612 { 2613 char_u *str; 2614 int i; 2615 2616 /* allocate memory */ 2617 str = alloc((unsigned)cnt + 1); 2618 if (str != NULL) 2619 { 2620 /* Read the string. Doesn't check for truncated file. */ 2621 for (i = 0; i < cnt; ++i) 2622 str[i] = getc(fd); 2623 str[i] = NUL; 2624 } 2625 return str; 2626 } 2627 2628 /* 2629 * Read SN_REGION: <regionname> ... 2630 * Return SP_*ERROR flags. 2631 */ 2632 static int 2633 read_region_section(fd, lp, len) 2634 FILE *fd; 2635 slang_T *lp; 2636 int len; 2637 { 2638 int i; 2639 2640 if (len > 16) 2641 return SP_FORMERROR; 2642 for (i = 0; i < len; ++i) 2643 lp->sl_regions[i] = getc(fd); /* <regionname> */ 2644 lp->sl_regions[len] = NUL; 2645 return 0; 2646 } 2647 2648 /* 2649 * Read SN_CHARFLAGS section: <charflagslen> <charflags> 2650 * <folcharslen> <folchars> 2651 * Return SP_*ERROR flags. 2652 */ 2653 static int 2654 read_charflags_section(fd) 2655 FILE *fd; 2656 { 2657 char_u *flags; 2658 char_u *fol; 2659 int flagslen, follen; 2660 2661 /* <charflagslen> <charflags> */ 2662 flags = read_cnt_string(fd, 1, &flagslen); 2663 if (flagslen < 0) 2664 return flagslen; 2665 2666 /* <folcharslen> <folchars> */ 2667 fol = read_cnt_string(fd, 2, &follen); 2668 if (follen < 0) 2669 { 2670 vim_free(flags); 2671 return follen; 2672 } 2673 2674 /* Set the word-char flags and fill SPELL_ISUPPER() table. */ 2675 if (flags != NULL && fol != NULL) 2676 set_spell_charflags(flags, flagslen, fol); 2677 2678 vim_free(flags); 2679 vim_free(fol); 2680 2681 /* When <charflagslen> is zero then <fcharlen> must also be zero. */ 2682 if ((flags == NULL) != (fol == NULL)) 2683 return SP_FORMERROR; 2684 return 0; 2685 } 2686 2687 /* 2688 * Read SN_PREFCOND section. 2689 * Return SP_*ERROR flags. 2690 */ 2691 static int 2692 read_prefcond_section(fd, lp) 2693 FILE *fd; 2694 slang_T *lp; 2695 { 2696 int cnt; 2697 int i; 2698 int n; 2699 char_u *p; 2700 char_u buf[MAXWLEN + 1]; 2701 2702 /* <prefcondcnt> <prefcond> ... */ 2703 cnt = (getc(fd) << 8) + getc(fd); /* <prefcondcnt> */ 2704 if (cnt <= 0) 2705 return SP_FORMERROR; 2706 2707 lp->sl_prefprog = (regprog_T **)alloc_clear( 2708 (unsigned)sizeof(regprog_T *) * cnt); 2709 if (lp->sl_prefprog == NULL) 2710 return SP_OTHERERROR; 2711 lp->sl_prefixcnt = cnt; 2712 2713 for (i = 0; i < cnt; ++i) 2714 { 2715 /* <prefcond> : <condlen> <condstr> */ 2716 n = getc(fd); /* <condlen> */ 2717 if (n < 0 || n >= MAXWLEN) 2718 return SP_FORMERROR; 2719 2720 /* When <condlen> is zero we have an empty condition. Otherwise 2721 * compile the regexp program used to check for the condition. */ 2722 if (n > 0) 2723 { 2724 buf[0] = '^'; /* always match at one position only */ 2725 p = buf + 1; 2726 while (n-- > 0) 2727 *p++ = getc(fd); /* <condstr> */ 2728 *p = NUL; 2729 lp->sl_prefprog[i] = vim_regcomp(buf, RE_MAGIC + RE_STRING); 2730 } 2731 } 2732 return 0; 2733 } 2734 2735 /* 2736 * Read REP items section from "fd": <repcount> <rep> ... 2737 * Return SP_*ERROR flags. 2738 */ 2739 static int 2740 read_rep_section(fd, slang) 2741 FILE *fd; 2742 slang_T *slang; 2743 { 2744 int cnt; 2745 garray_T *gap; 2746 fromto_T *ftp; 2747 short *first; 2748 int i; 2749 2750 cnt = (getc(fd) << 8) + getc(fd); /* <repcount> */ 2751 if (cnt < 0) 2752 return SP_TRUNCERROR; 2753 2754 gap = &slang->sl_rep; 2755 if (ga_grow(gap, cnt) == FAIL) 2756 return SP_OTHERERROR; 2757 2758 /* <rep> : <repfromlen> <repfrom> <reptolen> <repto> */ 2759 for (; gap->ga_len < cnt; ++gap->ga_len) 2760 { 2761 ftp = &((fromto_T *)gap->ga_data)[gap->ga_len]; 2762 ftp->ft_from = read_cnt_string(fd, 1, &i); 2763 if (i < 0) 2764 return i; 2765 if (i == 0) 2766 return SP_FORMERROR; 2767 ftp->ft_to = read_cnt_string(fd, 1, &i); 2768 if (i <= 0) 2769 { 2770 vim_free(ftp->ft_from); 2771 if (i < 0) 2772 return i; 2773 return SP_FORMERROR; 2774 } 2775 } 2776 2777 /* Fill the first-index table. */ 2778 first = slang->sl_rep_first; 2779 for (i = 0; i < 256; ++i) 2780 first[i] = -1; 2781 for (i = 0; i < gap->ga_len; ++i) 2782 { 2783 ftp = &((fromto_T *)gap->ga_data)[i]; 2784 if (first[*ftp->ft_from] == -1) 2785 first[*ftp->ft_from] = i; 2786 } 2787 return 0; 2788 } 2789 2790 /* 2791 * Read SN_SAL section: <salflags> <salcount> <sal> ... 2792 * Return SP_*ERROR flags. 2793 */ 2794 static int 2795 read_sal_section(fd, slang) 2796 FILE *fd; 2797 slang_T *slang; 2798 { 2799 int i; 2800 int cnt; 2801 garray_T *gap; 2802 salitem_T *smp; 2803 int ccnt; 2804 char_u *p; 2805 int c = NUL; 2806 2807 slang->sl_sofo = FALSE; 2808 2809 i = getc(fd); /* <salflags> */ 2810 if (i & SAL_F0LLOWUP) 2811 slang->sl_followup = TRUE; 2812 if (i & SAL_COLLAPSE) 2813 slang->sl_collapse = TRUE; 2814 if (i & SAL_REM_ACCENTS) 2815 slang->sl_rem_accents = TRUE; 2816 2817 cnt = (getc(fd) << 8) + getc(fd); /* <salcount> */ 2818 if (cnt < 0) 2819 return SP_TRUNCERROR; 2820 2821 gap = &slang->sl_sal; 2822 ga_init2(gap, sizeof(salitem_T), 10); 2823 if (ga_grow(gap, cnt + 1) == FAIL) 2824 return SP_OTHERERROR; 2825 2826 /* <sal> : <salfromlen> <salfrom> <saltolen> <salto> */ 2827 for (; gap->ga_len < cnt; ++gap->ga_len) 2828 { 2829 smp = &((salitem_T *)gap->ga_data)[gap->ga_len]; 2830 ccnt = getc(fd); /* <salfromlen> */ 2831 if (ccnt < 0) 2832 return SP_TRUNCERROR; 2833 if ((p = alloc(ccnt + 2)) == NULL) 2834 return SP_OTHERERROR; 2835 smp->sm_lead = p; 2836 2837 /* Read up to the first special char into sm_lead. */ 2838 for (i = 0; i < ccnt; ++i) 2839 { 2840 c = getc(fd); /* <salfrom> */ 2841 if (vim_strchr((char_u *)"0123456789(-<^$", c) != NULL) 2842 break; 2843 *p++ = c; 2844 } 2845 smp->sm_leadlen = p - smp->sm_lead; 2846 *p++ = NUL; 2847 2848 /* Put (abc) chars in sm_oneof, if any. */ 2849 if (c == '(') 2850 { 2851 smp->sm_oneof = p; 2852 for (++i; i < ccnt; ++i) 2853 { 2854 c = getc(fd); /* <salfrom> */ 2855 if (c == ')') 2856 break; 2857 *p++ = c; 2858 } 2859 *p++ = NUL; 2860 if (++i < ccnt) 2861 c = getc(fd); 2862 } 2863 else 2864 smp->sm_oneof = NULL; 2865 2866 /* Any following chars go in sm_rules. */ 2867 smp->sm_rules = p; 2868 if (i < ccnt) 2869 /* store the char we got while checking for end of sm_lead */ 2870 *p++ = c; 2871 for (++i; i < ccnt; ++i) 2872 *p++ = getc(fd); /* <salfrom> */ 2873 *p++ = NUL; 2874 2875 /* <saltolen> <salto> */ 2876 smp->sm_to = read_cnt_string(fd, 1, &ccnt); 2877 if (ccnt < 0) 2878 { 2879 vim_free(smp->sm_lead); 2880 return ccnt; 2881 } 2882 2883 #ifdef FEAT_MBYTE 2884 if (has_mbyte) 2885 { 2886 /* convert the multi-byte strings to wide char strings */ 2887 smp->sm_lead_w = mb_str2wide(smp->sm_lead); 2888 smp->sm_leadlen = mb_charlen(smp->sm_lead); 2889 if (smp->sm_oneof == NULL) 2890 smp->sm_oneof_w = NULL; 2891 else 2892 smp->sm_oneof_w = mb_str2wide(smp->sm_oneof); 2893 if (smp->sm_to == NULL) 2894 smp->sm_to_w = NULL; 2895 else 2896 smp->sm_to_w = mb_str2wide(smp->sm_to); 2897 if (smp->sm_lead_w == NULL 2898 || (smp->sm_oneof_w == NULL && smp->sm_oneof != NULL) 2899 || (smp->sm_to_w == NULL && smp->sm_to != NULL)) 2900 { 2901 vim_free(smp->sm_lead); 2902 vim_free(smp->sm_to); 2903 vim_free(smp->sm_lead_w); 2904 vim_free(smp->sm_oneof_w); 2905 vim_free(smp->sm_to_w); 2906 return SP_OTHERERROR; 2907 } 2908 } 2909 #endif 2910 } 2911 2912 if (gap->ga_len > 0) 2913 { 2914 /* Add one extra entry to mark the end with an empty sm_lead. Avoids 2915 * that we need to check the index every time. */ 2916 smp = &((salitem_T *)gap->ga_data)[gap->ga_len]; 2917 if ((p = alloc(1)) == NULL) 2918 return SP_OTHERERROR; 2919 p[0] = NUL; 2920 smp->sm_lead = p; 2921 smp->sm_leadlen = 0; 2922 smp->sm_oneof = NULL; 2923 smp->sm_rules = p; 2924 smp->sm_to = NULL; 2925 #ifdef FEAT_MBYTE 2926 if (has_mbyte) 2927 { 2928 smp->sm_lead_w = mb_str2wide(smp->sm_lead); 2929 smp->sm_leadlen = 0; 2930 smp->sm_oneof_w = NULL; 2931 smp->sm_to_w = NULL; 2932 } 2933 #endif 2934 ++gap->ga_len; 2935 } 2936 2937 /* Fill the first-index table. */ 2938 set_sal_first(slang); 2939 2940 return 0; 2941 } 2942 2943 /* 2944 * SN_SOFO: <sofofromlen> <sofofrom> <sofotolen> <sofoto> 2945 * Return SP_*ERROR flags. 2946 */ 2947 static int 2948 read_sofo_section(fd, slang) 2949 FILE *fd; 2950 slang_T *slang; 2951 { 2952 int cnt; 2953 char_u *from, *to; 2954 int res; 2955 2956 slang->sl_sofo = TRUE; 2957 2958 /* <sofofromlen> <sofofrom> */ 2959 from = read_cnt_string(fd, 2, &cnt); 2960 if (cnt < 0) 2961 return cnt; 2962 2963 /* <sofotolen> <sofoto> */ 2964 to = read_cnt_string(fd, 2, &cnt); 2965 if (cnt < 0) 2966 { 2967 vim_free(from); 2968 return cnt; 2969 } 2970 2971 /* Store the info in slang->sl_sal and/or slang->sl_sal_first. */ 2972 if (from != NULL && to != NULL) 2973 res = set_sofo(slang, from, to); 2974 else if (from != NULL || to != NULL) 2975 res = SP_FORMERROR; /* only one of two strings is an error */ 2976 else 2977 res = 0; 2978 2979 vim_free(from); 2980 vim_free(to); 2981 return res; 2982 } 2983 2984 /* 2985 * Read the compound section from the .spl file: 2986 * <compmax> <compminlen> <compsylmax> <compflags> 2987 * Returns SP_*ERROR flags. 2988 */ 2989 static int 2990 read_compound(fd, slang, len) 2991 FILE *fd; 2992 slang_T *slang; 2993 int len; 2994 { 2995 int todo = len; 2996 int c; 2997 int atstart; 2998 char_u *pat; 2999 char_u *pp; 3000 char_u *cp; 3001 char_u *ap; 3002 3003 if (todo < 2) 3004 return SP_FORMERROR; /* need at least two bytes */ 3005 3006 --todo; 3007 c = getc(fd); /* <compmax> */ 3008 if (c < 2) 3009 c = MAXWLEN; 3010 slang->sl_compmax = c; 3011 3012 --todo; 3013 c = getc(fd); /* <compminlen> */ 3014 if (c < 1) 3015 c = 0; 3016 slang->sl_compminlen = c; 3017 3018 --todo; 3019 c = getc(fd); /* <compsylmax> */ 3020 if (c < 1) 3021 c = MAXWLEN; 3022 slang->sl_compsylmax = c; 3023 3024 /* Turn the COMPOUNDFLAGS items into a regexp pattern: 3025 * "a[bc]/a*b+" -> "^\(a[bc]\|a*b\+\)$". 3026 * Inserting backslashes may double the length, "^\(\)$<Nul>" is 7 bytes. 3027 * Conversion to utf-8 may double the size. */ 3028 c = todo * 2 + 7; 3029 #ifdef FEAT_MBYTE 3030 if (enc_utf8) 3031 c += todo * 2; 3032 #endif 3033 pat = alloc((unsigned)c); 3034 if (pat == NULL) 3035 return SP_OTHERERROR; 3036 3037 /* We also need a list of all flags that can appear at the start and one 3038 * for all flags. */ 3039 cp = alloc(todo + 1); 3040 if (cp == NULL) 3041 { 3042 vim_free(pat); 3043 return SP_OTHERERROR; 3044 } 3045 slang->sl_compstartflags = cp; 3046 *cp = NUL; 3047 3048 ap = alloc(todo + 1); 3049 if (ap == NULL) 3050 { 3051 vim_free(pat); 3052 return SP_OTHERERROR; 3053 } 3054 slang->sl_compallflags = ap; 3055 *ap = NUL; 3056 3057 pp = pat; 3058 *pp++ = '^'; 3059 *pp++ = '\\'; 3060 *pp++ = '('; 3061 3062 atstart = 1; 3063 while (todo-- > 0) 3064 { 3065 c = getc(fd); /* <compflags> */ 3066 3067 /* Add all flags to "sl_compallflags". */ 3068 if (vim_strchr((char_u *)"+*[]/", c) == NULL 3069 && !byte_in_str(slang->sl_compallflags, c)) 3070 { 3071 *ap++ = c; 3072 *ap = NUL; 3073 } 3074 3075 if (atstart != 0) 3076 { 3077 /* At start of item: copy flags to "sl_compstartflags". For a 3078 * [abc] item set "atstart" to 2 and copy up to the ']'. */ 3079 if (c == '[') 3080 atstart = 2; 3081 else if (c == ']') 3082 atstart = 0; 3083 else 3084 { 3085 if (!byte_in_str(slang->sl_compstartflags, c)) 3086 { 3087 *cp++ = c; 3088 *cp = NUL; 3089 } 3090 if (atstart == 1) 3091 atstart = 0; 3092 } 3093 } 3094 if (c == '/') /* slash separates two items */ 3095 { 3096 *pp++ = '\\'; 3097 *pp++ = '|'; 3098 atstart = 1; 3099 } 3100 else /* normal char, "[abc]" and '*' are copied as-is */ 3101 { 3102 if (c == '+' || c == '~') 3103 *pp++ = '\\'; /* "a+" becomes "a\+" */ 3104 #ifdef FEAT_MBYTE 3105 if (enc_utf8) 3106 pp += mb_char2bytes(c, pp); 3107 else 3108 #endif 3109 *pp++ = c; 3110 } 3111 } 3112 3113 *pp++ = '\\'; 3114 *pp++ = ')'; 3115 *pp++ = '$'; 3116 *pp = NUL; 3117 3118 slang->sl_compprog = vim_regcomp(pat, RE_MAGIC + RE_STRING + RE_STRICT); 3119 vim_free(pat); 3120 if (slang->sl_compprog == NULL) 3121 return SP_FORMERROR; 3122 3123 return 0; 3124 } 3125 3126 /* 3127 * Return TRUE if byte "n" appears in "str". 3128 * Like strchr() but independent of locale. 3129 */ 3130 static int 3131 byte_in_str(str, n) 3132 char_u *str; 3133 int n; 3134 { 3135 char_u *p; 3136 3137 for (p = str; *p != NUL; ++p) 3138 if (*p == n) 3139 return TRUE; 3140 return FALSE; 3141 } 3142 3143 #define SY_MAXLEN 30 3144 typedef struct syl_item_S 3145 { 3146 char_u sy_chars[SY_MAXLEN]; /* the sequence of chars */ 3147 int sy_len; 3148 } syl_item_T; 3149 3150 /* 3151 * Truncate "slang->sl_syllable" at the first slash and put the following items 3152 * in "slang->sl_syl_items". 3153 */ 3154 static int 3155 init_syl_tab(slang) 3156 slang_T *slang; 3157 { 3158 char_u *p; 3159 char_u *s; 3160 int l; 3161 syl_item_T *syl; 3162 3163 ga_init2(&slang->sl_syl_items, sizeof(syl_item_T), 4); 3164 p = vim_strchr(slang->sl_syllable, '/'); 3165 while (p != NULL) 3166 { 3167 *p++ = NUL; 3168 if (*p == NUL) /* trailing slash */ 3169 break; 3170 s = p; 3171 p = vim_strchr(p, '/'); 3172 if (p == NULL) 3173 l = STRLEN(s); 3174 else 3175 l = p - s; 3176 if (l >= SY_MAXLEN) 3177 return SP_FORMERROR; 3178 if (ga_grow(&slang->sl_syl_items, 1) == FAIL) 3179 return SP_OTHERERROR; 3180 syl = ((syl_item_T *)slang->sl_syl_items.ga_data) 3181 + slang->sl_syl_items.ga_len++; 3182 vim_strncpy(syl->sy_chars, s, l); 3183 syl->sy_len = l; 3184 } 3185 return OK; 3186 } 3187 3188 /* 3189 * Count the number of syllables in "word". 3190 * When "word" contains spaces the syllables after the last space are counted. 3191 * Returns zero if syllables are not defines. 3192 */ 3193 static int 3194 count_syllables(slang, word) 3195 slang_T *slang; 3196 char_u *word; 3197 { 3198 int cnt = 0; 3199 int skip = FALSE; 3200 char_u *p; 3201 int len; 3202 int i; 3203 syl_item_T *syl; 3204 int c; 3205 3206 if (slang->sl_syllable == NULL) 3207 return 0; 3208 3209 for (p = word; *p != NUL; p += len) 3210 { 3211 /* When running into a space reset counter. */ 3212 if (*p == ' ') 3213 { 3214 len = 1; 3215 cnt = 0; 3216 continue; 3217 } 3218 3219 /* Find longest match of syllable items. */ 3220 len = 0; 3221 for (i = 0; i < slang->sl_syl_items.ga_len; ++i) 3222 { 3223 syl = ((syl_item_T *)slang->sl_syl_items.ga_data) + i; 3224 if (syl->sy_len > len 3225 && STRNCMP(p, syl->sy_chars, syl->sy_len) == 0) 3226 len = syl->sy_len; 3227 } 3228 if (len != 0) /* found a match, count syllable */ 3229 { 3230 ++cnt; 3231 skip = FALSE; 3232 } 3233 else 3234 { 3235 /* No recognized syllable item, at least a syllable char then? */ 3236 #ifdef FEAT_MBYTE 3237 c = mb_ptr2char(p); 3238 len = (*mb_ptr2len)(p); 3239 #else 3240 c = *p; 3241 len = 1; 3242 #endif 3243 if (vim_strchr(slang->sl_syllable, c) == NULL) 3244 skip = FALSE; /* No, search for next syllable */ 3245 else if (!skip) 3246 { 3247 ++cnt; /* Yes, count it */ 3248 skip = TRUE; /* don't count following syllable chars */ 3249 } 3250 } 3251 } 3252 return cnt; 3253 } 3254 3255 /* 3256 * Set the SOFOFROM and SOFOTO items in language "lp". 3257 * Returns SP_*ERROR flags when there is something wrong. 3258 */ 3259 static int 3260 set_sofo(lp, from, to) 3261 slang_T *lp; 3262 char_u *from; 3263 char_u *to; 3264 { 3265 int i; 3266 3267 #ifdef FEAT_MBYTE 3268 garray_T *gap; 3269 char_u *s; 3270 char_u *p; 3271 int c; 3272 int *inp; 3273 3274 if (has_mbyte) 3275 { 3276 /* Use "sl_sal" as an array with 256 pointers to a list of wide 3277 * characters. The index is the low byte of the character. 3278 * The list contains from-to pairs with a terminating NUL. 3279 * sl_sal_first[] is used for latin1 "from" characters. */ 3280 gap = &lp->sl_sal; 3281 ga_init2(gap, sizeof(int *), 1); 3282 if (ga_grow(gap, 256) == FAIL) 3283 return SP_OTHERERROR; 3284 vim_memset(gap->ga_data, 0, sizeof(int *) * 256); 3285 gap->ga_len = 256; 3286 3287 /* First count the number of items for each list. Temporarily use 3288 * sl_sal_first[] for this. */ 3289 for (p = from, s = to; *p != NUL && *s != NUL; ) 3290 { 3291 c = mb_cptr2char_adv(&p); 3292 mb_cptr_adv(s); 3293 if (c >= 256) 3294 ++lp->sl_sal_first[c & 0xff]; 3295 } 3296 if (*p != NUL || *s != NUL) /* lengths differ */ 3297 return SP_FORMERROR; 3298 3299 /* Allocate the lists. */ 3300 for (i = 0; i < 256; ++i) 3301 if (lp->sl_sal_first[i] > 0) 3302 { 3303 p = alloc(sizeof(int) * (lp->sl_sal_first[i] * 2 + 1)); 3304 if (p == NULL) 3305 return SP_OTHERERROR; 3306 ((int **)gap->ga_data)[i] = (int *)p; 3307 *(int *)p = 0; 3308 } 3309 3310 /* Put the characters up to 255 in sl_sal_first[] the rest in a sl_sal 3311 * list. */ 3312 vim_memset(lp->sl_sal_first, 0, sizeof(salfirst_T) * 256); 3313 for (p = from, s = to; *p != NUL && *s != NUL; ) 3314 { 3315 c = mb_cptr2char_adv(&p); 3316 i = mb_cptr2char_adv(&s); 3317 if (c >= 256) 3318 { 3319 /* Append the from-to chars at the end of the list with 3320 * the low byte. */ 3321 inp = ((int **)gap->ga_data)[c & 0xff]; 3322 while (*inp != 0) 3323 ++inp; 3324 *inp++ = c; /* from char */ 3325 *inp++ = i; /* to char */ 3326 *inp++ = NUL; /* NUL at the end */ 3327 } 3328 else 3329 /* mapping byte to char is done in sl_sal_first[] */ 3330 lp->sl_sal_first[c] = i; 3331 } 3332 } 3333 else 3334 #endif 3335 { 3336 /* mapping bytes to bytes is done in sl_sal_first[] */ 3337 if (STRLEN(from) != STRLEN(to)) 3338 return SP_FORMERROR; 3339 3340 for (i = 0; to[i] != NUL; ++i) 3341 lp->sl_sal_first[from[i]] = to[i]; 3342 lp->sl_sal.ga_len = 1; /* indicates we have soundfolding */ 3343 } 3344 3345 return 0; 3346 } 3347 3348 /* 3349 * Fill the first-index table for "lp". 3350 */ 3351 static void 3352 set_sal_first(lp) 3353 slang_T *lp; 3354 { 3355 salfirst_T *sfirst; 3356 int i; 3357 salitem_T *smp; 3358 int c; 3359 garray_T *gap = &lp->sl_sal; 3360 3361 sfirst = lp->sl_sal_first; 3362 for (i = 0; i < 256; ++i) 3363 sfirst[i] = -1; 3364 smp = (salitem_T *)gap->ga_data; 3365 for (i = 0; i < gap->ga_len; ++i) 3366 { 3367 #ifdef FEAT_MBYTE 3368 if (has_mbyte) 3369 /* Use the lowest byte of the first character. For latin1 it's 3370 * the character, for other encodings it should differ for most 3371 * characters. */ 3372 c = *smp[i].sm_lead_w & 0xff; 3373 else 3374 #endif 3375 c = *smp[i].sm_lead; 3376 if (sfirst[c] == -1) 3377 { 3378 sfirst[c] = i; 3379 #ifdef FEAT_MBYTE 3380 if (has_mbyte) 3381 { 3382 int n; 3383 3384 /* Make sure all entries with this byte are following each 3385 * other. Move the ones that are in the wrong position. Do 3386 * keep the same ordering! */ 3387 while (i + 1 < gap->ga_len 3388 && (*smp[i + 1].sm_lead_w & 0xff) == c) 3389 /* Skip over entry with same index byte. */ 3390 ++i; 3391 3392 for (n = 1; i + n < gap->ga_len; ++n) 3393 if ((*smp[i + n].sm_lead_w & 0xff) == c) 3394 { 3395 salitem_T tsal; 3396 3397 /* Move entry with same index byte after the entries 3398 * we already found. */ 3399 ++i; 3400 --n; 3401 tsal = smp[i + n]; 3402 mch_memmove(smp + i + 1, smp + i, 3403 sizeof(salitem_T) * n); 3404 smp[i] = tsal; 3405 } 3406 } 3407 #endif 3408 } 3409 } 3410 } 3411 3412 #ifdef FEAT_MBYTE 3413 /* 3414 * Turn a multi-byte string into a wide character string. 3415 * Return it in allocated memory (NULL for out-of-memory) 3416 */ 3417 static int * 3418 mb_str2wide(s) 3419 char_u *s; 3420 { 3421 int *res; 3422 char_u *p; 3423 int i = 0; 3424 3425 res = (int *)alloc(sizeof(int) * (mb_charlen(s) + 1)); 3426 if (res != NULL) 3427 { 3428 for (p = s; *p != NUL; ) 3429 res[i++] = mb_ptr2char_adv(&p); 3430 res[i] = NUL; 3431 } 3432 return res; 3433 } 3434 #endif 3435 3436 /* 3437 * Read one row of siblings from the spell file and store it in the byte array 3438 * "byts" and index array "idxs". Recursively read the children. 3439 * 3440 * NOTE: The code here must match put_node(). 3441 * 3442 * Returns the index follosing the siblings. 3443 * Returns -1 if the file is shorter than expected. 3444 * Returns -2 if there is a format error. 3445 */ 3446 static idx_T 3447 read_tree(fd, byts, idxs, maxidx, startidx, prefixtree, maxprefcondnr) 3448 FILE *fd; 3449 char_u *byts; 3450 idx_T *idxs; 3451 int maxidx; /* size of arrays */ 3452 idx_T startidx; /* current index in "byts" and "idxs" */ 3453 int prefixtree; /* TRUE for reading PREFIXTREE */ 3454 int maxprefcondnr; /* maximum for <prefcondnr> */ 3455 { 3456 int len; 3457 int i; 3458 int n; 3459 idx_T idx = startidx; 3460 int c; 3461 int c2; 3462 #define SHARED_MASK 0x8000000 3463 3464 len = getc(fd); /* <siblingcount> */ 3465 if (len <= 0) 3466 return -1; 3467 3468 if (startidx + len >= maxidx) 3469 return -2; 3470 byts[idx++] = len; 3471 3472 /* Read the byte values, flag/region bytes and shared indexes. */ 3473 for (i = 1; i <= len; ++i) 3474 { 3475 c = getc(fd); /* <byte> */ 3476 if (c < 0) 3477 return -1; 3478 if (c <= BY_SPECIAL) 3479 { 3480 if (c == BY_NOFLAGS && !prefixtree) 3481 { 3482 /* No flags, all regions. */ 3483 idxs[idx] = 0; 3484 c = 0; 3485 } 3486 else if (c != BY_INDEX) 3487 { 3488 if (prefixtree) 3489 { 3490 /* Read the optional pflags byte, the prefix ID and the 3491 * condition nr. In idxs[] store the prefix ID in the low 3492 * byte, the condition index shifted up 8 bits, the flags 3493 * shifted up 24 bits. */ 3494 if (c == BY_FLAGS) 3495 c = getc(fd) << 24; /* <pflags> */ 3496 else 3497 c = 0; 3498 3499 c |= getc(fd); /* <affixID> */ 3500 3501 n = (getc(fd) << 8) + getc(fd); /* <prefcondnr> */ 3502 if (n >= maxprefcondnr) 3503 return -2; 3504 c |= (n << 8); 3505 } 3506 else /* c must be BY_FLAGS or BY_FLAGS2 */ 3507 { 3508 /* Read flags and optional region and prefix ID. In 3509 * idxs[] the flags go in the low two bytes, region above 3510 * that and prefix ID above the region. */ 3511 c2 = c; 3512 c = getc(fd); /* <flags> */ 3513 if (c2 == BY_FLAGS2) 3514 c = (getc(fd) << 8) + c; /* <flags2> */ 3515 if (c & WF_REGION) 3516 c = (getc(fd) << 16) + c; /* <region> */ 3517 if (c & WF_AFX) 3518 c = (getc(fd) << 24) + c; /* <affixID> */ 3519 } 3520 3521 idxs[idx] = c; 3522 c = 0; 3523 } 3524 else /* c == BY_INDEX */ 3525 { 3526 /* <nodeidx> */ 3527 n = (getc(fd) << 16) + (getc(fd) << 8) + getc(fd); 3528 if (n < 0 || n >= maxidx) 3529 return -2; 3530 idxs[idx] = n + SHARED_MASK; 3531 c = getc(fd); /* <xbyte> */ 3532 } 3533 } 3534 byts[idx++] = c; 3535 } 3536 3537 /* Recursively read the children for non-shared siblings. 3538 * Skip the end-of-word ones (zero byte value) and the shared ones (and 3539 * remove SHARED_MASK) */ 3540 for (i = 1; i <= len; ++i) 3541 if (byts[startidx + i] != 0) 3542 { 3543 if (idxs[startidx + i] & SHARED_MASK) 3544 idxs[startidx + i] &= ~SHARED_MASK; 3545 else 3546 { 3547 idxs[startidx + i] = idx; 3548 idx = read_tree(fd, byts, idxs, maxidx, idx, 3549 prefixtree, maxprefcondnr); 3550 if (idx < 0) 3551 break; 3552 } 3553 } 3554 3555 return idx; 3556 } 3557 3558 /* 3559 * Parse 'spelllang' and set buf->b_langp accordingly. 3560 * Returns NULL if it's OK, an error message otherwise. 3561 */ 3562 char_u * 3563 did_set_spelllang(buf) 3564 buf_T *buf; 3565 { 3566 garray_T ga; 3567 char_u *splp; 3568 char_u *region; 3569 char_u region_cp[3]; 3570 int filename; 3571 int region_mask; 3572 slang_T *slang; 3573 int c; 3574 char_u lang[MAXWLEN + 1]; 3575 char_u spf_name[MAXPATHL]; 3576 int len; 3577 char_u *p; 3578 int round; 3579 char_u *spf; 3580 char_u *use_region = NULL; 3581 int dont_use_region = FALSE; 3582 int nobreak = FALSE; 3583 int i, j; 3584 langp_T *lp, *lp2; 3585 3586 ga_init2(&ga, sizeof(langp_T), 2); 3587 clear_midword(buf); 3588 3589 /* loop over comma separated language names. */ 3590 for (splp = buf->b_p_spl; *splp != NUL; ) 3591 { 3592 /* Get one language name. */ 3593 copy_option_part(&splp, lang, MAXWLEN, ","); 3594 3595 region = NULL; 3596 len = STRLEN(lang); 3597 3598 /* If the name ends in ".spl" use it as the name of the spell file. 3599 * If there is a region name let "region" point to it and remove it 3600 * from the name. */ 3601 if (len > 4 && fnamecmp(lang + len - 4, ".spl") == 0) 3602 { 3603 filename = TRUE; 3604 3605 /* Locate a region and remove it from the file name. */ 3606 p = vim_strchr(gettail(lang), '_'); 3607 if (p != NULL && ASCII_ISALPHA(p[1]) && ASCII_ISALPHA(p[2]) 3608 && !ASCII_ISALPHA(p[3])) 3609 { 3610 vim_strncpy(region_cp, p + 1, 2); 3611 mch_memmove(p, p + 3, len - (p - lang) - 2); 3612 len -= 3; 3613 region = region_cp; 3614 } 3615 else 3616 dont_use_region = TRUE; 3617 3618 /* Check if we loaded this language before. */ 3619 for (slang = first_lang; slang != NULL; slang = slang->sl_next) 3620 if (fullpathcmp(lang, slang->sl_fname, FALSE) == FPC_SAME) 3621 break; 3622 } 3623 else 3624 { 3625 filename = FALSE; 3626 if (len > 3 && lang[len - 3] == '_') 3627 { 3628 region = lang + len - 2; 3629 len -= 3; 3630 lang[len] = NUL; 3631 } 3632 else 3633 dont_use_region = TRUE; 3634 3635 /* Check if we loaded this language before. */ 3636 for (slang = first_lang; slang != NULL; slang = slang->sl_next) 3637 if (STRICMP(lang, slang->sl_name) == 0) 3638 break; 3639 } 3640 3641 if (region != NULL) 3642 { 3643 /* If the region differs from what was used before then don't 3644 * use it for 'spellfile'. */ 3645 if (use_region != NULL && STRCMP(region, use_region) != 0) 3646 dont_use_region = TRUE; 3647 use_region = region; 3648 } 3649 3650 /* If not found try loading the language now. */ 3651 if (slang == NULL) 3652 { 3653 if (filename) 3654 (void)spell_load_file(lang, lang, NULL, FALSE); 3655 else 3656 spell_load_lang(lang); 3657 } 3658 3659 /* 3660 * Loop over the languages, there can be several files for "lang". 3661 */ 3662 for (slang = first_lang; slang != NULL; slang = slang->sl_next) 3663 if (filename ? fullpathcmp(lang, slang->sl_fname, FALSE) == FPC_SAME 3664 : STRICMP(lang, slang->sl_name) == 0) 3665 { 3666 region_mask = REGION_ALL; 3667 if (!filename && region != NULL) 3668 { 3669 /* find region in sl_regions */ 3670 c = find_region(slang->sl_regions, region); 3671 if (c == REGION_ALL) 3672 { 3673 if (slang->sl_add) 3674 { 3675 if (*slang->sl_regions != NUL) 3676 /* This addition file is for other regions. */ 3677 region_mask = 0; 3678 } 3679 else 3680 /* This is probably an error. Give a warning and 3681 * accept the words anyway. */ 3682 smsg((char_u *) 3683 _("Warning: region %s not supported"), 3684 region); 3685 } 3686 else 3687 region_mask = 1 << c; 3688 } 3689 3690 if (region_mask != 0) 3691 { 3692 if (ga_grow(&ga, 1) == FAIL) 3693 { 3694 ga_clear(&ga); 3695 return e_outofmem; 3696 } 3697 LANGP_ENTRY(ga, ga.ga_len)->lp_slang = slang; 3698 LANGP_ENTRY(ga, ga.ga_len)->lp_region = region_mask; 3699 ++ga.ga_len; 3700 use_midword(slang, buf); 3701 if (slang->sl_nobreak) 3702 nobreak = TRUE; 3703 } 3704 } 3705 } 3706 3707 /* round 0: load int_wordlist, if possible. 3708 * round 1: load first name in 'spellfile'. 3709 * round 2: load second name in 'spellfile. 3710 * etc. */ 3711 spf = curbuf->b_p_spf; 3712 for (round = 0; round == 0 || *spf != NUL; ++round) 3713 { 3714 if (round == 0) 3715 { 3716 /* Internal wordlist, if there is one. */ 3717 if (int_wordlist == NULL) 3718 continue; 3719 int_wordlist_spl(spf_name); 3720 } 3721 else 3722 { 3723 /* One entry in 'spellfile'. */ 3724 copy_option_part(&spf, spf_name, MAXPATHL - 5, ","); 3725 STRCAT(spf_name, ".spl"); 3726 3727 /* If it was already found above then skip it. */ 3728 for (c = 0; c < ga.ga_len; ++c) 3729 { 3730 p = LANGP_ENTRY(ga, c)->lp_slang->sl_fname; 3731 if (p != NULL && fullpathcmp(spf_name, p, FALSE) == FPC_SAME) 3732 break; 3733 } 3734 if (c < ga.ga_len) 3735 continue; 3736 } 3737 3738 /* Check if it was loaded already. */ 3739 for (slang = first_lang; slang != NULL; slang = slang->sl_next) 3740 if (fullpathcmp(spf_name, slang->sl_fname, FALSE) == FPC_SAME) 3741 break; 3742 if (slang == NULL) 3743 { 3744 /* Not loaded, try loading it now. The language name includes the 3745 * region name, the region is ignored otherwise. for int_wordlist 3746 * use an arbitrary name. */ 3747 if (round == 0) 3748 STRCPY(lang, "internal wordlist"); 3749 else 3750 { 3751 vim_strncpy(lang, gettail(spf_name), MAXWLEN); 3752 p = vim_strchr(lang, '.'); 3753 if (p != NULL) 3754 *p = NUL; /* truncate at ".encoding.add" */ 3755 } 3756 slang = spell_load_file(spf_name, lang, NULL, TRUE); 3757 3758 /* If one of the languages has NOBREAK we assume the addition 3759 * files also have this. */ 3760 if (slang != NULL && nobreak) 3761 slang->sl_nobreak = TRUE; 3762 } 3763 if (slang != NULL && ga_grow(&ga, 1) == OK) 3764 { 3765 region_mask = REGION_ALL; 3766 if (use_region != NULL && !dont_use_region) 3767 { 3768 /* find region in sl_regions */ 3769 c = find_region(slang->sl_regions, use_region); 3770 if (c != REGION_ALL) 3771 region_mask = 1 << c; 3772 else if (*slang->sl_regions != NUL) 3773 /* This spell file is for other regions. */ 3774 region_mask = 0; 3775 } 3776 3777 if (region_mask != 0) 3778 { 3779 LANGP_ENTRY(ga, ga.ga_len)->lp_slang = slang; 3780 LANGP_ENTRY(ga, ga.ga_len)->lp_sallang = NULL; 3781 LANGP_ENTRY(ga, ga.ga_len)->lp_replang = NULL; 3782 LANGP_ENTRY(ga, ga.ga_len)->lp_region = region_mask; 3783 ++ga.ga_len; 3784 use_midword(slang, buf); 3785 } 3786 } 3787 } 3788 3789 /* Everything is fine, store the new b_langp value. */ 3790 ga_clear(&buf->b_langp); 3791 buf->b_langp = ga; 3792 3793 /* For each language figure out what language to use for sound folding and 3794 * REP items. If the language doesn't support it itself use another one 3795 * with the same name. E.g. for "en-math" use "en". */ 3796 for (i = 0; i < ga.ga_len; ++i) 3797 { 3798 lp = LANGP_ENTRY(ga, i); 3799 3800 /* sound folding */ 3801 if (lp->lp_slang->sl_sal.ga_len > 0) 3802 /* language does sound folding itself */ 3803 lp->lp_sallang = lp->lp_slang; 3804 else 3805 /* find first similar language that does sound folding */ 3806 for (j = 0; j < ga.ga_len; ++j) 3807 { 3808 lp2 = LANGP_ENTRY(ga, j); 3809 if (lp2->lp_slang->sl_sal.ga_len > 0 3810 && STRNCMP(lp->lp_slang->sl_name, 3811 lp2->lp_slang->sl_name, 2) == 0) 3812 { 3813 lp->lp_sallang = lp2->lp_slang; 3814 break; 3815 } 3816 } 3817 3818 /* REP items */ 3819 if (lp->lp_slang->sl_rep.ga_len > 0) 3820 /* language has REP items itself */ 3821 lp->lp_replang = lp->lp_slang; 3822 else 3823 /* find first similar language that does sound folding */ 3824 for (j = 0; j < ga.ga_len; ++j) 3825 { 3826 lp2 = LANGP_ENTRY(ga, j); 3827 if (lp2->lp_slang->sl_rep.ga_len > 0 3828 && STRNCMP(lp->lp_slang->sl_name, 3829 lp2->lp_slang->sl_name, 2) == 0) 3830 { 3831 lp->lp_replang = lp2->lp_slang; 3832 break; 3833 } 3834 } 3835 } 3836 3837 return NULL; 3838 } 3839 3840 /* 3841 * Clear the midword characters for buffer "buf". 3842 */ 3843 static void 3844 clear_midword(buf) 3845 buf_T *buf; 3846 { 3847 vim_memset(buf->b_spell_ismw, 0, 256); 3848 #ifdef FEAT_MBYTE 3849 vim_free(buf->b_spell_ismw_mb); 3850 buf->b_spell_ismw_mb = NULL; 3851 #endif 3852 } 3853 3854 /* 3855 * Use the "sl_midword" field of language "lp" for buffer "buf". 3856 * They add up to any currently used midword characters. 3857 */ 3858 static void 3859 use_midword(lp, buf) 3860 slang_T *lp; 3861 buf_T *buf; 3862 { 3863 char_u *p; 3864 3865 if (lp->sl_midword == NULL) /* there aren't any */ 3866 return; 3867 3868 for (p = lp->sl_midword; *p != NUL; ) 3869 #ifdef FEAT_MBYTE 3870 if (has_mbyte) 3871 { 3872 int c, l, n; 3873 char_u *bp; 3874 3875 c = mb_ptr2char(p); 3876 l = (*mb_ptr2len)(p); 3877 if (c < 256 && l <= 2) 3878 buf->b_spell_ismw[c] = TRUE; 3879 else if (buf->b_spell_ismw_mb == NULL) 3880 /* First multi-byte char in "b_spell_ismw_mb". */ 3881 buf->b_spell_ismw_mb = vim_strnsave(p, l); 3882 else 3883 { 3884 /* Append multi-byte chars to "b_spell_ismw_mb". */ 3885 n = STRLEN(buf->b_spell_ismw_mb); 3886 bp = vim_strnsave(buf->b_spell_ismw_mb, n + l); 3887 if (bp != NULL) 3888 { 3889 vim_free(buf->b_spell_ismw_mb); 3890 buf->b_spell_ismw_mb = bp; 3891 vim_strncpy(bp + n, p, l); 3892 } 3893 } 3894 p += l; 3895 } 3896 else 3897 #endif 3898 buf->b_spell_ismw[*p++] = TRUE; 3899 } 3900 3901 /* 3902 * Find the region "region[2]" in "rp" (points to "sl_regions"). 3903 * Each region is simply stored as the two characters of it's name. 3904 * Returns the index if found (first is 0), REGION_ALL if not found. 3905 */ 3906 static int 3907 find_region(rp, region) 3908 char_u *rp; 3909 char_u *region; 3910 { 3911 int i; 3912 3913 for (i = 0; ; i += 2) 3914 { 3915 if (rp[i] == NUL) 3916 return REGION_ALL; 3917 if (rp[i] == region[0] && rp[i + 1] == region[1]) 3918 break; 3919 } 3920 return i / 2; 3921 } 3922 3923 /* 3924 * Return case type of word: 3925 * w word 0 3926 * Word WF_ONECAP 3927 * W WORD WF_ALLCAP 3928 * WoRd wOrd WF_KEEPCAP 3929 */ 3930 static int 3931 captype(word, end) 3932 char_u *word; 3933 char_u *end; /* When NULL use up to NUL byte. */ 3934 { 3935 char_u *p; 3936 int c; 3937 int firstcap; 3938 int allcap; 3939 int past_second = FALSE; /* past second word char */ 3940 3941 /* find first letter */ 3942 for (p = word; !spell_iswordp_nmw(p); mb_ptr_adv(p)) 3943 if (end == NULL ? *p == NUL : p >= end) 3944 return 0; /* only non-word characters, illegal word */ 3945 #ifdef FEAT_MBYTE 3946 if (has_mbyte) 3947 c = mb_ptr2char_adv(&p); 3948 else 3949 #endif 3950 c = *p++; 3951 firstcap = allcap = SPELL_ISUPPER(c); 3952 3953 /* 3954 * Need to check all letters to find a word with mixed upper/lower. 3955 * But a word with an upper char only at start is a ONECAP. 3956 */ 3957 for ( ; end == NULL ? *p != NUL : p < end; mb_ptr_adv(p)) 3958 if (spell_iswordp_nmw(p)) 3959 { 3960 c = PTR2CHAR(p); 3961 if (!SPELL_ISUPPER(c)) 3962 { 3963 /* UUl -> KEEPCAP */ 3964 if (past_second && allcap) 3965 return WF_KEEPCAP; 3966 allcap = FALSE; 3967 } 3968 else if (!allcap) 3969 /* UlU -> KEEPCAP */ 3970 return WF_KEEPCAP; 3971 past_second = TRUE; 3972 } 3973 3974 if (allcap) 3975 return WF_ALLCAP; 3976 if (firstcap) 3977 return WF_ONECAP; 3978 return 0; 3979 } 3980 3981 /* 3982 * Like captype() but for a KEEPCAP word add ONECAP if the word starts with a 3983 * capital. So that make_case_word() can turn WOrd into Word. 3984 * Add ALLCAP for "WOrD". 3985 */ 3986 static int 3987 badword_captype(word, end) 3988 char_u *word; 3989 char_u *end; 3990 { 3991 int flags = captype(word, end); 3992 int c; 3993 int l, u; 3994 int first; 3995 char_u *p; 3996 3997 if (flags & WF_KEEPCAP) 3998 { 3999 /* Count the number of UPPER and lower case letters. */ 4000 l = u = 0; 4001 first = FALSE; 4002 for (p = word; p < end; mb_ptr_adv(p)) 4003 { 4004 c = PTR2CHAR(p); 4005 if (SPELL_ISUPPER(c)) 4006 { 4007 ++u; 4008 if (p == word) 4009 first = TRUE; 4010 } 4011 else 4012 ++l; 4013 } 4014 4015 /* If there are more UPPER than lower case letters suggest an 4016 * ALLCAP word. Otherwise, if the first letter is UPPER then 4017 * suggest ONECAP. Exception: "ALl" most likely should be "All", 4018 * require three upper case letters. */ 4019 if (u > l && u > 2) 4020 flags |= WF_ALLCAP; 4021 else if (first) 4022 flags |= WF_ONECAP; 4023 } 4024 return flags; 4025 } 4026 4027 # if defined(FEAT_MBYTE) || defined(EXITFREE) || defined(PROTO) 4028 /* 4029 * Free all languages. 4030 */ 4031 void 4032 spell_free_all() 4033 { 4034 slang_T *slang; 4035 buf_T *buf; 4036 char_u fname[MAXPATHL]; 4037 4038 /* Go through all buffers and handle 'spelllang'. */ 4039 for (buf = firstbuf; buf != NULL; buf = buf->b_next) 4040 ga_clear(&buf->b_langp); 4041 4042 while (first_lang != NULL) 4043 { 4044 slang = first_lang; 4045 first_lang = slang->sl_next; 4046 slang_free(slang); 4047 } 4048 4049 if (int_wordlist != NULL) 4050 { 4051 /* Delete the internal wordlist and its .spl file */ 4052 mch_remove(int_wordlist); 4053 int_wordlist_spl(fname); 4054 mch_remove(fname); 4055 vim_free(int_wordlist); 4056 int_wordlist = NULL; 4057 } 4058 4059 init_spell_chartab(); 4060 } 4061 # endif 4062 4063 # if defined(FEAT_MBYTE) || defined(PROTO) 4064 /* 4065 * Clear all spelling tables and reload them. 4066 * Used after 'encoding' is set and when ":mkspell" was used. 4067 */ 4068 void 4069 spell_reload() 4070 { 4071 buf_T *buf; 4072 win_T *wp; 4073 4074 /* Initialize the table for spell_iswordp(). */ 4075 init_spell_chartab(); 4076 4077 /* Unload all allocated memory. */ 4078 spell_free_all(); 4079 4080 /* Go through all buffers and handle 'spelllang'. */ 4081 for (buf = firstbuf; buf != NULL; buf = buf->b_next) 4082 { 4083 /* Only load the wordlists when 'spelllang' is set and there is a 4084 * window for this buffer in which 'spell' is set. */ 4085 if (*buf->b_p_spl != NUL) 4086 { 4087 FOR_ALL_WINDOWS(wp) 4088 if (wp->w_buffer == buf && wp->w_p_spell) 4089 { 4090 (void)did_set_spelllang(buf); 4091 # ifdef FEAT_WINDOWS 4092 break; 4093 # endif 4094 } 4095 } 4096 } 4097 } 4098 # endif 4099 4100 /* 4101 * Reload the spell file "fname" if it's loaded. 4102 */ 4103 static void 4104 spell_reload_one(fname, added_word) 4105 char_u *fname; 4106 int added_word; /* invoked through "zg" */ 4107 { 4108 slang_T *slang; 4109 int didit = FALSE; 4110 4111 for (slang = first_lang; slang != NULL; slang = slang->sl_next) 4112 { 4113 if (fullpathcmp(fname, slang->sl_fname, FALSE) == FPC_SAME) 4114 { 4115 slang_clear(slang); 4116 if (spell_load_file(fname, NULL, slang, FALSE) == NULL) 4117 /* reloading failed, clear the language */ 4118 slang_clear(slang); 4119 redraw_all_later(NOT_VALID); 4120 didit = TRUE; 4121 } 4122 } 4123 4124 /* When "zg" was used and the file wasn't loaded yet, should redo 4125 * 'spelllang' to get it loaded. */ 4126 if (added_word && !didit) 4127 did_set_spelllang(curbuf); 4128 } 4129 4130 4131 /* 4132 * Functions for ":mkspell". 4133 */ 4134 4135 #define MAXLINELEN 500 /* Maximum length in bytes of a line in a .aff 4136 and .dic file. */ 4137 /* 4138 * Main structure to store the contents of a ".aff" file. 4139 */ 4140 typedef struct afffile_S 4141 { 4142 char_u *af_enc; /* "SET", normalized, alloc'ed string or NULL */ 4143 int af_flagtype; /* AFT_CHAR, AFT_LONG, AFT_NUM or AFT_CAPLONG */ 4144 int af_slash; /* character used in word for slash */ 4145 unsigned af_rar; /* RAR ID for rare word */ 4146 unsigned af_kep; /* KEP ID for keep-case word */ 4147 unsigned af_bad; /* BAD ID for banned word */ 4148 unsigned af_needaffix; /* NEEDAFFIX ID */ 4149 unsigned af_needcomp; /* NEEDCOMPOUND ID */ 4150 int af_pfxpostpone; /* postpone prefixes without chop string */ 4151 hashtab_T af_pref; /* hashtable for prefixes, affheader_T */ 4152 hashtab_T af_suff; /* hashtable for suffixes, affheader_T */ 4153 hashtab_T af_comp; /* hashtable for compound flags, compitem_T */ 4154 } afffile_T; 4155 4156 #define AFT_CHAR 0 /* flags are one character */ 4157 #define AFT_LONG 1 /* flags are two characters */ 4158 #define AFT_CAPLONG 2 /* flags are one or two characters */ 4159 #define AFT_NUM 3 /* flags are numbers, comma separated */ 4160 4161 typedef struct affentry_S affentry_T; 4162 /* Affix entry from ".aff" file. Used for prefixes and suffixes. */ 4163 struct affentry_S 4164 { 4165 affentry_T *ae_next; /* next affix with same name/number */ 4166 char_u *ae_chop; /* text to chop off basic word (can be NULL) */ 4167 char_u *ae_add; /* text to add to basic word (can be NULL) */ 4168 char_u *ae_cond; /* condition (NULL for ".") */ 4169 regprog_T *ae_prog; /* regexp program for ae_cond or NULL */ 4170 char_u ae_rare; /* rare affix */ 4171 char_u ae_nocomp; /* word with affix not compoundable */ 4172 }; 4173 4174 #ifdef FEAT_MBYTE 4175 # define AH_KEY_LEN 17 /* 2 x 8 bytes + NUL */ 4176 #else 4177 # define AH_KEY_LEN 7 /* 6 digits + NUL */ 4178 #endif 4179 4180 /* Affix header from ".aff" file. Used for af_pref and af_suff. */ 4181 typedef struct affheader_S 4182 { 4183 char_u ah_key[AH_KEY_LEN]; /* key for hashtab == name of affix */ 4184 unsigned ah_flag; /* affix name as number, uses "af_flagtype" */ 4185 int ah_newID; /* prefix ID after renumbering; 0 if not used */ 4186 int ah_combine; /* suffix may combine with prefix */ 4187 int ah_follows; /* another affix block should be following */ 4188 affentry_T *ah_first; /* first affix entry */ 4189 } affheader_T; 4190 4191 #define HI2AH(hi) ((affheader_T *)(hi)->hi_key) 4192 4193 /* Flag used in compound items. */ 4194 typedef struct compitem_S 4195 { 4196 char_u ci_key[AH_KEY_LEN]; /* key for hashtab == name of compound */ 4197 unsigned ci_flag; /* affix name as number, uses "af_flagtype" */ 4198 int ci_newID; /* affix ID after renumbering. */ 4199 } compitem_T; 4200 4201 #define HI2CI(hi) ((compitem_T *)(hi)->hi_key) 4202 4203 /* 4204 * Structure that is used to store the items in the word tree. This avoids 4205 * the need to keep track of each allocated thing, everything is freed all at 4206 * once after ":mkspell" is done. 4207 */ 4208 #define SBLOCKSIZE 16000 /* size of sb_data */ 4209 typedef struct sblock_S sblock_T; 4210 struct sblock_S 4211 { 4212 sblock_T *sb_next; /* next block in list */ 4213 int sb_used; /* nr of bytes already in use */ 4214 char_u sb_data[1]; /* data, actually longer */ 4215 }; 4216 4217 /* 4218 * A node in the tree. 4219 */ 4220 typedef struct wordnode_S wordnode_T; 4221 struct wordnode_S 4222 { 4223 union /* shared to save space */ 4224 { 4225 char_u hashkey[6]; /* the hash key, only used while compressing */ 4226 int index; /* index in written nodes (valid after first 4227 round) */ 4228 } wn_u1; 4229 union /* shared to save space */ 4230 { 4231 wordnode_T *next; /* next node with same hash key */ 4232 wordnode_T *wnode; /* parent node that will write this node */ 4233 } wn_u2; 4234 wordnode_T *wn_child; /* child (next byte in word) */ 4235 wordnode_T *wn_sibling; /* next sibling (alternate byte in word, 4236 always sorted) */ 4237 int wn_refs; /* Nr. of references to this node. Only 4238 relevant for first node in a list of 4239 siblings, in following siblings it is 4240 always one. */ 4241 char_u wn_byte; /* Byte for this node. NUL for word end */ 4242 char_u wn_affixID; /* when "wn_byte" is NUL: supported/required 4243 prefix ID or 0 */ 4244 short_u wn_flags; /* when "wn_byte" is NUL: WF_ flags */ 4245 short wn_region; /* when "wn_byte" is NUL: region mask; for 4246 PREFIXTREE it's the prefcondnr */ 4247 #ifdef SPELL_PRINTTREE 4248 int wn_nr; /* sequence nr for printing */ 4249 #endif 4250 }; 4251 4252 #define WN_MASK 0xffff /* mask relevant bits of "wn_flags" */ 4253 4254 #define HI2WN(hi) (wordnode_T *)((hi)->hi_key) 4255 4256 /* 4257 * Info used while reading the spell files. 4258 */ 4259 typedef struct spellinfo_S 4260 { 4261 wordnode_T *si_foldroot; /* tree with case-folded words */ 4262 long si_foldwcount; /* nr of words in si_foldroot */ 4263 4264 wordnode_T *si_keeproot; /* tree with keep-case words */ 4265 long si_keepwcount; /* nr of words in si_keeproot */ 4266 4267 wordnode_T *si_prefroot; /* tree with postponed prefixes */ 4268 4269 sblock_T *si_blocks; /* memory blocks used */ 4270 long si_blocks_cnt; /* memory blocks allocated */ 4271 long si_compress_cnt; /* words to add before lowering 4272 compression limit */ 4273 wordnode_T *si_first_free; /* List of nodes that have been freed during 4274 compression, linked by "wn_child" field. */ 4275 long si_free_count; /* number of nodes in si_first_free */ 4276 #ifdef SPELL_PRINTTREE 4277 int si_wordnode_nr; /* sequence nr for nodes */ 4278 #endif 4279 4280 4281 int si_ascii; /* handling only ASCII words */ 4282 int si_add; /* addition file */ 4283 int si_clear_chartab; /* when TRUE clear char tables */ 4284 int si_region; /* region mask */ 4285 vimconv_T si_conv; /* for conversion to 'encoding' */ 4286 int si_memtot; /* runtime memory used */ 4287 int si_verbose; /* verbose messages */ 4288 int si_msg_count; /* number of words added since last message */ 4289 int si_region_count; /* number of regions supported (1 when there 4290 are no regions) */ 4291 char_u si_region_name[16]; /* region names; used only if 4292 * si_region_count > 1) */ 4293 4294 garray_T si_rep; /* list of fromto_T entries from REP lines */ 4295 garray_T si_sal; /* list of fromto_T entries from SAL lines */ 4296 char_u *si_sofofr; /* SOFOFROM text */ 4297 char_u *si_sofoto; /* SOFOTO text */ 4298 int si_followup; /* soundsalike: ? */ 4299 int si_collapse; /* soundsalike: ? */ 4300 int si_rem_accents; /* soundsalike: remove accents */ 4301 garray_T si_map; /* MAP info concatenated */ 4302 char_u *si_midword; /* MIDWORD chars or NULL */ 4303 int si_compmax; /* max nr of words for compounding */ 4304 int si_compminlen; /* minimal length for compounding */ 4305 int si_compsylmax; /* max nr of syllables for compounding */ 4306 char_u *si_compflags; /* flags used for compounding */ 4307 char_u si_nobreak; /* NOBREAK */ 4308 char_u *si_syllable; /* syllable string */ 4309 garray_T si_prefcond; /* table with conditions for postponed 4310 * prefixes, each stored as a string */ 4311 int si_newprefID; /* current value for ah_newID */ 4312 int si_newcompID; /* current value for compound ID */ 4313 } spellinfo_T; 4314 4315 static afffile_T *spell_read_aff __ARGS((spellinfo_T *spin, char_u *fname)); 4316 static unsigned affitem2flag __ARGS((int flagtype, char_u *item, char_u *fname, int lnum)); 4317 static unsigned get_affitem __ARGS((int flagtype, char_u **pp)); 4318 static void process_compflags __ARGS((spellinfo_T *spin, afffile_T *aff, char_u *compflags)); 4319 static void check_renumber __ARGS((spellinfo_T *spin)); 4320 static int flag_in_afflist __ARGS((int flagtype, char_u *afflist, unsigned flag)); 4321 static void aff_check_number __ARGS((int spinval, int affval, char *name)); 4322 static void aff_check_string __ARGS((char_u *spinval, char_u *affval, char *name)); 4323 static int str_equal __ARGS((char_u *s1, char_u *s2)); 4324 static void add_fromto __ARGS((spellinfo_T *spin, garray_T *gap, char_u *from, char_u *to)); 4325 static int sal_to_bool __ARGS((char_u *s)); 4326 static int has_non_ascii __ARGS((char_u *s)); 4327 static void spell_free_aff __ARGS((afffile_T *aff)); 4328 static int spell_read_dic __ARGS((spellinfo_T *spin, char_u *fname, afffile_T *affile)); 4329 static int get_pfxlist __ARGS((afffile_T *affile, char_u *afflist, char_u *store_afflist)); 4330 static void get_compflags __ARGS((afffile_T *affile, char_u *afflist, char_u *store_afflist)); 4331 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 comb, int flags, char_u *pfxlist, int pfxlen)); 4332 static int spell_read_wordfile __ARGS((spellinfo_T *spin, char_u *fname)); 4333 static void *getroom __ARGS((spellinfo_T *spin, size_t len, int align)); 4334 static char_u *getroom_save __ARGS((spellinfo_T *spin, char_u *s)); 4335 static void free_blocks __ARGS((sblock_T *bl)); 4336 static wordnode_T *wordtree_alloc __ARGS((spellinfo_T *spin)); 4337 static int store_word __ARGS((spellinfo_T *spin, char_u *word, int flags, int region, char_u *pfxlist, int need_affix)); 4338 static int tree_add_word __ARGS((spellinfo_T *spin, char_u *word, wordnode_T *tree, int flags, int region, int affixID)); 4339 static wordnode_T *get_wordnode __ARGS((spellinfo_T *spin)); 4340 static void deref_wordnode __ARGS((spellinfo_T *spin, wordnode_T *node)); 4341 static void free_wordnode __ARGS((spellinfo_T *spin, wordnode_T *n)); 4342 static void wordtree_compress __ARGS((spellinfo_T *spin, wordnode_T *root)); 4343 static int node_compress __ARGS((spellinfo_T *spin, wordnode_T *node, hashtab_T *ht, int *tot)); 4344 static int node_equal __ARGS((wordnode_T *n1, wordnode_T *n2)); 4345 static int write_vim_spell __ARGS((spellinfo_T *spin, char_u *fname)); 4346 static void clear_node __ARGS((wordnode_T *node)); 4347 static int put_node __ARGS((FILE *fd, wordnode_T *node, int index, int regionmask, int prefixtree)); 4348 static void mkspell __ARGS((int fcount, char_u **fnames, int ascii, int overwrite, int added_word)); 4349 static void init_spellfile __ARGS((void)); 4350 4351 /* In the postponed prefixes tree wn_flags is used to store the WFP_ flags, 4352 * but it must be negative to indicate the prefix tree to tree_add_word(). 4353 * Use a negative number with the lower 8 bits zero. */ 4354 #define PFX_FLAGS -256 4355 4356 /* 4357 * Tunable parameters for when the tree is compressed. See 'mkspellmem'. 4358 */ 4359 static long compress_start = 30000; /* memory / SBLOCKSIZE */ 4360 static long compress_inc = 100; /* memory / SBLOCKSIZE */ 4361 static long compress_added = 500000; /* word count */ 4362 4363 #ifdef SPELL_PRINTTREE 4364 /* 4365 * For debugging the tree code: print the current tree in a (more or less) 4366 * readable format, so that we can see what happens when adding a word and/or 4367 * compressing the tree. 4368 * Based on code from Olaf Seibert. 4369 */ 4370 #define PRINTLINESIZE 1000 4371 #define PRINTWIDTH 6 4372 4373 #define PRINTSOME(l, depth, fmt, a1, a2) vim_snprintf(l + depth * PRINTWIDTH, \ 4374 PRINTLINESIZE - PRINTWIDTH * depth, fmt, a1, a2) 4375 4376 static char line1[PRINTLINESIZE]; 4377 static char line2[PRINTLINESIZE]; 4378 static char line3[PRINTLINESIZE]; 4379 4380 static void 4381 spell_clear_flags(wordnode_T *node) 4382 { 4383 wordnode_T *np; 4384 4385 for (np = node; np != NULL; np = np->wn_sibling) 4386 { 4387 np->wn_u1.index = FALSE; 4388 spell_clear_flags(np->wn_child); 4389 } 4390 } 4391 4392 static void 4393 spell_print_node(wordnode_T *node, int depth) 4394 { 4395 if (node->wn_u1.index) 4396 { 4397 /* Done this node before, print the reference. */ 4398 PRINTSOME(line1, depth, "(%d)", node->wn_nr, 0); 4399 PRINTSOME(line2, depth, " ", 0, 0); 4400 PRINTSOME(line3, depth, " ", 0, 0); 4401 msg(line1); 4402 msg(line2); 4403 msg(line3); 4404 } 4405 else 4406 { 4407 node->wn_u1.index = TRUE; 4408 4409 if (node->wn_byte != NUL) 4410 { 4411 if (node->wn_child != NULL) 4412 PRINTSOME(line1, depth, " %c -> ", node->wn_byte, 0); 4413 else 4414 /* Cannot happen? */ 4415 PRINTSOME(line1, depth, " %c ???", node->wn_byte, 0); 4416 } 4417 else 4418 PRINTSOME(line1, depth, " $ ", 0, 0); 4419 4420 PRINTSOME(line2, depth, "%d/%d ", node->wn_nr, node->wn_refs); 4421 4422 if (node->wn_sibling != NULL) 4423 PRINTSOME(line3, depth, " | ", 0, 0); 4424 else 4425 PRINTSOME(line3, depth, " ", 0, 0); 4426 4427 if (node->wn_byte == NUL) 4428 { 4429 msg(line1); 4430 msg(line2); 4431 msg(line3); 4432 } 4433 4434 /* do the children */ 4435 if (node->wn_byte != NUL && node->wn_child != NULL) 4436 spell_print_node(node->wn_child, depth + 1); 4437 4438 /* do the siblings */ 4439 if (node->wn_sibling != NULL) 4440 { 4441 /* get rid of all parent details except | */ 4442 STRCPY(line1, line3); 4443 STRCPY(line2, line3); 4444 spell_print_node(node->wn_sibling, depth); 4445 } 4446 } 4447 } 4448 4449 static void 4450 spell_print_tree(wordnode_T *root) 4451 { 4452 if (root != NULL) 4453 { 4454 /* Clear the "wn_u1.index" fields, used to remember what has been 4455 * done. */ 4456 spell_clear_flags(root); 4457 4458 /* Recursively print the tree. */ 4459 spell_print_node(root, 0); 4460 } 4461 } 4462 #endif /* SPELL_PRINTTREE */ 4463 4464 /* 4465 * Read the affix file "fname". 4466 * Returns an afffile_T, NULL for complete failure. 4467 */ 4468 static afffile_T * 4469 spell_read_aff(spin, fname) 4470 spellinfo_T *spin; 4471 char_u *fname; 4472 { 4473 FILE *fd; 4474 afffile_T *aff; 4475 char_u rline[MAXLINELEN]; 4476 char_u *line; 4477 char_u *pc = NULL; 4478 #define MAXITEMCNT 7 4479 char_u *(items[MAXITEMCNT]); 4480 int itemcnt; 4481 char_u *p; 4482 int lnum = 0; 4483 affheader_T *cur_aff = NULL; 4484 int did_postpone_prefix = FALSE; 4485 int aff_todo = 0; 4486 hashtab_T *tp; 4487 char_u *low = NULL; 4488 char_u *fol = NULL; 4489 char_u *upp = NULL; 4490 int do_rep; 4491 int do_sal; 4492 int do_map; 4493 int found_map = FALSE; 4494 hashitem_T *hi; 4495 int l; 4496 int compminlen = 0; /* COMPOUNDMIN value */ 4497 int compsylmax = 0; /* COMPOUNDSYLMAX value */ 4498 int compmax = 0; /* COMPOUNDMAX value */ 4499 char_u *compflags = NULL; /* COMPOUNDFLAG and COMPOUNDFLAGS 4500 concatenated */ 4501 char_u *midword = NULL; /* MIDWORD value */ 4502 char_u *syllable = NULL; /* SYLLABLE value */ 4503 char_u *sofofrom = NULL; /* SOFOFROM value */ 4504 char_u *sofoto = NULL; /* SOFOTO value */ 4505 4506 /* 4507 * Open the file. 4508 */ 4509 fd = mch_fopen((char *)fname, "r"); 4510 if (fd == NULL) 4511 { 4512 EMSG2(_(e_notopen), fname); 4513 return NULL; 4514 } 4515 4516 if (spin->si_verbose || p_verbose > 2) 4517 { 4518 if (!spin->si_verbose) 4519 verbose_enter(); 4520 smsg((char_u *)_("Reading affix file %s ..."), fname); 4521 out_flush(); 4522 if (!spin->si_verbose) 4523 verbose_leave(); 4524 } 4525 4526 /* Only do REP lines when not done in another .aff file already. */ 4527 do_rep = spin->si_rep.ga_len == 0; 4528 4529 /* Only do SAL lines when not done in another .aff file already. */ 4530 do_sal = spin->si_sal.ga_len == 0; 4531 4532 /* Only do MAP lines when not done in another .aff file already. */ 4533 do_map = spin->si_map.ga_len == 0; 4534 4535 /* 4536 * Allocate and init the afffile_T structure. 4537 */ 4538 aff = (afffile_T *)getroom(spin, sizeof(afffile_T), TRUE); 4539 if (aff == NULL) 4540 return NULL; 4541 hash_init(&aff->af_pref); 4542 hash_init(&aff->af_suff); 4543 hash_init(&aff->af_comp); 4544 4545 /* 4546 * Read all the lines in the file one by one. 4547 */ 4548 while (!vim_fgets(rline, MAXLINELEN, fd) && !got_int) 4549 { 4550 line_breakcheck(); 4551 ++lnum; 4552 4553 /* Skip comment lines. */ 4554 if (*rline == '#') 4555 continue; 4556 4557 /* Convert from "SET" to 'encoding' when needed. */ 4558 vim_free(pc); 4559 #ifdef FEAT_MBYTE 4560 if (spin->si_conv.vc_type != CONV_NONE) 4561 { 4562 pc = string_convert(&spin->si_conv, rline, NULL); 4563 if (pc == NULL) 4564 { 4565 smsg((char_u *)_("Conversion failure for word in %s line %d: %s"), 4566 fname, lnum, rline); 4567 continue; 4568 } 4569 line = pc; 4570 } 4571 else 4572 #endif 4573 { 4574 pc = NULL; 4575 line = rline; 4576 } 4577 4578 /* Split the line up in white separated items. Put a NUL after each 4579 * item. */ 4580 itemcnt = 0; 4581 for (p = line; ; ) 4582 { 4583 while (*p != NUL && *p <= ' ') /* skip white space and CR/NL */ 4584 ++p; 4585 if (*p == NUL) 4586 break; 4587 if (itemcnt == MAXITEMCNT) /* too many items */ 4588 break; 4589 items[itemcnt++] = p; 4590 while (*p > ' ') /* skip until white space or CR/NL */ 4591 ++p; 4592 if (*p == NUL) 4593 break; 4594 *p++ = NUL; 4595 } 4596 4597 /* Handle non-empty lines. */ 4598 if (itemcnt > 0) 4599 { 4600 if (STRCMP(items[0], "SET") == 0 && itemcnt == 2 4601 && aff->af_enc == NULL) 4602 { 4603 #ifdef FEAT_MBYTE 4604 /* Setup for conversion from "ENC" to 'encoding'. */ 4605 aff->af_enc = enc_canonize(items[1]); 4606 if (aff->af_enc != NULL && !spin->si_ascii 4607 && convert_setup(&spin->si_conv, aff->af_enc, 4608 p_enc) == FAIL) 4609 smsg((char_u *)_("Conversion in %s not supported: from %s to %s"), 4610 fname, aff->af_enc, p_enc); 4611 spin->si_conv.vc_fail = TRUE; 4612 #else 4613 smsg((char_u *)_("Conversion in %s not supported"), fname); 4614 #endif 4615 } 4616 else if (STRCMP(items[0], "FLAG") == 0 && itemcnt == 2 4617 && aff->af_flagtype == AFT_CHAR) 4618 { 4619 if (STRCMP(items[1], "long") == 0) 4620 aff->af_flagtype = AFT_LONG; 4621 else if (STRCMP(items[1], "num") == 0) 4622 aff->af_flagtype = AFT_NUM; 4623 else if (STRCMP(items[1], "caplong") == 0) 4624 aff->af_flagtype = AFT_CAPLONG; 4625 else 4626 smsg((char_u *)_("Invalid value for FLAG in %s line %d: %s"), 4627 fname, lnum, items[1]); 4628 if (aff->af_rar != 0 || aff->af_kep != 0 || aff->af_bad != 0 4629 || aff->af_needaffix != 0 4630 || aff->af_needcomp != 0 4631 || compflags != NULL 4632 || aff->af_suff.ht_used > 0 4633 || aff->af_pref.ht_used > 0) 4634 smsg((char_u *)_("FLAG after using flags in %s line %d: %s"), 4635 fname, lnum, items[1]); 4636 } 4637 else if (STRCMP(items[0], "MIDWORD") == 0 && itemcnt == 2 4638 && midword == NULL) 4639 { 4640 midword = getroom_save(spin, items[1]); 4641 } 4642 else if (STRCMP(items[0], "NOSPLITSUGS") == 0 && itemcnt == 1) 4643 { 4644 /* ignored, we always split */ 4645 } 4646 else if (STRCMP(items[0], "TRY") == 0 && itemcnt == 2) 4647 { 4648 /* ignored, we look in the tree for what chars may appear */ 4649 } 4650 else if (STRCMP(items[0], "SLASH") == 0 && itemcnt == 2 4651 && aff->af_slash == 0) 4652 { 4653 aff->af_slash = items[1][0]; 4654 if (items[1][1] != NUL) 4655 smsg((char_u *)_("Character used for SLASH must be ASCII; in %s line %d: %s"), 4656 fname, lnum, items[1]); 4657 } 4658 else if (STRCMP(items[0], "RAR") == 0 && itemcnt == 2 4659 && aff->af_rar == 0) 4660 { 4661 aff->af_rar = affitem2flag(aff->af_flagtype, items[1], 4662 fname, lnum); 4663 } 4664 else if (STRCMP(items[0], "KEP") == 0 && itemcnt == 2 4665 && aff->af_kep == 0) 4666 { 4667 aff->af_kep = affitem2flag(aff->af_flagtype, items[1], 4668 fname, lnum); 4669 } 4670 else if (STRCMP(items[0], "BAD") == 0 && itemcnt == 2 4671 && aff->af_bad == 0) 4672 { 4673 aff->af_bad = affitem2flag(aff->af_flagtype, items[1], 4674 fname, lnum); 4675 } 4676 else if (STRCMP(items[0], "NEEDAFFIX") == 0 && itemcnt == 2 4677 && aff->af_needaffix == 0) 4678 { 4679 aff->af_needaffix = affitem2flag(aff->af_flagtype, items[1], 4680 fname, lnum); 4681 } 4682 else if (STRCMP(items[0], "NEEDCOMPOUND") == 0 && itemcnt == 2 4683 && aff->af_needcomp == 0) 4684 { 4685 aff->af_needcomp = affitem2flag(aff->af_flagtype, items[1], 4686 fname, lnum); 4687 } 4688 else if (STRCMP(items[0], "COMPOUNDFLAG") == 0 && itemcnt == 2 4689 && compflags == NULL) 4690 { 4691 /* Turn flag "c" into COMPOUNDFLAGS compatible string "c+", 4692 * "Na" into "Na+", "1234" into "1234+". */ 4693 p = getroom(spin, STRLEN(items[1]) + 2, FALSE); 4694 if (p != NULL) 4695 { 4696 STRCPY(p, items[1]); 4697 STRCAT(p, "+"); 4698 compflags = p; 4699 } 4700 } 4701 else if (STRCMP(items[0], "COMPOUNDFLAGS") == 0 && itemcnt == 2) 4702 { 4703 /* Concatenate this string to previously defined ones, using a 4704 * slash to separate them. */ 4705 l = STRLEN(items[1]) + 1; 4706 if (compflags != NULL) 4707 l += STRLEN(compflags) + 1; 4708 p = getroom(spin, l, FALSE); 4709 if (p != NULL) 4710 { 4711 if (compflags != NULL) 4712 { 4713 STRCPY(p, compflags); 4714 STRCAT(p, "/"); 4715 } 4716 STRCAT(p, items[1]); 4717 compflags = p; 4718 } 4719 } 4720 else if (STRCMP(items[0], "COMPOUNDMAX") == 0 && itemcnt == 2 4721 && compmax == 0) 4722 { 4723 compmax = atoi((char *)items[1]); 4724 if (compmax == 0) 4725 smsg((char_u *)_("Wrong COMPOUNDMAX value in %s line %d: %s"), 4726 fname, lnum, items[1]); 4727 } 4728 else if (STRCMP(items[0], "COMPOUNDMIN") == 0 && itemcnt == 2 4729 && compminlen == 0) 4730 { 4731 compminlen = atoi((char *)items[1]); 4732 if (compminlen == 0) 4733 smsg((char_u *)_("Wrong COMPOUNDMIN value in %s line %d: %s"), 4734 fname, lnum, items[1]); 4735 } 4736 else if (STRCMP(items[0], "COMPOUNDSYLMAX") == 0 && itemcnt == 2 4737 && compsylmax == 0) 4738 { 4739 compsylmax = atoi((char *)items[1]); 4740 if (compsylmax == 0) 4741 smsg((char_u *)_("Wrong COMPOUNDSYLMAX value in %s line %d: %s"), 4742 fname, lnum, items[1]); 4743 } 4744 else if (STRCMP(items[0], "SYLLABLE") == 0 && itemcnt == 2 4745 && syllable == NULL) 4746 { 4747 syllable = getroom_save(spin, items[1]); 4748 } 4749 else if (STRCMP(items[0], "NOBREAK") == 0 && itemcnt == 1) 4750 { 4751 spin->si_nobreak = TRUE; 4752 } 4753 else if (STRCMP(items[0], "PFXPOSTPONE") == 0 && itemcnt == 1) 4754 { 4755 aff->af_pfxpostpone = TRUE; 4756 } 4757 else if ((STRCMP(items[0], "PFX") == 0 4758 || STRCMP(items[0], "SFX") == 0) 4759 && aff_todo == 0 4760 && itemcnt >= 4) 4761 { 4762 int lasti = 4; 4763 char_u key[AH_KEY_LEN]; 4764 4765 if (*items[0] == 'P') 4766 tp = &aff->af_pref; 4767 else 4768 tp = &aff->af_suff; 4769 4770 /* Myspell allows the same affix name to be used multiple 4771 * times. The affix files that do this have an undocumented 4772 * "S" flag on all but the last block, thus we check for that 4773 * and store it in ah_follows. */ 4774 vim_strncpy(key, items[1], AH_KEY_LEN - 1); 4775 hi = hash_find(tp, key); 4776 if (!HASHITEM_EMPTY(hi)) 4777 { 4778 cur_aff = HI2AH(hi); 4779 if (cur_aff->ah_combine != (*items[2] == 'Y')) 4780 smsg((char_u *)_("Different combining flag in continued affix block in %s line %d: %s"), 4781 fname, lnum, items[1]); 4782 if (!cur_aff->ah_follows) 4783 smsg((char_u *)_("Duplicate affix in %s line %d: %s"), 4784 fname, lnum, items[1]); 4785 } 4786 else 4787 { 4788 /* New affix letter. */ 4789 cur_aff = (affheader_T *)getroom(spin, 4790 sizeof(affheader_T), TRUE); 4791 if (cur_aff == NULL) 4792 break; 4793 cur_aff->ah_flag = affitem2flag(aff->af_flagtype, items[1], 4794 fname, lnum); 4795 if (cur_aff->ah_flag == 0 || STRLEN(items[1]) >= AH_KEY_LEN) 4796 break; 4797 if (cur_aff->ah_flag == aff->af_bad 4798 || cur_aff->ah_flag == aff->af_rar 4799 || cur_aff->ah_flag == aff->af_kep 4800 || cur_aff->ah_flag == aff->af_needaffix 4801 || cur_aff->ah_flag == aff->af_needcomp) 4802 smsg((char_u *)_("Affix also used for BAD/RAR/KEP/NEEDAFFIX/NEEDCOMPOUND in %s line %d: %s"), 4803 fname, lnum, items[1]); 4804 STRCPY(cur_aff->ah_key, items[1]); 4805 hash_add(tp, cur_aff->ah_key); 4806 4807 cur_aff->ah_combine = (*items[2] == 'Y'); 4808 } 4809 4810 /* Check for the "S" flag, which apparently means that another 4811 * block with the same affix name is following. */ 4812 if (itemcnt > lasti && STRCMP(items[lasti], "S") == 0) 4813 { 4814 ++lasti; 4815 cur_aff->ah_follows = TRUE; 4816 } 4817 else 4818 cur_aff->ah_follows = FALSE; 4819 4820 /* Myspell allows extra text after the item, but that might 4821 * mean mistakes go unnoticed. Require a comment-starter. */ 4822 if (itemcnt > lasti && *items[lasti] != '#') 4823 smsg((char_u *)_("Trailing text in %s line %d: %s"), 4824 fname, lnum, items[4]); 4825 4826 if (STRCMP(items[2], "Y") != 0 && STRCMP(items[2], "N") != 0) 4827 smsg((char_u *)_("Expected Y or N in %s line %d: %s"), 4828 fname, lnum, items[2]); 4829 4830 if (*items[0] == 'P' && aff->af_pfxpostpone) 4831 { 4832 if (cur_aff->ah_newID == 0) 4833 { 4834 /* Use a new number in the .spl file later, to be able 4835 * to handle multiple .aff files. */ 4836 check_renumber(spin); 4837 cur_aff->ah_newID = ++spin->si_newprefID; 4838 4839 /* We only really use ah_newID if the prefix is 4840 * postponed. We know that only after handling all 4841 * the items. */ 4842 did_postpone_prefix = FALSE; 4843 } 4844 else 4845 /* Did use the ID in a previous block. */ 4846 did_postpone_prefix = TRUE; 4847 } 4848 4849 aff_todo = atoi((char *)items[3]); 4850 } 4851 else if ((STRCMP(items[0], "PFX") == 0 4852 || STRCMP(items[0], "SFX") == 0) 4853 && aff_todo > 0 4854 && STRCMP(cur_aff->ah_key, items[1]) == 0 4855 && itemcnt >= 5) 4856 { 4857 affentry_T *aff_entry; 4858 int rare = FALSE; 4859 int nocomp = FALSE; 4860 int upper = FALSE; 4861 int lasti = 5; 4862 4863 /* Check for "rare" and "nocomp" after the other info. */ 4864 while (itemcnt > lasti) 4865 { 4866 if (!rare && STRICMP(items[lasti], "rare") == 0) 4867 { 4868 rare = TRUE; 4869 ++lasti; 4870 } 4871 else if (!nocomp && STRICMP(items[lasti], "nocomp") == 0) 4872 { 4873 nocomp = TRUE; 4874 ++lasti; 4875 } 4876 else 4877 break; 4878 } 4879 4880 /* Myspell allows extra text after the item, but that might 4881 * mean mistakes go unnoticed. Require a comment-starter. */ 4882 if (itemcnt > lasti && *items[lasti] != '#') 4883 smsg((char_u *)_(e_afftrailing), fname, lnum, items[lasti]); 4884 4885 /* New item for an affix letter. */ 4886 --aff_todo; 4887 aff_entry = (affentry_T *)getroom(spin, 4888 sizeof(affentry_T), TRUE); 4889 if (aff_entry == NULL) 4890 break; 4891 aff_entry->ae_rare = rare; 4892 aff_entry->ae_nocomp = nocomp; 4893 4894 if (STRCMP(items[2], "0") != 0) 4895 aff_entry->ae_chop = getroom_save(spin, items[2]); 4896 if (STRCMP(items[3], "0") != 0) 4897 aff_entry->ae_add = getroom_save(spin, items[3]); 4898 4899 /* Don't use an affix entry with non-ASCII characters when 4900 * "spin->si_ascii" is TRUE. */ 4901 if (!spin->si_ascii || !(has_non_ascii(aff_entry->ae_chop) 4902 || has_non_ascii(aff_entry->ae_add))) 4903 { 4904 aff_entry->ae_next = cur_aff->ah_first; 4905 cur_aff->ah_first = aff_entry; 4906 4907 if (STRCMP(items[4], ".") != 0) 4908 { 4909 char_u buf[MAXLINELEN]; 4910 4911 aff_entry->ae_cond = getroom_save(spin, items[4]); 4912 if (*items[0] == 'P') 4913 sprintf((char *)buf, "^%s", items[4]); 4914 else 4915 sprintf((char *)buf, "%s$", items[4]); 4916 aff_entry->ae_prog = vim_regcomp(buf, 4917 RE_MAGIC + RE_STRING + RE_STRICT); 4918 if (aff_entry->ae_prog == NULL) 4919 smsg((char_u *)_("Broken condition in %s line %d: %s"), 4920 fname, lnum, items[4]); 4921 } 4922 4923 /* For postponed prefixes we need an entry in si_prefcond 4924 * for the condition. Use an existing one if possible. */ 4925 if (*items[0] == 'P' && aff->af_pfxpostpone) 4926 { 4927 /* When the chop string is one lower-case letter and 4928 * the add string ends in the upper-case letter we set 4929 * the "upper" flag, clear "ae_chop" and remove the 4930 * letters from "ae_add". The condition must either 4931 * be empty or start with the same letter. */ 4932 if (aff_entry->ae_chop != NULL 4933 && aff_entry->ae_add != NULL 4934 #ifdef FEAT_MBYTE 4935 && aff_entry->ae_chop[(*mb_ptr2len)( 4936 aff_entry->ae_chop)] == NUL 4937 #else 4938 && aff_entry->ae_chop[1] == NUL 4939 #endif 4940 ) 4941 { 4942 int c, c_up; 4943 4944 c = PTR2CHAR(aff_entry->ae_chop); 4945 c_up = SPELL_TOUPPER(c); 4946 if (c_up != c 4947 && (aff_entry->ae_cond == NULL 4948 || PTR2CHAR(aff_entry->ae_cond) == c)) 4949 { 4950 p = aff_entry->ae_add 4951 + STRLEN(aff_entry->ae_add); 4952 mb_ptr_back(aff_entry->ae_add, p); 4953 if (PTR2CHAR(p) == c_up) 4954 { 4955 upper = TRUE; 4956 aff_entry->ae_chop = NULL; 4957 *p = NUL; 4958 4959 /* The condition is matched with the 4960 * actual word, thus must check for the 4961 * upper-case letter. */ 4962 if (aff_entry->ae_cond != NULL) 4963 { 4964 char_u buf[MAXLINELEN]; 4965 #ifdef FEAT_MBYTE 4966 if (has_mbyte) 4967 { 4968 onecap_copy(items[4], buf, TRUE); 4969 aff_entry->ae_cond = getroom_save( 4970 spin, buf); 4971 } 4972 else 4973 #endif 4974 *aff_entry->ae_cond = c_up; 4975 if (aff_entry->ae_cond != NULL) 4976 { 4977 sprintf((char *)buf, "^%s", 4978 aff_entry->ae_cond); 4979 vim_free(aff_entry->ae_prog); 4980 aff_entry->ae_prog = vim_regcomp( 4981 buf, RE_MAGIC + RE_STRING); 4982 } 4983 } 4984 } 4985 } 4986 } 4987 4988 if (aff_entry->ae_chop == NULL) 4989 { 4990 int idx; 4991 char_u **pp; 4992 int n; 4993 4994 /* Find a previously used condition. */ 4995 for (idx = spin->si_prefcond.ga_len - 1; idx >= 0; 4996 --idx) 4997 { 4998 p = ((char_u **)spin->si_prefcond.ga_data)[idx]; 4999 if (str_equal(p, aff_entry->ae_cond)) 5000 break; 5001 } 5002 if (idx < 0 && ga_grow(&spin->si_prefcond, 1) == OK) 5003 { 5004 /* Not found, add a new condition. */ 5005 idx = spin->si_prefcond.ga_len++; 5006 pp = ((char_u **)spin->si_prefcond.ga_data) 5007 + idx; 5008 if (aff_entry->ae_cond == NULL) 5009 *pp = NULL; 5010 else 5011 *pp = getroom_save(spin, 5012 aff_entry->ae_cond); 5013 } 5014 5015 /* Add the prefix to the prefix tree. */ 5016 if (aff_entry->ae_add == NULL) 5017 p = (char_u *)""; 5018 else 5019 p = aff_entry->ae_add; 5020 /* PFX_FLAGS is a negative number, so that 5021 * tree_add_word() knows this is the prefix tree. */ 5022 n = PFX_FLAGS; 5023 if (rare) 5024 n |= WFP_RARE; 5025 if (!cur_aff->ah_combine) 5026 n |= WFP_NC; 5027 if (upper) 5028 n |= WFP_UP; 5029 tree_add_word(spin, p, spin->si_prefroot, n, 5030 idx, cur_aff->ah_newID); 5031 did_postpone_prefix = TRUE; 5032 } 5033 5034 /* Didn't actually use ah_newID, backup si_newprefID. */ 5035 if (aff_todo == 0 && !did_postpone_prefix) 5036 { 5037 --spin->si_newprefID; 5038 cur_aff->ah_newID = 0; 5039 } 5040 } 5041 } 5042 } 5043 else if (STRCMP(items[0], "FOL") == 0 && itemcnt == 2 5044 && fol == NULL) 5045 { 5046 fol = vim_strsave(items[1]); 5047 } 5048 else if (STRCMP(items[0], "LOW") == 0 && itemcnt == 2 5049 && low == NULL) 5050 { 5051 low = vim_strsave(items[1]); 5052 } 5053 else if (STRCMP(items[0], "UPP") == 0 && itemcnt == 2 5054 && upp == NULL) 5055 { 5056 upp = vim_strsave(items[1]); 5057 } 5058 else if (STRCMP(items[0], "REP") == 0 && itemcnt == 2) 5059 { 5060 /* Ignore REP count */; 5061 if (!isdigit(*items[1])) 5062 smsg((char_u *)_("Expected REP count in %s line %d"), 5063 fname, lnum); 5064 } 5065 else if (STRCMP(items[0], "REP") == 0 && itemcnt >= 3) 5066 { 5067 /* REP item */ 5068 /* Myspell ignores extra arguments, we require it starts with 5069 * # to detect mistakes. */ 5070 if (itemcnt > 3 && items[3][0] != '#') 5071 smsg((char_u *)_(e_afftrailing), fname, lnum, items[3]); 5072 if (do_rep) 5073 { 5074 /* Replace underscore with space (can't include a space 5075 * directly). */ 5076 for (p = items[1]; *p != NUL; mb_ptr_adv(p)) 5077 if (*p == '_') 5078 *p = ' '; 5079 for (p = items[2]; *p != NUL; mb_ptr_adv(p)) 5080 if (*p == '_') 5081 *p = ' '; 5082 add_fromto(spin, &spin->si_rep, items[1], items[2]); 5083 } 5084 } 5085 else if (STRCMP(items[0], "MAP") == 0 && itemcnt == 2) 5086 { 5087 /* MAP item or count */ 5088 if (!found_map) 5089 { 5090 /* First line contains the count. */ 5091 found_map = TRUE; 5092 if (!isdigit(*items[1])) 5093 smsg((char_u *)_("Expected MAP count in %s line %d"), 5094 fname, lnum); 5095 } 5096 else if (do_map) 5097 { 5098 int c; 5099 5100 /* Check that every character appears only once. */ 5101 for (p = items[1]; *p != NUL; ) 5102 { 5103 #ifdef FEAT_MBYTE 5104 c = mb_ptr2char_adv(&p); 5105 #else 5106 c = *p++; 5107 #endif 5108 if ((spin->si_map.ga_len > 0 5109 && vim_strchr(spin->si_map.ga_data, c) 5110 != NULL) 5111 || vim_strchr(p, c) != NULL) 5112 smsg((char_u *)_("Duplicate character in MAP in %s line %d"), 5113 fname, lnum); 5114 } 5115 5116 /* We simply concatenate all the MAP strings, separated by 5117 * slashes. */ 5118 ga_concat(&spin->si_map, items[1]); 5119 ga_append(&spin->si_map, '/'); 5120 } 5121 } 5122 /* Accept "SAL from to" and "SAL from to # comment". */ 5123 else if (STRCMP(items[0], "SAL") == 0 5124 && (itemcnt == 3 || (itemcnt > 3 && items[3][0] == '#'))) 5125 { 5126 if (do_sal) 5127 { 5128 /* SAL item (sounds-a-like) 5129 * Either one of the known keys or a from-to pair. */ 5130 if (STRCMP(items[1], "followup") == 0) 5131 spin->si_followup = sal_to_bool(items[2]); 5132 else if (STRCMP(items[1], "collapse_result") == 0) 5133 spin->si_collapse = sal_to_bool(items[2]); 5134 else if (STRCMP(items[1], "remove_accents") == 0) 5135 spin->si_rem_accents = sal_to_bool(items[2]); 5136 else 5137 /* when "to" is "_" it means empty */ 5138 add_fromto(spin, &spin->si_sal, items[1], 5139 STRCMP(items[2], "_") == 0 ? (char_u *)"" 5140 : items[2]); 5141 } 5142 } 5143 else if (STRCMP(items[0], "SOFOFROM") == 0 && itemcnt == 2 5144 && sofofrom == NULL) 5145 { 5146 sofofrom = getroom_save(spin, items[1]); 5147 } 5148 else if (STRCMP(items[0], "SOFOTO") == 0 && itemcnt == 2 5149 && sofoto == NULL) 5150 { 5151 sofoto = getroom_save(spin, items[1]); 5152 } 5153 else 5154 smsg((char_u *)_("Unrecognized or duplicate item in %s line %d: %s"), 5155 fname, lnum, items[0]); 5156 } 5157 } 5158 5159 if (fol != NULL || low != NULL || upp != NULL) 5160 { 5161 if (spin->si_clear_chartab) 5162 { 5163 /* Clear the char type tables, don't want to use any of the 5164 * currently used spell properties. */ 5165 init_spell_chartab(); 5166 spin->si_clear_chartab = FALSE; 5167 } 5168 5169 /* 5170 * Don't write a word table for an ASCII file, so that we don't check 5171 * for conflicts with a word table that matches 'encoding'. 5172 * Don't write one for utf-8 either, we use utf_*() and 5173 * mb_get_class(), the list of chars in the file will be incomplete. 5174 */ 5175 if (!spin->si_ascii 5176 #ifdef FEAT_MBYTE 5177 && !enc_utf8 5178 #endif 5179 ) 5180 { 5181 if (fol == NULL || low == NULL || upp == NULL) 5182 smsg((char_u *)_("Missing FOL/LOW/UPP line in %s"), fname); 5183 else 5184 (void)set_spell_chartab(fol, low, upp); 5185 } 5186 5187 vim_free(fol); 5188 vim_free(low); 5189 vim_free(upp); 5190 } 5191 5192 /* Use compound specifications of the .aff file for the spell info. */ 5193 if (compmax != 0) 5194 { 5195 aff_check_number(spin->si_compmax, compmax, "COMPOUNDMAX"); 5196 spin->si_compmax = compmax; 5197 } 5198 5199 if (compminlen != 0) 5200 { 5201 aff_check_number(spin->si_compminlen, compminlen, "COMPOUNDMIN"); 5202 spin->si_compminlen = compminlen; 5203 } 5204 5205 if (compsylmax != 0) 5206 { 5207 if (syllable == NULL) 5208 smsg((char_u *)_("COMPOUNDSYLMAX used without SYLLABLE")); 5209 aff_check_number(spin->si_compsylmax, compsylmax, "COMPOUNDSYLMAX"); 5210 spin->si_compsylmax = compsylmax; 5211 } 5212 5213 if (compflags != NULL) 5214 process_compflags(spin, aff, compflags); 5215 5216 /* Check that we didn't use too many renumbered flags. */ 5217 if (spin->si_newcompID < spin->si_newprefID) 5218 { 5219 if (spin->si_newcompID == 127 || spin->si_newcompID == 255) 5220 MSG(_("Too many postponed prefixes")); 5221 else if (spin->si_newprefID == 0 || spin->si_newprefID == 127) 5222 MSG(_("Too many compound flags")); 5223 else 5224 MSG(_("Too many posponed prefixes and/or compound flags")); 5225 } 5226 5227 if (syllable != NULL) 5228 { 5229 aff_check_string(spin->si_syllable, syllable, "SYLLABLE"); 5230 spin->si_syllable = syllable; 5231 } 5232 5233 if (sofofrom != NULL || sofoto != NULL) 5234 { 5235 if (sofofrom == NULL || sofoto == NULL) 5236 smsg((char_u *)_("Missing SOFO%s line in %s"), 5237 sofofrom == NULL ? "FROM" : "TO", fname); 5238 else if (spin->si_sal.ga_len > 0) 5239 smsg((char_u *)_("Both SAL and SOFO lines in %s"), fname); 5240 else 5241 { 5242 aff_check_string(spin->si_sofofr, sofofrom, "SOFOFROM"); 5243 aff_check_string(spin->si_sofoto, sofoto, "SOFOTO"); 5244 spin->si_sofofr = sofofrom; 5245 spin->si_sofoto = sofoto; 5246 } 5247 } 5248 5249 if (midword != NULL) 5250 { 5251 aff_check_string(spin->si_midword, midword, "MIDWORD"); 5252 spin->si_midword = midword; 5253 } 5254 5255 vim_free(pc); 5256 fclose(fd); 5257 return aff; 5258 } 5259 5260 /* 5261 * Turn an affix flag name into a number, according to the FLAG type. 5262 * returns zero for failure. 5263 */ 5264 static unsigned 5265 affitem2flag(flagtype, item, fname, lnum) 5266 int flagtype; 5267 char_u *item; 5268 char_u *fname; 5269 int lnum; 5270 { 5271 unsigned res; 5272 char_u *p = item; 5273 5274 res = get_affitem(flagtype, &p); 5275 if (res == 0) 5276 { 5277 if (flagtype == AFT_NUM) 5278 smsg((char_u *)_("Flag is not a number in %s line %d: %s"), 5279 fname, lnum, item); 5280 else 5281 smsg((char_u *)_("Illegal flag in %s line %d: %s"), 5282 fname, lnum, item); 5283 } 5284 if (*p != NUL) 5285 { 5286 smsg((char_u *)_(e_affname), fname, lnum, item); 5287 return 0; 5288 } 5289 5290 return res; 5291 } 5292 5293 /* 5294 * Get one affix name from "*pp" and advance the pointer. 5295 * Returns zero for an error, still advances the pointer then. 5296 */ 5297 static unsigned 5298 get_affitem(flagtype, pp) 5299 int flagtype; 5300 char_u **pp; 5301 { 5302 int res; 5303 5304 if (flagtype == AFT_NUM) 5305 { 5306 if (!VIM_ISDIGIT(**pp)) 5307 { 5308 ++*pp; /* always advance, avoid getting stuck */ 5309 return 0; 5310 } 5311 res = getdigits(pp); 5312 } 5313 else 5314 { 5315 #ifdef FEAT_MBYTE 5316 res = mb_ptr2char_adv(pp); 5317 #else 5318 res = *(*pp)++; 5319 #endif 5320 if (flagtype == AFT_LONG || (flagtype == AFT_CAPLONG 5321 && res >= 'A' && res <= 'Z')) 5322 { 5323 if (**pp == NUL) 5324 return 0; 5325 #ifdef FEAT_MBYTE 5326 res = mb_ptr2char_adv(pp) + (res << 16); 5327 #else 5328 res = *(*pp)++ + (res << 16); 5329 #endif 5330 } 5331 } 5332 return res; 5333 } 5334 5335 /* 5336 * Process the "compflags" string used in an affix file and append it to 5337 * spin->si_compflags. 5338 * The processing involves changing the affix names to ID numbers, so that 5339 * they fit in one byte. 5340 */ 5341 static void 5342 process_compflags(spin, aff, compflags) 5343 spellinfo_T *spin; 5344 afffile_T *aff; 5345 char_u *compflags; 5346 { 5347 char_u *p; 5348 char_u *prevp; 5349 unsigned flag; 5350 compitem_T *ci; 5351 int id; 5352 int len; 5353 char_u *tp; 5354 char_u key[AH_KEY_LEN]; 5355 hashitem_T *hi; 5356 5357 /* Make room for the old and the new compflags, concatenated with a / in 5358 * between. Processing it makes it shorter, but we don't know by how 5359 * much, thus allocate the maximum. */ 5360 len = STRLEN(compflags) + 1; 5361 if (spin->si_compflags != NULL) 5362 len += STRLEN(spin->si_compflags) + 1; 5363 p = getroom(spin, len, FALSE); 5364 if (p == NULL) 5365 return; 5366 if (spin->si_compflags != NULL) 5367 { 5368 STRCPY(p, spin->si_compflags); 5369 STRCAT(p, "/"); 5370 } 5371 spin->si_compflags = p; 5372 tp = p + STRLEN(p); 5373 5374 for (p = compflags; *p != NUL; ) 5375 { 5376 if (vim_strchr((char_u *)"/*+[]", *p) != NULL) 5377 /* Copy non-flag characters directly. */ 5378 *tp++ = *p++; 5379 else 5380 { 5381 /* First get the flag number, also checks validity. */ 5382 prevp = p; 5383 flag = get_affitem(aff->af_flagtype, &p); 5384 if (flag != 0) 5385 { 5386 /* Find the flag in the hashtable. If it was used before, use 5387 * the existing ID. Otherwise add a new entry. */ 5388 vim_strncpy(key, prevp, p - prevp); 5389 hi = hash_find(&aff->af_comp, key); 5390 if (!HASHITEM_EMPTY(hi)) 5391 id = HI2CI(hi)->ci_newID; 5392 else 5393 { 5394 ci = (compitem_T *)getroom(spin, sizeof(compitem_T), TRUE); 5395 if (ci == NULL) 5396 break; 5397 STRCPY(ci->ci_key, key); 5398 ci->ci_flag = flag; 5399 /* Avoid using a flag ID that has a special meaning in a 5400 * regexp (also inside []). */ 5401 do 5402 { 5403 check_renumber(spin); 5404 id = spin->si_newcompID--; 5405 } while (vim_strchr((char_u *)"/+*[]\\-^", id) != NULL); 5406 ci->ci_newID = id; 5407 hash_add(&aff->af_comp, ci->ci_key); 5408 } 5409 *tp++ = id; 5410 } 5411 if (aff->af_flagtype == AFT_NUM && *p == ',') 5412 ++p; 5413 } 5414 } 5415 5416 *tp = NUL; 5417 } 5418 5419 /* 5420 * Check that the new IDs for postponed affixes and compounding don't overrun 5421 * each other. We have almost 255 available, but start at 0-127 to avoid 5422 * using two bytes for utf-8. When the 0-127 range is used up go to 128-255. 5423 * When that is used up an error message is given. 5424 */ 5425 static void 5426 check_renumber(spin) 5427 spellinfo_T *spin; 5428 { 5429 if (spin->si_newprefID == spin->si_newcompID && spin->si_newcompID < 128) 5430 { 5431 spin->si_newprefID = 127; 5432 spin->si_newcompID = 255; 5433 } 5434 } 5435 5436 /* 5437 * Return TRUE if flag "flag" appears in affix list "afflist". 5438 */ 5439 static int 5440 flag_in_afflist(flagtype, afflist, flag) 5441 int flagtype; 5442 char_u *afflist; 5443 unsigned flag; 5444 { 5445 char_u *p; 5446 unsigned n; 5447 5448 switch (flagtype) 5449 { 5450 case AFT_CHAR: 5451 return vim_strchr(afflist, flag) != NULL; 5452 5453 case AFT_CAPLONG: 5454 case AFT_LONG: 5455 for (p = afflist; *p != NUL; ) 5456 { 5457 #ifdef FEAT_MBYTE 5458 n = mb_ptr2char_adv(&p); 5459 #else 5460 n = *p++; 5461 #endif 5462 if ((flagtype == AFT_LONG || (n >= 'A' && n <= 'Z')) 5463 && *p != NUL) 5464 #ifdef FEAT_MBYTE 5465 n = mb_ptr2char_adv(&p) + (n << 16); 5466 #else 5467 n = *p++ + (n << 16); 5468 #endif 5469 if (n == flag) 5470 return TRUE; 5471 } 5472 break; 5473 5474 case AFT_NUM: 5475 for (p = afflist; *p != NUL; ) 5476 { 5477 n = getdigits(&p); 5478 if (n == flag) 5479 return TRUE; 5480 if (*p != NUL) /* skip over comma */ 5481 ++p; 5482 } 5483 break; 5484 } 5485 return FALSE; 5486 } 5487 5488 /* 5489 * Give a warning when "spinval" and "affval" numbers are set and not the same. 5490 */ 5491 static void 5492 aff_check_number(spinval, affval, name) 5493 int spinval; 5494 int affval; 5495 char *name; 5496 { 5497 if (spinval != 0 && spinval != affval) 5498 smsg((char_u *)_("%s value differs from what is used in another .aff file"), name); 5499 } 5500 5501 /* 5502 * Give a warning when "spinval" and "affval" strings are set and not the same. 5503 */ 5504 static void 5505 aff_check_string(spinval, affval, name) 5506 char_u *spinval; 5507 char_u *affval; 5508 char *name; 5509 { 5510 if (spinval != NULL && STRCMP(spinval, affval) != 0) 5511 smsg((char_u *)_("%s value differs from what is used in another .aff file"), name); 5512 } 5513 5514 /* 5515 * Return TRUE if strings "s1" and "s2" are equal. Also consider both being 5516 * NULL as equal. 5517 */ 5518 static int 5519 str_equal(s1, s2) 5520 char_u *s1; 5521 char_u *s2; 5522 { 5523 if (s1 == NULL || s2 == NULL) 5524 return s1 == s2; 5525 return STRCMP(s1, s2) == 0; 5526 } 5527 5528 /* 5529 * Add a from-to item to "gap". Used for REP and SAL items. 5530 * They are stored case-folded. 5531 */ 5532 static void 5533 add_fromto(spin, gap, from, to) 5534 spellinfo_T *spin; 5535 garray_T *gap; 5536 char_u *from; 5537 char_u *to; 5538 { 5539 fromto_T *ftp; 5540 char_u word[MAXWLEN]; 5541 5542 if (ga_grow(gap, 1) == OK) 5543 { 5544 ftp = ((fromto_T *)gap->ga_data) + gap->ga_len; 5545 (void)spell_casefold(from, STRLEN(from), word, MAXWLEN); 5546 ftp->ft_from = getroom_save(spin, word); 5547 (void)spell_casefold(to, STRLEN(to), word, MAXWLEN); 5548 ftp->ft_to = getroom_save(spin, word); 5549 ++gap->ga_len; 5550 } 5551 } 5552 5553 /* 5554 * Convert a boolean argument in a SAL line to TRUE or FALSE; 5555 */ 5556 static int 5557 sal_to_bool(s) 5558 char_u *s; 5559 { 5560 return STRCMP(s, "1") == 0 || STRCMP(s, "true") == 0; 5561 } 5562 5563 /* 5564 * Return TRUE if string "s" contains a non-ASCII character (128 or higher). 5565 * When "s" is NULL FALSE is returned. 5566 */ 5567 static int 5568 has_non_ascii(s) 5569 char_u *s; 5570 { 5571 char_u *p; 5572 5573 if (s != NULL) 5574 for (p = s; *p != NUL; ++p) 5575 if (*p >= 128) 5576 return TRUE; 5577 return FALSE; 5578 } 5579 5580 /* 5581 * Free the structure filled by spell_read_aff(). 5582 */ 5583 static void 5584 spell_free_aff(aff) 5585 afffile_T *aff; 5586 { 5587 hashtab_T *ht; 5588 hashitem_T *hi; 5589 int todo; 5590 affheader_T *ah; 5591 affentry_T *ae; 5592 5593 vim_free(aff->af_enc); 5594 5595 /* All this trouble to free the "ae_prog" items... */ 5596 for (ht = &aff->af_pref; ; ht = &aff->af_suff) 5597 { 5598 todo = ht->ht_used; 5599 for (hi = ht->ht_array; todo > 0; ++hi) 5600 { 5601 if (!HASHITEM_EMPTY(hi)) 5602 { 5603 --todo; 5604 ah = HI2AH(hi); 5605 for (ae = ah->ah_first; ae != NULL; ae = ae->ae_next) 5606 vim_free(ae->ae_prog); 5607 } 5608 } 5609 if (ht == &aff->af_suff) 5610 break; 5611 } 5612 5613 hash_clear(&aff->af_pref); 5614 hash_clear(&aff->af_suff); 5615 hash_clear(&aff->af_comp); 5616 } 5617 5618 /* 5619 * Read dictionary file "fname". 5620 * Returns OK or FAIL; 5621 */ 5622 static int 5623 spell_read_dic(spin, fname, affile) 5624 spellinfo_T *spin; 5625 char_u *fname; 5626 afffile_T *affile; 5627 { 5628 hashtab_T ht; 5629 char_u line[MAXLINELEN]; 5630 char_u *p; 5631 char_u *afflist; 5632 char_u store_afflist[MAXWLEN]; 5633 int pfxlen; 5634 int need_affix; 5635 char_u *dw; 5636 char_u *pc; 5637 char_u *w; 5638 int l; 5639 hash_T hash; 5640 hashitem_T *hi; 5641 FILE *fd; 5642 int lnum = 1; 5643 int non_ascii = 0; 5644 int retval = OK; 5645 char_u message[MAXLINELEN + MAXWLEN]; 5646 int flags; 5647 int duplicate = 0; 5648 5649 /* 5650 * Open the file. 5651 */ 5652 fd = mch_fopen((char *)fname, "r"); 5653 if (fd == NULL) 5654 { 5655 EMSG2(_(e_notopen), fname); 5656 return FAIL; 5657 } 5658 5659 /* The hashtable is only used to detect duplicated words. */ 5660 hash_init(&ht); 5661 5662 if (spin->si_verbose || p_verbose > 2) 5663 { 5664 if (!spin->si_verbose) 5665 verbose_enter(); 5666 smsg((char_u *)_("Reading dictionary file %s ..."), fname); 5667 out_flush(); 5668 if (!spin->si_verbose) 5669 verbose_leave(); 5670 } 5671 5672 /* start with a message for the first line */ 5673 spin->si_msg_count = 999999; 5674 5675 /* Read and ignore the first line: word count. */ 5676 (void)vim_fgets(line, MAXLINELEN, fd); 5677 if (!vim_isdigit(*skipwhite(line))) 5678 EMSG2(_("E760: No word count in %s"), fname); 5679 5680 /* 5681 * Read all the lines in the file one by one. 5682 * The words are converted to 'encoding' here, before being added to 5683 * the hashtable. 5684 */ 5685 while (!vim_fgets(line, MAXLINELEN, fd) && !got_int) 5686 { 5687 line_breakcheck(); 5688 ++lnum; 5689 if (line[0] == '#' || line[0] == '/') 5690 continue; /* comment line */ 5691 5692 /* Remove CR, LF and white space from the end. White space halfway 5693 * the word is kept to allow e.g., "et al.". */ 5694 l = STRLEN(line); 5695 while (l > 0 && line[l - 1] <= ' ') 5696 --l; 5697 if (l == 0) 5698 continue; /* empty line */ 5699 line[l] = NUL; 5700 5701 /* Find the optional affix names. Replace the SLASH character by a 5702 * slash. */ 5703 afflist = NULL; 5704 for (p = line; *p != NUL; mb_ptr_adv(p)) 5705 { 5706 if (*p == affile->af_slash) 5707 *p = '/'; 5708 else if (*p == '/') 5709 { 5710 *p = NUL; 5711 afflist = p + 1; 5712 break; 5713 } 5714 } 5715 5716 /* Skip non-ASCII words when "spin->si_ascii" is TRUE. */ 5717 if (spin->si_ascii && has_non_ascii(line)) 5718 { 5719 ++non_ascii; 5720 continue; 5721 } 5722 5723 #ifdef FEAT_MBYTE 5724 /* Convert from "SET" to 'encoding' when needed. */ 5725 if (spin->si_conv.vc_type != CONV_NONE) 5726 { 5727 pc = string_convert(&spin->si_conv, line, NULL); 5728 if (pc == NULL) 5729 { 5730 smsg((char_u *)_("Conversion failure for word in %s line %d: %s"), 5731 fname, lnum, line); 5732 continue; 5733 } 5734 w = pc; 5735 } 5736 else 5737 #endif 5738 { 5739 pc = NULL; 5740 w = line; 5741 } 5742 5743 /* This takes time, print a message every 10000 words. */ 5744 if (spin->si_verbose && spin->si_msg_count > 10000) 5745 { 5746 spin->si_msg_count = 0; 5747 vim_snprintf((char *)message, sizeof(message), 5748 _("line %6d, word %6d - %s"), 5749 lnum, spin->si_foldwcount + spin->si_keepwcount, w); 5750 msg_start(); 5751 msg_puts_long_attr(message, 0); 5752 msg_clr_eos(); 5753 msg_didout = FALSE; 5754 msg_col = 0; 5755 out_flush(); 5756 } 5757 5758 /* Store the word in the hashtable to be able to find duplicates. */ 5759 dw = (char_u *)getroom_save(spin, w); 5760 if (dw == NULL) 5761 retval = FAIL; 5762 vim_free(pc); 5763 if (retval == FAIL) 5764 break; 5765 5766 hash = hash_hash(dw); 5767 hi = hash_lookup(&ht, dw, hash); 5768 if (!HASHITEM_EMPTY(hi)) 5769 { 5770 if (p_verbose > 0) 5771 smsg((char_u *)_("Duplicate word in %s line %d: %s"), 5772 fname, lnum, dw); 5773 else if (duplicate == 0) 5774 smsg((char_u *)_("First duplicate word in %s line %d: %s"), 5775 fname, lnum, dw); 5776 ++duplicate; 5777 } 5778 else 5779 hash_add_item(&ht, hi, dw, hash); 5780 5781 flags = 0; 5782 store_afflist[0] = NUL; 5783 pfxlen = 0; 5784 need_affix = FALSE; 5785 if (afflist != NULL) 5786 { 5787 /* Check for affix name that stands for keep-case word and stands 5788 * for rare word (if defined). */ 5789 if (affile->af_kep != 0 && flag_in_afflist( 5790 affile->af_flagtype, afflist, affile->af_kep)) 5791 flags |= WF_KEEPCAP | WF_FIXCAP; 5792 if (affile->af_rar != 0 && flag_in_afflist( 5793 affile->af_flagtype, afflist, affile->af_rar)) 5794 flags |= WF_RARE; 5795 if (affile->af_bad != 0 && flag_in_afflist( 5796 affile->af_flagtype, afflist, affile->af_bad)) 5797 flags |= WF_BANNED; 5798 if (affile->af_needaffix != 0 && flag_in_afflist( 5799 affile->af_flagtype, afflist, affile->af_needaffix)) 5800 need_affix = TRUE; 5801 if (affile->af_needcomp != 0 && flag_in_afflist( 5802 affile->af_flagtype, afflist, affile->af_needcomp)) 5803 flags |= WF_NEEDCOMP; 5804 5805 if (affile->af_pfxpostpone) 5806 /* Need to store the list of prefix IDs with the word. */ 5807 pfxlen = get_pfxlist(affile, afflist, store_afflist); 5808 5809 if (spin->si_compflags != NULL) 5810 /* Need to store the list of compound flags with the word. 5811 * Concatenate them to the list of prefix IDs. */ 5812 get_compflags(affile, afflist, store_afflist + pfxlen); 5813 } 5814 5815 /* Add the word to the word tree(s). */ 5816 if (store_word(spin, dw, flags, spin->si_region, 5817 store_afflist, need_affix) == FAIL) 5818 retval = FAIL; 5819 5820 if (afflist != NULL) 5821 { 5822 /* Find all matching suffixes and add the resulting words. 5823 * Additionally do matching prefixes that combine. */ 5824 if (store_aff_word(spin, dw, afflist, affile, 5825 &affile->af_suff, &affile->af_pref, 5826 FALSE, flags, store_afflist, pfxlen) == FAIL) 5827 retval = FAIL; 5828 5829 /* Find all matching prefixes and add the resulting words. */ 5830 if (store_aff_word(spin, dw, afflist, affile, 5831 &affile->af_pref, NULL, 5832 FALSE, flags, store_afflist, pfxlen) == FAIL) 5833 retval = FAIL; 5834 } 5835 } 5836 5837 if (duplicate > 0) 5838 smsg((char_u *)_("%d duplicate word(s) in %s"), duplicate, fname); 5839 if (spin->si_ascii && non_ascii > 0) 5840 smsg((char_u *)_("Ignored %d word(s) with non-ASCII characters in %s"), 5841 non_ascii, fname); 5842 hash_clear(&ht); 5843 5844 fclose(fd); 5845 return retval; 5846 } 5847 5848 /* 5849 * Get the list of prefix IDs from the affix list "afflist". 5850 * Used for PFXPOSTPONE. 5851 * Put the resulting flags in "store_afflist[MAXWLEN]" with a terminating NUL 5852 * and return the number of affixes. 5853 */ 5854 static int 5855 get_pfxlist(affile, afflist, store_afflist) 5856 afffile_T *affile; 5857 char_u *afflist; 5858 char_u *store_afflist; 5859 { 5860 char_u *p; 5861 char_u *prevp; 5862 int cnt = 0; 5863 int id; 5864 char_u key[AH_KEY_LEN]; 5865 hashitem_T *hi; 5866 5867 for (p = afflist; *p != NUL; ) 5868 { 5869 prevp = p; 5870 if (get_affitem(affile->af_flagtype, &p) != 0) 5871 { 5872 /* A flag is a postponed prefix flag if it appears in "af_pref" 5873 * and it's ID is not zero. */ 5874 vim_strncpy(key, prevp, p - prevp); 5875 hi = hash_find(&affile->af_pref, key); 5876 if (!HASHITEM_EMPTY(hi)) 5877 { 5878 id = HI2AH(hi)->ah_newID; 5879 if (id != 0) 5880 store_afflist[cnt++] = id; 5881 } 5882 } 5883 if (affile->af_flagtype == AFT_NUM && *p == ',') 5884 ++p; 5885 } 5886 5887 store_afflist[cnt] = NUL; 5888 return cnt; 5889 } 5890 5891 /* 5892 * Get the list of compound IDs from the affix list "afflist" that are used 5893 * for compound words. 5894 * Puts the flags in "store_afflist[]". 5895 */ 5896 static void 5897 get_compflags(affile, afflist, store_afflist) 5898 afffile_T *affile; 5899 char_u *afflist; 5900 char_u *store_afflist; 5901 { 5902 char_u *p; 5903 char_u *prevp; 5904 int cnt = 0; 5905 char_u key[AH_KEY_LEN]; 5906 hashitem_T *hi; 5907 5908 for (p = afflist; *p != NUL; ) 5909 { 5910 prevp = p; 5911 if (get_affitem(affile->af_flagtype, &p) != 0) 5912 { 5913 /* A flag is a compound flag if it appears in "af_comp". */ 5914 vim_strncpy(key, prevp, p - prevp); 5915 hi = hash_find(&affile->af_comp, key); 5916 if (!HASHITEM_EMPTY(hi)) 5917 store_afflist[cnt++] = HI2CI(hi)->ci_newID; 5918 } 5919 if (affile->af_flagtype == AFT_NUM && *p == ',') 5920 ++p; 5921 } 5922 5923 store_afflist[cnt] = NUL; 5924 } 5925 5926 /* 5927 * Apply affixes to a word and store the resulting words. 5928 * "ht" is the hashtable with affentry_T that need to be applied, either 5929 * prefixes or suffixes. 5930 * "xht", when not NULL, is the prefix hashtable, to be used additionally on 5931 * the resulting words for combining affixes. 5932 * 5933 * Returns FAIL when out of memory. 5934 */ 5935 static int 5936 store_aff_word(spin, word, afflist, affile, ht, xht, comb, flags, 5937 pfxlist, pfxlen) 5938 spellinfo_T *spin; /* spell info */ 5939 char_u *word; /* basic word start */ 5940 char_u *afflist; /* list of names of supported affixes */ 5941 afffile_T *affile; 5942 hashtab_T *ht; 5943 hashtab_T *xht; 5944 int comb; /* only use affixes that combine */ 5945 int flags; /* flags for the word */ 5946 char_u *pfxlist; /* list of prefix IDs */ 5947 int pfxlen; /* nr of flags in "pfxlist" for prefixes, rest 5948 * is compound flags */ 5949 { 5950 int todo; 5951 hashitem_T *hi; 5952 affheader_T *ah; 5953 affentry_T *ae; 5954 regmatch_T regmatch; 5955 char_u newword[MAXWLEN]; 5956 int retval = OK; 5957 int i; 5958 char_u *p; 5959 int use_flags; 5960 char_u *use_pfxlist; 5961 char_u pfx_pfxlist[MAXWLEN]; 5962 size_t wordlen = STRLEN(word); 5963 5964 todo = ht->ht_used; 5965 for (hi = ht->ht_array; todo > 0 && retval == OK; ++hi) 5966 { 5967 if (!HASHITEM_EMPTY(hi)) 5968 { 5969 --todo; 5970 ah = HI2AH(hi); 5971 5972 /* Check that the affix combines, if required, and that the word 5973 * supports this affix. */ 5974 if ((!comb || ah->ah_combine) && flag_in_afflist( 5975 affile->af_flagtype, afflist, ah->ah_flag)) 5976 { 5977 /* Loop over all affix entries with this name. */ 5978 for (ae = ah->ah_first; ae != NULL; ae = ae->ae_next) 5979 { 5980 /* Check the condition. It's not logical to match case 5981 * here, but it is required for compatibility with 5982 * Myspell. 5983 * Another requirement from Myspell is that the chop 5984 * string is shorter than the word itself. 5985 * For prefixes, when "PFXPOSTPONE" was used, only do 5986 * prefixes with a chop string. */ 5987 regmatch.regprog = ae->ae_prog; 5988 regmatch.rm_ic = FALSE; 5989 if ((xht != NULL || !affile->af_pfxpostpone 5990 || ae->ae_chop != NULL) 5991 && (ae->ae_chop == NULL 5992 || STRLEN(ae->ae_chop) < wordlen) 5993 && (ae->ae_prog == NULL 5994 || vim_regexec(®match, word, (colnr_T)0))) 5995 { 5996 /* Match. Remove the chop and add the affix. */ 5997 if (xht == NULL) 5998 { 5999 /* prefix: chop/add at the start of the word */ 6000 if (ae->ae_add == NULL) 6001 *newword = NUL; 6002 else 6003 STRCPY(newword, ae->ae_add); 6004 p = word; 6005 if (ae->ae_chop != NULL) 6006 { 6007 /* Skip chop string. */ 6008 #ifdef FEAT_MBYTE 6009 if (has_mbyte) 6010 { 6011 i = mb_charlen(ae->ae_chop); 6012 for ( ; i > 0; --i) 6013 mb_ptr_adv(p); 6014 } 6015 else 6016 #endif 6017 p += STRLEN(ae->ae_chop); 6018 } 6019 STRCAT(newword, p); 6020 } 6021 else 6022 { 6023 /* suffix: chop/add at the end of the word */ 6024 STRCPY(newword, word); 6025 if (ae->ae_chop != NULL) 6026 { 6027 /* Remove chop string. */ 6028 p = newword + STRLEN(newword); 6029 i = MB_CHARLEN(ae->ae_chop); 6030 for ( ; i > 0; --i) 6031 mb_ptr_back(newword, p); 6032 *p = NUL; 6033 } 6034 if (ae->ae_add != NULL) 6035 STRCAT(newword, ae->ae_add); 6036 } 6037 6038 /* Obey the "rare" flag of the affix. */ 6039 if (ae->ae_rare) 6040 use_flags = flags | WF_RARE; 6041 else 6042 use_flags = flags; 6043 6044 /* Obey the "nocomp" flag of the affix: don't use the 6045 * compound flags. */ 6046 use_pfxlist = pfxlist; 6047 if (ae->ae_nocomp && pfxlist != NULL) 6048 { 6049 vim_strncpy(pfx_pfxlist, pfxlist, pfxlen); 6050 use_pfxlist = pfx_pfxlist; 6051 } 6052 6053 /* When there are postponed prefixes... */ 6054 if (spin->si_prefroot != NULL 6055 && spin->si_prefroot->wn_sibling != NULL) 6056 { 6057 /* ... add a flag to indicate an affix was used. */ 6058 use_flags |= WF_HAS_AFF; 6059 6060 /* ... don't use a prefix list if combining 6061 * affixes is not allowed. But do use the 6062 * compound flags after them. */ 6063 if ((!ah->ah_combine || comb) && pfxlist != NULL) 6064 use_pfxlist += pfxlen; 6065 } 6066 6067 /* Store the modified word. */ 6068 if (store_word(spin, newword, use_flags, 6069 spin->si_region, use_pfxlist, FALSE) == FAIL) 6070 retval = FAIL; 6071 6072 /* When added a suffix and combining is allowed also 6073 * try adding prefixes additionally. */ 6074 if (xht != NULL && ah->ah_combine) 6075 if (store_aff_word(spin, newword, afflist, affile, 6076 xht, NULL, TRUE, 6077 use_flags, use_pfxlist, pfxlen) == FAIL) 6078 retval = FAIL; 6079 } 6080 } 6081 } 6082 } 6083 } 6084 6085 return retval; 6086 } 6087 6088 /* 6089 * Read a file with a list of words. 6090 */ 6091 static int 6092 spell_read_wordfile(spin, fname) 6093 spellinfo_T *spin; 6094 char_u *fname; 6095 { 6096 FILE *fd; 6097 long lnum = 0; 6098 char_u rline[MAXLINELEN]; 6099 char_u *line; 6100 char_u *pc = NULL; 6101 char_u *p; 6102 int l; 6103 int retval = OK; 6104 int did_word = FALSE; 6105 int non_ascii = 0; 6106 int flags; 6107 int regionmask; 6108 6109 /* 6110 * Open the file. 6111 */ 6112 fd = mch_fopen((char *)fname, "r"); 6113 if (fd == NULL) 6114 { 6115 EMSG2(_(e_notopen), fname); 6116 return FAIL; 6117 } 6118 6119 if (spin->si_verbose || p_verbose > 2) 6120 { 6121 if (!spin->si_verbose) 6122 verbose_enter(); 6123 smsg((char_u *)_("Reading word file %s ..."), fname); 6124 out_flush(); 6125 if (!spin->si_verbose) 6126 verbose_leave(); 6127 } 6128 6129 /* 6130 * Read all the lines in the file one by one. 6131 */ 6132 while (!vim_fgets(rline, MAXLINELEN, fd) && !got_int) 6133 { 6134 line_breakcheck(); 6135 ++lnum; 6136 6137 /* Skip comment lines. */ 6138 if (*rline == '#') 6139 continue; 6140 6141 /* Remove CR, LF and white space from the end. */ 6142 l = STRLEN(rline); 6143 while (l > 0 && rline[l - 1] <= ' ') 6144 --l; 6145 if (l == 0) 6146 continue; /* empty or blank line */ 6147 rline[l] = NUL; 6148 6149 /* Convert from "=encoding={encoding}" to 'encoding' when needed. */ 6150 vim_free(pc); 6151 #ifdef FEAT_MBYTE 6152 if (spin->si_conv.vc_type != CONV_NONE) 6153 { 6154 pc = string_convert(&spin->si_conv, rline, NULL); 6155 if (pc == NULL) 6156 { 6157 smsg((char_u *)_("Conversion failure for word in %s line %d: %s"), 6158 fname, lnum, rline); 6159 continue; 6160 } 6161 line = pc; 6162 } 6163 else 6164 #endif 6165 { 6166 pc = NULL; 6167 line = rline; 6168 } 6169 6170 if (*line == '/') 6171 { 6172 ++line; 6173 if (STRNCMP(line, "encoding=", 9) == 0) 6174 { 6175 if (spin->si_conv.vc_type != CONV_NONE) 6176 smsg((char_u *)_("Duplicate /encoding= line ignored in %s line %d: %s"), 6177 fname, lnum, line - 1); 6178 else if (did_word) 6179 smsg((char_u *)_("/encoding= line after word ignored in %s line %d: %s"), 6180 fname, lnum, line - 1); 6181 else 6182 { 6183 #ifdef FEAT_MBYTE 6184 char_u *enc; 6185 6186 /* Setup for conversion to 'encoding'. */ 6187 line += 10; 6188 enc = enc_canonize(line); 6189 if (enc != NULL && !spin->si_ascii 6190 && convert_setup(&spin->si_conv, enc, 6191 p_enc) == FAIL) 6192 smsg((char_u *)_("Conversion in %s not supported: from %s to %s"), 6193 fname, line, p_enc); 6194 vim_free(enc); 6195 spin->si_conv.vc_fail = TRUE; 6196 #else 6197 smsg((char_u *)_("Conversion in %s not supported"), fname); 6198 #endif 6199 } 6200 continue; 6201 } 6202 6203 if (STRNCMP(line, "regions=", 8) == 0) 6204 { 6205 if (spin->si_region_count > 1) 6206 smsg((char_u *)_("Duplicate /regions= line ignored in %s line %d: %s"), 6207 fname, lnum, line); 6208 else 6209 { 6210 line += 8; 6211 if (STRLEN(line) > 16) 6212 smsg((char_u *)_("Too many regions in %s line %d: %s"), 6213 fname, lnum, line); 6214 else 6215 { 6216 spin->si_region_count = STRLEN(line) / 2; 6217 STRCPY(spin->si_region_name, line); 6218 6219 /* Adjust the mask for a word valid in all regions. */ 6220 spin->si_region = (1 << spin->si_region_count) - 1; 6221 } 6222 } 6223 continue; 6224 } 6225 6226 smsg((char_u *)_("/ line ignored in %s line %d: %s"), 6227 fname, lnum, line - 1); 6228 continue; 6229 } 6230 6231 flags = 0; 6232 regionmask = spin->si_region; 6233 6234 /* Check for flags and region after a slash. */ 6235 p = vim_strchr(line, '/'); 6236 if (p != NULL) 6237 { 6238 *p++ = NUL; 6239 while (*p != NUL) 6240 { 6241 if (*p == '=') /* keep-case word */ 6242 flags |= WF_KEEPCAP | WF_FIXCAP; 6243 else if (*p == '!') /* Bad, bad, wicked word. */ 6244 flags |= WF_BANNED; 6245 else if (*p == '?') /* Rare word. */ 6246 flags |= WF_RARE; 6247 else if (VIM_ISDIGIT(*p)) /* region number(s) */ 6248 { 6249 if ((flags & WF_REGION) == 0) /* first one */ 6250 regionmask = 0; 6251 flags |= WF_REGION; 6252 6253 l = *p - '0'; 6254 if (l > spin->si_region_count) 6255 { 6256 smsg((char_u *)_("Invalid region nr in %s line %d: %s"), 6257 fname, lnum, p); 6258 break; 6259 } 6260 regionmask |= 1 << (l - 1); 6261 } 6262 else 6263 { 6264 smsg((char_u *)_("Unrecognized flags in %s line %d: %s"), 6265 fname, lnum, p); 6266 break; 6267 } 6268 ++p; 6269 } 6270 } 6271 6272 /* Skip non-ASCII words when "spin->si_ascii" is TRUE. */ 6273 if (spin->si_ascii && has_non_ascii(line)) 6274 { 6275 ++non_ascii; 6276 continue; 6277 } 6278 6279 /* Normal word: store it. */ 6280 if (store_word(spin, line, flags, regionmask, NULL, FALSE) == FAIL) 6281 { 6282 retval = FAIL; 6283 break; 6284 } 6285 did_word = TRUE; 6286 } 6287 6288 vim_free(pc); 6289 fclose(fd); 6290 6291 if (spin->si_ascii && non_ascii > 0 && (spin->si_verbose || p_verbose > 2)) 6292 { 6293 if (p_verbose > 2) 6294 verbose_enter(); 6295 smsg((char_u *)_("Ignored %d words with non-ASCII characters"), 6296 non_ascii); 6297 if (p_verbose > 2) 6298 verbose_leave(); 6299 } 6300 return retval; 6301 } 6302 6303 /* 6304 * Get part of an sblock_T, "len" bytes long. 6305 * This avoids calling free() for every little struct we use (and keeping 6306 * track of them). 6307 * The memory is cleared to all zeros. 6308 * Returns NULL when out of memory. 6309 */ 6310 static void * 6311 getroom(spin, len, align) 6312 spellinfo_T *spin; 6313 size_t len; /* length needed */ 6314 int align; /* align for pointer */ 6315 { 6316 char_u *p; 6317 sblock_T *bl = spin->si_blocks; 6318 6319 if (align && bl != NULL) 6320 /* Round size up for alignment. On some systems structures need to be 6321 * aligned to the size of a pointer (e.g., SPARC). */ 6322 bl->sb_used = (bl->sb_used + sizeof(char *) - 1) 6323 & ~(sizeof(char *) - 1); 6324 6325 if (bl == NULL || bl->sb_used + len > SBLOCKSIZE) 6326 { 6327 /* Allocate a block of memory. This is not freed until much later. */ 6328 bl = (sblock_T *)alloc_clear((unsigned)(sizeof(sblock_T) + SBLOCKSIZE)); 6329 if (bl == NULL) 6330 return NULL; 6331 bl->sb_next = spin->si_blocks; 6332 spin->si_blocks = bl; 6333 bl->sb_used = 0; 6334 ++spin->si_blocks_cnt; 6335 } 6336 6337 p = bl->sb_data + bl->sb_used; 6338 bl->sb_used += len; 6339 6340 return p; 6341 } 6342 6343 /* 6344 * Make a copy of a string into memory allocated with getroom(). 6345 */ 6346 static char_u * 6347 getroom_save(spin, s) 6348 spellinfo_T *spin; 6349 char_u *s; 6350 { 6351 char_u *sc; 6352 6353 sc = (char_u *)getroom(spin, STRLEN(s) + 1, FALSE); 6354 if (sc != NULL) 6355 STRCPY(sc, s); 6356 return sc; 6357 } 6358 6359 6360 /* 6361 * Free the list of allocated sblock_T. 6362 */ 6363 static void 6364 free_blocks(bl) 6365 sblock_T *bl; 6366 { 6367 sblock_T *next; 6368 6369 while (bl != NULL) 6370 { 6371 next = bl->sb_next; 6372 vim_free(bl); 6373 bl = next; 6374 } 6375 } 6376 6377 /* 6378 * Allocate the root of a word tree. 6379 */ 6380 static wordnode_T * 6381 wordtree_alloc(spin) 6382 spellinfo_T *spin; 6383 { 6384 return (wordnode_T *)getroom(spin, sizeof(wordnode_T), TRUE); 6385 } 6386 6387 /* 6388 * Store a word in the tree(s). 6389 * Always store it in the case-folded tree. For a keep-case word this is 6390 * useful when the word can also be used with all caps (no WF_FIXCAP flag) and 6391 * used to find suggestions. 6392 * For a keep-case word also store it in the keep-case tree. 6393 * When "pfxlist" is not NULL store the word for each postponed prefix ID and 6394 * compound flag. 6395 */ 6396 static int 6397 store_word(spin, word, flags, region, pfxlist, need_affix) 6398 spellinfo_T *spin; 6399 char_u *word; 6400 int flags; /* extra flags, WF_BANNED */ 6401 int region; /* supported region(s) */ 6402 char_u *pfxlist; /* list of prefix IDs or NULL */ 6403 int need_affix; /* only store word with affix ID */ 6404 { 6405 int len = STRLEN(word); 6406 int ct = captype(word, word + len); 6407 char_u foldword[MAXWLEN]; 6408 int res = OK; 6409 char_u *p; 6410 6411 (void)spell_casefold(word, len, foldword, MAXWLEN); 6412 for (p = pfxlist; res == OK; ++p) 6413 { 6414 if (!need_affix || (p != NULL && *p != NUL)) 6415 res = tree_add_word(spin, foldword, spin->si_foldroot, ct | flags, 6416 region, p == NULL ? 0 : *p); 6417 if (p == NULL || *p == NUL) 6418 break; 6419 } 6420 ++spin->si_foldwcount; 6421 6422 if (res == OK && (ct == WF_KEEPCAP || (flags & WF_KEEPCAP))) 6423 { 6424 for (p = pfxlist; res == OK; ++p) 6425 { 6426 if (!need_affix || (p != NULL && *p != NUL)) 6427 res = tree_add_word(spin, word, spin->si_keeproot, flags, 6428 region, p == NULL ? 0 : *p); 6429 if (p == NULL || *p == NUL) 6430 break; 6431 } 6432 ++spin->si_keepwcount; 6433 } 6434 return res; 6435 } 6436 6437 /* 6438 * Add word "word" to a word tree at "root". 6439 * When "flags" < 0 we are adding to the prefix tree where flags is used for 6440 * "rare" and "region" is the condition nr. 6441 * Returns FAIL when out of memory. 6442 */ 6443 static int 6444 tree_add_word(spin, word, root, flags, region, affixID) 6445 spellinfo_T *spin; 6446 char_u *word; 6447 wordnode_T *root; 6448 int flags; 6449 int region; 6450 int affixID; 6451 { 6452 wordnode_T *node = root; 6453 wordnode_T *np; 6454 wordnode_T *copyp, **copyprev; 6455 wordnode_T **prev = NULL; 6456 int i; 6457 6458 /* Add each byte of the word to the tree, including the NUL at the end. */ 6459 for (i = 0; ; ++i) 6460 { 6461 /* When there is more than one reference to this node we need to make 6462 * a copy, so that we can modify it. Copy the whole list of siblings 6463 * (we don't optimize for a partly shared list of siblings). */ 6464 if (node != NULL && node->wn_refs > 1) 6465 { 6466 --node->wn_refs; 6467 copyprev = prev; 6468 for (copyp = node; copyp != NULL; copyp = copyp->wn_sibling) 6469 { 6470 /* Allocate a new node and copy the info. */ 6471 np = get_wordnode(spin); 6472 if (np == NULL) 6473 return FAIL; 6474 np->wn_child = copyp->wn_child; 6475 if (np->wn_child != NULL) 6476 ++np->wn_child->wn_refs; /* child gets extra ref */ 6477 np->wn_byte = copyp->wn_byte; 6478 if (np->wn_byte == NUL) 6479 { 6480 np->wn_flags = copyp->wn_flags; 6481 np->wn_region = copyp->wn_region; 6482 np->wn_affixID = copyp->wn_affixID; 6483 } 6484 6485 /* Link the new node in the list, there will be one ref. */ 6486 np->wn_refs = 1; 6487 *copyprev = np; 6488 copyprev = &np->wn_sibling; 6489 6490 /* Let "node" point to the head of the copied list. */ 6491 if (copyp == node) 6492 node = np; 6493 } 6494 } 6495 6496 /* Look for the sibling that has the same character. They are sorted 6497 * on byte value, thus stop searching when a sibling is found with a 6498 * higher byte value. For zero bytes (end of word) the sorting is 6499 * done on flags and then on affixID. */ 6500 while (node != NULL 6501 && (node->wn_byte < word[i] 6502 || (node->wn_byte == NUL 6503 && (flags < 0 6504 ? node->wn_affixID < affixID 6505 : node->wn_flags < (flags & WN_MASK) 6506 || (node->wn_flags == (flags & WN_MASK) 6507 && node->wn_affixID < affixID))))) 6508 { 6509 prev = &node->wn_sibling; 6510 node = *prev; 6511 } 6512 if (node == NULL 6513 || node->wn_byte != word[i] 6514 || (word[i] == NUL 6515 && (flags < 0 6516 || node->wn_flags != (flags & WN_MASK) 6517 || node->wn_affixID != affixID))) 6518 { 6519 /* Allocate a new node. */ 6520 np = get_wordnode(spin); 6521 if (np == NULL) 6522 return FAIL; 6523 np->wn_byte = word[i]; 6524 6525 /* If "node" is NULL this is a new child or the end of the sibling 6526 * list: ref count is one. Otherwise use ref count of sibling and 6527 * make ref count of sibling one (matters when inserting in front 6528 * of the list of siblings). */ 6529 if (node == NULL) 6530 np->wn_refs = 1; 6531 else 6532 { 6533 np->wn_refs = node->wn_refs; 6534 node->wn_refs = 1; 6535 } 6536 *prev = np; 6537 np->wn_sibling = node; 6538 node = np; 6539 } 6540 6541 if (word[i] == NUL) 6542 { 6543 node->wn_flags = flags; 6544 node->wn_region |= region; 6545 node->wn_affixID = affixID; 6546 break; 6547 } 6548 prev = &node->wn_child; 6549 node = *prev; 6550 } 6551 #ifdef SPELL_PRINTTREE 6552 smsg("Added \"%s\"", word); 6553 spell_print_tree(root->wn_sibling); 6554 #endif 6555 6556 /* count nr of words added since last message */ 6557 ++spin->si_msg_count; 6558 6559 if (spin->si_compress_cnt > 1) 6560 { 6561 if (--spin->si_compress_cnt == 1) 6562 /* Did enough words to lower the block count limit. */ 6563 spin->si_blocks_cnt += compress_inc; 6564 } 6565 6566 /* 6567 * When we have allocated lots of memory we need to compress the word tree 6568 * to free up some room. But compression is slow, and we might actually 6569 * need that room, thus only compress in the following situations: 6570 * 1. When not compressed before (si_compress_cnt == 0): when using 6571 * "compress_start" blocks. 6572 * 2. When compressed before and used "compress_inc" blocks before 6573 * adding "compress_added" words (si_compress_cnt > 1). 6574 * 3. When compressed before, added "compress_added" words 6575 * (si_compress_cnt == 1) and the number of free nodes drops below the 6576 * maximum word length. 6577 */ 6578 #ifndef SPELL_PRINTTREE 6579 if (spin->si_compress_cnt == 1 6580 ? spin->si_free_count < MAXWLEN 6581 : spin->si_blocks_cnt >= compress_start) 6582 #endif 6583 { 6584 /* Decrement the block counter. The effect is that we compress again 6585 * when the freed up room has been used and another "compress_inc" 6586 * blocks have been allocated. Unless "compress_added" words have 6587 * been added, then the limit is put back again. */ 6588 spin->si_blocks_cnt -= compress_inc; 6589 spin->si_compress_cnt = compress_added; 6590 6591 if (spin->si_verbose) 6592 { 6593 msg_start(); 6594 msg_puts((char_u *)_(msg_compressing)); 6595 msg_clr_eos(); 6596 msg_didout = FALSE; 6597 msg_col = 0; 6598 out_flush(); 6599 } 6600 6601 /* Compress both trees. Either they both have many nodes, which makes 6602 * compression useful, or one of them is small, which means 6603 * compression goes fast. */ 6604 wordtree_compress(spin, spin->si_foldroot); 6605 wordtree_compress(spin, spin->si_keeproot); 6606 } 6607 6608 return OK; 6609 } 6610 6611 /* 6612 * Check the 'mkspellmem' option. Return FAIL if it's wrong. 6613 * Sets "sps_flags". 6614 */ 6615 int 6616 spell_check_msm() 6617 { 6618 char_u *p = p_msm; 6619 long start = 0; 6620 long inc = 0; 6621 long added = 0; 6622 6623 if (!VIM_ISDIGIT(*p)) 6624 return FAIL; 6625 /* block count = (value * 1024) / SBLOCKSIZE (but avoid overflow)*/ 6626 start = (getdigits(&p) * 10) / (SBLOCKSIZE / 102); 6627 if (*p != ',') 6628 return FAIL; 6629 ++p; 6630 if (!VIM_ISDIGIT(*p)) 6631 return FAIL; 6632 inc = (getdigits(&p) * 102) / (SBLOCKSIZE / 10); 6633 if (*p != ',') 6634 return FAIL; 6635 ++p; 6636 if (!VIM_ISDIGIT(*p)) 6637 return FAIL; 6638 added = getdigits(&p) * 1024; 6639 if (*p != NUL) 6640 return FAIL; 6641 6642 if (start == 0 || inc == 0 || added == 0 || inc > start) 6643 return FAIL; 6644 6645 compress_start = start; 6646 compress_inc = inc; 6647 compress_added = added; 6648 return OK; 6649 } 6650 6651 6652 /* 6653 * Get a wordnode_T, either from the list of previously freed nodes or 6654 * allocate a new one. 6655 */ 6656 static wordnode_T * 6657 get_wordnode(spin) 6658 spellinfo_T *spin; 6659 { 6660 wordnode_T *n; 6661 6662 if (spin->si_first_free == NULL) 6663 n = (wordnode_T *)getroom(spin, sizeof(wordnode_T), TRUE); 6664 else 6665 { 6666 n = spin->si_first_free; 6667 spin->si_first_free = n->wn_child; 6668 vim_memset(n, 0, sizeof(wordnode_T)); 6669 --spin->si_free_count; 6670 } 6671 #ifdef SPELL_PRINTTREE 6672 n->wn_nr = ++spin->si_wordnode_nr; 6673 #endif 6674 return n; 6675 } 6676 6677 /* 6678 * Decrement the reference count on a node (which is the head of a list of 6679 * siblings). If the reference count becomes zero free the node and its 6680 * siblings. 6681 */ 6682 static void 6683 deref_wordnode(spin, node) 6684 spellinfo_T *spin; 6685 wordnode_T *node; 6686 { 6687 wordnode_T *np; 6688 6689 if (--node->wn_refs == 0) 6690 for (np = node; np != NULL; np = np->wn_sibling) 6691 { 6692 if (np->wn_child != NULL) 6693 deref_wordnode(spin, np->wn_child); 6694 free_wordnode(spin, np); 6695 } 6696 } 6697 6698 /* 6699 * Free a wordnode_T for re-use later. 6700 * Only the "wn_child" field becomes invalid. 6701 */ 6702 static void 6703 free_wordnode(spin, n) 6704 spellinfo_T *spin; 6705 wordnode_T *n; 6706 { 6707 n->wn_child = spin->si_first_free; 6708 spin->si_first_free = n; 6709 ++spin->si_free_count; 6710 } 6711 6712 /* 6713 * Compress a tree: find tails that are identical and can be shared. 6714 */ 6715 static void 6716 wordtree_compress(spin, root) 6717 spellinfo_T *spin; 6718 wordnode_T *root; 6719 { 6720 hashtab_T ht; 6721 int n; 6722 int tot = 0; 6723 int perc; 6724 6725 /* Skip the root itself, it's not actually used. The first sibling is the 6726 * start of the tree. */ 6727 if (root->wn_sibling != NULL) 6728 { 6729 hash_init(&ht); 6730 n = node_compress(spin, root->wn_sibling, &ht, &tot); 6731 6732 #ifndef SPELL_PRINTTREE 6733 if (spin->si_verbose || p_verbose > 2) 6734 #endif 6735 { 6736 if (!spin->si_verbose) 6737 verbose_enter(); 6738 if (tot > 1000000) 6739 perc = (tot - n) / (tot / 100); 6740 else if (tot == 0) 6741 perc = 0; 6742 else 6743 perc = (tot - n) * 100 / tot; 6744 smsg((char_u *)_("Compressed %d of %d nodes; %d%% remaining"), 6745 n, tot, perc); 6746 if (p_verbose > 2) 6747 verbose_leave(); 6748 } 6749 #ifdef SPELL_PRINTTREE 6750 spell_print_tree(root->wn_sibling); 6751 #endif 6752 hash_clear(&ht); 6753 } 6754 } 6755 6756 /* 6757 * Compress a node, its siblings and its children, depth first. 6758 * Returns the number of compressed nodes. 6759 */ 6760 static int 6761 node_compress(spin, node, ht, tot) 6762 spellinfo_T *spin; 6763 wordnode_T *node; 6764 hashtab_T *ht; 6765 int *tot; /* total count of nodes before compressing, 6766 incremented while going through the tree */ 6767 { 6768 wordnode_T *np; 6769 wordnode_T *tp; 6770 wordnode_T *child; 6771 hash_T hash; 6772 hashitem_T *hi; 6773 int len = 0; 6774 unsigned nr, n; 6775 int compressed = 0; 6776 6777 /* 6778 * Go through the list of siblings. Compress each child and then try 6779 * finding an identical child to replace it. 6780 * Note that with "child" we mean not just the node that is pointed to, 6781 * but the whole list of siblings, of which the node is the first. 6782 */ 6783 for (np = node; np != NULL && !got_int; np = np->wn_sibling) 6784 { 6785 ++len; 6786 if ((child = np->wn_child) != NULL) 6787 { 6788 /* Compress the child. This fills hashkey. */ 6789 compressed += node_compress(spin, child, ht, tot); 6790 6791 /* Try to find an identical child. */ 6792 hash = hash_hash(child->wn_u1.hashkey); 6793 hi = hash_lookup(ht, child->wn_u1.hashkey, hash); 6794 tp = NULL; 6795 if (!HASHITEM_EMPTY(hi)) 6796 { 6797 /* There are children with an identical hash value. Now check 6798 * if there is one that is really identical. */ 6799 for (tp = HI2WN(hi); tp != NULL; tp = tp->wn_u2.next) 6800 if (node_equal(child, tp)) 6801 { 6802 /* Found one! Now use that child in place of the 6803 * current one. This means the current child and all 6804 * its siblings is unlinked from the tree. */ 6805 ++tp->wn_refs; 6806 deref_wordnode(spin, child); 6807 np->wn_child = tp; 6808 ++compressed; 6809 break; 6810 } 6811 if (tp == NULL) 6812 { 6813 /* No other child with this hash value equals the child of 6814 * the node, add it to the linked list after the first 6815 * item. */ 6816 tp = HI2WN(hi); 6817 child->wn_u2.next = tp->wn_u2.next; 6818 tp->wn_u2.next = child; 6819 } 6820 } 6821 else 6822 /* No other child has this hash value, add it to the 6823 * hashtable. */ 6824 hash_add_item(ht, hi, child->wn_u1.hashkey, hash); 6825 } 6826 } 6827 *tot += len; 6828 6829 /* 6830 * Make a hash key for the node and its siblings, so that we can quickly 6831 * find a lookalike node. This must be done after compressing the sibling 6832 * list, otherwise the hash key would become invalid by the compression. 6833 */ 6834 node->wn_u1.hashkey[0] = len; 6835 nr = 0; 6836 for (np = node; np != NULL; np = np->wn_sibling) 6837 { 6838 if (np->wn_byte == NUL) 6839 /* end node: use wn_flags, wn_region and wn_affixID */ 6840 n = np->wn_flags + (np->wn_region << 8) + (np->wn_affixID << 16); 6841 else 6842 /* byte node: use the byte value and the child pointer */ 6843 n = np->wn_byte + ((long_u)np->wn_child << 8); 6844 nr = nr * 101 + n; 6845 } 6846 6847 /* Avoid NUL bytes, it terminates the hash key. */ 6848 n = nr & 0xff; 6849 node->wn_u1.hashkey[1] = n == 0 ? 1 : n; 6850 n = (nr >> 8) & 0xff; 6851 node->wn_u1.hashkey[2] = n == 0 ? 1 : n; 6852 n = (nr >> 16) & 0xff; 6853 node->wn_u1.hashkey[3] = n == 0 ? 1 : n; 6854 n = (nr >> 24) & 0xff; 6855 node->wn_u1.hashkey[4] = n == 0 ? 1 : n; 6856 node->wn_u1.hashkey[5] = NUL; 6857 6858 /* Check for CTRL-C pressed now and then. */ 6859 fast_breakcheck(); 6860 6861 return compressed; 6862 } 6863 6864 /* 6865 * Return TRUE when two nodes have identical siblings and children. 6866 */ 6867 static int 6868 node_equal(n1, n2) 6869 wordnode_T *n1; 6870 wordnode_T *n2; 6871 { 6872 wordnode_T *p1; 6873 wordnode_T *p2; 6874 6875 for (p1 = n1, p2 = n2; p1 != NULL && p2 != NULL; 6876 p1 = p1->wn_sibling, p2 = p2->wn_sibling) 6877 if (p1->wn_byte != p2->wn_byte 6878 || (p1->wn_byte == NUL 6879 ? (p1->wn_flags != p2->wn_flags 6880 || p1->wn_region != p2->wn_region 6881 || p1->wn_affixID != p2->wn_affixID) 6882 : (p1->wn_child != p2->wn_child))) 6883 break; 6884 6885 return p1 == NULL && p2 == NULL; 6886 } 6887 6888 /* 6889 * Write a number to file "fd", MSB first, in "len" bytes. 6890 */ 6891 void 6892 put_bytes(fd, nr, len) 6893 FILE *fd; 6894 long_u nr; 6895 int len; 6896 { 6897 int i; 6898 6899 for (i = len - 1; i >= 0; --i) 6900 putc((int)(nr >> (i * 8)), fd); 6901 } 6902 6903 static int 6904 #ifdef __BORLANDC__ 6905 _RTLENTRYF 6906 #endif 6907 rep_compare __ARGS((const void *s1, const void *s2)); 6908 6909 /* 6910 * Function given to qsort() to sort the REP items on "from" string. 6911 */ 6912 static int 6913 #ifdef __BORLANDC__ 6914 _RTLENTRYF 6915 #endif 6916 rep_compare(s1, s2) 6917 const void *s1; 6918 const void *s2; 6919 { 6920 fromto_T *p1 = (fromto_T *)s1; 6921 fromto_T *p2 = (fromto_T *)s2; 6922 6923 return STRCMP(p1->ft_from, p2->ft_from); 6924 } 6925 6926 /* 6927 * Write the Vim .spl file "fname". 6928 * Return FAIL or OK; 6929 */ 6930 static int 6931 write_vim_spell(spin, fname) 6932 spellinfo_T *spin; 6933 char_u *fname; 6934 { 6935 FILE *fd; 6936 int regionmask; 6937 int round; 6938 wordnode_T *tree; 6939 int nodecount; 6940 int i; 6941 int l; 6942 garray_T *gap; 6943 fromto_T *ftp; 6944 char_u *p; 6945 int rr; 6946 int retval = OK; 6947 6948 fd = mch_fopen((char *)fname, "w"); 6949 if (fd == NULL) 6950 { 6951 EMSG2(_(e_notopen), fname); 6952 return FAIL; 6953 } 6954 6955 /* <HEADER>: <fileID> <versionnr> */ 6956 /* <fileID> */ 6957 if (fwrite(VIMSPELLMAGIC, VIMSPELLMAGICL, (size_t)1, fd) != 1) 6958 { 6959 EMSG(_(e_write)); 6960 retval = FAIL; 6961 } 6962 putc(VIMSPELLVERSION, fd); /* <versionnr> */ 6963 6964 /* 6965 * <SECTIONS>: <section> ... <sectionend> 6966 */ 6967 6968 /* SN_REGION: <regionname> ... 6969 * Write the region names only if there is more than one. */ 6970 if (spin->si_region_count > 1) 6971 { 6972 putc(SN_REGION, fd); /* <sectionID> */ 6973 putc(SNF_REQUIRED, fd); /* <sectionflags> */ 6974 l = spin->si_region_count * 2; 6975 put_bytes(fd, (long_u)l, 4); /* <sectionlen> */ 6976 fwrite(spin->si_region_name, (size_t)l, (size_t)1, fd); 6977 /* <regionname> ... */ 6978 regionmask = (1 << spin->si_region_count) - 1; 6979 } 6980 else 6981 regionmask = 0; 6982 6983 /* SN_CHARFLAGS: <charflagslen> <charflags> <folcharslen> <folchars> 6984 * 6985 * The table with character flags and the table for case folding. 6986 * This makes sure the same characters are recognized as word characters 6987 * when generating an when using a spell file. 6988 * Skip this for ASCII, the table may conflict with the one used for 6989 * 'encoding'. 6990 * Also skip this for an .add.spl file, the main spell file must contain 6991 * the table (avoids that it conflicts). File is shorter too. 6992 */ 6993 if (!spin->si_ascii && !spin->si_add) 6994 { 6995 char_u folchars[128 * 8]; 6996 int flags; 6997 6998 putc(SN_CHARFLAGS, fd); /* <sectionID> */ 6999 putc(SNF_REQUIRED, fd); /* <sectionflags> */ 7000 7001 /* Form the <folchars> string first, we need to know its length. */ 7002 l = 0; 7003 for (i = 128; i < 256; ++i) 7004 { 7005 #ifdef FEAT_MBYTE 7006 if (has_mbyte) 7007 l += mb_char2bytes(spelltab.st_fold[i], folchars + l); 7008 else 7009 #endif 7010 folchars[l++] = spelltab.st_fold[i]; 7011 } 7012 put_bytes(fd, (long_u)(1 + 128 + 2 + l), 4); /* <sectionlen> */ 7013 7014 fputc(128, fd); /* <charflagslen> */ 7015 for (i = 128; i < 256; ++i) 7016 { 7017 flags = 0; 7018 if (spelltab.st_isw[i]) 7019 flags |= CF_WORD; 7020 if (spelltab.st_isu[i]) 7021 flags |= CF_UPPER; 7022 fputc(flags, fd); /* <charflags> */ 7023 } 7024 7025 put_bytes(fd, (long_u)l, 2); /* <folcharslen> */ 7026 fwrite(folchars, (size_t)l, (size_t)1, fd); /* <folchars> */ 7027 } 7028 7029 /* SN_MIDWORD: <midword> */ 7030 if (spin->si_midword != NULL) 7031 { 7032 putc(SN_MIDWORD, fd); /* <sectionID> */ 7033 putc(SNF_REQUIRED, fd); /* <sectionflags> */ 7034 7035 i = STRLEN(spin->si_midword); 7036 put_bytes(fd, (long_u)i, 4); /* <sectionlen> */ 7037 fwrite(spin->si_midword, (size_t)i, (size_t)1, fd); /* <midword> */ 7038 } 7039 7040 /* SN_PREFCOND: <prefcondcnt> <prefcond> ... */ 7041 if (spin->si_prefcond.ga_len > 0) 7042 { 7043 putc(SN_PREFCOND, fd); /* <sectionID> */ 7044 putc(SNF_REQUIRED, fd); /* <sectionflags> */ 7045 7046 l = write_spell_prefcond(NULL, &spin->si_prefcond); 7047 put_bytes(fd, (long_u)l, 4); /* <sectionlen> */ 7048 7049 write_spell_prefcond(fd, &spin->si_prefcond); 7050 } 7051 7052 /* SN_REP: <repcount> <rep> ... 7053 * SN_SAL: <salflags> <salcount> <sal> ... */ 7054 7055 /* Sort the REP items. */ 7056 qsort(spin->si_rep.ga_data, (size_t)spin->si_rep.ga_len, 7057 sizeof(fromto_T), rep_compare); 7058 7059 /* round 1: SN_REP section 7060 * round 2: SN_SAL section (unless SN_SOFO is used) */ 7061 for (round = 1; round <= 2; ++round) 7062 { 7063 if (round == 1) 7064 { 7065 gap = &spin->si_rep; 7066 putc(SN_REP, fd); /* <sectionID> */ 7067 } 7068 else 7069 { 7070 if (spin->si_sofofr != NULL && spin->si_sofoto != NULL) 7071 /* using SN_SOFO section instead of SN_SAL */ 7072 break; 7073 gap = &spin->si_sal; 7074 putc(SN_SAL, fd); /* <sectionID> */ 7075 } 7076 7077 /* This is for making suggestions, section is not required. */ 7078 putc(0, fd); /* <sectionflags> */ 7079 7080 /* Compute the length of what follows. */ 7081 l = 2; /* count <repcount> or <salcount> */ 7082 for (i = 0; i < gap->ga_len; ++i) 7083 { 7084 ftp = &((fromto_T *)gap->ga_data)[i]; 7085 l += 1 + STRLEN(ftp->ft_from); /* count <*fromlen> and <*from> */ 7086 l += 1 + STRLEN(ftp->ft_to); /* count <*tolen> and <*to> */ 7087 } 7088 if (round == 2) 7089 ++l; /* count <salflags> */ 7090 put_bytes(fd, (long_u)l, 4); /* <sectionlen> */ 7091 7092 if (round == 2) 7093 { 7094 i = 0; 7095 if (spin->si_followup) 7096 i |= SAL_F0LLOWUP; 7097 if (spin->si_collapse) 7098 i |= SAL_COLLAPSE; 7099 if (spin->si_rem_accents) 7100 i |= SAL_REM_ACCENTS; 7101 putc(i, fd); /* <salflags> */ 7102 } 7103 7104 put_bytes(fd, (long_u)gap->ga_len, 2); /* <repcount> or <salcount> */ 7105 for (i = 0; i < gap->ga_len; ++i) 7106 { 7107 /* <rep> : <repfromlen> <repfrom> <reptolen> <repto> */ 7108 /* <sal> : <salfromlen> <salfrom> <saltolen> <salto> */ 7109 ftp = &((fromto_T *)gap->ga_data)[i]; 7110 for (rr = 1; rr <= 2; ++rr) 7111 { 7112 p = rr == 1 ? ftp->ft_from : ftp->ft_to; 7113 l = STRLEN(p); 7114 putc(l, fd); 7115 fwrite(p, l, (size_t)1, fd); 7116 } 7117 } 7118 7119 } 7120 7121 /* SN_SOFO: <sofofromlen> <sofofrom> <sofotolen> <sofoto> 7122 * This is for making suggestions, section is not required. */ 7123 if (spin->si_sofofr != NULL && spin->si_sofoto != NULL) 7124 { 7125 putc(SN_SOFO, fd); /* <sectionID> */ 7126 putc(0, fd); /* <sectionflags> */ 7127 7128 l = STRLEN(spin->si_sofofr); 7129 put_bytes(fd, (long_u)(l + STRLEN(spin->si_sofoto) + 4), 4); 7130 /* <sectionlen> */ 7131 7132 put_bytes(fd, (long_u)l, 2); /* <sofofromlen> */ 7133 fwrite(spin->si_sofofr, l, (size_t)1, fd); /* <sofofrom> */ 7134 7135 l = STRLEN(spin->si_sofoto); 7136 put_bytes(fd, (long_u)l, 2); /* <sofotolen> */ 7137 fwrite(spin->si_sofoto, l, (size_t)1, fd); /* <sofoto> */ 7138 } 7139 7140 /* SN_MAP: <mapstr> 7141 * This is for making suggestions, section is not required. */ 7142 if (spin->si_map.ga_len > 0) 7143 { 7144 putc(SN_MAP, fd); /* <sectionID> */ 7145 putc(0, fd); /* <sectionflags> */ 7146 l = spin->si_map.ga_len; 7147 put_bytes(fd, (long_u)l, 4); /* <sectionlen> */ 7148 fwrite(spin->si_map.ga_data, (size_t)l, (size_t)1, fd); 7149 /* <mapstr> */ 7150 } 7151 7152 /* SN_COMPOUND: compound info. 7153 * We don't mark it required, when not supported all compound words will 7154 * be bad words. */ 7155 if (spin->si_compflags != NULL) 7156 { 7157 putc(SN_COMPOUND, fd); /* <sectionID> */ 7158 putc(0, fd); /* <sectionflags> */ 7159 7160 l = STRLEN(spin->si_compflags); 7161 put_bytes(fd, (long_u)(l + 3), 4); /* <sectionlen> */ 7162 putc(spin->si_compmax, fd); /* <compmax> */ 7163 putc(spin->si_compminlen, fd); /* <compminlen> */ 7164 putc(spin->si_compsylmax, fd); /* <compsylmax> */ 7165 /* <compflags> */ 7166 fwrite(spin->si_compflags, (size_t)l, (size_t)1, fd); 7167 } 7168 7169 /* SN_NOBREAK: NOBREAK flag */ 7170 if (spin->si_nobreak) 7171 { 7172 putc(SN_NOBREAK, fd); /* <sectionID> */ 7173 putc(0, fd); /* <sectionflags> */ 7174 7175 /* It's empty, the precense of the section flags the feature. */ 7176 put_bytes(fd, (long_u)0, 4); /* <sectionlen> */ 7177 } 7178 7179 /* SN_SYLLABLE: syllable info. 7180 * We don't mark it required, when not supported syllables will not be 7181 * counted. */ 7182 if (spin->si_syllable != NULL) 7183 { 7184 putc(SN_SYLLABLE, fd); /* <sectionID> */ 7185 putc(0, fd); /* <sectionflags> */ 7186 7187 l = STRLEN(spin->si_syllable); 7188 put_bytes(fd, (long_u)l, 4); /* <sectionlen> */ 7189 fwrite(spin->si_syllable, (size_t)l, (size_t)1, fd); /* <syllable> */ 7190 } 7191 7192 /* end of <SECTIONS> */ 7193 putc(SN_END, fd); /* <sectionend> */ 7194 7195 7196 /* 7197 * <LWORDTREE> <KWORDTREE> <PREFIXTREE> 7198 */ 7199 spin->si_memtot = 0; 7200 for (round = 1; round <= 3; ++round) 7201 { 7202 if (round == 1) 7203 tree = spin->si_foldroot->wn_sibling; 7204 else if (round == 2) 7205 tree = spin->si_keeproot->wn_sibling; 7206 else 7207 tree = spin->si_prefroot->wn_sibling; 7208 7209 /* Clear the index and wnode fields in the tree. */ 7210 clear_node(tree); 7211 7212 /* Count the number of nodes. Needed to be able to allocate the 7213 * memory when reading the nodes. Also fills in index for shared 7214 * nodes. */ 7215 nodecount = put_node(NULL, tree, 0, regionmask, round == 3); 7216 7217 /* number of nodes in 4 bytes */ 7218 put_bytes(fd, (long_u)nodecount, 4); /* <nodecount> */ 7219 spin->si_memtot += nodecount + nodecount * sizeof(int); 7220 7221 /* Write the nodes. */ 7222 (void)put_node(fd, tree, 0, regionmask, round == 3); 7223 } 7224 7225 /* Write another byte to check for errors. */ 7226 if (putc(0, fd) == EOF) 7227 retval = FAIL; 7228 7229 if (fclose(fd) == EOF) 7230 retval = FAIL; 7231 7232 return retval; 7233 } 7234 7235 /* 7236 * Clear the index and wnode fields of "node", it siblings and its 7237 * children. This is needed because they are a union with other items to save 7238 * space. 7239 */ 7240 static void 7241 clear_node(node) 7242 wordnode_T *node; 7243 { 7244 wordnode_T *np; 7245 7246 if (node != NULL) 7247 for (np = node; np != NULL; np = np->wn_sibling) 7248 { 7249 np->wn_u1.index = 0; 7250 np->wn_u2.wnode = NULL; 7251 7252 if (np->wn_byte != NUL) 7253 clear_node(np->wn_child); 7254 } 7255 } 7256 7257 7258 /* 7259 * Dump a word tree at node "node". 7260 * 7261 * This first writes the list of possible bytes (siblings). Then for each 7262 * byte recursively write the children. 7263 * 7264 * NOTE: The code here must match the code in read_tree(), since assumptions 7265 * are made about the indexes (so that we don't have to write them in the 7266 * file). 7267 * 7268 * Returns the number of nodes used. 7269 */ 7270 static int 7271 put_node(fd, node, index, regionmask, prefixtree) 7272 FILE *fd; /* NULL when only counting */ 7273 wordnode_T *node; 7274 int index; 7275 int regionmask; 7276 int prefixtree; /* TRUE for PREFIXTREE */ 7277 { 7278 int newindex = index; 7279 int siblingcount = 0; 7280 wordnode_T *np; 7281 int flags; 7282 7283 /* If "node" is zero the tree is empty. */ 7284 if (node == NULL) 7285 return 0; 7286 7287 /* Store the index where this node is written. */ 7288 node->wn_u1.index = index; 7289 7290 /* Count the number of siblings. */ 7291 for (np = node; np != NULL; np = np->wn_sibling) 7292 ++siblingcount; 7293 7294 /* Write the sibling count. */ 7295 if (fd != NULL) 7296 putc(siblingcount, fd); /* <siblingcount> */ 7297 7298 /* Write each sibling byte and optionally extra info. */ 7299 for (np = node; np != NULL; np = np->wn_sibling) 7300 { 7301 if (np->wn_byte == 0) 7302 { 7303 if (fd != NULL) 7304 { 7305 /* For a NUL byte (end of word) write the flags etc. */ 7306 if (prefixtree) 7307 { 7308 /* In PREFIXTREE write the required affixID and the 7309 * associated condition nr (stored in wn_region). The 7310 * byte value is misused to store the "rare" and "not 7311 * combining" flags */ 7312 if (np->wn_flags == (short_u)PFX_FLAGS) 7313 putc(BY_NOFLAGS, fd); /* <byte> */ 7314 else 7315 { 7316 putc(BY_FLAGS, fd); /* <byte> */ 7317 putc(np->wn_flags, fd); /* <pflags> */ 7318 } 7319 putc(np->wn_affixID, fd); /* <affixID> */ 7320 put_bytes(fd, (long_u)np->wn_region, 2); /* <prefcondnr> */ 7321 } 7322 else 7323 { 7324 /* For word trees we write the flag/region items. */ 7325 flags = np->wn_flags; 7326 if (regionmask != 0 && np->wn_region != regionmask) 7327 flags |= WF_REGION; 7328 if (np->wn_affixID != 0) 7329 flags |= WF_AFX; 7330 if (flags == 0) 7331 { 7332 /* word without flags or region */ 7333 putc(BY_NOFLAGS, fd); /* <byte> */ 7334 } 7335 else 7336 { 7337 if (np->wn_flags >= 0x100) 7338 { 7339 putc(BY_FLAGS2, fd); /* <byte> */ 7340 putc(flags, fd); /* <flags> */ 7341 putc((unsigned)flags >> 8, fd); /* <flags2> */ 7342 } 7343 else 7344 { 7345 putc(BY_FLAGS, fd); /* <byte> */ 7346 putc(flags, fd); /* <flags> */ 7347 } 7348 if (flags & WF_REGION) 7349 putc(np->wn_region, fd); /* <region> */ 7350 if (flags & WF_AFX) 7351 putc(np->wn_affixID, fd); /* <affixID> */ 7352 } 7353 } 7354 } 7355 } 7356 else 7357 { 7358 if (np->wn_child->wn_u1.index != 0 7359 && np->wn_child->wn_u2.wnode != node) 7360 { 7361 /* The child is written elsewhere, write the reference. */ 7362 if (fd != NULL) 7363 { 7364 putc(BY_INDEX, fd); /* <byte> */ 7365 /* <nodeidx> */ 7366 put_bytes(fd, (long_u)np->wn_child->wn_u1.index, 3); 7367 } 7368 } 7369 else if (np->wn_child->wn_u2.wnode == NULL) 7370 /* We will write the child below and give it an index. */ 7371 np->wn_child->wn_u2.wnode = node; 7372 7373 if (fd != NULL) 7374 if (putc(np->wn_byte, fd) == EOF) /* <byte> or <xbyte> */ 7375 { 7376 EMSG(_(e_write)); 7377 return 0; 7378 } 7379 } 7380 } 7381 7382 /* Space used in the array when reading: one for each sibling and one for 7383 * the count. */ 7384 newindex += siblingcount + 1; 7385 7386 /* Recursively dump the children of each sibling. */ 7387 for (np = node; np != NULL; np = np->wn_sibling) 7388 if (np->wn_byte != 0 && np->wn_child->wn_u2.wnode == node) 7389 newindex = put_node(fd, np->wn_child, newindex, regionmask, 7390 prefixtree); 7391 7392 return newindex; 7393 } 7394 7395 7396 /* 7397 * ":mkspell [-ascii] outfile infile ..." 7398 * ":mkspell [-ascii] addfile" 7399 */ 7400 void 7401 ex_mkspell(eap) 7402 exarg_T *eap; 7403 { 7404 int fcount; 7405 char_u **fnames; 7406 char_u *arg = eap->arg; 7407 int ascii = FALSE; 7408 7409 if (STRNCMP(arg, "-ascii", 6) == 0) 7410 { 7411 ascii = TRUE; 7412 arg = skipwhite(arg + 6); 7413 } 7414 7415 /* Expand all the remaining arguments (e.g., $VIMRUNTIME). */ 7416 if (get_arglist_exp(arg, &fcount, &fnames) == OK) 7417 { 7418 mkspell(fcount, fnames, ascii, eap->forceit, FALSE); 7419 FreeWild(fcount, fnames); 7420 } 7421 } 7422 7423 /* 7424 * Create a Vim spell file from one or more word lists. 7425 * "fnames[0]" is the output file name. 7426 * "fnames[fcount - 1]" is the last input file name. 7427 * Exception: when "fnames[0]" ends in ".add" it's used as the input file name 7428 * and ".spl" is appended to make the output file name. 7429 */ 7430 static void 7431 mkspell(fcount, fnames, ascii, overwrite, added_word) 7432 int fcount; 7433 char_u **fnames; 7434 int ascii; /* -ascii argument given */ 7435 int overwrite; /* overwrite existing output file */ 7436 int added_word; /* invoked through "zg" */ 7437 { 7438 char_u fname[MAXPATHL]; 7439 char_u wfname[MAXPATHL]; 7440 char_u **innames; 7441 int incount; 7442 afffile_T *(afile[8]); 7443 int i; 7444 int len; 7445 struct stat st; 7446 int error = FALSE; 7447 spellinfo_T spin; 7448 7449 vim_memset(&spin, 0, sizeof(spin)); 7450 spin.si_verbose = !added_word; 7451 spin.si_ascii = ascii; 7452 spin.si_followup = TRUE; 7453 spin.si_rem_accents = TRUE; 7454 ga_init2(&spin.si_rep, (int)sizeof(fromto_T), 20); 7455 ga_init2(&spin.si_sal, (int)sizeof(fromto_T), 20); 7456 ga_init2(&spin.si_map, (int)sizeof(char_u), 100); 7457 ga_init2(&spin.si_prefcond, (int)sizeof(char_u *), 50); 7458 spin.si_newcompID = 127; /* start compound ID at first maximum */ 7459 7460 /* default: fnames[0] is output file, following are input files */ 7461 innames = &fnames[1]; 7462 incount = fcount - 1; 7463 7464 if (fcount >= 1) 7465 { 7466 len = STRLEN(fnames[0]); 7467 if (fcount == 1 && len > 4 && STRCMP(fnames[0] + len - 4, ".add") == 0) 7468 { 7469 /* For ":mkspell path/en.latin1.add" output file is 7470 * "path/en.latin1.add.spl". */ 7471 innames = &fnames[0]; 7472 incount = 1; 7473 vim_snprintf((char *)wfname, sizeof(wfname), "%s.spl", fnames[0]); 7474 } 7475 else if (fcount == 1) 7476 { 7477 /* For ":mkspell path/vim" output file is "path/vim.latin1.spl". */ 7478 innames = &fnames[0]; 7479 incount = 1; 7480 vim_snprintf((char *)wfname, sizeof(wfname), "%s.%s.spl", fnames[0], 7481 spin.si_ascii ? (char_u *)"ascii" : spell_enc()); 7482 } 7483 else if (len > 4 && STRCMP(fnames[0] + len - 4, ".spl") == 0) 7484 { 7485 /* Name ends in ".spl", use as the file name. */ 7486 vim_strncpy(wfname, fnames[0], sizeof(wfname) - 1); 7487 } 7488 else 7489 /* Name should be language, make the file name from it. */ 7490 vim_snprintf((char *)wfname, sizeof(wfname), "%s.%s.spl", fnames[0], 7491 spin.si_ascii ? (char_u *)"ascii" : spell_enc()); 7492 7493 /* Check for .ascii.spl. */ 7494 if (strstr((char *)gettail(wfname), ".ascii.") != NULL) 7495 spin.si_ascii = TRUE; 7496 7497 /* Check for .add.spl. */ 7498 if (strstr((char *)gettail(wfname), ".add.") != NULL) 7499 spin.si_add = TRUE; 7500 } 7501 7502 if (incount <= 0) 7503 EMSG(_(e_invarg)); /* need at least output and input names */ 7504 else if (vim_strchr(gettail(wfname), '_') != NULL) 7505 EMSG(_("E751: Output file name must not have region name")); 7506 else if (incount > 8) 7507 EMSG(_("E754: Only up to 8 regions supported")); 7508 else 7509 { 7510 /* Check for overwriting before doing things that may take a lot of 7511 * time. */ 7512 if (!overwrite && mch_stat((char *)wfname, &st) >= 0) 7513 { 7514 EMSG(_(e_exists)); 7515 return; 7516 } 7517 if (mch_isdir(wfname)) 7518 { 7519 EMSG2(_(e_isadir2), wfname); 7520 return; 7521 } 7522 7523 /* 7524 * Init the aff and dic pointers. 7525 * Get the region names if there are more than 2 arguments. 7526 */ 7527 for (i = 0; i < incount; ++i) 7528 { 7529 afile[i] = NULL; 7530 7531 if (incount > 1) 7532 { 7533 len = STRLEN(innames[i]); 7534 if (STRLEN(gettail(innames[i])) < 5 7535 || innames[i][len - 3] != '_') 7536 { 7537 EMSG2(_("E755: Invalid region in %s"), innames[i]); 7538 return; 7539 } 7540 spin.si_region_name[i * 2] = TOLOWER_ASC(innames[i][len - 2]); 7541 spin.si_region_name[i * 2 + 1] = 7542 TOLOWER_ASC(innames[i][len - 1]); 7543 } 7544 } 7545 spin.si_region_count = incount; 7546 7547 spin.si_foldroot = wordtree_alloc(&spin); 7548 spin.si_keeproot = wordtree_alloc(&spin); 7549 spin.si_prefroot = wordtree_alloc(&spin); 7550 if (spin.si_foldroot == NULL 7551 || spin.si_keeproot == NULL 7552 || spin.si_prefroot == NULL) 7553 { 7554 free_blocks(spin.si_blocks); 7555 return; 7556 } 7557 7558 /* When not producing a .add.spl file clear the character table when 7559 * we encounter one in the .aff file. This means we dump the current 7560 * one in the .spl file if the .aff file doesn't define one. That's 7561 * better than guessing the contents, the table will match a 7562 * previously loaded spell file. */ 7563 if (!spin.si_add) 7564 spin.si_clear_chartab = TRUE; 7565 7566 /* 7567 * Read all the .aff and .dic files. 7568 * Text is converted to 'encoding'. 7569 * Words are stored in the case-folded and keep-case trees. 7570 */ 7571 for (i = 0; i < incount && !error; ++i) 7572 { 7573 spin.si_conv.vc_type = CONV_NONE; 7574 spin.si_region = 1 << i; 7575 7576 vim_snprintf((char *)fname, sizeof(fname), "%s.aff", innames[i]); 7577 if (mch_stat((char *)fname, &st) >= 0) 7578 { 7579 /* Read the .aff file. Will init "spin->si_conv" based on the 7580 * "SET" line. */ 7581 afile[i] = spell_read_aff(&spin, fname); 7582 if (afile[i] == NULL) 7583 error = TRUE; 7584 else 7585 { 7586 /* Read the .dic file and store the words in the trees. */ 7587 vim_snprintf((char *)fname, sizeof(fname), "%s.dic", 7588 innames[i]); 7589 if (spell_read_dic(&spin, fname, afile[i]) == FAIL) 7590 error = TRUE; 7591 } 7592 } 7593 else 7594 { 7595 /* No .aff file, try reading the file as a word list. Store 7596 * the words in the trees. */ 7597 if (spell_read_wordfile(&spin, innames[i]) == FAIL) 7598 error = TRUE; 7599 } 7600 7601 #ifdef FEAT_MBYTE 7602 /* Free any conversion stuff. */ 7603 convert_setup(&spin.si_conv, NULL, NULL); 7604 #endif 7605 } 7606 7607 if (spin.si_compflags != NULL && spin.si_nobreak) 7608 MSG(_("Warning: both compounding and NOBREAK specified")); 7609 7610 if (!error) 7611 { 7612 /* 7613 * Combine tails in the tree. 7614 */ 7615 if (spin.si_verbose || p_verbose > 2) 7616 { 7617 if (!spin.si_verbose) 7618 verbose_enter(); 7619 MSG(_(msg_compressing)); 7620 out_flush(); 7621 if (!spin.si_verbose) 7622 verbose_leave(); 7623 } 7624 wordtree_compress(&spin, spin.si_foldroot); 7625 wordtree_compress(&spin, spin.si_keeproot); 7626 wordtree_compress(&spin, spin.si_prefroot); 7627 } 7628 7629 if (!error) 7630 { 7631 /* 7632 * Write the info in the spell file. 7633 */ 7634 if (spin.si_verbose || p_verbose > 2) 7635 { 7636 if (!spin.si_verbose) 7637 verbose_enter(); 7638 smsg((char_u *)_("Writing spell file %s ..."), wfname); 7639 out_flush(); 7640 if (!spin.si_verbose) 7641 verbose_leave(); 7642 } 7643 7644 error = write_vim_spell(&spin, wfname) == FAIL; 7645 7646 if (spin.si_verbose || p_verbose > 2) 7647 { 7648 if (!spin.si_verbose) 7649 verbose_enter(); 7650 MSG(_("Done!")); 7651 smsg((char_u *)_("Estimated runtime memory use: %d bytes"), 7652 spin.si_memtot); 7653 out_flush(); 7654 if (!spin.si_verbose) 7655 verbose_leave(); 7656 } 7657 7658 /* If the file is loaded need to reload it. */ 7659 if (!error) 7660 spell_reload_one(wfname, added_word); 7661 } 7662 7663 /* Free the allocated memory. */ 7664 ga_clear(&spin.si_rep); 7665 ga_clear(&spin.si_sal); 7666 ga_clear(&spin.si_map); 7667 ga_clear(&spin.si_prefcond); 7668 7669 /* Free the .aff file structures. */ 7670 for (i = 0; i < incount; ++i) 7671 if (afile[i] != NULL) 7672 spell_free_aff(afile[i]); 7673 7674 /* Free all the bits and pieces at once. */ 7675 free_blocks(spin.si_blocks); 7676 } 7677 } 7678 7679 7680 /* 7681 * ":[count]spellgood {word}" 7682 * ":[count]spellwrong {word}" 7683 */ 7684 void 7685 ex_spell(eap) 7686 exarg_T *eap; 7687 { 7688 spell_add_word(eap->arg, STRLEN(eap->arg), eap->cmdidx == CMD_spellwrong, 7689 eap->forceit ? 0 : (int)eap->line2); 7690 } 7691 7692 /* 7693 * Add "word[len]" to 'spellfile' as a good or bad word. 7694 */ 7695 void 7696 spell_add_word(word, len, bad, index) 7697 char_u *word; 7698 int len; 7699 int bad; 7700 int index; /* "zG" and "zW": zero, otherwise index in 7701 'spellfile' */ 7702 { 7703 FILE *fd; 7704 buf_T *buf = NULL; 7705 int new_spf = FALSE; 7706 char_u *fname; 7707 char_u fnamebuf[MAXPATHL]; 7708 char_u line[MAXWLEN * 2]; 7709 long fpos, fpos_next = 0; 7710 int i; 7711 char_u *spf; 7712 7713 if (index == 0) /* use internal wordlist */ 7714 { 7715 if (int_wordlist == NULL) 7716 { 7717 int_wordlist = vim_tempname('s'); 7718 if (int_wordlist == NULL) 7719 return; 7720 } 7721 fname = int_wordlist; 7722 } 7723 else 7724 { 7725 /* If 'spellfile' isn't set figure out a good default value. */ 7726 if (*curbuf->b_p_spf == NUL) 7727 { 7728 init_spellfile(); 7729 new_spf = TRUE; 7730 } 7731 7732 if (*curbuf->b_p_spf == NUL) 7733 { 7734 EMSG2(_(e_notset), "spellfile"); 7735 return; 7736 } 7737 7738 for (spf = curbuf->b_p_spf, i = 1; *spf != NUL; ++i) 7739 { 7740 copy_option_part(&spf, fnamebuf, MAXPATHL, ","); 7741 if (i == index) 7742 break; 7743 if (*spf == NUL) 7744 { 7745 EMSGN(_("E765: 'spellfile' does not have %ld entries"), index); 7746 return; 7747 } 7748 } 7749 7750 /* Check that the user isn't editing the .add file somewhere. */ 7751 buf = buflist_findname_exp(fnamebuf); 7752 if (buf != NULL && buf->b_ml.ml_mfp == NULL) 7753 buf = NULL; 7754 if (buf != NULL && bufIsChanged(buf)) 7755 { 7756 EMSG(_(e_bufloaded)); 7757 return; 7758 } 7759 7760 fname = fnamebuf; 7761 } 7762 7763 if (bad) 7764 { 7765 /* When the word also appears as good word we need to remove that one, 7766 * since its flags sort before the one with WF_BANNED. */ 7767 fd = mch_fopen((char *)fname, "r"); 7768 if (fd != NULL) 7769 { 7770 while (!vim_fgets(line, MAXWLEN * 2, fd)) 7771 { 7772 fpos = fpos_next; 7773 fpos_next = ftell(fd); 7774 if (STRNCMP(word, line, len) == 0 7775 && (line[len] == '/' || line[len] < ' ')) 7776 { 7777 /* Found duplicate word. Remove it by writing a '#' at 7778 * the start of the line. Mixing reading and writing 7779 * doesn't work for all systems, close the file first. */ 7780 fclose(fd); 7781 fd = mch_fopen((char *)fname, "r+"); 7782 if (fd == NULL) 7783 break; 7784 if (fseek(fd, fpos, SEEK_SET) == 0) 7785 fputc('#', fd); 7786 fseek(fd, fpos_next, SEEK_SET); 7787 } 7788 } 7789 fclose(fd); 7790 } 7791 } 7792 7793 fd = mch_fopen((char *)fname, "a"); 7794 if (fd == NULL && new_spf) 7795 { 7796 /* We just initialized the 'spellfile' option and can't open the file. 7797 * We may need to create the "spell" directory first. We already 7798 * checked the runtime directory is writable in init_spellfile(). */ 7799 if (!dir_of_file_exists(fname)) 7800 { 7801 /* The directory doesn't exist. Try creating it and opening the 7802 * file again. */ 7803 vim_mkdir(NameBuff, 0755); 7804 fd = mch_fopen((char *)fname, "a"); 7805 } 7806 } 7807 7808 if (fd == NULL) 7809 EMSG2(_(e_notopen), fname); 7810 else 7811 { 7812 if (bad) 7813 fprintf(fd, "%.*s/!\n", len, word); 7814 else 7815 fprintf(fd, "%.*s\n", len, word); 7816 fclose(fd); 7817 7818 /* Update the .add.spl file. */ 7819 mkspell(1, &fname, FALSE, TRUE, TRUE); 7820 7821 /* If the .add file is edited somewhere, reload it. */ 7822 if (buf != NULL) 7823 buf_reload(buf); 7824 7825 redraw_all_later(NOT_VALID); 7826 } 7827 } 7828 7829 /* 7830 * Initialize 'spellfile' for the current buffer. 7831 */ 7832 static void 7833 init_spellfile() 7834 { 7835 char_u buf[MAXPATHL]; 7836 int l; 7837 char_u *fname; 7838 char_u *rtp; 7839 char_u *lend; 7840 int aspath = FALSE; 7841 char_u *lstart = curbuf->b_p_spl; 7842 7843 if (*curbuf->b_p_spl != NUL && curbuf->b_langp.ga_len > 0) 7844 { 7845 /* Find the end of the language name. Exclude the region. If there 7846 * is a path separator remember the start of the tail. */ 7847 for (lend = curbuf->b_p_spl; *lend != NUL 7848 && vim_strchr((char_u *)",._", *lend) == NULL; ++lend) 7849 if (vim_ispathsep(*lend)) 7850 { 7851 aspath = TRUE; 7852 lstart = lend + 1; 7853 } 7854 7855 /* Loop over all entries in 'runtimepath'. Use the first one where we 7856 * are allowed to write. */ 7857 rtp = p_rtp; 7858 while (*rtp != NUL) 7859 { 7860 if (aspath) 7861 /* Use directory of an entry with path, e.g., for 7862 * "/dir/lg.utf-8.spl" use "/dir". */ 7863 vim_strncpy(buf, curbuf->b_p_spl, lstart - curbuf->b_p_spl - 1); 7864 else 7865 /* Copy the path from 'runtimepath' to buf[]. */ 7866 copy_option_part(&rtp, buf, MAXPATHL, ","); 7867 if (filewritable(buf) == 2) 7868 { 7869 /* Use the first language name from 'spelllang' and the 7870 * encoding used in the first loaded .spl file. */ 7871 if (aspath) 7872 vim_strncpy(buf, curbuf->b_p_spl, lend - curbuf->b_p_spl); 7873 else 7874 { 7875 l = STRLEN(buf); 7876 vim_snprintf((char *)buf + l, MAXPATHL - l, 7877 "/spell/%.*s", (int)(lend - lstart), lstart); 7878 } 7879 l = STRLEN(buf); 7880 fname = LANGP_ENTRY(curbuf->b_langp, 0)->lp_slang->sl_fname; 7881 vim_snprintf((char *)buf + l, MAXPATHL - l, ".%s.add", 7882 fname != NULL 7883 && strstr((char *)gettail(fname), ".ascii.") != NULL 7884 ? (char_u *)"ascii" : spell_enc()); 7885 set_option_value((char_u *)"spellfile", 0L, buf, OPT_LOCAL); 7886 break; 7887 } 7888 aspath = FALSE; 7889 } 7890 } 7891 } 7892 7893 7894 /* 7895 * Init the chartab used for spelling for ASCII. 7896 * EBCDIC is not supported! 7897 */ 7898 static void 7899 clear_spell_chartab(sp) 7900 spelltab_T *sp; 7901 { 7902 int i; 7903 7904 /* Init everything to FALSE. */ 7905 vim_memset(sp->st_isw, FALSE, sizeof(sp->st_isw)); 7906 vim_memset(sp->st_isu, FALSE, sizeof(sp->st_isu)); 7907 for (i = 0; i < 256; ++i) 7908 { 7909 sp->st_fold[i] = i; 7910 sp->st_upper[i] = i; 7911 } 7912 7913 /* We include digits. A word shouldn't start with a digit, but handling 7914 * that is done separately. */ 7915 for (i = '0'; i <= '9'; ++i) 7916 sp->st_isw[i] = TRUE; 7917 for (i = 'A'; i <= 'Z'; ++i) 7918 { 7919 sp->st_isw[i] = TRUE; 7920 sp->st_isu[i] = TRUE; 7921 sp->st_fold[i] = i + 0x20; 7922 } 7923 for (i = 'a'; i <= 'z'; ++i) 7924 { 7925 sp->st_isw[i] = TRUE; 7926 sp->st_upper[i] = i - 0x20; 7927 } 7928 } 7929 7930 /* 7931 * Init the chartab used for spelling. Only depends on 'encoding'. 7932 * Called once while starting up and when 'encoding' changes. 7933 * The default is to use isalpha(), but the spell file should define the word 7934 * characters to make it possible that 'encoding' differs from the current 7935 * locale. For utf-8 we don't use isalpha() but our own functions. 7936 */ 7937 void 7938 init_spell_chartab() 7939 { 7940 int i; 7941 7942 did_set_spelltab = FALSE; 7943 clear_spell_chartab(&spelltab); 7944 #ifdef FEAT_MBYTE 7945 if (enc_dbcs) 7946 { 7947 /* DBCS: assume double-wide characters are word characters. */ 7948 for (i = 128; i <= 255; ++i) 7949 if (MB_BYTE2LEN(i) == 2) 7950 spelltab.st_isw[i] = TRUE; 7951 } 7952 else if (enc_utf8) 7953 { 7954 for (i = 128; i < 256; ++i) 7955 { 7956 spelltab.st_isu[i] = utf_isupper(i); 7957 spelltab.st_isw[i] = spelltab.st_isu[i] || utf_islower(i); 7958 spelltab.st_fold[i] = utf_fold(i); 7959 spelltab.st_upper[i] = utf_toupper(i); 7960 } 7961 } 7962 else 7963 #endif 7964 { 7965 /* Rough guess: use locale-dependent library functions. */ 7966 for (i = 128; i < 256; ++i) 7967 { 7968 if (MB_ISUPPER(i)) 7969 { 7970 spelltab.st_isw[i] = TRUE; 7971 spelltab.st_isu[i] = TRUE; 7972 spelltab.st_fold[i] = MB_TOLOWER(i); 7973 } 7974 else if (MB_ISLOWER(i)) 7975 { 7976 spelltab.st_isw[i] = TRUE; 7977 spelltab.st_upper[i] = MB_TOUPPER(i); 7978 } 7979 } 7980 } 7981 } 7982 7983 /* 7984 * Set the spell character tables from strings in the affix file. 7985 */ 7986 static int 7987 set_spell_chartab(fol, low, upp) 7988 char_u *fol; 7989 char_u *low; 7990 char_u *upp; 7991 { 7992 /* We build the new tables here first, so that we can compare with the 7993 * previous one. */ 7994 spelltab_T new_st; 7995 char_u *pf = fol, *pl = low, *pu = upp; 7996 int f, l, u; 7997 7998 clear_spell_chartab(&new_st); 7999 8000 while (*pf != NUL) 8001 { 8002 if (*pl == NUL || *pu == NUL) 8003 { 8004 EMSG(_(e_affform)); 8005 return FAIL; 8006 } 8007 #ifdef FEAT_MBYTE 8008 f = mb_ptr2char_adv(&pf); 8009 l = mb_ptr2char_adv(&pl); 8010 u = mb_ptr2char_adv(&pu); 8011 #else 8012 f = *pf++; 8013 l = *pl++; 8014 u = *pu++; 8015 #endif 8016 /* Every character that appears is a word character. */ 8017 if (f < 256) 8018 new_st.st_isw[f] = TRUE; 8019 if (l < 256) 8020 new_st.st_isw[l] = TRUE; 8021 if (u < 256) 8022 new_st.st_isw[u] = TRUE; 8023 8024 /* if "LOW" and "FOL" are not the same the "LOW" char needs 8025 * case-folding */ 8026 if (l < 256 && l != f) 8027 { 8028 if (f >= 256) 8029 { 8030 EMSG(_(e_affrange)); 8031 return FAIL; 8032 } 8033 new_st.st_fold[l] = f; 8034 } 8035 8036 /* if "UPP" and "FOL" are not the same the "UPP" char needs 8037 * case-folding, it's upper case and the "UPP" is the upper case of 8038 * "FOL" . */ 8039 if (u < 256 && u != f) 8040 { 8041 if (f >= 256) 8042 { 8043 EMSG(_(e_affrange)); 8044 return FAIL; 8045 } 8046 new_st.st_fold[u] = f; 8047 new_st.st_isu[u] = TRUE; 8048 new_st.st_upper[f] = u; 8049 } 8050 } 8051 8052 if (*pl != NUL || *pu != NUL) 8053 { 8054 EMSG(_(e_affform)); 8055 return FAIL; 8056 } 8057 8058 return set_spell_finish(&new_st); 8059 } 8060 8061 /* 8062 * Set the spell character tables from strings in the .spl file. 8063 */ 8064 static void 8065 set_spell_charflags(flags, cnt, fol) 8066 char_u *flags; 8067 int cnt; /* length of "flags" */ 8068 char_u *fol; 8069 { 8070 /* We build the new tables here first, so that we can compare with the 8071 * previous one. */ 8072 spelltab_T new_st; 8073 int i; 8074 char_u *p = fol; 8075 int c; 8076 8077 clear_spell_chartab(&new_st); 8078 8079 for (i = 0; i < 128; ++i) 8080 { 8081 if (i < cnt) 8082 { 8083 new_st.st_isw[i + 128] = (flags[i] & CF_WORD) != 0; 8084 new_st.st_isu[i + 128] = (flags[i] & CF_UPPER) != 0; 8085 } 8086 8087 if (*p != NUL) 8088 { 8089 #ifdef FEAT_MBYTE 8090 c = mb_ptr2char_adv(&p); 8091 #else 8092 c = *p++; 8093 #endif 8094 new_st.st_fold[i + 128] = c; 8095 if (i + 128 != c && new_st.st_isu[i + 128] && c < 256) 8096 new_st.st_upper[c] = i + 128; 8097 } 8098 } 8099 8100 (void)set_spell_finish(&new_st); 8101 } 8102 8103 static int 8104 set_spell_finish(new_st) 8105 spelltab_T *new_st; 8106 { 8107 int i; 8108 8109 if (did_set_spelltab) 8110 { 8111 /* check that it's the same table */ 8112 for (i = 0; i < 256; ++i) 8113 { 8114 if (spelltab.st_isw[i] != new_st->st_isw[i] 8115 || spelltab.st_isu[i] != new_st->st_isu[i] 8116 || spelltab.st_fold[i] != new_st->st_fold[i] 8117 || spelltab.st_upper[i] != new_st->st_upper[i]) 8118 { 8119 EMSG(_("E763: Word characters differ between spell files")); 8120 return FAIL; 8121 } 8122 } 8123 } 8124 else 8125 { 8126 /* copy the new spelltab into the one being used */ 8127 spelltab = *new_st; 8128 did_set_spelltab = TRUE; 8129 } 8130 8131 return OK; 8132 } 8133 8134 /* 8135 * Return TRUE if "p" points to a word character. 8136 * As a special case we see "midword" characters as word character when it is 8137 * followed by a word character. This finds they'there but not 'they there'. 8138 * Thus this only works properly when past the first character of the word. 8139 */ 8140 static int 8141 spell_iswordp(p, buf) 8142 char_u *p; 8143 buf_T *buf; /* buffer used */ 8144 { 8145 #ifdef FEAT_MBYTE 8146 char_u *s; 8147 int l; 8148 int c; 8149 8150 if (has_mbyte) 8151 { 8152 l = MB_BYTE2LEN(*p); 8153 s = p; 8154 if (l == 1) 8155 { 8156 /* be quick for ASCII */ 8157 if (buf->b_spell_ismw[*p]) 8158 { 8159 s = p + 1; /* skip a mid-word character */ 8160 l = MB_BYTE2LEN(*s); 8161 } 8162 } 8163 else 8164 { 8165 c = mb_ptr2char(p); 8166 if (c < 256 ? buf->b_spell_ismw[c] 8167 : (buf->b_spell_ismw_mb != NULL 8168 && vim_strchr(buf->b_spell_ismw_mb, c) != NULL)) 8169 { 8170 s = p + l; 8171 l = MB_BYTE2LEN(*s); 8172 } 8173 } 8174 8175 c = mb_ptr2char(s); 8176 if (c > 255) 8177 return mb_get_class(s) >= 2; 8178 return spelltab.st_isw[c]; 8179 } 8180 #endif 8181 8182 return spelltab.st_isw[buf->b_spell_ismw[*p] ? p[1] : p[0]]; 8183 } 8184 8185 /* 8186 * Return TRUE if "p" points to a word character. 8187 * Unlike spell_iswordp() this doesn't check for "midword" characters. 8188 */ 8189 static int 8190 spell_iswordp_nmw(p) 8191 char_u *p; 8192 { 8193 #ifdef FEAT_MBYTE 8194 int c; 8195 8196 if (has_mbyte) 8197 { 8198 c = mb_ptr2char(p); 8199 if (c > 255) 8200 return mb_get_class(p) >= 2; 8201 return spelltab.st_isw[c]; 8202 } 8203 #endif 8204 return spelltab.st_isw[*p]; 8205 } 8206 8207 #ifdef FEAT_MBYTE 8208 /* 8209 * Return TRUE if "p" points to a word character. 8210 * Wide version of spell_iswordp(). 8211 */ 8212 static int 8213 spell_iswordp_w(p, buf) 8214 int *p; 8215 buf_T *buf; 8216 { 8217 int *s; 8218 8219 if (*p < 256 ? buf->b_spell_ismw[*p] 8220 : (buf->b_spell_ismw_mb != NULL 8221 && vim_strchr(buf->b_spell_ismw_mb, *p) != NULL)) 8222 s = p + 1; 8223 else 8224 s = p; 8225 8226 if (*s > 255) 8227 { 8228 if (enc_utf8) 8229 return utf_class(*s) >= 2; 8230 if (enc_dbcs) 8231 return dbcs_class((unsigned)*s >> 8, *s & 0xff) >= 2; 8232 return 0; 8233 } 8234 return spelltab.st_isw[*s]; 8235 } 8236 #endif 8237 8238 /* 8239 * Write the table with prefix conditions to the .spl file. 8240 * When "fd" is NULL only count the length of what is written. 8241 */ 8242 static int 8243 write_spell_prefcond(fd, gap) 8244 FILE *fd; 8245 garray_T *gap; 8246 { 8247 int i; 8248 char_u *p; 8249 int len; 8250 int totlen; 8251 8252 if (fd != NULL) 8253 put_bytes(fd, (long_u)gap->ga_len, 2); /* <prefcondcnt> */ 8254 8255 totlen = 2 + gap->ga_len; /* length of <prefcondcnt> and <condlen> bytes */ 8256 8257 for (i = 0; i < gap->ga_len; ++i) 8258 { 8259 /* <prefcond> : <condlen> <condstr> */ 8260 p = ((char_u **)gap->ga_data)[i]; 8261 if (p != NULL) 8262 { 8263 len = STRLEN(p); 8264 if (fd != NULL) 8265 { 8266 fputc(len, fd); 8267 fwrite(p, (size_t)len, (size_t)1, fd); 8268 } 8269 totlen += len; 8270 } 8271 else if (fd != NULL) 8272 fputc(0, fd); 8273 } 8274 8275 return totlen; 8276 } 8277 8278 /* 8279 * Case-fold "str[len]" into "buf[buflen]". The result is NUL terminated. 8280 * Uses the character definitions from the .spl file. 8281 * When using a multi-byte 'encoding' the length may change! 8282 * Returns FAIL when something wrong. 8283 */ 8284 static int 8285 spell_casefold(str, len, buf, buflen) 8286 char_u *str; 8287 int len; 8288 char_u *buf; 8289 int buflen; 8290 { 8291 int i; 8292 8293 if (len >= buflen) 8294 { 8295 buf[0] = NUL; 8296 return FAIL; /* result will not fit */ 8297 } 8298 8299 #ifdef FEAT_MBYTE 8300 if (has_mbyte) 8301 { 8302 int outi = 0; 8303 char_u *p; 8304 int c; 8305 8306 /* Fold one character at a time. */ 8307 for (p = str; p < str + len; ) 8308 { 8309 if (outi + MB_MAXBYTES > buflen) 8310 { 8311 buf[outi] = NUL; 8312 return FAIL; 8313 } 8314 c = mb_cptr2char_adv(&p); 8315 outi += mb_char2bytes(SPELL_TOFOLD(c), buf + outi); 8316 } 8317 buf[outi] = NUL; 8318 } 8319 else 8320 #endif 8321 { 8322 /* Be quick for non-multibyte encodings. */ 8323 for (i = 0; i < len; ++i) 8324 buf[i] = spelltab.st_fold[str[i]]; 8325 buf[i] = NUL; 8326 } 8327 8328 return OK; 8329 } 8330 8331 #define SPS_BEST 1 8332 #define SPS_FAST 2 8333 #define SPS_DOUBLE 4 8334 8335 static int sps_flags = SPS_BEST; 8336 static int sps_limit = 9999; 8337 8338 /* 8339 * Check the 'spellsuggest' option. Return FAIL if it's wrong. 8340 * Sets "sps_flags" and "sps_limit". 8341 */ 8342 int 8343 spell_check_sps() 8344 { 8345 char_u *p; 8346 char_u *s; 8347 char_u buf[MAXPATHL]; 8348 int f; 8349 8350 sps_flags = 0; 8351 sps_limit = 9999; 8352 8353 for (p = p_sps; *p != NUL; ) 8354 { 8355 copy_option_part(&p, buf, MAXPATHL, ","); 8356 8357 f = 0; 8358 if (VIM_ISDIGIT(*buf)) 8359 { 8360 s = buf; 8361 sps_limit = getdigits(&s); 8362 if (*s != NUL && !VIM_ISDIGIT(*s)) 8363 f = -1; 8364 } 8365 else if (STRCMP(buf, "best") == 0) 8366 f = SPS_BEST; 8367 else if (STRCMP(buf, "fast") == 0) 8368 f = SPS_FAST; 8369 else if (STRCMP(buf, "double") == 0) 8370 f = SPS_DOUBLE; 8371 else if (STRNCMP(buf, "expr:", 5) != 0 8372 && STRNCMP(buf, "file:", 5) != 0) 8373 f = -1; 8374 8375 if (f == -1 || (sps_flags != 0 && f != 0)) 8376 { 8377 sps_flags = SPS_BEST; 8378 sps_limit = 9999; 8379 return FAIL; 8380 } 8381 if (f != 0) 8382 sps_flags = f; 8383 } 8384 8385 if (sps_flags == 0) 8386 sps_flags = SPS_BEST; 8387 8388 return OK; 8389 } 8390 8391 /* Remember what "z?" replaced. */ 8392 static char_u *repl_from = NULL; 8393 static char_u *repl_to = NULL; 8394 8395 /* 8396 * "z?": Find badly spelled word under or after the cursor. 8397 * Give suggestions for the properly spelled word. 8398 * When "count" is non-zero use that suggestion. 8399 */ 8400 void 8401 spell_suggest(count) 8402 int count; 8403 { 8404 char_u *line; 8405 pos_T prev_cursor = curwin->w_cursor; 8406 char_u wcopy[MAXWLEN + 2]; 8407 char_u *p; 8408 int i; 8409 int c; 8410 suginfo_T sug; 8411 suggest_T *stp; 8412 int mouse_used; 8413 int need_cap; 8414 int limit; 8415 int selected = count; 8416 8417 /* Find the start of the badly spelled word. */ 8418 if (spell_move_to(curwin, FORWARD, TRUE, TRUE, NULL) == 0 8419 || curwin->w_cursor.col > prev_cursor.col) 8420 { 8421 if (!curwin->w_p_spell || *curbuf->b_p_spl == NUL) 8422 return; 8423 8424 /* No bad word or it starts after the cursor: use the word under the 8425 * cursor. */ 8426 curwin->w_cursor = prev_cursor; 8427 line = ml_get_curline(); 8428 p = line + curwin->w_cursor.col; 8429 /* Backup to before start of word. */ 8430 while (p > line && spell_iswordp_nmw(p)) 8431 mb_ptr_back(line, p); 8432 /* Forward to start of word. */ 8433 while (*p != NUL && !spell_iswordp_nmw(p)) 8434 mb_ptr_adv(p); 8435 8436 if (!spell_iswordp_nmw(p)) /* No word found. */ 8437 { 8438 beep_flush(); 8439 return; 8440 } 8441 curwin->w_cursor.col = p - line; 8442 } 8443 8444 /* Get the word and its length. */ 8445 8446 /* Figure out if the word should be capitalised. */ 8447 need_cap = check_need_cap(curwin->w_cursor.lnum, curwin->w_cursor.col); 8448 8449 line = ml_get_curline(); 8450 8451 /* Get the list of suggestions. Limit to 'lines' - 2 or the number in 8452 * 'spellsuggest', whatever is smaller. */ 8453 if (sps_limit > (int)Rows - 2) 8454 limit = (int)Rows - 2; 8455 else 8456 limit = sps_limit; 8457 spell_find_suggest(line + curwin->w_cursor.col, &sug, limit, 8458 TRUE, need_cap); 8459 8460 if (sug.su_ga.ga_len == 0) 8461 MSG(_("Sorry, no suggestions")); 8462 else if (count > 0) 8463 { 8464 if (count > sug.su_ga.ga_len) 8465 smsg((char_u *)_("Sorry, only %ld suggestions"), 8466 (long)sug.su_ga.ga_len); 8467 } 8468 else 8469 { 8470 vim_free(repl_from); 8471 repl_from = NULL; 8472 vim_free(repl_to); 8473 repl_to = NULL; 8474 8475 #ifdef FEAT_RIGHTLEFT 8476 /* When 'rightleft' is set the list is drawn right-left. */ 8477 cmdmsg_rl = curwin->w_p_rl; 8478 if (cmdmsg_rl) 8479 msg_col = Columns - 1; 8480 #endif 8481 8482 /* List the suggestions. */ 8483 msg_start(); 8484 lines_left = Rows; /* avoid more prompt */ 8485 vim_snprintf((char *)IObuff, IOSIZE, _("Change \"%.*s\" to:"), 8486 sug.su_badlen, sug.su_badptr); 8487 #ifdef FEAT_RIGHTLEFT 8488 if (cmdmsg_rl && STRNCMP(IObuff, "Change", 6) == 0) 8489 { 8490 /* And now the rabbit from the high hat: Avoid showing the 8491 * untranslated message rightleft. */ 8492 vim_snprintf((char *)IObuff, IOSIZE, ":ot \"%.*s\" egnahC", 8493 sug.su_badlen, sug.su_badptr); 8494 } 8495 #endif 8496 msg_puts(IObuff); 8497 msg_clr_eos(); 8498 msg_putchar('\n'); 8499 8500 msg_scroll = TRUE; 8501 for (i = 0; i < sug.su_ga.ga_len; ++i) 8502 { 8503 stp = &SUG(sug.su_ga, i); 8504 8505 /* The suggested word may replace only part of the bad word, add 8506 * the not replaced part. */ 8507 STRCPY(wcopy, stp->st_word); 8508 if (sug.su_badlen > stp->st_orglen) 8509 vim_strncpy(wcopy + STRLEN(wcopy), 8510 sug.su_badptr + stp->st_orglen, 8511 sug.su_badlen - stp->st_orglen); 8512 vim_snprintf((char *)IObuff, IOSIZE, "%2d", i + 1); 8513 #ifdef FEAT_RIGHTLEFT 8514 if (cmdmsg_rl) 8515 rl_mirror(IObuff); 8516 #endif 8517 msg_puts(IObuff); 8518 8519 vim_snprintf((char *)IObuff, IOSIZE, " \"%s\"", wcopy); 8520 msg_puts(IObuff); 8521 8522 /* The word may replace more than "su_badlen". */ 8523 if (sug.su_badlen < stp->st_orglen) 8524 { 8525 vim_snprintf((char *)IObuff, IOSIZE, _(" < \"%.*s\""), 8526 stp->st_orglen, sug.su_badptr); 8527 msg_puts(IObuff); 8528 } 8529 8530 if (p_verbose > 0) 8531 { 8532 /* Add the score. */ 8533 if (sps_flags & (SPS_DOUBLE | SPS_BEST)) 8534 vim_snprintf((char *)IObuff, IOSIZE, " (%s%d - %d)", 8535 stp->st_salscore ? "s " : "", 8536 stp->st_score, stp->st_altscore); 8537 else 8538 vim_snprintf((char *)IObuff, IOSIZE, " (%d)", 8539 stp->st_score); 8540 #ifdef FEAT_RIGHTLEFT 8541 if (cmdmsg_rl) 8542 /* Mirror the numbers, but keep the leading space. */ 8543 rl_mirror(IObuff + 1); 8544 #endif 8545 msg_advance(30); 8546 msg_puts(IObuff); 8547 } 8548 msg_putchar('\n'); 8549 } 8550 8551 #ifdef FEAT_RIGHTLEFT 8552 cmdmsg_rl = FALSE; 8553 msg_col = 0; 8554 #endif 8555 /* Ask for choice. */ 8556 selected = prompt_for_number(&mouse_used); 8557 if (mouse_used) 8558 selected -= lines_left; 8559 } 8560 8561 if (selected > 0 && selected <= sug.su_ga.ga_len && u_save_cursor() == OK) 8562 { 8563 /* Save the from and to text for :spellrepall. */ 8564 stp = &SUG(sug.su_ga, selected - 1); 8565 if (sug.su_badlen > stp->st_orglen) 8566 { 8567 /* Replacing less than "su_badlen", append the remainder to 8568 * repl_to. */ 8569 repl_from = vim_strnsave(sug.su_badptr, sug.su_badlen); 8570 vim_snprintf((char *)IObuff, IOSIZE, "%s%.*s", stp->st_word, 8571 sug.su_badlen - stp->st_orglen, 8572 sug.su_badptr + stp->st_orglen); 8573 repl_to = vim_strsave(IObuff); 8574 } 8575 else 8576 { 8577 /* Replacing su_badlen or more, use the whole word. */ 8578 repl_from = vim_strnsave(sug.su_badptr, stp->st_orglen); 8579 repl_to = vim_strsave(stp->st_word); 8580 } 8581 8582 /* Replace the word. */ 8583 p = alloc(STRLEN(line) - stp->st_orglen + STRLEN(stp->st_word) + 1); 8584 if (p != NULL) 8585 { 8586 c = sug.su_badptr - line; 8587 mch_memmove(p, line, c); 8588 STRCPY(p + c, stp->st_word); 8589 STRCAT(p, sug.su_badptr + stp->st_orglen); 8590 ml_replace(curwin->w_cursor.lnum, p, FALSE); 8591 curwin->w_cursor.col = c; 8592 changed_bytes(curwin->w_cursor.lnum, c); 8593 8594 /* For redo we use a change-word command. */ 8595 ResetRedobuff(); 8596 AppendToRedobuff((char_u *)"ciw"); 8597 AppendToRedobuff(stp->st_word); 8598 AppendCharToRedobuff(ESC); 8599 } 8600 } 8601 else 8602 curwin->w_cursor = prev_cursor; 8603 8604 spell_find_cleanup(&sug); 8605 } 8606 8607 /* 8608 * Check if the word at line "lnum" column "col" is required to start with a 8609 * capital. This uses 'spellcapcheck' of the current buffer. 8610 */ 8611 static int 8612 check_need_cap(lnum, col) 8613 linenr_T lnum; 8614 colnr_T col; 8615 { 8616 int need_cap = FALSE; 8617 char_u *line; 8618 char_u *line_copy = NULL; 8619 char_u *p; 8620 colnr_T endcol; 8621 regmatch_T regmatch; 8622 8623 if (curbuf->b_cap_prog == NULL) 8624 return FALSE; 8625 8626 line = ml_get_curline(); 8627 endcol = 0; 8628 if ((int)(skipwhite(line) - line) >= (int)col) 8629 { 8630 /* At start of line, check if previous line is empty or sentence 8631 * ends there. */ 8632 if (lnum == 1) 8633 need_cap = TRUE; 8634 else 8635 { 8636 line = ml_get(lnum - 1); 8637 if (*skipwhite(line) == NUL) 8638 need_cap = TRUE; 8639 else 8640 { 8641 /* Append a space in place of the line break. */ 8642 line_copy = concat_str(line, (char_u *)" "); 8643 line = line_copy; 8644 endcol = STRLEN(line); 8645 } 8646 } 8647 } 8648 else 8649 endcol = col; 8650 8651 if (endcol > 0) 8652 { 8653 /* Check if sentence ends before the bad word. */ 8654 regmatch.regprog = curbuf->b_cap_prog; 8655 regmatch.rm_ic = FALSE; 8656 p = line + endcol; 8657 for (;;) 8658 { 8659 mb_ptr_back(line, p); 8660 if (p == line || spell_iswordp_nmw(p)) 8661 break; 8662 if (vim_regexec(®match, p, 0) 8663 && regmatch.endp[0] == line + endcol) 8664 { 8665 need_cap = TRUE; 8666 break; 8667 } 8668 } 8669 } 8670 8671 vim_free(line_copy); 8672 8673 return need_cap; 8674 } 8675 8676 8677 /* 8678 * ":spellrepall" 8679 */ 8680 /*ARGSUSED*/ 8681 void 8682 ex_spellrepall(eap) 8683 exarg_T *eap; 8684 { 8685 pos_T pos = curwin->w_cursor; 8686 char_u *frompat; 8687 int addlen; 8688 char_u *line; 8689 char_u *p; 8690 int save_ws = p_ws; 8691 linenr_T prev_lnum = 0; 8692 8693 if (repl_from == NULL || repl_to == NULL) 8694 { 8695 EMSG(_("E752: No previous spell replacement")); 8696 return; 8697 } 8698 addlen = STRLEN(repl_to) - STRLEN(repl_from); 8699 8700 frompat = alloc(STRLEN(repl_from) + 7); 8701 if (frompat == NULL) 8702 return; 8703 sprintf((char *)frompat, "\\V\\<%s\\>", repl_from); 8704 p_ws = FALSE; 8705 8706 sub_nsubs = 0; 8707 sub_nlines = 0; 8708 curwin->w_cursor.lnum = 0; 8709 while (!got_int) 8710 { 8711 if (do_search(NULL, '/', frompat, 1L, SEARCH_KEEP) == 0 8712 || u_save_cursor() == FAIL) 8713 break; 8714 8715 /* Only replace when the right word isn't there yet. This happens 8716 * when changing "etc" to "etc.". */ 8717 line = ml_get_curline(); 8718 if (addlen <= 0 || STRNCMP(line + curwin->w_cursor.col, 8719 repl_to, STRLEN(repl_to)) != 0) 8720 { 8721 p = alloc(STRLEN(line) + addlen + 1); 8722 if (p == NULL) 8723 break; 8724 mch_memmove(p, line, curwin->w_cursor.col); 8725 STRCPY(p + curwin->w_cursor.col, repl_to); 8726 STRCAT(p, line + curwin->w_cursor.col + STRLEN(repl_from)); 8727 ml_replace(curwin->w_cursor.lnum, p, FALSE); 8728 changed_bytes(curwin->w_cursor.lnum, curwin->w_cursor.col); 8729 8730 if (curwin->w_cursor.lnum != prev_lnum) 8731 { 8732 ++sub_nlines; 8733 prev_lnum = curwin->w_cursor.lnum; 8734 } 8735 ++sub_nsubs; 8736 } 8737 curwin->w_cursor.col += STRLEN(repl_to); 8738 } 8739 8740 p_ws = save_ws; 8741 curwin->w_cursor = pos; 8742 vim_free(frompat); 8743 8744 if (sub_nsubs == 0) 8745 EMSG2(_("E753: Not found: %s"), repl_from); 8746 else 8747 do_sub_msg(FALSE); 8748 } 8749 8750 /* 8751 * Find spell suggestions for "word". Return them in the growarray "*gap" as 8752 * a list of allocated strings. 8753 */ 8754 void 8755 spell_suggest_list(gap, word, maxcount, need_cap) 8756 garray_T *gap; 8757 char_u *word; 8758 int maxcount; /* maximum nr of suggestions */ 8759 int need_cap; /* 'spellcapcheck' matched */ 8760 { 8761 suginfo_T sug; 8762 int i; 8763 suggest_T *stp; 8764 char_u *wcopy; 8765 8766 spell_find_suggest(word, &sug, maxcount, FALSE, need_cap); 8767 8768 /* Make room in "gap". */ 8769 ga_init2(gap, sizeof(char_u *), sug.su_ga.ga_len + 1); 8770 if (ga_grow(gap, sug.su_ga.ga_len) == FAIL) 8771 return; 8772 8773 for (i = 0; i < sug.su_ga.ga_len; ++i) 8774 { 8775 stp = &SUG(sug.su_ga, i); 8776 8777 /* The suggested word may replace only part of "word", add the not 8778 * replaced part. */ 8779 wcopy = alloc(STRLEN(stp->st_word) 8780 + STRLEN(sug.su_badptr + stp->st_orglen) + 1); 8781 if (wcopy == NULL) 8782 break; 8783 STRCPY(wcopy, stp->st_word); 8784 STRCAT(wcopy, sug.su_badptr + stp->st_orglen); 8785 ((char_u **)gap->ga_data)[gap->ga_len++] = wcopy; 8786 } 8787 8788 spell_find_cleanup(&sug); 8789 } 8790 8791 /* 8792 * Find spell suggestions for the word at the start of "badptr". 8793 * Return the suggestions in "su->su_ga". 8794 * The maximum number of suggestions is "maxcount". 8795 * Note: does use info for the current window. 8796 * This is based on the mechanisms of Aspell, but completely reimplemented. 8797 */ 8798 static void 8799 spell_find_suggest(badptr, su, maxcount, banbadword, need_cap) 8800 char_u *badptr; 8801 suginfo_T *su; 8802 int maxcount; 8803 int banbadword; /* don't include badword in suggestions */ 8804 int need_cap; /* word should start with capital */ 8805 { 8806 hlf_T attr = HLF_COUNT; 8807 char_u buf[MAXPATHL]; 8808 char_u *p; 8809 int do_combine = FALSE; 8810 char_u *sps_copy; 8811 #ifdef FEAT_EVAL 8812 static int expr_busy = FALSE; 8813 #endif 8814 int c; 8815 int i; 8816 langp_T *lp; 8817 8818 /* 8819 * Set the info in "*su". 8820 */ 8821 vim_memset(su, 0, sizeof(suginfo_T)); 8822 ga_init2(&su->su_ga, (int)sizeof(suggest_T), 10); 8823 ga_init2(&su->su_sga, (int)sizeof(suggest_T), 10); 8824 if (*badptr == NUL) 8825 return; 8826 hash_init(&su->su_banned); 8827 8828 su->su_badptr = badptr; 8829 su->su_badlen = spell_check(curwin, su->su_badptr, &attr, NULL); 8830 su->su_maxcount = maxcount; 8831 su->su_maxscore = SCORE_MAXINIT; 8832 8833 if (su->su_badlen >= MAXWLEN) 8834 su->su_badlen = MAXWLEN - 1; /* just in case */ 8835 vim_strncpy(su->su_badword, su->su_badptr, su->su_badlen); 8836 (void)spell_casefold(su->su_badptr, su->su_badlen, 8837 su->su_fbadword, MAXWLEN); 8838 /* get caps flags for bad word */ 8839 su->su_badflags = badword_captype(su->su_badptr, 8840 su->su_badptr + su->su_badlen); 8841 if (need_cap) 8842 su->su_badflags |= WF_ONECAP; 8843 8844 /* Find the default language for sound folding. We simply use the first 8845 * one in 'spelllang' that supports sound folding. That's good for when 8846 * using multiple files for one language, it's not that bad when mixing 8847 * languages (e.g., "pl,en"). */ 8848 for (i = 0; i < curbuf->b_langp.ga_len; ++i) 8849 { 8850 lp = LANGP_ENTRY(curbuf->b_langp, i); 8851 if (lp->lp_sallang != NULL) 8852 { 8853 su->su_sallang = lp->lp_sallang; 8854 break; 8855 } 8856 } 8857 8858 /* Soundfold the bad word with the default sound folding, so that we don't 8859 * have to do this many times. */ 8860 if (su->su_sallang != NULL) 8861 spell_soundfold(su->su_sallang, su->su_fbadword, TRUE, 8862 su->su_sal_badword); 8863 8864 /* If the word is not capitalised and spell_check() doesn't consider the 8865 * word to be bad then it might need to be capitalised. Add a suggestion 8866 * for that. */ 8867 c = PTR2CHAR(su->su_badptr); 8868 if (!SPELL_ISUPPER(c) && attr == HLF_COUNT) 8869 { 8870 make_case_word(su->su_badword, buf, WF_ONECAP); 8871 add_suggestion(su, &su->su_ga, buf, su->su_badlen, SCORE_ICASE, 8872 0, TRUE, su->su_sallang); 8873 } 8874 8875 /* Ban the bad word itself. It may appear in another region. */ 8876 if (banbadword) 8877 add_banned(su, su->su_badword); 8878 8879 /* Make a copy of 'spellsuggest', because the expression may change it. */ 8880 sps_copy = vim_strsave(p_sps); 8881 if (sps_copy == NULL) 8882 return; 8883 8884 /* Loop over the items in 'spellsuggest'. */ 8885 for (p = sps_copy; *p != NUL; ) 8886 { 8887 copy_option_part(&p, buf, MAXPATHL, ","); 8888 8889 if (STRNCMP(buf, "expr:", 5) == 0) 8890 { 8891 #ifdef FEAT_EVAL 8892 /* Evaluate an expression. Skip this when called recursively, 8893 * when using spellsuggest() in the expression. */ 8894 if (!expr_busy) 8895 { 8896 expr_busy = TRUE; 8897 spell_suggest_expr(su, buf + 5); 8898 expr_busy = FALSE; 8899 } 8900 #endif 8901 } 8902 else if (STRNCMP(buf, "file:", 5) == 0) 8903 /* Use list of suggestions in a file. */ 8904 spell_suggest_file(su, buf + 5); 8905 else 8906 { 8907 /* Use internal method. */ 8908 spell_suggest_intern(su); 8909 if (sps_flags & SPS_DOUBLE) 8910 do_combine = TRUE; 8911 } 8912 } 8913 8914 vim_free(sps_copy); 8915 8916 if (do_combine) 8917 /* Combine the two list of suggestions. This must be done last, 8918 * because sorting changes the order again. */ 8919 score_combine(su); 8920 } 8921 8922 #ifdef FEAT_EVAL 8923 /* 8924 * Find suggestions by evaluating expression "expr". 8925 */ 8926 static void 8927 spell_suggest_expr(su, expr) 8928 suginfo_T *su; 8929 char_u *expr; 8930 { 8931 list_T *list; 8932 listitem_T *li; 8933 int score; 8934 char_u *p; 8935 8936 /* The work is split up in a few parts to avoid having to export 8937 * suginfo_T. 8938 * First evaluate the expression and get the resulting list. */ 8939 list = eval_spell_expr(su->su_badword, expr); 8940 if (list != NULL) 8941 { 8942 /* Loop over the items in the list. */ 8943 for (li = list->lv_first; li != NULL; li = li->li_next) 8944 if (li->li_tv.v_type == VAR_LIST) 8945 { 8946 /* Get the word and the score from the items. */ 8947 score = get_spellword(li->li_tv.vval.v_list, &p); 8948 if (score >= 0) 8949 add_suggestion(su, &su->su_ga, p, 8950 su->su_badlen, score, 0, TRUE, su->su_sallang); 8951 } 8952 list_unref(list); 8953 } 8954 8955 /* Sort the suggestions and truncate at "maxcount". */ 8956 (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount); 8957 } 8958 #endif 8959 8960 /* 8961 * Find suggestions in file "fname". Used for "file:" in 'spellsuggest'. 8962 */ 8963 static void 8964 spell_suggest_file(su, fname) 8965 suginfo_T *su; 8966 char_u *fname; 8967 { 8968 FILE *fd; 8969 char_u line[MAXWLEN * 2]; 8970 char_u *p; 8971 int len; 8972 char_u cword[MAXWLEN]; 8973 8974 /* Open the file. */ 8975 fd = mch_fopen((char *)fname, "r"); 8976 if (fd == NULL) 8977 { 8978 EMSG2(_(e_notopen), fname); 8979 return; 8980 } 8981 8982 /* Read it line by line. */ 8983 while (!vim_fgets(line, MAXWLEN * 2, fd) && !got_int) 8984 { 8985 line_breakcheck(); 8986 8987 p = vim_strchr(line, '/'); 8988 if (p == NULL) 8989 continue; /* No Tab found, just skip the line. */ 8990 *p++ = NUL; 8991 if (STRICMP(su->su_badword, line) == 0) 8992 { 8993 /* Match! Isolate the good word, until CR or NL. */ 8994 for (len = 0; p[len] >= ' '; ++len) 8995 ; 8996 p[len] = NUL; 8997 8998 /* If the suggestion doesn't have specific case duplicate the case 8999 * of the bad word. */ 9000 if (captype(p, NULL) == 0) 9001 { 9002 make_case_word(p, cword, su->su_badflags); 9003 p = cword; 9004 } 9005 9006 add_suggestion(su, &su->su_ga, p, su->su_badlen, 9007 SCORE_FILE, 0, TRUE, su->su_sallang); 9008 } 9009 } 9010 9011 fclose(fd); 9012 9013 /* Sort the suggestions and truncate at "maxcount". */ 9014 (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount); 9015 } 9016 9017 /* 9018 * Find suggestions for the internal method indicated by "sps_flags". 9019 */ 9020 static void 9021 spell_suggest_intern(su) 9022 suginfo_T *su; 9023 { 9024 /* 9025 * 1. Try special cases, such as repeating a word: "the the" -> "the". 9026 * 9027 * Set a maximum score to limit the combination of operations that is 9028 * tried. 9029 */ 9030 suggest_try_special(su); 9031 9032 /* 9033 * 2. Try inserting/deleting/swapping/changing a letter, use REP entries 9034 * from the .aff file and inserting a space (split the word). 9035 */ 9036 suggest_try_change(su); 9037 9038 /* For the resulting top-scorers compute the sound-a-like score. */ 9039 if (sps_flags & SPS_DOUBLE) 9040 score_comp_sal(su); 9041 9042 /* 9043 * 3. Try finding sound-a-like words. 9044 * 9045 * Only do this when we don't have a lot of suggestions yet, because it's 9046 * very slow and often doesn't find new suggestions. 9047 */ 9048 if ((sps_flags & SPS_DOUBLE) 9049 || (!(sps_flags & SPS_FAST) 9050 && su->su_ga.ga_len < SUG_CLEAN_COUNT(su))) 9051 { 9052 /* Allow a higher score now. */ 9053 su->su_maxscore = SCORE_MAXMAX; 9054 suggest_try_soundalike(su); 9055 } 9056 9057 /* When CTRL-C was hit while searching do show the results. */ 9058 ui_breakcheck(); 9059 if (got_int) 9060 { 9061 (void)vgetc(); 9062 got_int = FALSE; 9063 } 9064 9065 if ((sps_flags & SPS_DOUBLE) == 0 && su->su_ga.ga_len != 0) 9066 { 9067 if (sps_flags & SPS_BEST) 9068 /* Adjust the word score for how it sounds like. */ 9069 rescore_suggestions(su); 9070 9071 /* Sort the suggestions and truncate at "maxcount". */ 9072 (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount); 9073 } 9074 } 9075 9076 /* 9077 * Free the info put in "*su" by spell_find_suggest(). 9078 */ 9079 static void 9080 spell_find_cleanup(su) 9081 suginfo_T *su; 9082 { 9083 int i; 9084 9085 /* Free the suggestions. */ 9086 for (i = 0; i < su->su_ga.ga_len; ++i) 9087 vim_free(SUG(su->su_ga, i).st_word); 9088 ga_clear(&su->su_ga); 9089 for (i = 0; i < su->su_sga.ga_len; ++i) 9090 vim_free(SUG(su->su_sga, i).st_word); 9091 ga_clear(&su->su_sga); 9092 9093 /* Free the banned words. */ 9094 free_banned(su); 9095 } 9096 9097 /* 9098 * Make a copy of "word", with the first letter upper or lower cased, to 9099 * "wcopy[MAXWLEN]". "word" must not be empty. 9100 * The result is NUL terminated. 9101 */ 9102 static void 9103 onecap_copy(word, wcopy, upper) 9104 char_u *word; 9105 char_u *wcopy; 9106 int upper; /* TRUE: first letter made upper case */ 9107 { 9108 char_u *p; 9109 int c; 9110 int l; 9111 9112 p = word; 9113 #ifdef FEAT_MBYTE 9114 if (has_mbyte) 9115 c = mb_cptr2char_adv(&p); 9116 else 9117 #endif 9118 c = *p++; 9119 if (upper) 9120 c = SPELL_TOUPPER(c); 9121 else 9122 c = SPELL_TOFOLD(c); 9123 #ifdef FEAT_MBYTE 9124 if (has_mbyte) 9125 l = mb_char2bytes(c, wcopy); 9126 else 9127 #endif 9128 { 9129 l = 1; 9130 wcopy[0] = c; 9131 } 9132 vim_strncpy(wcopy + l, p, MAXWLEN - l - 1); 9133 } 9134 9135 /* 9136 * Make a copy of "word" with all the letters upper cased into 9137 * "wcopy[MAXWLEN]". The result is NUL terminated. 9138 */ 9139 static void 9140 allcap_copy(word, wcopy) 9141 char_u *word; 9142 char_u *wcopy; 9143 { 9144 char_u *s; 9145 char_u *d; 9146 int c; 9147 9148 d = wcopy; 9149 for (s = word; *s != NUL; ) 9150 { 9151 #ifdef FEAT_MBYTE 9152 if (has_mbyte) 9153 c = mb_cptr2char_adv(&s); 9154 else 9155 #endif 9156 c = *s++; 9157 9158 #ifdef FEAT_MBYTE 9159 /* We only change � to SS when we are certain latin1 is used. It 9160 * would cause weird errors in other 8-bit encodings. */ 9161 if (enc_latin1like && c == 0xdf) 9162 { 9163 c = 'S'; 9164 if (d - wcopy >= MAXWLEN - 1) 9165 break; 9166 *d++ = c; 9167 } 9168 else 9169 #endif 9170 c = SPELL_TOUPPER(c); 9171 9172 #ifdef FEAT_MBYTE 9173 if (has_mbyte) 9174 { 9175 if (d - wcopy >= MAXWLEN - MB_MAXBYTES) 9176 break; 9177 d += mb_char2bytes(c, d); 9178 } 9179 else 9180 #endif 9181 { 9182 if (d - wcopy >= MAXWLEN - 1) 9183 break; 9184 *d++ = c; 9185 } 9186 } 9187 *d = NUL; 9188 } 9189 9190 /* 9191 * Try finding suggestions by recognizing specific situations. 9192 */ 9193 static void 9194 suggest_try_special(su) 9195 suginfo_T *su; 9196 { 9197 char_u *p; 9198 size_t len; 9199 int c; 9200 char_u word[MAXWLEN]; 9201 9202 /* 9203 * Recognize a word that is repeated: "the the". 9204 */ 9205 p = skiptowhite(su->su_fbadword); 9206 len = p - su->su_fbadword; 9207 p = skipwhite(p); 9208 if (STRLEN(p) == len && STRNCMP(su->su_fbadword, p, len) == 0) 9209 { 9210 /* Include badflags: if the badword is onecap or allcap 9211 * use that for the goodword too: "The the" -> "The". */ 9212 c = su->su_fbadword[len]; 9213 su->su_fbadword[len] = NUL; 9214 make_case_word(su->su_fbadword, word, su->su_badflags); 9215 su->su_fbadword[len] = c; 9216 9217 /* Give a soundalike score of 0, compute the score as if deleting one 9218 * character. */ 9219 add_suggestion(su, &su->su_ga, word, su->su_badlen, 9220 RESCORE(SCORE_REP, 0), 0, TRUE, su->su_sallang); 9221 } 9222 } 9223 9224 /* 9225 * Try finding suggestions by adding/removing/swapping letters. 9226 * 9227 * This uses a state machine. At each node in the tree we try various 9228 * operations. When trying if an operation work "depth" is increased and the 9229 * stack[] is used to store info. This allows combinations, thus insert one 9230 * character, replace one and delete another. The number of changes is 9231 * limited by su->su_maxscore, checked in try_deeper(). 9232 * 9233 * After implementing this I noticed an article by Kemal Oflazer that 9234 * describes something similar: "Error-tolerant Finite State Recognition with 9235 * Applications to Morphological Analysis and Spelling Correction" (1996). 9236 * The implementation in the article is simplified and requires a stack of 9237 * unknown depth. The implementation here only needs a stack depth of the 9238 * length of the word. 9239 */ 9240 static void 9241 suggest_try_change(su) 9242 suginfo_T *su; 9243 { 9244 char_u fword[MAXWLEN]; /* copy of the bad word, case-folded */ 9245 char_u tword[MAXWLEN]; /* good word collected so far */ 9246 trystate_T stack[MAXWLEN]; 9247 char_u preword[MAXWLEN * 3]; /* word found with proper case; 9248 * concatanation of prefix compound 9249 * words and split word. NUL terminated 9250 * when going deeper but not when coming 9251 * back. */ 9252 char_u compflags[MAXWLEN]; /* compound flags, one for each word */ 9253 trystate_T *sp; 9254 int newscore; 9255 langp_T *lp; 9256 char_u *byts, *fbyts, *pbyts; 9257 idx_T *idxs, *fidxs, *pidxs; 9258 int depth; 9259 int c, c2, c3; 9260 int n; 9261 int flags; 9262 garray_T *gap; 9263 idx_T arridx; 9264 int len; 9265 char_u *p; 9266 fromto_T *ftp; 9267 int fl = 0, tl; 9268 int repextra = 0; /* extra bytes in fword[] from REP item */ 9269 slang_T *slang; 9270 int fword_ends; 9271 int lpi; 9272 int maysplit; 9273 int goodword_ends; 9274 9275 /* We make a copy of the case-folded bad word, so that we can modify it 9276 * to find matches (esp. REP items). Append some more text, changing 9277 * chars after the bad word may help. */ 9278 STRCPY(fword, su->su_fbadword); 9279 n = STRLEN(fword); 9280 p = su->su_badptr + su->su_badlen; 9281 (void)spell_casefold(p, STRLEN(p), fword + n, MAXWLEN - n); 9282 9283 for (lpi = 0; lpi < curbuf->b_langp.ga_len; ++lpi) 9284 { 9285 lp = LANGP_ENTRY(curbuf->b_langp, lpi); 9286 slang = lp->lp_slang; 9287 9288 /* If reloading a spell file fails it's still in the list but 9289 * everything has been cleared. */ 9290 if (slang->sl_fbyts == NULL) 9291 continue; 9292 9293 /* 9294 * Go through the whole case-fold tree, try changes at each node. 9295 * "tword[]" contains the word collected from nodes in the tree. 9296 * "fword[]" the word we are trying to match with (initially the bad 9297 * word). 9298 */ 9299 depth = 0; 9300 sp = &stack[0]; 9301 vim_memset(sp, 0, sizeof(trystate_T)); 9302 sp->ts_curi = 1; 9303 9304 /* 9305 * When there are postponed prefixes we need to use these first. At 9306 * the end of the prefix we continue in the case-fold tree. 9307 */ 9308 fbyts = slang->sl_fbyts; 9309 fidxs = slang->sl_fidxs; 9310 pbyts = slang->sl_pbyts; 9311 pidxs = slang->sl_pidxs; 9312 if (pbyts != NULL) 9313 { 9314 byts = pbyts; 9315 idxs = pidxs; 9316 sp->ts_prefixdepth = PFD_PREFIXTREE; 9317 sp->ts_state = STATE_NOPREFIX; /* try without prefix first */ 9318 } 9319 else 9320 { 9321 byts = fbyts; 9322 idxs = fidxs; 9323 sp->ts_prefixdepth = PFD_NOPREFIX; 9324 sp->ts_state = STATE_START; 9325 } 9326 9327 /* 9328 * Loop to find all suggestions. At each round we either: 9329 * - For the current state try one operation, advance "ts_curi", 9330 * increase "depth". 9331 * - When a state is done go to the next, set "ts_state". 9332 * - When all states are tried decrease "depth". 9333 */ 9334 while (depth >= 0 && !got_int) 9335 { 9336 sp = &stack[depth]; 9337 switch (sp->ts_state) 9338 { 9339 case STATE_START: 9340 case STATE_NOPREFIX: 9341 /* 9342 * Start of node: Deal with NUL bytes, which means 9343 * tword[] may end here. 9344 */ 9345 arridx = sp->ts_arridx; /* current node in the tree */ 9346 len = byts[arridx]; /* bytes in this node */ 9347 arridx += sp->ts_curi; /* index of current byte */ 9348 9349 if (sp->ts_prefixdepth == PFD_PREFIXTREE) 9350 { 9351 /* Skip over the NUL bytes, we use them later. */ 9352 for (n = 0; n < len && byts[arridx + n] == 0; ++n) 9353 ; 9354 sp->ts_curi += n; 9355 9356 /* Always past NUL bytes now. */ 9357 n = (int)sp->ts_state; 9358 sp->ts_state = STATE_ENDNUL; 9359 sp->ts_save_badflags = su->su_badflags; 9360 9361 /* At end of a prefix or at start of prefixtree: check for 9362 * following word. */ 9363 if (byts[arridx] == 0 || n == (int)STATE_NOPREFIX) 9364 { 9365 /* Set su->su_badflags to the caps type at this 9366 * position. Use the caps type until here for the 9367 * prefix itself. */ 9368 #ifdef FEAT_MBYTE 9369 if (has_mbyte) 9370 n = nofold_len(fword, sp->ts_fidx, su->su_badptr); 9371 else 9372 #endif 9373 n = sp->ts_fidx; 9374 flags = badword_captype(su->su_badptr, 9375 su->su_badptr + n); 9376 su->su_badflags = badword_captype(su->su_badptr + n, 9377 su->su_badptr + su->su_badlen); 9378 ++depth; 9379 stack[depth] = stack[depth - 1]; 9380 sp = &stack[depth]; 9381 sp->ts_prefixdepth = depth - 1; 9382 byts = fbyts; 9383 idxs = fidxs; 9384 sp->ts_state = STATE_START; 9385 sp->ts_curi = 1; /* start just after length byte */ 9386 sp->ts_arridx = 0; 9387 9388 /* Move the prefix to preword[] with the right case 9389 * and make find_keepcap_word() works. */ 9390 tword[sp->ts_twordlen] = NUL; 9391 make_case_word(tword + sp->ts_splitoff, 9392 preword + sp->ts_prewordlen, 9393 flags); 9394 sp->ts_prewordlen = STRLEN(preword); 9395 sp->ts_splitoff = sp->ts_twordlen; 9396 } 9397 break; 9398 } 9399 9400 if (sp->ts_curi > len || byts[arridx] != 0) 9401 { 9402 /* Past bytes in node and/or past NUL bytes. */ 9403 sp->ts_state = STATE_ENDNUL; 9404 sp->ts_save_badflags = su->su_badflags; 9405 break; 9406 } 9407 9408 /* 9409 * End of word in tree. 9410 */ 9411 ++sp->ts_curi; /* eat one NUL byte */ 9412 9413 flags = (int)idxs[arridx]; 9414 fword_ends = (fword[sp->ts_fidx] == NUL 9415 || !spell_iswordp(fword + sp->ts_fidx, curbuf)); 9416 tword[sp->ts_twordlen] = NUL; 9417 9418 if (sp->ts_prefixdepth <= PFD_NOTSPECIAL 9419 && (sp->ts_flags & TSF_PREFIXOK) == 0) 9420 { 9421 /* There was a prefix before the word. Check that the 9422 * prefix can be used with this word. */ 9423 /* Count the length of the NULs in the prefix. If there 9424 * are none this must be the first try without a prefix. 9425 */ 9426 n = stack[sp->ts_prefixdepth].ts_arridx; 9427 len = pbyts[n++]; 9428 for (c = 0; c < len && pbyts[n + c] == 0; ++c) 9429 ; 9430 if (c > 0) 9431 { 9432 c = valid_word_prefix(c, n, flags, 9433 tword + sp->ts_splitoff, slang, FALSE); 9434 if (c == 0) 9435 break; 9436 9437 /* Use the WF_RARE flag for a rare prefix. */ 9438 if (c & WF_RAREPFX) 9439 flags |= WF_RARE; 9440 9441 /* Tricky: when checking for both prefix and 9442 * compounding we run into the prefix flag first. 9443 * Remember that it's OK, so that we accept the prefix 9444 * when arriving at a compound flag. */ 9445 sp->ts_flags |= TSF_PREFIXOK; 9446 } 9447 } 9448 9449 /* Check NEEDCOMPOUND: can't use word without compounding. Do 9450 * try appending another compound word below. */ 9451 if (sp->ts_complen == sp->ts_compsplit && fword_ends 9452 && (flags & WF_NEEDCOMP)) 9453 goodword_ends = FALSE; 9454 else 9455 goodword_ends = TRUE; 9456 9457 if (sp->ts_complen > sp->ts_compsplit) 9458 { 9459 if (slang->sl_nobreak) 9460 { 9461 /* There was a word before this word. When there was 9462 * no change in this word (it was correct) add the 9463 * first word as a suggestion. If this word was 9464 * corrected too, we need to check if a correct word 9465 * follows. */ 9466 if (sp->ts_fidx - sp->ts_splitfidx 9467 == sp->ts_twordlen - sp->ts_splitoff 9468 && STRNCMP(fword + sp->ts_splitfidx, 9469 tword + sp->ts_splitoff, 9470 sp->ts_fidx - sp->ts_splitfidx) == 0) 9471 { 9472 preword[sp->ts_prewordlen] = NUL; 9473 add_suggestion(su, &su->su_ga, preword, 9474 sp->ts_splitfidx - repextra, 9475 sp->ts_score, 0, FALSE, 9476 lp->lp_sallang); 9477 break; 9478 } 9479 } 9480 else 9481 { 9482 /* There was a compound word before this word. If 9483 * this word does not support compounding then give up 9484 * (splitting is tried for the word without compound 9485 * flag). */ 9486 if (((unsigned)flags >> 24) == 0 9487 || sp->ts_twordlen - sp->ts_splitoff 9488 < slang->sl_compminlen) 9489 break; 9490 #ifdef FEAT_MBYTE 9491 /* For multi-byte chars check character length against 9492 * COMPOUNDMIN. */ 9493 if (has_mbyte 9494 && slang->sl_compminlen > 0 9495 && mb_charlen(tword + sp->ts_splitoff) 9496 < slang->sl_compminlen) 9497 break; 9498 #endif 9499 9500 compflags[sp->ts_complen] = ((unsigned)flags >> 24); 9501 compflags[sp->ts_complen + 1] = NUL; 9502 vim_strncpy(preword + sp->ts_prewordlen, 9503 tword + sp->ts_splitoff, 9504 sp->ts_twordlen - sp->ts_splitoff); 9505 p = preword; 9506 while (*skiptowhite(p) != NUL) 9507 p = skipwhite(skiptowhite(p)); 9508 if (fword_ends && !can_compound(slang, p, 9509 compflags + sp->ts_compsplit)) 9510 break; 9511 9512 /* Get pointer to last char of previous word. */ 9513 p = preword + sp->ts_prewordlen; 9514 mb_ptr_back(preword, p); 9515 } 9516 } 9517 else 9518 p = NULL; 9519 9520 /* 9521 * Form the word with proper case in preword. 9522 * If there is a word from a previous split, append. 9523 */ 9524 if (flags & WF_KEEPCAP) 9525 /* Must find the word in the keep-case tree. */ 9526 find_keepcap_word(slang, tword + sp->ts_splitoff, 9527 preword + sp->ts_prewordlen); 9528 else 9529 { 9530 /* Include badflags: if the badword is onecap or allcap 9531 * use that for the goodword too. But if the badword is 9532 * allcap and it's only one char long use onecap. */ 9533 c = su->su_badflags; 9534 if ((c & WF_ALLCAP) 9535 #ifdef FEAT_MBYTE 9536 && su->su_badlen == (*mb_ptr2len)(su->su_badptr) 9537 #else 9538 && su->su_badlen == 1 9539 #endif 9540 ) 9541 c = WF_ONECAP; 9542 c |= flags; 9543 9544 /* When appending a compound word after a word character 9545 * don't use Onecap. */ 9546 if (p != NULL && spell_iswordp_nmw(p)) 9547 c &= ~WF_ONECAP; 9548 make_case_word(tword + sp->ts_splitoff, 9549 preword + sp->ts_prewordlen, c); 9550 } 9551 9552 /* Don't use a banned word. It may appear again as a good 9553 * word, thus remember it. */ 9554 if (flags & WF_BANNED) 9555 { 9556 add_banned(su, preword + sp->ts_prewordlen); 9557 break; 9558 } 9559 if ((sp->ts_complen == sp->ts_compsplit 9560 && was_banned(su, preword + sp->ts_prewordlen)) 9561 || was_banned(su, preword)) 9562 { 9563 if (slang->sl_compprog == NULL) 9564 break; 9565 /* the word so far was banned but we may try compounding */ 9566 goodword_ends = FALSE; 9567 } 9568 9569 newscore = 0; 9570 if ((flags & WF_REGION) 9571 && (((unsigned)flags >> 16) & lp->lp_region) == 0) 9572 newscore += SCORE_REGION; 9573 if (flags & WF_RARE) 9574 newscore += SCORE_RARE; 9575 9576 if (!spell_valid_case(su->su_badflags, 9577 captype(preword + sp->ts_prewordlen, NULL))) 9578 newscore += SCORE_ICASE; 9579 9580 maysplit = TRUE; 9581 if (fword_ends && goodword_ends 9582 && sp->ts_fidx >= sp->ts_fidxtry) 9583 { 9584 /* The badword also ends: add suggestions. Give a penalty 9585 * when changing non-word char to word char, e.g., "thes," 9586 * -> "these". */ 9587 p = fword + sp->ts_fidx; 9588 #ifdef FEAT_MBYTE 9589 if (has_mbyte) 9590 mb_ptr_back(fword, p); 9591 else 9592 #endif 9593 --p; 9594 if (!spell_iswordp(p, curbuf)) 9595 { 9596 p = preword + STRLEN(preword); 9597 #ifdef FEAT_MBYTE 9598 if (has_mbyte) 9599 mb_ptr_back(preword, p); 9600 else 9601 #endif 9602 --p; 9603 if (spell_iswordp(p, curbuf)) 9604 newscore += SCORE_NONWORD; 9605 } 9606 9607 add_suggestion(su, &su->su_ga, preword, 9608 sp->ts_fidx - repextra, 9609 sp->ts_score + newscore, 0, FALSE, 9610 lp->lp_sallang); 9611 9612 /* When the bad word doesn't end yet, try changing the 9613 * next word. E.g., find suggestions for "the the" where 9614 * the second "the" is different. It's done like a split. 9615 */ 9616 if (sp->ts_fidx - repextra >= su->su_badlen) 9617 maysplit = FALSE; 9618 } 9619 9620 if (maysplit 9621 && (sp->ts_fidx >= sp->ts_fidxtry || fword_ends) 9622 #ifdef FEAT_MBYTE 9623 /* Don't split halfway a character. */ 9624 && (!has_mbyte || sp->ts_tcharlen == 0) 9625 #endif 9626 ) 9627 { 9628 int try_compound; 9629 9630 /* Get here in two situations: 9631 * 1. The word in the tree ends but the badword continues: 9632 * If the word allows compounding try that. Otherwise 9633 * try a split by inserting a space. For both check 9634 * that a valid words starts at fword[sp->ts_fidx]. 9635 * For NOBREAK do like compounding to be able to check 9636 * if the next word is valid. 9637 * 2. The badword does end, but it was due to a change 9638 * (e.g., a swap). No need to split, but do check that 9639 * the following word is valid. 9640 */ 9641 try_compound = FALSE; 9642 if ((!fword_ends || !goodword_ends) 9643 && slang->sl_compprog != NULL 9644 && ((unsigned)flags >> 24) != 0 9645 && sp->ts_twordlen - sp->ts_splitoff 9646 >= slang->sl_compminlen 9647 #ifdef FEAT_MBYTE 9648 && (!has_mbyte 9649 || slang->sl_compminlen == 0 9650 || mb_charlen(tword + sp->ts_splitoff) 9651 >= slang->sl_compminlen) 9652 #endif 9653 && (slang->sl_compsylmax < MAXWLEN 9654 || sp->ts_complen + 1 - sp->ts_compsplit 9655 < slang->sl_compmax) 9656 && (byte_in_str(sp->ts_complen == sp->ts_compsplit 9657 ? slang->sl_compstartflags 9658 : slang->sl_compallflags, 9659 ((unsigned)flags >> 24)))) 9660 { 9661 try_compound = TRUE; 9662 compflags[sp->ts_complen] = ((unsigned)flags >> 24); 9663 compflags[sp->ts_complen + 1] = NUL; 9664 } 9665 9666 /* For NOBREAK we never try splitting, it won't make any 9667 * word valid. */ 9668 if (slang->sl_nobreak) 9669 try_compound = TRUE; 9670 9671 /* If we could add a compound word, and it's also possible 9672 * to split at this point, do the split first and set 9673 * TSF_DIDSPLIT to avoid doing it again. */ 9674 else if (!fword_ends 9675 && try_compound 9676 && (sp->ts_flags & TSF_DIDSPLIT) == 0) 9677 { 9678 try_compound = FALSE; 9679 sp->ts_flags |= TSF_DIDSPLIT; 9680 --sp->ts_curi; /* do the same NUL again */ 9681 compflags[sp->ts_complen] = NUL; 9682 } 9683 else 9684 sp->ts_flags &= ~TSF_DIDSPLIT; 9685 9686 if (!try_compound && (!fword_ends || !goodword_ends)) 9687 { 9688 /* If we're going to split need to check that the 9689 * words so far are valid for compounding. If there 9690 * is only one word it must not have the NEEDCOMPOUND 9691 * flag. */ 9692 if (sp->ts_complen == sp->ts_compsplit 9693 && (flags & WF_NEEDCOMP)) 9694 break; 9695 p = preword; 9696 while (*skiptowhite(p) != NUL) 9697 p = skipwhite(skiptowhite(p)); 9698 if (sp->ts_complen > sp->ts_compsplit 9699 && !can_compound(slang, p, 9700 compflags + sp->ts_compsplit)) 9701 break; 9702 newscore += SCORE_SPLIT; 9703 } 9704 9705 if (try_deeper(su, stack, depth, newscore)) 9706 { 9707 /* Save things to be restored at STATE_SPLITUNDO. */ 9708 sp->ts_save_badflags = su->su_badflags; 9709 sp->ts_state = STATE_SPLITUNDO; 9710 9711 ++depth; 9712 sp = &stack[depth]; 9713 9714 /* Append a space to preword when splitting. */ 9715 if (!try_compound && !fword_ends) 9716 STRCAT(preword, " "); 9717 sp->ts_prewordlen = STRLEN(preword); 9718 sp->ts_splitoff = sp->ts_twordlen; 9719 sp->ts_splitfidx = sp->ts_fidx; 9720 9721 /* If the badword has a non-word character at this 9722 * position skip it. That means replacing the 9723 * non-word character with a space. Always skip a 9724 * character when the word ends. But only when the 9725 * good word can end. */ 9726 if (((!try_compound 9727 && !spell_iswordp_nmw(fword + sp->ts_fidx)) 9728 || fword_ends) 9729 && goodword_ends) 9730 { 9731 int l; 9732 9733 #ifdef FEAT_MBYTE 9734 if (has_mbyte) 9735 l = MB_BYTE2LEN(fword[sp->ts_fidx]); 9736 else 9737 #endif 9738 l = 1; 9739 if (fword_ends) 9740 { 9741 /* Copy the skipped character to preword. */ 9742 mch_memmove(preword + sp->ts_prewordlen, 9743 fword + sp->ts_fidx, l); 9744 sp->ts_prewordlen += l; 9745 preword[sp->ts_prewordlen] = NUL; 9746 } 9747 else 9748 sp->ts_score -= SCORE_SPLIT - SCORE_SUBST; 9749 sp->ts_fidx += l; 9750 } 9751 9752 /* When compounding include compound flag in 9753 * compflags[] (already set above). When splitting we 9754 * may start compounding over again. */ 9755 if (try_compound) 9756 ++sp->ts_complen; 9757 else 9758 sp->ts_compsplit = sp->ts_complen; 9759 sp->ts_prefixdepth = PFD_NOPREFIX; 9760 9761 /* set su->su_badflags to the caps type at this 9762 * position */ 9763 #ifdef FEAT_MBYTE 9764 if (has_mbyte) 9765 n = nofold_len(fword, sp->ts_fidx, su->su_badptr); 9766 else 9767 #endif 9768 n = sp->ts_fidx; 9769 su->su_badflags = badword_captype(su->su_badptr + n, 9770 su->su_badptr + su->su_badlen); 9771 9772 /* Restart at top of the tree. */ 9773 sp->ts_arridx = 0; 9774 9775 /* If there are postponed prefixes, try these too. */ 9776 if (pbyts != NULL) 9777 { 9778 byts = pbyts; 9779 idxs = pidxs; 9780 sp->ts_prefixdepth = PFD_PREFIXTREE; 9781 sp->ts_state = STATE_NOPREFIX; 9782 } 9783 } 9784 } 9785 break; 9786 9787 case STATE_SPLITUNDO: 9788 /* Undo the changes done for word split or compound word. */ 9789 su->su_badflags = sp->ts_save_badflags; 9790 9791 /* Continue looking for NUL bytes. */ 9792 sp->ts_state = STATE_START; 9793 9794 /* In case we went into the prefix tree. */ 9795 byts = fbyts; 9796 idxs = fidxs; 9797 break; 9798 9799 case STATE_ENDNUL: 9800 /* Past the NUL bytes in the node. */ 9801 su->su_badflags = sp->ts_save_badflags; 9802 if (fword[sp->ts_fidx] == NUL 9803 #ifdef FEAT_MBYTE 9804 && sp->ts_tcharlen == 0 9805 #endif 9806 ) 9807 { 9808 /* The badword ends, can't use the bytes in this node. */ 9809 sp->ts_state = STATE_DEL; 9810 break; 9811 } 9812 sp->ts_state = STATE_PLAIN; 9813 /*FALLTHROUGH*/ 9814 9815 case STATE_PLAIN: 9816 /* 9817 * Go over all possible bytes at this node, add each to 9818 * tword[] and use child node. "ts_curi" is the index. 9819 */ 9820 arridx = sp->ts_arridx; 9821 if (sp->ts_curi > byts[arridx]) 9822 { 9823 /* Done all bytes at this node, do next state. When still 9824 * at already changed bytes skip the other tricks. */ 9825 if (sp->ts_fidx >= sp->ts_fidxtry) 9826 sp->ts_state = STATE_DEL; 9827 else 9828 sp->ts_state = STATE_FINAL; 9829 } 9830 else 9831 { 9832 arridx += sp->ts_curi++; 9833 c = byts[arridx]; 9834 9835 /* Normal byte, go one level deeper. If it's not equal to 9836 * the byte in the bad word adjust the score. But don't 9837 * even try when the byte was already changed. */ 9838 if (c == fword[sp->ts_fidx] 9839 #ifdef FEAT_MBYTE 9840 || (sp->ts_tcharlen > 0 9841 && sp->ts_isdiff != DIFF_NONE) 9842 #endif 9843 ) 9844 newscore = 0; 9845 else 9846 newscore = SCORE_SUBST; 9847 if ((newscore == 0 || sp->ts_fidx >= sp->ts_fidxtry) 9848 && try_deeper(su, stack, depth, newscore)) 9849 { 9850 ++depth; 9851 sp = &stack[depth]; 9852 ++sp->ts_fidx; 9853 tword[sp->ts_twordlen++] = c; 9854 sp->ts_arridx = idxs[arridx]; 9855 #ifdef FEAT_MBYTE 9856 if (newscore == SCORE_SUBST) 9857 sp->ts_isdiff = DIFF_YES; 9858 if (has_mbyte) 9859 { 9860 /* Multi-byte characters are a bit complicated to 9861 * handle: They differ when any of the bytes 9862 * differ and then their length may also differ. */ 9863 if (sp->ts_tcharlen == 0) 9864 { 9865 /* First byte. */ 9866 sp->ts_tcharidx = 0; 9867 sp->ts_tcharlen = MB_BYTE2LEN(c); 9868 sp->ts_fcharstart = sp->ts_fidx - 1; 9869 sp->ts_isdiff = (newscore != 0) 9870 ? DIFF_YES : DIFF_NONE; 9871 } 9872 else if (sp->ts_isdiff == DIFF_INSERT) 9873 /* When inserting trail bytes don't advance in 9874 * the bad word. */ 9875 --sp->ts_fidx; 9876 if (++sp->ts_tcharidx == sp->ts_tcharlen) 9877 { 9878 /* Last byte of character. */ 9879 if (sp->ts_isdiff == DIFF_YES) 9880 { 9881 /* Correct ts_fidx for the byte length of 9882 * the character (we didn't check that 9883 * before). */ 9884 sp->ts_fidx = sp->ts_fcharstart 9885 + MB_BYTE2LEN( 9886 fword[sp->ts_fcharstart]); 9887 9888 /* For changing a composing character 9889 * adjust the score from SCORE_SUBST to 9890 * SCORE_SUBCOMP. */ 9891 if (enc_utf8 9892 && utf_iscomposing( 9893 mb_ptr2char(tword 9894 + sp->ts_twordlen 9895 - sp->ts_tcharlen)) 9896 && utf_iscomposing( 9897 mb_ptr2char(fword 9898 + sp->ts_fcharstart))) 9899 sp->ts_score -= 9900 SCORE_SUBST - SCORE_SUBCOMP; 9901 9902 /* For a similar character adjust score 9903 * from SCORE_SUBST to SCORE_SIMILAR. */ 9904 else if (slang->sl_has_map 9905 && similar_chars(slang, 9906 mb_ptr2char(tword 9907 + sp->ts_twordlen 9908 - sp->ts_tcharlen), 9909 mb_ptr2char(fword 9910 + sp->ts_fcharstart))) 9911 sp->ts_score -= 9912 SCORE_SUBST - SCORE_SIMILAR; 9913 } 9914 else if (sp->ts_isdiff == DIFF_INSERT 9915 && sp->ts_twordlen > sp->ts_tcharlen) 9916 { 9917 p = tword + sp->ts_twordlen 9918 - sp->ts_tcharlen; 9919 c = mb_ptr2char(p); 9920 if (enc_utf8 && utf_iscomposing(c)) 9921 { 9922 /* Inserting a composing char doesn't 9923 * count that much. */ 9924 sp->ts_score -= SCORE_INS 9925 - SCORE_INSCOMP; 9926 } 9927 else 9928 { 9929 /* If the previous character was the 9930 * same, thus doubling a character, 9931 * give a bonus to the score. */ 9932 mb_ptr_back(tword, p); 9933 if (c == mb_ptr2char(p)) 9934 sp->ts_score -= SCORE_INS 9935 - SCORE_INSDUP; 9936 } 9937 } 9938 9939 /* Starting a new char, reset the length. */ 9940 sp->ts_tcharlen = 0; 9941 } 9942 } 9943 else 9944 #endif 9945 { 9946 /* If we found a similar char adjust the score. 9947 * We do this after calling try_deeper() because 9948 * it's slow. */ 9949 if (newscore != 0 9950 && slang->sl_has_map 9951 && similar_chars(slang, 9952 c, fword[sp->ts_fidx - 1])) 9953 sp->ts_score -= SCORE_SUBST - SCORE_SIMILAR; 9954 } 9955 } 9956 } 9957 break; 9958 9959 case STATE_DEL: 9960 #ifdef FEAT_MBYTE 9961 /* When past the first byte of a multi-byte char don't try 9962 * delete/insert/swap a character. */ 9963 if (has_mbyte && sp->ts_tcharlen > 0) 9964 { 9965 sp->ts_state = STATE_FINAL; 9966 break; 9967 } 9968 #endif 9969 /* 9970 * Try skipping one character in the bad word (delete it). 9971 */ 9972 sp->ts_state = STATE_INS; 9973 sp->ts_curi = 1; 9974 if (fword[sp->ts_fidx] != NUL 9975 && try_deeper(su, stack, depth, SCORE_DEL)) 9976 { 9977 ++depth; 9978 9979 /* Advance over the character in fword[]. Give a bonus to 9980 * the score if the same character is following "nn" -> 9981 * "n". */ 9982 #ifdef FEAT_MBYTE 9983 if (has_mbyte) 9984 { 9985 c = mb_ptr2char(fword + sp->ts_fidx); 9986 stack[depth].ts_fidx += MB_BYTE2LEN(fword[sp->ts_fidx]); 9987 if (enc_utf8 && utf_iscomposing(c)) 9988 stack[depth].ts_score -= SCORE_DEL - SCORE_DELCOMP; 9989 else if (c == mb_ptr2char(fword + stack[depth].ts_fidx)) 9990 stack[depth].ts_score -= SCORE_DEL - SCORE_DELDUP; 9991 } 9992 else 9993 #endif 9994 { 9995 ++stack[depth].ts_fidx; 9996 if (fword[sp->ts_fidx] == fword[sp->ts_fidx + 1]) 9997 stack[depth].ts_score -= SCORE_DEL - SCORE_DELDUP; 9998 } 9999 break; 10000 } 10001 /*FALLTHROUGH*/ 10002 10003 case STATE_INS: 10004 /* Insert one byte. Do this for each possible byte at this 10005 * node. */ 10006 n = sp->ts_arridx; 10007 if (sp->ts_curi > byts[n]) 10008 { 10009 /* Done all bytes at this node, do next state. */ 10010 sp->ts_state = STATE_SWAP; 10011 } 10012 else 10013 { 10014 /* Do one more byte at this node. Skip NUL bytes. */ 10015 n += sp->ts_curi++; 10016 c = byts[n]; 10017 if (c != 0 && try_deeper(su, stack, depth, SCORE_INS)) 10018 { 10019 ++depth; 10020 sp = &stack[depth]; 10021 tword[sp->ts_twordlen++] = c; 10022 sp->ts_arridx = idxs[n]; 10023 #ifdef FEAT_MBYTE 10024 if (has_mbyte) 10025 { 10026 fl = MB_BYTE2LEN(c); 10027 if (fl > 1) 10028 { 10029 /* There are following bytes for the same 10030 * character. We must find all bytes before 10031 * trying delete/insert/swap/etc. */ 10032 sp->ts_tcharlen = fl; 10033 sp->ts_tcharidx = 1; 10034 sp->ts_isdiff = DIFF_INSERT; 10035 } 10036 } 10037 else 10038 fl = 1; 10039 if (fl == 1) 10040 #endif 10041 { 10042 /* If the previous character was the same, thus 10043 * doubling a character, give a bonus to the 10044 * score. */ 10045 if (sp->ts_twordlen >= 2 10046 && tword[sp->ts_twordlen - 2] == c) 10047 sp->ts_score -= SCORE_INS - SCORE_INSDUP; 10048 } 10049 } 10050 } 10051 break; 10052 10053 case STATE_SWAP: 10054 /* 10055 * Swap two bytes in the bad word: "12" -> "21". 10056 * We change "fword" here, it's changed back afterwards. 10057 */ 10058 p = fword + sp->ts_fidx; 10059 c = *p; 10060 if (c == NUL) 10061 { 10062 /* End of word, can't swap or replace. */ 10063 sp->ts_state = STATE_FINAL; 10064 break; 10065 } 10066 10067 /* Don't swap if the first character is not a word character. 10068 * SWAP3 etc. also don't make sense then. */ 10069 if (!spell_iswordp(p, curbuf)) 10070 { 10071 sp->ts_state = STATE_REP_INI; 10072 break; 10073 } 10074 10075 #ifdef FEAT_MBYTE 10076 if (has_mbyte) 10077 { 10078 n = mb_cptr2len(p); 10079 c = mb_ptr2char(p); 10080 if (!spell_iswordp(p + n, curbuf)) 10081 c2 = c; /* don't swap non-word char */ 10082 else 10083 c2 = mb_ptr2char(p + n); 10084 } 10085 else 10086 #endif 10087 { 10088 if (!spell_iswordp(p + 1, curbuf)) 10089 c2 = c; /* don't swap non-word char */ 10090 else 10091 c2 = p[1]; 10092 } 10093 10094 /* When characters are identical, swap won't do anything. 10095 * Also get here if the second char is not a word character. */ 10096 if (c == c2) 10097 { 10098 sp->ts_state = STATE_SWAP3; 10099 break; 10100 } 10101 if (c2 != NUL && try_deeper(su, stack, depth, SCORE_SWAP)) 10102 { 10103 sp->ts_state = STATE_UNSWAP; 10104 ++depth; 10105 #ifdef FEAT_MBYTE 10106 if (has_mbyte) 10107 { 10108 fl = mb_char2len(c2); 10109 mch_memmove(p, p + n, fl); 10110 mb_char2bytes(c, p + fl); 10111 stack[depth].ts_fidxtry = sp->ts_fidx + n + fl; 10112 } 10113 else 10114 #endif 10115 { 10116 p[0] = c2; 10117 p[1] = c; 10118 stack[depth].ts_fidxtry = sp->ts_fidx + 2; 10119 } 10120 } 10121 else 10122 /* If this swap doesn't work then SWAP3 won't either. */ 10123 sp->ts_state = STATE_REP_INI; 10124 break; 10125 10126 case STATE_UNSWAP: 10127 /* Undo the STATE_SWAP swap: "21" -> "12". */ 10128 p = fword + sp->ts_fidx; 10129 #ifdef FEAT_MBYTE 10130 if (has_mbyte) 10131 { 10132 n = MB_BYTE2LEN(*p); 10133 c = mb_ptr2char(p + n); 10134 mch_memmove(p + MB_BYTE2LEN(p[n]), p, n); 10135 mb_char2bytes(c, p); 10136 } 10137 else 10138 #endif 10139 { 10140 c = *p; 10141 *p = p[1]; 10142 p[1] = c; 10143 } 10144 /*FALLTHROUGH*/ 10145 10146 case STATE_SWAP3: 10147 /* Swap two bytes, skipping one: "123" -> "321". We change 10148 * "fword" here, it's changed back afterwards. */ 10149 p = fword + sp->ts_fidx; 10150 #ifdef FEAT_MBYTE 10151 if (has_mbyte) 10152 { 10153 n = mb_cptr2len(p); 10154 c = mb_ptr2char(p); 10155 fl = mb_cptr2len(p + n); 10156 c2 = mb_ptr2char(p + n); 10157 if (!spell_iswordp(p + n + fl, curbuf)) 10158 c3 = c; /* don't swap non-word char */ 10159 else 10160 c3 = mb_ptr2char(p + n + fl); 10161 } 10162 else 10163 #endif 10164 { 10165 c = *p; 10166 c2 = p[1]; 10167 if (!spell_iswordp(p + 2, curbuf)) 10168 c3 = c; /* don't swap non-word char */ 10169 else 10170 c3 = p[2]; 10171 } 10172 10173 /* When characters are identical: "121" then SWAP3 result is 10174 * identical, ROT3L result is same as SWAP: "211", ROT3L 10175 * result is same as SWAP on next char: "112". Thus skip all 10176 * swapping. Also skip when c3 is NUL. 10177 * Also get here when the third character is not a word 10178 * character. Second character may any char: "a.b" -> "b.a" */ 10179 if (c == c3 || c3 == NUL) 10180 { 10181 sp->ts_state = STATE_REP_INI; 10182 break; 10183 } 10184 if (try_deeper(su, stack, depth, SCORE_SWAP3)) 10185 { 10186 sp->ts_state = STATE_UNSWAP3; 10187 ++depth; 10188 #ifdef FEAT_MBYTE 10189 if (has_mbyte) 10190 { 10191 tl = mb_char2len(c3); 10192 mch_memmove(p, p + n + fl, tl); 10193 mb_char2bytes(c2, p + tl); 10194 mb_char2bytes(c, p + fl + tl); 10195 stack[depth].ts_fidxtry = sp->ts_fidx + n + fl + tl; 10196 } 10197 else 10198 #endif 10199 { 10200 p[0] = p[2]; 10201 p[2] = c; 10202 stack[depth].ts_fidxtry = sp->ts_fidx + 3; 10203 } 10204 } 10205 else 10206 sp->ts_state = STATE_REP_INI; 10207 break; 10208 10209 case STATE_UNSWAP3: 10210 /* Undo STATE_SWAP3: "321" -> "123" */ 10211 p = fword + sp->ts_fidx; 10212 #ifdef FEAT_MBYTE 10213 if (has_mbyte) 10214 { 10215 n = MB_BYTE2LEN(*p); 10216 c2 = mb_ptr2char(p + n); 10217 fl = MB_BYTE2LEN(p[n]); 10218 c = mb_ptr2char(p + n + fl); 10219 tl = MB_BYTE2LEN(p[n + fl]); 10220 mch_memmove(p + fl + tl, p, n); 10221 mb_char2bytes(c, p); 10222 mb_char2bytes(c2, p + tl); 10223 p = p + tl; 10224 } 10225 else 10226 #endif 10227 { 10228 c = *p; 10229 *p = p[2]; 10230 p[2] = c; 10231 ++p; 10232 } 10233 10234 if (!spell_iswordp(p, curbuf)) 10235 { 10236 /* Middle char is not a word char, skip the rotate. 10237 * First and third char were already checked at swap 10238 * and swap3. */ 10239 sp->ts_state = STATE_REP_INI; 10240 break; 10241 } 10242 10243 /* Rotate three characters left: "123" -> "231". We change 10244 * "fword" here, it's changed back afterwards. */ 10245 if (try_deeper(su, stack, depth, SCORE_SWAP3)) 10246 { 10247 sp->ts_state = STATE_UNROT3L; 10248 ++depth; 10249 p = fword + sp->ts_fidx; 10250 #ifdef FEAT_MBYTE 10251 if (has_mbyte) 10252 { 10253 n = mb_cptr2len(p); 10254 c = mb_ptr2char(p); 10255 fl = mb_cptr2len(p + n); 10256 fl += mb_cptr2len(p + n + fl); 10257 mch_memmove(p, p + n, fl); 10258 mb_char2bytes(c, p + fl); 10259 stack[depth].ts_fidxtry = sp->ts_fidx + n + fl; 10260 } 10261 else 10262 #endif 10263 { 10264 c = *p; 10265 *p = p[1]; 10266 p[1] = p[2]; 10267 p[2] = c; 10268 stack[depth].ts_fidxtry = sp->ts_fidx + 3; 10269 } 10270 } 10271 else 10272 sp->ts_state = STATE_REP_INI; 10273 break; 10274 10275 case STATE_UNROT3L: 10276 /* Undo ROT3L: "231" -> "123" */ 10277 p = fword + sp->ts_fidx; 10278 #ifdef FEAT_MBYTE 10279 if (has_mbyte) 10280 { 10281 n = MB_BYTE2LEN(*p); 10282 n += MB_BYTE2LEN(p[n]); 10283 c = mb_ptr2char(p + n); 10284 tl = MB_BYTE2LEN(p[n]); 10285 mch_memmove(p + tl, p, n); 10286 mb_char2bytes(c, p); 10287 } 10288 else 10289 #endif 10290 { 10291 c = p[2]; 10292 p[2] = p[1]; 10293 p[1] = *p; 10294 *p = c; 10295 } 10296 10297 /* Rotate three bytes right: "123" -> "312". We change 10298 * "fword" here, it's changed back afterwards. */ 10299 if (try_deeper(su, stack, depth, SCORE_SWAP3)) 10300 { 10301 sp->ts_state = STATE_UNROT3R; 10302 ++depth; 10303 p = fword + sp->ts_fidx; 10304 #ifdef FEAT_MBYTE 10305 if (has_mbyte) 10306 { 10307 n = mb_cptr2len(p); 10308 n += mb_cptr2len(p + n); 10309 c = mb_ptr2char(p + n); 10310 tl = mb_cptr2len(p + n); 10311 mch_memmove(p + tl, p, n); 10312 mb_char2bytes(c, p); 10313 stack[depth].ts_fidxtry = sp->ts_fidx + n + tl; 10314 } 10315 else 10316 #endif 10317 { 10318 c = p[2]; 10319 p[2] = p[1]; 10320 p[1] = *p; 10321 *p = c; 10322 stack[depth].ts_fidxtry = sp->ts_fidx + 3; 10323 } 10324 } 10325 else 10326 sp->ts_state = STATE_REP_INI; 10327 break; 10328 10329 case STATE_UNROT3R: 10330 /* Undo ROT3R: "312" -> "123" */ 10331 p = fword + sp->ts_fidx; 10332 #ifdef FEAT_MBYTE 10333 if (has_mbyte) 10334 { 10335 c = mb_ptr2char(p); 10336 tl = MB_BYTE2LEN(*p); 10337 n = MB_BYTE2LEN(p[tl]); 10338 n += MB_BYTE2LEN(p[tl + n]); 10339 mch_memmove(p, p + tl, n); 10340 mb_char2bytes(c, p + n); 10341 } 10342 else 10343 #endif 10344 { 10345 c = *p; 10346 *p = p[1]; 10347 p[1] = p[2]; 10348 p[2] = c; 10349 } 10350 /*FALLTHROUGH*/ 10351 10352 case STATE_REP_INI: 10353 /* Check if matching with REP items from the .aff file would 10354 * work. Quickly skip if: 10355 * - there are no REP items 10356 * - the score is going to be too high anyway 10357 * - already applied a REP item or swapped here */ 10358 if (lp->lp_replang == NULL 10359 || sp->ts_score + SCORE_REP >= su->su_maxscore 10360 || sp->ts_fidx < sp->ts_fidxtry) 10361 { 10362 sp->ts_state = STATE_FINAL; 10363 break; 10364 } 10365 gap = &lp->lp_replang->sl_rep; 10366 10367 /* Use the first byte to quickly find the first entry that 10368 * may match. If the index is -1 there is none. */ 10369 sp->ts_curi = lp->lp_replang->sl_rep_first[fword[sp->ts_fidx]]; 10370 if (sp->ts_curi < 0) 10371 { 10372 sp->ts_state = STATE_FINAL; 10373 break; 10374 } 10375 10376 sp->ts_state = STATE_REP; 10377 /*FALLTHROUGH*/ 10378 10379 case STATE_REP: 10380 /* Try matching with REP items from the .aff file. For each 10381 * match replace the characters and check if the resulting 10382 * word is valid. */ 10383 p = fword + sp->ts_fidx; 10384 10385 gap = &lp->lp_replang->sl_rep; 10386 while (sp->ts_curi < gap->ga_len) 10387 { 10388 ftp = (fromto_T *)gap->ga_data + sp->ts_curi++; 10389 if (*ftp->ft_from != *p) 10390 { 10391 /* past possible matching entries */ 10392 sp->ts_curi = gap->ga_len; 10393 break; 10394 } 10395 if (STRNCMP(ftp->ft_from, p, STRLEN(ftp->ft_from)) == 0 10396 && try_deeper(su, stack, depth, SCORE_REP)) 10397 { 10398 /* Need to undo this afterwards. */ 10399 sp->ts_state = STATE_REP_UNDO; 10400 10401 /* Change the "from" to the "to" string. */ 10402 ++depth; 10403 fl = STRLEN(ftp->ft_from); 10404 tl = STRLEN(ftp->ft_to); 10405 if (fl != tl) 10406 { 10407 mch_memmove(p + tl, p + fl, STRLEN(p + fl) + 1); 10408 repextra += tl - fl; 10409 } 10410 mch_memmove(p, ftp->ft_to, tl); 10411 stack[depth].ts_fidxtry = sp->ts_fidx + tl; 10412 #ifdef FEAT_MBYTE 10413 stack[depth].ts_tcharlen = 0; 10414 #endif 10415 break; 10416 } 10417 } 10418 10419 if (sp->ts_curi >= gap->ga_len && sp->ts_state == STATE_REP) 10420 /* No (more) matches. */ 10421 sp->ts_state = STATE_FINAL; 10422 10423 break; 10424 10425 case STATE_REP_UNDO: 10426 /* Undo a REP replacement and continue with the next one. */ 10427 ftp = (fromto_T *)lp->lp_replang->sl_rep.ga_data 10428 + sp->ts_curi - 1; 10429 fl = STRLEN(ftp->ft_from); 10430 tl = STRLEN(ftp->ft_to); 10431 p = fword + sp->ts_fidx; 10432 if (fl != tl) 10433 { 10434 mch_memmove(p + fl, p + tl, STRLEN(p + tl) + 1); 10435 repextra -= tl - fl; 10436 } 10437 mch_memmove(p, ftp->ft_from, fl); 10438 sp->ts_state = STATE_REP; 10439 break; 10440 10441 default: 10442 /* Did all possible states at this level, go up one level. */ 10443 --depth; 10444 10445 if (depth >= 0 && stack[depth].ts_prefixdepth == PFD_PREFIXTREE) 10446 { 10447 /* Continue in or go back to the prefix tree. */ 10448 byts = pbyts; 10449 idxs = pidxs; 10450 } 10451 10452 /* Don't check for CTRL-C too often, it takes time. */ 10453 line_breakcheck(); 10454 } 10455 } 10456 } 10457 } 10458 10459 /* 10460 * Try going one level deeper in the tree. 10461 */ 10462 static int 10463 try_deeper(su, stack, depth, score_add) 10464 suginfo_T *su; 10465 trystate_T *stack; 10466 int depth; 10467 int score_add; 10468 { 10469 int newscore; 10470 10471 /* Refuse to go deeper if the scrore is getting too big. */ 10472 newscore = stack[depth].ts_score + score_add; 10473 if (newscore >= su->su_maxscore) 10474 return FALSE; 10475 10476 stack[depth + 1] = stack[depth]; 10477 stack[depth + 1].ts_state = STATE_START; 10478 stack[depth + 1].ts_score = newscore; 10479 stack[depth + 1].ts_curi = 1; /* start just after length byte */ 10480 stack[depth + 1].ts_flags = 0; 10481 return TRUE; 10482 } 10483 10484 #ifdef FEAT_MBYTE 10485 /* 10486 * Case-folding may change the number of bytes: Count nr of chars in 10487 * fword[flen] and return the byte length of that many chars in "word". 10488 */ 10489 static int 10490 nofold_len(fword, flen, word) 10491 char_u *fword; 10492 int flen; 10493 char_u *word; 10494 { 10495 char_u *p; 10496 int i = 0; 10497 10498 for (p = fword; p < fword + flen; mb_ptr_adv(p)) 10499 ++i; 10500 for (p = word; i > 0; mb_ptr_adv(p)) 10501 --i; 10502 return (int)(p - word); 10503 } 10504 #endif 10505 10506 /* 10507 * "fword" is a good word with case folded. Find the matching keep-case 10508 * words and put it in "kword". 10509 * Theoretically there could be several keep-case words that result in the 10510 * same case-folded word, but we only find one... 10511 */ 10512 static void 10513 find_keepcap_word(slang, fword, kword) 10514 slang_T *slang; 10515 char_u *fword; 10516 char_u *kword; 10517 { 10518 char_u uword[MAXWLEN]; /* "fword" in upper-case */ 10519 int depth; 10520 idx_T tryidx; 10521 10522 /* The following arrays are used at each depth in the tree. */ 10523 idx_T arridx[MAXWLEN]; 10524 int round[MAXWLEN]; 10525 int fwordidx[MAXWLEN]; 10526 int uwordidx[MAXWLEN]; 10527 int kwordlen[MAXWLEN]; 10528 10529 int flen, ulen; 10530 int l; 10531 int len; 10532 int c; 10533 idx_T lo, hi, m; 10534 char_u *p; 10535 char_u *byts = slang->sl_kbyts; /* array with bytes of the words */ 10536 idx_T *idxs = slang->sl_kidxs; /* array with indexes */ 10537 10538 if (byts == NULL) 10539 { 10540 /* array is empty: "cannot happen" */ 10541 *kword = NUL; 10542 return; 10543 } 10544 10545 /* Make an all-cap version of "fword". */ 10546 allcap_copy(fword, uword); 10547 10548 /* 10549 * Each character needs to be tried both case-folded and upper-case. 10550 * All this gets very complicated if we keep in mind that changing case 10551 * may change the byte length of a multi-byte character... 10552 */ 10553 depth = 0; 10554 arridx[0] = 0; 10555 round[0] = 0; 10556 fwordidx[0] = 0; 10557 uwordidx[0] = 0; 10558 kwordlen[0] = 0; 10559 while (depth >= 0) 10560 { 10561 if (fword[fwordidx[depth]] == NUL) 10562 { 10563 /* We are at the end of "fword". If the tree allows a word to end 10564 * here we have found a match. */ 10565 if (byts[arridx[depth] + 1] == 0) 10566 { 10567 kword[kwordlen[depth]] = NUL; 10568 return; 10569 } 10570 10571 /* kword is getting too long, continue one level up */ 10572 --depth; 10573 } 10574 else if (++round[depth] > 2) 10575 { 10576 /* tried both fold-case and upper-case character, continue one 10577 * level up */ 10578 --depth; 10579 } 10580 else 10581 { 10582 /* 10583 * round[depth] == 1: Try using the folded-case character. 10584 * round[depth] == 2: Try using the upper-case character. 10585 */ 10586 #ifdef FEAT_MBYTE 10587 if (has_mbyte) 10588 { 10589 flen = mb_cptr2len(fword + fwordidx[depth]); 10590 ulen = mb_cptr2len(uword + uwordidx[depth]); 10591 } 10592 else 10593 #endif 10594 ulen = flen = 1; 10595 if (round[depth] == 1) 10596 { 10597 p = fword + fwordidx[depth]; 10598 l = flen; 10599 } 10600 else 10601 { 10602 p = uword + uwordidx[depth]; 10603 l = ulen; 10604 } 10605 10606 for (tryidx = arridx[depth]; l > 0; --l) 10607 { 10608 /* Perform a binary search in the list of accepted bytes. */ 10609 len = byts[tryidx++]; 10610 c = *p++; 10611 lo = tryidx; 10612 hi = tryidx + len - 1; 10613 while (lo < hi) 10614 { 10615 m = (lo + hi) / 2; 10616 if (byts[m] > c) 10617 hi = m - 1; 10618 else if (byts[m] < c) 10619 lo = m + 1; 10620 else 10621 { 10622 lo = hi = m; 10623 break; 10624 } 10625 } 10626 10627 /* Stop if there is no matching byte. */ 10628 if (hi < lo || byts[lo] != c) 10629 break; 10630 10631 /* Continue at the child (if there is one). */ 10632 tryidx = idxs[lo]; 10633 } 10634 10635 if (l == 0) 10636 { 10637 /* 10638 * Found the matching char. Copy it to "kword" and go a 10639 * level deeper. 10640 */ 10641 if (round[depth] == 1) 10642 { 10643 STRNCPY(kword + kwordlen[depth], fword + fwordidx[depth], 10644 flen); 10645 kwordlen[depth + 1] = kwordlen[depth] + flen; 10646 } 10647 else 10648 { 10649 STRNCPY(kword + kwordlen[depth], uword + uwordidx[depth], 10650 ulen); 10651 kwordlen[depth + 1] = kwordlen[depth] + ulen; 10652 } 10653 fwordidx[depth + 1] = fwordidx[depth] + flen; 10654 uwordidx[depth + 1] = uwordidx[depth] + ulen; 10655 10656 ++depth; 10657 arridx[depth] = tryidx; 10658 round[depth] = 0; 10659 } 10660 } 10661 } 10662 10663 /* Didn't find it: "cannot happen". */ 10664 *kword = NUL; 10665 } 10666 10667 /* 10668 * Compute the sound-a-like score for suggestions in su->su_ga and add them to 10669 * su->su_sga. 10670 */ 10671 static void 10672 score_comp_sal(su) 10673 suginfo_T *su; 10674 { 10675 langp_T *lp; 10676 char_u badsound[MAXWLEN]; 10677 int i; 10678 suggest_T *stp; 10679 suggest_T *sstp; 10680 int score; 10681 int lpi; 10682 10683 if (ga_grow(&su->su_sga, su->su_ga.ga_len) == FAIL) 10684 return; 10685 10686 /* Use the sound-folding of the first language that supports it. */ 10687 for (lpi = 0; lpi < curbuf->b_langp.ga_len; ++lpi) 10688 { 10689 lp = LANGP_ENTRY(curbuf->b_langp, lpi); 10690 if (lp->lp_slang->sl_sal.ga_len > 0) 10691 { 10692 /* soundfold the bad word */ 10693 spell_soundfold(lp->lp_slang, su->su_fbadword, TRUE, badsound); 10694 10695 for (i = 0; i < su->su_ga.ga_len; ++i) 10696 { 10697 stp = &SUG(su->su_ga, i); 10698 10699 /* Case-fold the suggested word, sound-fold it and compute the 10700 * sound-a-like score. */ 10701 score = stp_sal_score(stp, su, lp->lp_slang, badsound); 10702 if (score < SCORE_MAXMAX) 10703 { 10704 /* Add the suggestion. */ 10705 sstp = &SUG(su->su_sga, su->su_sga.ga_len); 10706 sstp->st_word = vim_strsave(stp->st_word); 10707 if (sstp->st_word != NULL) 10708 { 10709 sstp->st_score = score; 10710 sstp->st_altscore = 0; 10711 sstp->st_orglen = stp->st_orglen; 10712 ++su->su_sga.ga_len; 10713 } 10714 } 10715 } 10716 break; 10717 } 10718 } 10719 } 10720 10721 /* 10722 * Combine the list of suggestions in su->su_ga and su->su_sga. 10723 * They are intwined. 10724 */ 10725 static void 10726 score_combine(su) 10727 suginfo_T *su; 10728 { 10729 int i; 10730 int j; 10731 garray_T ga; 10732 garray_T *gap; 10733 langp_T *lp; 10734 suggest_T *stp; 10735 char_u *p; 10736 char_u badsound[MAXWLEN]; 10737 int round; 10738 int lpi; 10739 10740 /* Add the alternate score to su_ga. */ 10741 for (lpi = 0; lpi < curbuf->b_langp.ga_len; ++lpi) 10742 { 10743 lp = LANGP_ENTRY(curbuf->b_langp, lpi); 10744 if (lp->lp_slang->sl_sal.ga_len > 0) 10745 { 10746 /* soundfold the bad word */ 10747 spell_soundfold(lp->lp_slang, su->su_fbadword, TRUE, badsound); 10748 10749 for (i = 0; i < su->su_ga.ga_len; ++i) 10750 { 10751 stp = &SUG(su->su_ga, i); 10752 stp->st_altscore = stp_sal_score(stp, su, lp->lp_slang, 10753 badsound); 10754 if (stp->st_altscore == SCORE_MAXMAX) 10755 stp->st_score = (stp->st_score * 3 + SCORE_BIG) / 4; 10756 else 10757 stp->st_score = (stp->st_score * 3 10758 + stp->st_altscore) / 4; 10759 stp->st_salscore = FALSE; 10760 } 10761 break; 10762 } 10763 } 10764 10765 /* Add the alternate score to su_sga. */ 10766 for (i = 0; i < su->su_sga.ga_len; ++i) 10767 { 10768 stp = &SUG(su->su_sga, i); 10769 stp->st_altscore = spell_edit_score(su->su_badword, stp->st_word); 10770 if (stp->st_score == SCORE_MAXMAX) 10771 stp->st_score = (SCORE_BIG * 7 + stp->st_altscore) / 8; 10772 else 10773 stp->st_score = (stp->st_score * 7 + stp->st_altscore) / 8; 10774 stp->st_salscore = TRUE; 10775 } 10776 10777 /* Sort the suggestions and truncate at "maxcount" for both lists. */ 10778 (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, su->su_maxcount); 10779 (void)cleanup_suggestions(&su->su_sga, su->su_maxscore, su->su_maxcount); 10780 10781 ga_init2(&ga, (int)sizeof(suginfo_T), 1); 10782 if (ga_grow(&ga, su->su_ga.ga_len + su->su_sga.ga_len) == FAIL) 10783 return; 10784 10785 stp = &SUG(ga, 0); 10786 for (i = 0; i < su->su_ga.ga_len || i < su->su_sga.ga_len; ++i) 10787 { 10788 /* round 1: get a suggestion from su_ga 10789 * round 2: get a suggestion from su_sga */ 10790 for (round = 1; round <= 2; ++round) 10791 { 10792 gap = round == 1 ? &su->su_ga : &su->su_sga; 10793 if (i < gap->ga_len) 10794 { 10795 /* Don't add a word if it's already there. */ 10796 p = SUG(*gap, i).st_word; 10797 for (j = 0; j < ga.ga_len; ++j) 10798 if (STRCMP(stp[j].st_word, p) == 0) 10799 break; 10800 if (j == ga.ga_len) 10801 stp[ga.ga_len++] = SUG(*gap, i); 10802 else 10803 vim_free(p); 10804 } 10805 } 10806 } 10807 10808 ga_clear(&su->su_ga); 10809 ga_clear(&su->su_sga); 10810 10811 /* Truncate the list to the number of suggestions that will be displayed. */ 10812 if (ga.ga_len > su->su_maxcount) 10813 { 10814 for (i = su->su_maxcount; i < ga.ga_len; ++i) 10815 vim_free(stp[i].st_word); 10816 ga.ga_len = su->su_maxcount; 10817 } 10818 10819 su->su_ga = ga; 10820 } 10821 10822 /* 10823 * For the goodword in "stp" compute the soundalike score compared to the 10824 * badword. 10825 */ 10826 static int 10827 stp_sal_score(stp, su, slang, badsound) 10828 suggest_T *stp; 10829 suginfo_T *su; 10830 slang_T *slang; 10831 char_u *badsound; /* sound-folded badword */ 10832 { 10833 char_u *p; 10834 char_u *pbad; 10835 char_u *pgood; 10836 char_u badsound2[MAXWLEN]; 10837 char_u fword[MAXWLEN]; 10838 char_u goodsound[MAXWLEN]; 10839 char_u goodword[MAXWLEN]; 10840 int lendiff; 10841 10842 lendiff = (int)(su->su_badlen - stp->st_orglen); 10843 if (lendiff >= 0) 10844 pbad = badsound; 10845 else 10846 { 10847 /* soundfold the bad word with more characters following */ 10848 (void)spell_casefold(su->su_badptr, stp->st_orglen, fword, MAXWLEN); 10849 10850 /* When joining two words the sound often changes a lot. E.g., "t he" 10851 * sounds like "t h" while "the" sounds like "@". Avoid that by 10852 * removing the space. Don't do it when the good word also contains a 10853 * space. */ 10854 if (vim_iswhite(su->su_badptr[su->su_badlen]) 10855 && *skiptowhite(stp->st_word) == NUL) 10856 for (p = fword; *(p = skiptowhite(p)) != NUL; ) 10857 mch_memmove(p, p + 1, STRLEN(p)); 10858 10859 spell_soundfold(slang, fword, TRUE, badsound2); 10860 pbad = badsound2; 10861 } 10862 10863 if (lendiff > 0) 10864 { 10865 /* Add part of the bad word to the good word, so that we soundfold 10866 * what replaces the bad word. */ 10867 STRCPY(goodword, stp->st_word); 10868 STRNCAT(goodword, su->su_badptr + su->su_badlen - lendiff, lendiff); 10869 pgood = goodword; 10870 } 10871 else 10872 pgood = stp->st_word; 10873 10874 /* Sound-fold the word and compute the score for the difference. */ 10875 spell_soundfold(slang, pgood, FALSE, goodsound); 10876 10877 return soundalike_score(goodsound, pbad); 10878 } 10879 10880 /* 10881 * Find suggestions by comparing the word in a sound-a-like form. 10882 * Note: This doesn't support postponed prefixes. 10883 */ 10884 static void 10885 suggest_try_soundalike(su) 10886 suginfo_T *su; 10887 { 10888 char_u salword[MAXWLEN]; 10889 char_u tword[MAXWLEN]; 10890 char_u tsalword[MAXWLEN]; 10891 idx_T arridx[MAXWLEN]; 10892 int curi[MAXWLEN]; 10893 langp_T *lp; 10894 char_u *byts; 10895 idx_T *idxs; 10896 int depth; 10897 int c; 10898 idx_T n; 10899 int round; 10900 int flags; 10901 int sound_score; 10902 int local_score; 10903 int lpi; 10904 slang_T *slang; 10905 10906 /* Do this for all languages that support sound folding. */ 10907 for (lpi = 0; lpi < curbuf->b_langp.ga_len; ++lpi) 10908 { 10909 lp = LANGP_ENTRY(curbuf->b_langp, lpi); 10910 slang = lp->lp_slang; 10911 if (slang->sl_sal.ga_len > 0) 10912 { 10913 /* soundfold the bad word */ 10914 spell_soundfold(slang, su->su_fbadword, TRUE, salword); 10915 10916 /* 10917 * Go through the whole tree, soundfold each word and compare. 10918 * round 1: use the case-folded tree. 10919 * round 2: use the keep-case tree. 10920 */ 10921 for (round = 1; round <= 2; ++round) 10922 { 10923 if (round == 1) 10924 { 10925 byts = slang->sl_fbyts; 10926 idxs = slang->sl_fidxs; 10927 } 10928 else 10929 { 10930 byts = slang->sl_kbyts; 10931 idxs = slang->sl_kidxs; 10932 if (byts == NULL) /* no keep-case words */ 10933 continue; 10934 } 10935 10936 depth = 0; 10937 arridx[0] = 0; 10938 curi[0] = 1; 10939 while (depth >= 0 && !got_int) 10940 { 10941 if (curi[depth] > byts[arridx[depth]]) 10942 { 10943 /* Done all bytes at this node, go up one level. */ 10944 --depth; 10945 line_breakcheck(); 10946 } 10947 else 10948 { 10949 /* Do one more byte at this node. */ 10950 n = arridx[depth] + curi[depth]; 10951 ++curi[depth]; 10952 c = byts[n]; 10953 if (c == 0) 10954 { 10955 /* End of word, deal with the word. */ 10956 flags = (int)idxs[n]; 10957 if (round == 2 || (flags & WF_KEEPCAP) == 0) 10958 { 10959 tword[depth] = NUL; 10960 /* Sound-fold. Only in keep-case tree need to 10961 * case-fold the word. */ 10962 spell_soundfold(slang, tword, 10963 round == 1, tsalword); 10964 10965 /* Compute the edit distance between the 10966 * sound-a-like words. */ 10967 sound_score = soundalike_score(salword, 10968 tsalword); 10969 10970 /* Add a penalty for words in another region. */ 10971 if ((flags & WF_REGION) && (((unsigned)flags 10972 >> 16) & lp->lp_region) == 0) 10973 local_score = SCORE_REGION; 10974 else 10975 local_score = 0; 10976 sound_score += local_score; 10977 10978 if (sound_score < SCORE_MAXMAX) 10979 { 10980 char_u cword[MAXWLEN]; 10981 char_u *p; 10982 int score; 10983 10984 flags |= su->su_badflags; 10985 if (round == 1 && (flags & WF_CAPMASK) != 0) 10986 { 10987 /* Need to fix case according to 10988 * "flags". */ 10989 make_case_word(tword, cword, flags); 10990 p = cword; 10991 } 10992 else 10993 p = tword; 10994 10995 if (sps_flags & SPS_DOUBLE) 10996 add_suggestion(su, &su->su_sga, p, 10997 su->su_badlen, 10998 sound_score, 0, FALSE, 10999 lp->lp_sallang); 11000 else 11001 { 11002 /* Compute the score. */ 11003 score = spell_edit_score( 11004 su->su_badword, p) 11005 + local_score; 11006 if (sps_flags & SPS_BEST) 11007 /* give a bonus for the good word 11008 * sounding the same as the bad 11009 * word */ 11010 add_suggestion(su, &su->su_ga, p, 11011 su->su_badlen, 11012 RESCORE(score, sound_score), 11013 sound_score, TRUE, 11014 lp->lp_sallang); 11015 else 11016 add_suggestion(su, &su->su_ga, p, 11017 su->su_badlen, 11018 score + sound_score, 11019 0, FALSE, 11020 lp->lp_sallang); 11021 } 11022 } 11023 } 11024 11025 /* Skip over other NUL bytes. */ 11026 while (byts[n + 1] == 0) 11027 { 11028 ++n; 11029 ++curi[depth]; 11030 } 11031 } 11032 else 11033 { 11034 /* Normal char, go one level deeper. */ 11035 tword[depth++] = c; 11036 arridx[depth] = idxs[n]; 11037 curi[depth] = 1; 11038 } 11039 } 11040 } 11041 } 11042 } 11043 } 11044 } 11045 11046 /* 11047 * Copy "fword" to "cword", fixing case according to "flags". 11048 */ 11049 static void 11050 make_case_word(fword, cword, flags) 11051 char_u *fword; 11052 char_u *cword; 11053 int flags; 11054 { 11055 if (flags & WF_ALLCAP) 11056 /* Make it all upper-case */ 11057 allcap_copy(fword, cword); 11058 else if (flags & WF_ONECAP) 11059 /* Make the first letter upper-case */ 11060 onecap_copy(fword, cword, TRUE); 11061 else 11062 /* Use goodword as-is. */ 11063 STRCPY(cword, fword); 11064 } 11065 11066 /* 11067 * Use map string "map" for languages "lp". 11068 */ 11069 static void 11070 set_map_str(lp, map) 11071 slang_T *lp; 11072 char_u *map; 11073 { 11074 char_u *p; 11075 int headc = 0; 11076 int c; 11077 int i; 11078 11079 if (*map == NUL) 11080 { 11081 lp->sl_has_map = FALSE; 11082 return; 11083 } 11084 lp->sl_has_map = TRUE; 11085 11086 /* Init the array and hash table empty. */ 11087 for (i = 0; i < 256; ++i) 11088 lp->sl_map_array[i] = 0; 11089 #ifdef FEAT_MBYTE 11090 hash_init(&lp->sl_map_hash); 11091 #endif 11092 11093 /* 11094 * The similar characters are stored separated with slashes: 11095 * "aaa/bbb/ccc/". Fill sl_map_array[c] with the character before c and 11096 * before the same slash. For characters above 255 sl_map_hash is used. 11097 */ 11098 for (p = map; *p != NUL; ) 11099 { 11100 #ifdef FEAT_MBYTE 11101 c = mb_cptr2char_adv(&p); 11102 #else 11103 c = *p++; 11104 #endif 11105 if (c == '/') 11106 headc = 0; 11107 else 11108 { 11109 if (headc == 0) 11110 headc = c; 11111 11112 #ifdef FEAT_MBYTE 11113 /* Characters above 255 don't fit in sl_map_array[], put them in 11114 * the hash table. Each entry is the char, a NUL the headchar and 11115 * a NUL. */ 11116 if (c >= 256) 11117 { 11118 int cl = mb_char2len(c); 11119 int headcl = mb_char2len(headc); 11120 char_u *b; 11121 hash_T hash; 11122 hashitem_T *hi; 11123 11124 b = alloc((unsigned)(cl + headcl + 2)); 11125 if (b == NULL) 11126 return; 11127 mb_char2bytes(c, b); 11128 b[cl] = NUL; 11129 mb_char2bytes(headc, b + cl + 1); 11130 b[cl + 1 + headcl] = NUL; 11131 hash = hash_hash(b); 11132 hi = hash_lookup(&lp->sl_map_hash, b, hash); 11133 if (HASHITEM_EMPTY(hi)) 11134 hash_add_item(&lp->sl_map_hash, hi, b, hash); 11135 else 11136 { 11137 /* This should have been checked when generating the .spl 11138 * file. */ 11139 EMSG(_("E999: duplicate char in MAP entry")); 11140 vim_free(b); 11141 } 11142 } 11143 else 11144 #endif 11145 lp->sl_map_array[c] = headc; 11146 } 11147 } 11148 } 11149 11150 /* 11151 * Return TRUE if "c1" and "c2" are similar characters according to the MAP 11152 * lines in the .aff file. 11153 */ 11154 static int 11155 similar_chars(slang, c1, c2) 11156 slang_T *slang; 11157 int c1; 11158 int c2; 11159 { 11160 int m1, m2; 11161 #ifdef FEAT_MBYTE 11162 char_u buf[MB_MAXBYTES]; 11163 hashitem_T *hi; 11164 11165 if (c1 >= 256) 11166 { 11167 buf[mb_char2bytes(c1, buf)] = 0; 11168 hi = hash_find(&slang->sl_map_hash, buf); 11169 if (HASHITEM_EMPTY(hi)) 11170 m1 = 0; 11171 else 11172 m1 = mb_ptr2char(hi->hi_key + STRLEN(hi->hi_key) + 1); 11173 } 11174 else 11175 #endif 11176 m1 = slang->sl_map_array[c1]; 11177 if (m1 == 0) 11178 return FALSE; 11179 11180 11181 #ifdef FEAT_MBYTE 11182 if (c2 >= 256) 11183 { 11184 buf[mb_char2bytes(c2, buf)] = 0; 11185 hi = hash_find(&slang->sl_map_hash, buf); 11186 if (HASHITEM_EMPTY(hi)) 11187 m2 = 0; 11188 else 11189 m2 = mb_ptr2char(hi->hi_key + STRLEN(hi->hi_key) + 1); 11190 } 11191 else 11192 #endif 11193 m2 = slang->sl_map_array[c2]; 11194 11195 return m1 == m2; 11196 } 11197 11198 /* 11199 * Add a suggestion to the list of suggestions. 11200 * Do not add a duplicate suggestion or suggestions with a bad score. 11201 * When "use_score" is not zero it's used, otherwise the score is computed 11202 * with spell_edit_score(). 11203 */ 11204 static void 11205 add_suggestion(su, gap, goodword, badlenarg, score, altscore, had_bonus, slang) 11206 suginfo_T *su; 11207 garray_T *gap; 11208 char_u *goodword; 11209 int badlenarg; /* len of bad word replaced with "goodword" */ 11210 int score; 11211 int altscore; 11212 int had_bonus; /* value for st_had_bonus */ 11213 slang_T *slang; /* language for sound folding */ 11214 { 11215 int goodlen = STRLEN(goodword); /* len of goodword changed */ 11216 int badlen = badlenarg; /* len of bad word changed */ 11217 suggest_T *stp; 11218 suggest_T new_sug; 11219 int i; 11220 hlf_T attr = HLF_COUNT; 11221 char_u longword[MAXWLEN + 1]; 11222 char_u *pgood, *pbad; 11223 11224 /* Check that the word really is valid. Esp. for banned words and for 11225 * split words, such as "the the". Need to append what follows to check 11226 * for that. */ 11227 STRCPY(longword, goodword); 11228 vim_strncpy(longword + goodlen, su->su_badptr + badlen, MAXWLEN - goodlen); 11229 (void)spell_check(curwin, longword, &attr, NULL); 11230 if (attr != HLF_COUNT) 11231 return; 11232 11233 /* Minimize "badlen" for consistency. Avoids that changing "the the" to 11234 * "thee the" is added next to changing the first "the" the "thee". */ 11235 pgood = goodword + STRLEN(goodword); 11236 pbad = su->su_badptr + badlen; 11237 while (pgood > goodword && pbad > su->su_badptr) 11238 { 11239 mb_ptr_back(goodword, pgood); 11240 mb_ptr_back(su->su_badptr, pbad); 11241 #ifdef FEAT_MBYTE 11242 if (has_mbyte) 11243 { 11244 if (mb_ptr2char(pgood) != mb_ptr2char(pbad)) 11245 break; 11246 } 11247 else 11248 #endif 11249 if (*pgood != *pbad) 11250 break; 11251 badlen = pbad - su->su_badptr; 11252 goodlen = pgood - goodword; 11253 } 11254 if (badlen == 0 && goodlen == 0) 11255 /* goodword doesn't change anything; may happen for "the the" changing 11256 * the first "the" to itself. */ 11257 return; 11258 11259 if (score <= su->su_maxscore) 11260 { 11261 /* Check if the word is already there. Also check the length that is 11262 * being replaced "thes," -> "these" is a different suggestion from 11263 * "thes" -> "these". */ 11264 stp = &SUG(*gap, 0); 11265 for (i = gap->ga_len - 1; i >= 0; --i) 11266 if ((int)STRLEN(stp[i].st_word) == goodlen 11267 && STRNCMP(stp[i].st_word, goodword, goodlen) == 0 11268 && stp[i].st_orglen == badlen) 11269 { 11270 /* 11271 * Found it. Remember the word with the lowest score. 11272 */ 11273 if (stp[i].st_slang == NULL) 11274 stp[i].st_slang = slang; 11275 11276 new_sug.st_score = score; 11277 new_sug.st_altscore = altscore; 11278 new_sug.st_had_bonus = had_bonus; 11279 11280 if (stp[i].st_had_bonus != had_bonus) 11281 { 11282 /* Only one of the two had the soundalike score computed. 11283 * Need to do that for the other one now, otherwise the 11284 * scores can't be compared. This happens because 11285 * suggest_try_change() doesn't compute the soundalike 11286 * word to keep it fast, while some special methods set 11287 * the soundalike score to zero. */ 11288 if (had_bonus) 11289 rescore_one(su, &stp[i]); 11290 else 11291 { 11292 new_sug.st_word = goodword; 11293 new_sug.st_slang = stp[i].st_slang; 11294 new_sug.st_orglen = badlen; 11295 rescore_one(su, &new_sug); 11296 } 11297 } 11298 11299 if (stp[i].st_score > new_sug.st_score) 11300 { 11301 stp[i].st_score = new_sug.st_score; 11302 stp[i].st_altscore = new_sug.st_altscore; 11303 stp[i].st_had_bonus = new_sug.st_had_bonus; 11304 } 11305 break; 11306 } 11307 11308 if (i < 0 && ga_grow(gap, 1) == OK) 11309 { 11310 /* Add a suggestion. */ 11311 stp = &SUG(*gap, gap->ga_len); 11312 stp->st_word = vim_strnsave(goodword, goodlen); 11313 if (stp->st_word != NULL) 11314 { 11315 stp->st_score = score; 11316 stp->st_altscore = altscore; 11317 stp->st_had_bonus = had_bonus; 11318 stp->st_orglen = badlen; 11319 stp->st_slang = slang; 11320 ++gap->ga_len; 11321 11322 /* If we have too many suggestions now, sort the list and keep 11323 * the best suggestions. */ 11324 if (gap->ga_len > SUG_MAX_COUNT(su)) 11325 su->su_maxscore = cleanup_suggestions(gap, su->su_maxscore, 11326 SUG_CLEAN_COUNT(su)); 11327 } 11328 } 11329 } 11330 } 11331 11332 /* 11333 * Add a word to be banned. 11334 */ 11335 static void 11336 add_banned(su, word) 11337 suginfo_T *su; 11338 char_u *word; 11339 { 11340 char_u *s = vim_strsave(word); 11341 hash_T hash; 11342 hashitem_T *hi; 11343 11344 if (s != NULL) 11345 { 11346 hash = hash_hash(s); 11347 hi = hash_lookup(&su->su_banned, s, hash); 11348 if (HASHITEM_EMPTY(hi)) 11349 hash_add_item(&su->su_banned, hi, s, hash); 11350 else 11351 vim_free(s); 11352 } 11353 } 11354 11355 /* 11356 * Return TRUE if a word appears in the list of banned words. 11357 */ 11358 static int 11359 was_banned(su, word) 11360 suginfo_T *su; 11361 char_u *word; 11362 { 11363 hashitem_T *hi = hash_find(&su->su_banned, word); 11364 11365 return !HASHITEM_EMPTY(hi); 11366 } 11367 11368 /* 11369 * Free the banned words in "su". 11370 */ 11371 static void 11372 free_banned(su) 11373 suginfo_T *su; 11374 { 11375 int todo; 11376 hashitem_T *hi; 11377 11378 todo = su->su_banned.ht_used; 11379 for (hi = su->su_banned.ht_array; todo > 0; ++hi) 11380 { 11381 if (!HASHITEM_EMPTY(hi)) 11382 { 11383 vim_free(hi->hi_key); 11384 --todo; 11385 } 11386 } 11387 hash_clear(&su->su_banned); 11388 } 11389 11390 /* 11391 * Recompute the score for all suggestions if sound-folding is possible. This 11392 * is slow, thus only done for the final results. 11393 */ 11394 static void 11395 rescore_suggestions(su) 11396 suginfo_T *su; 11397 { 11398 int i; 11399 11400 if (su->su_sallang != NULL) 11401 for (i = 0; i < su->su_ga.ga_len; ++i) 11402 rescore_one(su, &SUG(su->su_ga, i)); 11403 } 11404 11405 /* 11406 * Recompute the score for one suggestion if sound-folding is possible. 11407 */ 11408 static void 11409 rescore_one(su, stp) 11410 suginfo_T *su; 11411 suggest_T *stp; 11412 { 11413 slang_T *slang = stp->st_slang; 11414 char_u sal_badword[MAXWLEN]; 11415 char_u *p; 11416 11417 /* Only rescore suggestions that have no sal score yet and do have a 11418 * language. */ 11419 if (slang != NULL && slang->sl_sal.ga_len > 0 && !stp->st_had_bonus) 11420 { 11421 if (slang == su->su_sallang) 11422 p = su->su_sal_badword; 11423 else 11424 { 11425 spell_soundfold(slang, su->su_fbadword, TRUE, sal_badword); 11426 p = sal_badword; 11427 } 11428 11429 stp->st_altscore = stp_sal_score(stp, su, slang, p); 11430 if (stp->st_altscore == SCORE_MAXMAX) 11431 stp->st_altscore = SCORE_BIG; 11432 stp->st_score = RESCORE(stp->st_score, stp->st_altscore); 11433 stp->st_had_bonus = TRUE; 11434 } 11435 } 11436 11437 static int 11438 #ifdef __BORLANDC__ 11439 _RTLENTRYF 11440 #endif 11441 sug_compare __ARGS((const void *s1, const void *s2)); 11442 11443 /* 11444 * Function given to qsort() to sort the suggestions on st_score. 11445 * First on "st_score", then "st_altscore" then alphabetically. 11446 */ 11447 static int 11448 #ifdef __BORLANDC__ 11449 _RTLENTRYF 11450 #endif 11451 sug_compare(s1, s2) 11452 const void *s1; 11453 const void *s2; 11454 { 11455 suggest_T *p1 = (suggest_T *)s1; 11456 suggest_T *p2 = (suggest_T *)s2; 11457 int n = p1->st_score - p2->st_score; 11458 11459 if (n == 0) 11460 { 11461 n = p1->st_altscore - p2->st_altscore; 11462 if (n == 0) 11463 n = STRICMP(p1->st_word, p2->st_word); 11464 } 11465 return n; 11466 } 11467 11468 /* 11469 * Cleanup the suggestions: 11470 * - Sort on score. 11471 * - Remove words that won't be displayed. 11472 * Returns the maximum score in the list or "maxscore" unmodified. 11473 */ 11474 static int 11475 cleanup_suggestions(gap, maxscore, keep) 11476 garray_T *gap; 11477 int maxscore; 11478 int keep; /* nr of suggestions to keep */ 11479 { 11480 suggest_T *stp = &SUG(*gap, 0); 11481 int i; 11482 11483 /* Sort the list. */ 11484 qsort(gap->ga_data, (size_t)gap->ga_len, sizeof(suggest_T), sug_compare); 11485 11486 /* Truncate the list to the number of suggestions that will be displayed. */ 11487 if (gap->ga_len > keep) 11488 { 11489 for (i = keep; i < gap->ga_len; ++i) 11490 vim_free(stp[i].st_word); 11491 gap->ga_len = keep; 11492 return stp[keep - 1].st_score; 11493 } 11494 return maxscore; 11495 } 11496 11497 #if defined(FEAT_EVAL) || defined(PROTO) 11498 /* 11499 * Soundfold a string, for soundfold(). 11500 * Result is in allocated memory, NULL for an error. 11501 */ 11502 char_u * 11503 eval_soundfold(word) 11504 char_u *word; 11505 { 11506 langp_T *lp; 11507 char_u sound[MAXWLEN]; 11508 int lpi; 11509 11510 if (curwin->w_p_spell && *curbuf->b_p_spl != NUL) 11511 /* Use the sound-folding of the first language that supports it. */ 11512 for (lpi = 0; lpi < curbuf->b_langp.ga_len; ++lpi) 11513 { 11514 lp = LANGP_ENTRY(curbuf->b_langp, lpi); 11515 if (lp->lp_slang->sl_sal.ga_len > 0) 11516 { 11517 /* soundfold the word */ 11518 spell_soundfold(lp->lp_slang, word, FALSE, sound); 11519 return vim_strsave(sound); 11520 } 11521 } 11522 11523 /* No language with sound folding, return word as-is. */ 11524 return vim_strsave(word); 11525 } 11526 #endif 11527 11528 /* 11529 * Turn "inword" into its sound-a-like equivalent in "res[MAXWLEN]". 11530 * 11531 * There are many ways to turn a word into a sound-a-like representation. The 11532 * oldest is Soundex (1918!). A nice overview can be found in "Approximate 11533 * swedish name matching - survey and test of different algorithms" by Klas 11534 * Erikson. 11535 * 11536 * We support two methods: 11537 * 1. SOFOFROM/SOFOTO do a simple character mapping. 11538 * 2. SAL items define a more advanced sound-folding (and much slower). 11539 */ 11540 static void 11541 spell_soundfold(slang, inword, folded, res) 11542 slang_T *slang; 11543 char_u *inword; 11544 int folded; /* "inword" is already case-folded */ 11545 char_u *res; 11546 { 11547 char_u fword[MAXWLEN]; 11548 char_u *word; 11549 11550 if (slang->sl_sofo) 11551 /* SOFOFROM and SOFOTO used */ 11552 spell_soundfold_sofo(slang, inword, res); 11553 else 11554 { 11555 /* SAL items used. Requires the word to be case-folded. */ 11556 if (folded) 11557 word = inword; 11558 else 11559 { 11560 (void)spell_casefold(inword, STRLEN(inword), fword, MAXWLEN); 11561 word = fword; 11562 } 11563 11564 #ifdef FEAT_MBYTE 11565 if (has_mbyte) 11566 spell_soundfold_wsal(slang, word, res); 11567 else 11568 #endif 11569 spell_soundfold_sal(slang, word, res); 11570 } 11571 } 11572 11573 /* 11574 * Perform sound folding of "inword" into "res" according to SOFOFROM and 11575 * SOFOTO lines. 11576 */ 11577 static void 11578 spell_soundfold_sofo(slang, inword, res) 11579 slang_T *slang; 11580 char_u *inword; 11581 char_u *res; 11582 { 11583 char_u *s; 11584 int ri = 0; 11585 int c; 11586 11587 #ifdef FEAT_MBYTE 11588 if (has_mbyte) 11589 { 11590 int prevc = 0; 11591 int *ip; 11592 11593 /* The sl_sal_first[] table contains the translation for chars up to 11594 * 255, sl_sal the rest. */ 11595 for (s = inword; *s != NUL; ) 11596 { 11597 c = mb_cptr2char_adv(&s); 11598 if (enc_utf8 ? utf_class(c) == 0 : vim_iswhite(c)) 11599 c = ' '; 11600 else if (c < 256) 11601 c = slang->sl_sal_first[c]; 11602 else 11603 { 11604 ip = ((int **)slang->sl_sal.ga_data)[c & 0xff]; 11605 if (ip == NULL) /* empty list, can't match */ 11606 c = NUL; 11607 else 11608 for (;;) /* find "c" in the list */ 11609 { 11610 if (*ip == 0) /* not found */ 11611 { 11612 c = NUL; 11613 break; 11614 } 11615 if (*ip == c) /* match! */ 11616 { 11617 c = ip[1]; 11618 break; 11619 } 11620 ip += 2; 11621 } 11622 } 11623 11624 if (c != NUL && c != prevc) 11625 { 11626 ri += mb_char2bytes(c, res + ri); 11627 if (ri + MB_MAXBYTES > MAXWLEN) 11628 break; 11629 prevc = c; 11630 } 11631 } 11632 } 11633 else 11634 #endif 11635 { 11636 /* The sl_sal_first[] table contains the translation. */ 11637 for (s = inword; (c = *s) != NUL; ++s) 11638 { 11639 if (vim_iswhite(c)) 11640 c = ' '; 11641 else 11642 c = slang->sl_sal_first[c]; 11643 if (c != NUL && (ri == 0 || res[ri - 1] != c)) 11644 res[ri++] = c; 11645 } 11646 } 11647 11648 res[ri] = NUL; 11649 } 11650 11651 static void 11652 spell_soundfold_sal(slang, inword, res) 11653 slang_T *slang; 11654 char_u *inword; 11655 char_u *res; 11656 { 11657 salitem_T *smp; 11658 char_u word[MAXWLEN]; 11659 char_u *s = inword; 11660 char_u *t; 11661 char_u *pf; 11662 int i, j, z; 11663 int reslen; 11664 int n, k = 0; 11665 int z0; 11666 int k0; 11667 int n0; 11668 int c; 11669 int pri; 11670 int p0 = -333; 11671 int c0; 11672 11673 /* Remove accents, if wanted. We actually remove all non-word characters. 11674 * But keep white space. We need a copy, the word may be changed here. */ 11675 if (slang->sl_rem_accents) 11676 { 11677 t = word; 11678 while (*s != NUL) 11679 { 11680 if (vim_iswhite(*s)) 11681 { 11682 *t++ = ' '; 11683 s = skipwhite(s); 11684 } 11685 else 11686 { 11687 if (spell_iswordp_nmw(s)) 11688 *t++ = *s; 11689 ++s; 11690 } 11691 } 11692 *t = NUL; 11693 } 11694 else 11695 STRCPY(word, s); 11696 11697 smp = (salitem_T *)slang->sl_sal.ga_data; 11698 11699 /* 11700 * This comes from Aspell phonet.cpp. Converted from C++ to C. 11701 * Changed to keep spaces. 11702 */ 11703 i = reslen = z = 0; 11704 while ((c = word[i]) != NUL) 11705 { 11706 /* Start with the first rule that has the character in the word. */ 11707 n = slang->sl_sal_first[c]; 11708 z0 = 0; 11709 11710 if (n >= 0) 11711 { 11712 /* check all rules for the same letter */ 11713 for (; (s = smp[n].sm_lead)[0] == c; ++n) 11714 { 11715 /* Quickly skip entries that don't match the word. Most 11716 * entries are less then three chars, optimize for that. */ 11717 k = smp[n].sm_leadlen; 11718 if (k > 1) 11719 { 11720 if (word[i + 1] != s[1]) 11721 continue; 11722 if (k > 2) 11723 { 11724 for (j = 2; j < k; ++j) 11725 if (word[i + j] != s[j]) 11726 break; 11727 if (j < k) 11728 continue; 11729 } 11730 } 11731 11732 if ((pf = smp[n].sm_oneof) != NULL) 11733 { 11734 /* Check for match with one of the chars in "sm_oneof". */ 11735 while (*pf != NUL && *pf != word[i + k]) 11736 ++pf; 11737 if (*pf == NUL) 11738 continue; 11739 ++k; 11740 } 11741 s = smp[n].sm_rules; 11742 pri = 5; /* default priority */ 11743 11744 p0 = *s; 11745 k0 = k; 11746 while (*s == '-' && k > 1) 11747 { 11748 k--; 11749 s++; 11750 } 11751 if (*s == '<') 11752 s++; 11753 if (VIM_ISDIGIT(*s)) 11754 { 11755 /* determine priority */ 11756 pri = *s - '0'; 11757 s++; 11758 } 11759 if (*s == '^' && *(s + 1) == '^') 11760 s++; 11761 11762 if (*s == NUL 11763 || (*s == '^' 11764 && (i == 0 || !(word[i - 1] == ' ' 11765 || spell_iswordp(word + i - 1, curbuf))) 11766 && (*(s + 1) != '$' 11767 || (!spell_iswordp(word + i + k0, curbuf)))) 11768 || (*s == '$' && i > 0 11769 && spell_iswordp(word + i - 1, curbuf) 11770 && (!spell_iswordp(word + i + k0, curbuf)))) 11771 { 11772 /* search for followup rules, if: */ 11773 /* followup and k > 1 and NO '-' in searchstring */ 11774 c0 = word[i + k - 1]; 11775 n0 = slang->sl_sal_first[c0]; 11776 11777 if (slang->sl_followup && k > 1 && n0 >= 0 11778 && p0 != '-' && word[i + k] != NUL) 11779 { 11780 /* test follow-up rule for "word[i + k]" */ 11781 for ( ; (s = smp[n0].sm_lead)[0] == c0; ++n0) 11782 { 11783 /* Quickly skip entries that don't match the word. 11784 * */ 11785 k0 = smp[n0].sm_leadlen; 11786 if (k0 > 1) 11787 { 11788 if (word[i + k] != s[1]) 11789 continue; 11790 if (k0 > 2) 11791 { 11792 pf = word + i + k + 1; 11793 for (j = 2; j < k0; ++j) 11794 if (*pf++ != s[j]) 11795 break; 11796 if (j < k0) 11797 continue; 11798 } 11799 } 11800 k0 += k - 1; 11801 11802 if ((pf = smp[n0].sm_oneof) != NULL) 11803 { 11804 /* Check for match with one of the chars in 11805 * "sm_oneof". */ 11806 while (*pf != NUL && *pf != word[i + k0]) 11807 ++pf; 11808 if (*pf == NUL) 11809 continue; 11810 ++k0; 11811 } 11812 11813 p0 = 5; 11814 s = smp[n0].sm_rules; 11815 while (*s == '-') 11816 { 11817 /* "k0" gets NOT reduced because 11818 * "if (k0 == k)" */ 11819 s++; 11820 } 11821 if (*s == '<') 11822 s++; 11823 if (VIM_ISDIGIT(*s)) 11824 { 11825 p0 = *s - '0'; 11826 s++; 11827 } 11828 11829 if (*s == NUL 11830 /* *s == '^' cuts */ 11831 || (*s == '$' 11832 && !spell_iswordp(word + i + k0, 11833 curbuf))) 11834 { 11835 if (k0 == k) 11836 /* this is just a piece of the string */ 11837 continue; 11838 11839 if (p0 < pri) 11840 /* priority too low */ 11841 continue; 11842 /* rule fits; stop search */ 11843 break; 11844 } 11845 } 11846 11847 if (p0 >= pri && smp[n0].sm_lead[0] == c0) 11848 continue; 11849 } 11850 11851 /* replace string */ 11852 s = smp[n].sm_to; 11853 if (s == NULL) 11854 s = (char_u *)""; 11855 pf = smp[n].sm_rules; 11856 p0 = (vim_strchr(pf, '<') != NULL) ? 1 : 0; 11857 if (p0 == 1 && z == 0) 11858 { 11859 /* rule with '<' is used */ 11860 if (reslen > 0 && *s != NUL && (res[reslen - 1] == c 11861 || res[reslen - 1] == *s)) 11862 reslen--; 11863 z0 = 1; 11864 z = 1; 11865 k0 = 0; 11866 while (*s != NUL && word[i + k0] != NUL) 11867 { 11868 word[i + k0] = *s; 11869 k0++; 11870 s++; 11871 } 11872 if (k > k0) 11873 mch_memmove(word + i + k0, word + i + k, 11874 STRLEN(word + i + k) + 1); 11875 11876 /* new "actual letter" */ 11877 c = word[i]; 11878 } 11879 else 11880 { 11881 /* no '<' rule used */ 11882 i += k - 1; 11883 z = 0; 11884 while (*s != NUL && s[1] != NUL && reslen < MAXWLEN) 11885 { 11886 if (reslen == 0 || res[reslen - 1] != *s) 11887 res[reslen++] = *s; 11888 s++; 11889 } 11890 /* new "actual letter" */ 11891 c = *s; 11892 if (strstr((char *)pf, "^^") != NULL) 11893 { 11894 if (c != NUL) 11895 res[reslen++] = c; 11896 mch_memmove(word, word + i + 1, 11897 STRLEN(word + i + 1) + 1); 11898 i = 0; 11899 z0 = 1; 11900 } 11901 } 11902 break; 11903 } 11904 } 11905 } 11906 else if (vim_iswhite(c)) 11907 { 11908 c = ' '; 11909 k = 1; 11910 } 11911 11912 if (z0 == 0) 11913 { 11914 if (k && !p0 && reslen < MAXWLEN && c != NUL 11915 && (!slang->sl_collapse || reslen == 0 11916 || res[reslen - 1] != c)) 11917 /* condense only double letters */ 11918 res[reslen++] = c; 11919 11920 i++; 11921 z = 0; 11922 k = 0; 11923 } 11924 } 11925 11926 res[reslen] = NUL; 11927 } 11928 11929 #ifdef FEAT_MBYTE 11930 /* 11931 * Turn "inword" into its sound-a-like equivalent in "res[MAXWLEN]". 11932 * Multi-byte version of spell_soundfold(). 11933 */ 11934 static void 11935 spell_soundfold_wsal(slang, inword, res) 11936 slang_T *slang; 11937 char_u *inword; 11938 char_u *res; 11939 { 11940 salitem_T *smp = (salitem_T *)slang->sl_sal.ga_data; 11941 int word[MAXWLEN]; 11942 int wres[MAXWLEN]; 11943 int l; 11944 char_u *s; 11945 int *ws; 11946 char_u *t; 11947 int *pf; 11948 int i, j, z; 11949 int reslen; 11950 int n, k = 0; 11951 int z0; 11952 int k0; 11953 int n0; 11954 int c; 11955 int pri; 11956 int p0 = -333; 11957 int c0; 11958 int did_white = FALSE; 11959 11960 /* 11961 * Convert the multi-byte string to a wide-character string. 11962 * Remove accents, if wanted. We actually remove all non-word characters. 11963 * But keep white space. 11964 */ 11965 n = 0; 11966 for (s = inword; *s != NUL; ) 11967 { 11968 t = s; 11969 c = mb_cptr2char_adv(&s); 11970 if (slang->sl_rem_accents) 11971 { 11972 if (enc_utf8 ? utf_class(c) == 0 : vim_iswhite(c)) 11973 { 11974 if (did_white) 11975 continue; 11976 c = ' '; 11977 did_white = TRUE; 11978 } 11979 else 11980 { 11981 did_white = FALSE; 11982 if (!spell_iswordp_nmw(t)) 11983 continue; 11984 } 11985 } 11986 word[n++] = c; 11987 } 11988 word[n] = NUL; 11989 11990 /* 11991 * This comes from Aspell phonet.cpp. 11992 * Converted from C++ to C. Added support for multi-byte chars. 11993 * Changed to keep spaces. 11994 */ 11995 i = reslen = z = 0; 11996 while ((c = word[i]) != NUL) 11997 { 11998 /* Start with the first rule that has the character in the word. */ 11999 n = slang->sl_sal_first[c & 0xff]; 12000 z0 = 0; 12001 12002 if (n >= 0) 12003 { 12004 /* check all rules for the same index byte */ 12005 for (; ((ws = smp[n].sm_lead_w)[0] & 0xff) == (c & 0xff); ++n) 12006 { 12007 /* Quickly skip entries that don't match the word. Most 12008 * entries are less then three chars, optimize for that. */ 12009 if (c != ws[0]) 12010 continue; 12011 k = smp[n].sm_leadlen; 12012 if (k > 1) 12013 { 12014 if (word[i + 1] != ws[1]) 12015 continue; 12016 if (k > 2) 12017 { 12018 for (j = 2; j < k; ++j) 12019 if (word[i + j] != ws[j]) 12020 break; 12021 if (j < k) 12022 continue; 12023 } 12024 } 12025 12026 if ((pf = smp[n].sm_oneof_w) != NULL) 12027 { 12028 /* Check for match with one of the chars in "sm_oneof". */ 12029 while (*pf != NUL && *pf != word[i + k]) 12030 ++pf; 12031 if (*pf == NUL) 12032 continue; 12033 ++k; 12034 } 12035 s = smp[n].sm_rules; 12036 pri = 5; /* default priority */ 12037 12038 p0 = *s; 12039 k0 = k; 12040 while (*s == '-' && k > 1) 12041 { 12042 k--; 12043 s++; 12044 } 12045 if (*s == '<') 12046 s++; 12047 if (VIM_ISDIGIT(*s)) 12048 { 12049 /* determine priority */ 12050 pri = *s - '0'; 12051 s++; 12052 } 12053 if (*s == '^' && *(s + 1) == '^') 12054 s++; 12055 12056 if (*s == NUL 12057 || (*s == '^' 12058 && (i == 0 || !(word[i - 1] == ' ' 12059 || spell_iswordp_w(word + i - 1, curbuf))) 12060 && (*(s + 1) != '$' 12061 || (!spell_iswordp_w(word + i + k0, curbuf)))) 12062 || (*s == '$' && i > 0 12063 && spell_iswordp_w(word + i - 1, curbuf) 12064 && (!spell_iswordp_w(word + i + k0, curbuf)))) 12065 { 12066 /* search for followup rules, if: */ 12067 /* followup and k > 1 and NO '-' in searchstring */ 12068 c0 = word[i + k - 1]; 12069 n0 = slang->sl_sal_first[c0 & 0xff]; 12070 12071 if (slang->sl_followup && k > 1 && n0 >= 0 12072 && p0 != '-' && word[i + k] != NUL) 12073 { 12074 /* Test follow-up rule for "word[i + k]"; loop over 12075 * all entries with the same index byte. */ 12076 for ( ; ((ws = smp[n0].sm_lead_w)[0] & 0xff) 12077 == (c0 & 0xff); ++n0) 12078 { 12079 /* Quickly skip entries that don't match the word. 12080 */ 12081 if (c0 != ws[0]) 12082 continue; 12083 k0 = smp[n0].sm_leadlen; 12084 if (k0 > 1) 12085 { 12086 if (word[i + k] != ws[1]) 12087 continue; 12088 if (k0 > 2) 12089 { 12090 pf = word + i + k + 1; 12091 for (j = 2; j < k0; ++j) 12092 if (*pf++ != ws[j]) 12093 break; 12094 if (j < k0) 12095 continue; 12096 } 12097 } 12098 k0 += k - 1; 12099 12100 if ((pf = smp[n0].sm_oneof_w) != NULL) 12101 { 12102 /* Check for match with one of the chars in 12103 * "sm_oneof". */ 12104 while (*pf != NUL && *pf != word[i + k0]) 12105 ++pf; 12106 if (*pf == NUL) 12107 continue; 12108 ++k0; 12109 } 12110 12111 p0 = 5; 12112 s = smp[n0].sm_rules; 12113 while (*s == '-') 12114 { 12115 /* "k0" gets NOT reduced because 12116 * "if (k0 == k)" */ 12117 s++; 12118 } 12119 if (*s == '<') 12120 s++; 12121 if (VIM_ISDIGIT(*s)) 12122 { 12123 p0 = *s - '0'; 12124 s++; 12125 } 12126 12127 if (*s == NUL 12128 /* *s == '^' cuts */ 12129 || (*s == '$' 12130 && !spell_iswordp_w(word + i + k0, 12131 curbuf))) 12132 { 12133 if (k0 == k) 12134 /* this is just a piece of the string */ 12135 continue; 12136 12137 if (p0 < pri) 12138 /* priority too low */ 12139 continue; 12140 /* rule fits; stop search */ 12141 break; 12142 } 12143 } 12144 12145 if (p0 >= pri && (smp[n0].sm_lead_w[0] & 0xff) 12146 == (c0 & 0xff)) 12147 continue; 12148 } 12149 12150 /* replace string */ 12151 ws = smp[n].sm_to_w; 12152 s = smp[n].sm_rules; 12153 p0 = (vim_strchr(s, '<') != NULL) ? 1 : 0; 12154 if (p0 == 1 && z == 0) 12155 { 12156 /* rule with '<' is used */ 12157 if (reslen > 0 && ws != NULL && *ws != NUL 12158 && (wres[reslen - 1] == c 12159 || wres[reslen - 1] == *ws)) 12160 reslen--; 12161 z0 = 1; 12162 z = 1; 12163 k0 = 0; 12164 if (ws != NULL) 12165 while (*ws != NUL && word[i + k0] != NUL) 12166 { 12167 word[i + k0] = *ws; 12168 k0++; 12169 ws++; 12170 } 12171 if (k > k0) 12172 mch_memmove(word + i + k0, word + i + k, 12173 sizeof(int) * (STRLEN(word + i + k) + 1)); 12174 12175 /* new "actual letter" */ 12176 c = word[i]; 12177 } 12178 else 12179 { 12180 /* no '<' rule used */ 12181 i += k - 1; 12182 z = 0; 12183 if (ws != NULL) 12184 while (*ws != NUL && ws[1] != NUL 12185 && reslen < MAXWLEN) 12186 { 12187 if (reslen == 0 || wres[reslen - 1] != *ws) 12188 wres[reslen++] = *ws; 12189 ws++; 12190 } 12191 /* new "actual letter" */ 12192 if (ws == NULL) 12193 c = NUL; 12194 else 12195 c = *ws; 12196 if (strstr((char *)s, "^^") != NULL) 12197 { 12198 if (c != NUL) 12199 wres[reslen++] = c; 12200 mch_memmove(word, word + i + 1, 12201 sizeof(int) * (STRLEN(word + i + 1) + 1)); 12202 i = 0; 12203 z0 = 1; 12204 } 12205 } 12206 break; 12207 } 12208 } 12209 } 12210 else if (vim_iswhite(c)) 12211 { 12212 c = ' '; 12213 k = 1; 12214 } 12215 12216 if (z0 == 0) 12217 { 12218 if (k && !p0 && reslen < MAXWLEN && c != NUL 12219 && (!slang->sl_collapse || reslen == 0 12220 || wres[reslen - 1] != c)) 12221 /* condense only double letters */ 12222 wres[reslen++] = c; 12223 12224 i++; 12225 z = 0; 12226 k = 0; 12227 } 12228 } 12229 12230 /* Convert wide characters in "wres" to a multi-byte string in "res". */ 12231 l = 0; 12232 for (n = 0; n < reslen; ++n) 12233 { 12234 l += mb_char2bytes(wres[n], res + l); 12235 if (l + MB_MAXBYTES > MAXWLEN) 12236 break; 12237 } 12238 res[l] = NUL; 12239 } 12240 #endif 12241 12242 /* 12243 * Compute a score for two sound-a-like words. 12244 * This permits up to two inserts/deletes/swaps/etc. to keep things fast. 12245 * Instead of a generic loop we write out the code. That keeps it fast by 12246 * avoiding checks that will not be possible. 12247 */ 12248 static int 12249 soundalike_score(goodstart, badstart) 12250 char_u *goodstart; /* sound-folded good word */ 12251 char_u *badstart; /* sound-folded bad word */ 12252 { 12253 char_u *goodsound = goodstart; 12254 char_u *badsound = badstart; 12255 int goodlen; 12256 int badlen; 12257 int n; 12258 char_u *pl, *ps; 12259 char_u *pl2, *ps2; 12260 int score = 0; 12261 12262 /* adding/inserting "*" at the start (word starts with vowel) shouldn't be 12263 * counted so much, vowels halfway the word aren't counted at all. */ 12264 if ((*badsound == '*' || *goodsound == '*') && *badsound != *goodsound) 12265 { 12266 score = SCORE_DEL / 2; 12267 if (*badsound == '*') 12268 ++badsound; 12269 else 12270 ++goodsound; 12271 } 12272 12273 goodlen = STRLEN(goodsound); 12274 badlen = STRLEN(badsound); 12275 12276 /* Return quickly if the lenghts are too different to be fixed by two 12277 * changes. */ 12278 n = goodlen - badlen; 12279 if (n < -2 || n > 2) 12280 return SCORE_MAXMAX; 12281 12282 if (n > 0) 12283 { 12284 pl = goodsound; /* goodsound is longest */ 12285 ps = badsound; 12286 } 12287 else 12288 { 12289 pl = badsound; /* badsound is longest */ 12290 ps = goodsound; 12291 } 12292 12293 /* Skip over the identical part. */ 12294 while (*pl == *ps && *pl != NUL) 12295 { 12296 ++pl; 12297 ++ps; 12298 } 12299 12300 switch (n) 12301 { 12302 case -2: 12303 case 2: 12304 /* 12305 * Must delete two characters from "pl". 12306 */ 12307 ++pl; /* first delete */ 12308 while (*pl == *ps) 12309 { 12310 ++pl; 12311 ++ps; 12312 } 12313 /* strings must be equal after second delete */ 12314 if (STRCMP(pl + 1, ps) == 0) 12315 return score + SCORE_DEL * 2; 12316 12317 /* Failed to compare. */ 12318 break; 12319 12320 case -1: 12321 case 1: 12322 /* 12323 * Minimal one delete from "pl" required. 12324 */ 12325 12326 /* 1: delete */ 12327 pl2 = pl + 1; 12328 ps2 = ps; 12329 while (*pl2 == *ps2) 12330 { 12331 if (*pl2 == NUL) /* reached the end */ 12332 return score + SCORE_DEL; 12333 ++pl2; 12334 ++ps2; 12335 } 12336 12337 /* 2: delete then swap, then rest must be equal */ 12338 if (pl2[0] == ps2[1] && pl2[1] == ps2[0] 12339 && STRCMP(pl2 + 2, ps2 + 2) == 0) 12340 return score + SCORE_DEL + SCORE_SWAP; 12341 12342 /* 3: delete then substitute, then the rest must be equal */ 12343 if (STRCMP(pl2 + 1, ps2 + 1) == 0) 12344 return score + SCORE_DEL + SCORE_SUBST; 12345 12346 /* 4: first swap then delete */ 12347 if (pl[0] == ps[1] && pl[1] == ps[0]) 12348 { 12349 pl2 = pl + 2; /* swap, skip two chars */ 12350 ps2 = ps + 2; 12351 while (*pl2 == *ps2) 12352 { 12353 ++pl2; 12354 ++ps2; 12355 } 12356 /* delete a char and then strings must be equal */ 12357 if (STRCMP(pl2 + 1, ps2) == 0) 12358 return score + SCORE_SWAP + SCORE_DEL; 12359 } 12360 12361 /* 5: first substitute then delete */ 12362 pl2 = pl + 1; /* substitute, skip one char */ 12363 ps2 = ps + 1; 12364 while (*pl2 == *ps2) 12365 { 12366 ++pl2; 12367 ++ps2; 12368 } 12369 /* delete a char and then strings must be equal */ 12370 if (STRCMP(pl2 + 1, ps2) == 0) 12371 return score + SCORE_SUBST + SCORE_DEL; 12372 12373 /* Failed to compare. */ 12374 break; 12375 12376 case 0: 12377 /* 12378 * Lenghts are equal, thus changes must result in same length: An 12379 * insert is only possible in combination with a delete. 12380 * 1: check if for identical strings 12381 */ 12382 if (*pl == NUL) 12383 return score; 12384 12385 /* 2: swap */ 12386 if (pl[0] == ps[1] && pl[1] == ps[0]) 12387 { 12388 pl2 = pl + 2; /* swap, skip two chars */ 12389 ps2 = ps + 2; 12390 while (*pl2 == *ps2) 12391 { 12392 if (*pl2 == NUL) /* reached the end */ 12393 return score + SCORE_SWAP; 12394 ++pl2; 12395 ++ps2; 12396 } 12397 /* 3: swap and swap again */ 12398 if (pl2[0] == ps2[1] && pl2[1] == ps2[0] 12399 && STRCMP(pl2 + 2, ps2 + 2) == 0) 12400 return score + SCORE_SWAP + SCORE_SWAP; 12401 12402 /* 4: swap and substitute */ 12403 if (STRCMP(pl2 + 1, ps2 + 1) == 0) 12404 return score + SCORE_SWAP + SCORE_SUBST; 12405 } 12406 12407 /* 5: substitute */ 12408 pl2 = pl + 1; 12409 ps2 = ps + 1; 12410 while (*pl2 == *ps2) 12411 { 12412 if (*pl2 == NUL) /* reached the end */ 12413 return score + SCORE_SUBST; 12414 ++pl2; 12415 ++ps2; 12416 } 12417 12418 /* 6: substitute and swap */ 12419 if (pl2[0] == ps2[1] && pl2[1] == ps2[0] 12420 && STRCMP(pl2 + 2, ps2 + 2) == 0) 12421 return score + SCORE_SUBST + SCORE_SWAP; 12422 12423 /* 7: substitute and substitute */ 12424 if (STRCMP(pl2 + 1, ps2 + 1) == 0) 12425 return score + SCORE_SUBST + SCORE_SUBST; 12426 12427 /* 8: insert then delete */ 12428 pl2 = pl; 12429 ps2 = ps + 1; 12430 while (*pl2 == *ps2) 12431 { 12432 ++pl2; 12433 ++ps2; 12434 } 12435 if (STRCMP(pl2 + 1, ps2) == 0) 12436 return score + SCORE_INS + SCORE_DEL; 12437 12438 /* 9: delete then insert */ 12439 pl2 = pl + 1; 12440 ps2 = ps; 12441 while (*pl2 == *ps2) 12442 { 12443 ++pl2; 12444 ++ps2; 12445 } 12446 if (STRCMP(pl2, ps2 + 1) == 0) 12447 return score + SCORE_INS + SCORE_DEL; 12448 12449 /* Failed to compare. */ 12450 break; 12451 } 12452 12453 return SCORE_MAXMAX; 12454 } 12455 12456 /* 12457 * Compute the "edit distance" to turn "badword" into "goodword". The less 12458 * deletes/inserts/substitutes/swaps are required the lower the score. 12459 * 12460 * The algorithm is described by Du and Chang, 1992. 12461 * The implementation of the algorithm comes from Aspell editdist.cpp, 12462 * edit_distance(). It has been converted from C++ to C and modified to 12463 * support multi-byte characters. 12464 */ 12465 static int 12466 spell_edit_score(badword, goodword) 12467 char_u *badword; 12468 char_u *goodword; 12469 { 12470 int *cnt; 12471 int badlen, goodlen; /* lenghts including NUL */ 12472 int j, i; 12473 int t; 12474 int bc, gc; 12475 int pbc, pgc; 12476 #ifdef FEAT_MBYTE 12477 char_u *p; 12478 int wbadword[MAXWLEN]; 12479 int wgoodword[MAXWLEN]; 12480 12481 if (has_mbyte) 12482 { 12483 /* Get the characters from the multi-byte strings and put them in an 12484 * int array for easy access. */ 12485 for (p = badword, badlen = 0; *p != NUL; ) 12486 wbadword[badlen++] = mb_cptr2char_adv(&p); 12487 wbadword[badlen++] = 0; 12488 for (p = goodword, goodlen = 0; *p != NUL; ) 12489 wgoodword[goodlen++] = mb_cptr2char_adv(&p); 12490 wgoodword[goodlen++] = 0; 12491 } 12492 else 12493 #endif 12494 { 12495 badlen = STRLEN(badword) + 1; 12496 goodlen = STRLEN(goodword) + 1; 12497 } 12498 12499 /* We use "cnt" as an array: CNT(badword_idx, goodword_idx). */ 12500 #define CNT(a, b) cnt[(a) + (b) * (badlen + 1)] 12501 cnt = (int *)lalloc((long_u)(sizeof(int) * (badlen + 1) * (goodlen + 1)), 12502 TRUE); 12503 if (cnt == NULL) 12504 return 0; /* out of memory */ 12505 12506 CNT(0, 0) = 0; 12507 for (j = 1; j <= goodlen; ++j) 12508 CNT(0, j) = CNT(0, j - 1) + SCORE_DEL; 12509 12510 for (i = 1; i <= badlen; ++i) 12511 { 12512 CNT(i, 0) = CNT(i - 1, 0) + SCORE_INS; 12513 for (j = 1; j <= goodlen; ++j) 12514 { 12515 #ifdef FEAT_MBYTE 12516 if (has_mbyte) 12517 { 12518 bc = wbadword[i - 1]; 12519 gc = wgoodword[j - 1]; 12520 } 12521 else 12522 #endif 12523 { 12524 bc = badword[i - 1]; 12525 gc = goodword[j - 1]; 12526 } 12527 if (bc == gc) 12528 CNT(i, j) = CNT(i - 1, j - 1); 12529 else 12530 { 12531 /* Use a better score when there is only a case difference. */ 12532 if (SPELL_TOFOLD(bc) == SPELL_TOFOLD(gc)) 12533 CNT(i, j) = SCORE_ICASE + CNT(i - 1, j - 1); 12534 else 12535 CNT(i, j) = SCORE_SUBST + CNT(i - 1, j - 1); 12536 12537 if (i > 1 && j > 1) 12538 { 12539 #ifdef FEAT_MBYTE 12540 if (has_mbyte) 12541 { 12542 pbc = wbadword[i - 2]; 12543 pgc = wgoodword[j - 2]; 12544 } 12545 else 12546 #endif 12547 { 12548 pbc = badword[i - 2]; 12549 pgc = goodword[j - 2]; 12550 } 12551 if (bc == pgc && pbc == gc) 12552 { 12553 t = SCORE_SWAP + CNT(i - 2, j - 2); 12554 if (t < CNT(i, j)) 12555 CNT(i, j) = t; 12556 } 12557 } 12558 t = SCORE_DEL + CNT(i - 1, j); 12559 if (t < CNT(i, j)) 12560 CNT(i, j) = t; 12561 t = SCORE_INS + CNT(i, j - 1); 12562 if (t < CNT(i, j)) 12563 CNT(i, j) = t; 12564 } 12565 } 12566 } 12567 12568 i = CNT(badlen - 1, goodlen - 1); 12569 vim_free(cnt); 12570 return i; 12571 } 12572 12573 /* 12574 * ":spelldump" 12575 */ 12576 /*ARGSUSED*/ 12577 void 12578 ex_spelldump(eap) 12579 exarg_T *eap; 12580 { 12581 buf_T *buf = curbuf; 12582 langp_T *lp; 12583 slang_T *slang; 12584 idx_T arridx[MAXWLEN]; 12585 int curi[MAXWLEN]; 12586 char_u word[MAXWLEN]; 12587 int c; 12588 char_u *byts; 12589 idx_T *idxs; 12590 linenr_T lnum = 0; 12591 int round; 12592 int depth; 12593 int n; 12594 int flags; 12595 char_u *region_names = NULL; /* region names being used */ 12596 int do_region = TRUE; /* dump region names and numbers */ 12597 char_u *p; 12598 int lpi; 12599 12600 if (no_spell_checking(curwin)) 12601 return; 12602 12603 /* Create a new empty buffer by splitting the window. */ 12604 do_cmdline_cmd((char_u *)"new"); 12605 if (!bufempty() || !buf_valid(buf)) 12606 return; 12607 12608 /* Find out if we can support regions: All languages must support the same 12609 * regions or none at all. */ 12610 for (lpi = 0; lpi < buf->b_langp.ga_len; ++lpi) 12611 { 12612 lp = LANGP_ENTRY(buf->b_langp, lpi); 12613 p = lp->lp_slang->sl_regions; 12614 if (p[0] != 0) 12615 { 12616 if (region_names == NULL) /* first language with regions */ 12617 region_names = p; 12618 else if (STRCMP(region_names, p) != 0) 12619 { 12620 do_region = FALSE; /* region names are different */ 12621 break; 12622 } 12623 } 12624 } 12625 12626 if (do_region && region_names != NULL) 12627 { 12628 vim_snprintf((char *)IObuff, IOSIZE, "/regions=%s", region_names); 12629 ml_append(lnum++, IObuff, (colnr_T)0, FALSE); 12630 } 12631 else 12632 do_region = FALSE; 12633 12634 /* 12635 * Loop over all files loaded for the entries in 'spelllang'. 12636 */ 12637 for (lpi = 0; lpi < buf->b_langp.ga_len; ++lpi) 12638 { 12639 lp = LANGP_ENTRY(buf->b_langp, lpi); 12640 slang = lp->lp_slang; 12641 if (slang->sl_fbyts == NULL) /* reloading failed */ 12642 continue; 12643 12644 vim_snprintf((char *)IObuff, IOSIZE, "# file: %s", slang->sl_fname); 12645 ml_append(lnum++, IObuff, (colnr_T)0, FALSE); 12646 12647 /* round 1: case-folded tree 12648 * round 2: keep-case tree */ 12649 for (round = 1; round <= 2; ++round) 12650 { 12651 if (round == 1) 12652 { 12653 byts = slang->sl_fbyts; 12654 idxs = slang->sl_fidxs; 12655 } 12656 else 12657 { 12658 byts = slang->sl_kbyts; 12659 idxs = slang->sl_kidxs; 12660 } 12661 if (byts == NULL) 12662 continue; /* array is empty */ 12663 12664 depth = 0; 12665 arridx[0] = 0; 12666 curi[0] = 1; 12667 while (depth >= 0 && !got_int) 12668 { 12669 if (curi[depth] > byts[arridx[depth]]) 12670 { 12671 /* Done all bytes at this node, go up one level. */ 12672 --depth; 12673 line_breakcheck(); 12674 } 12675 else 12676 { 12677 /* Do one more byte at this node. */ 12678 n = arridx[depth] + curi[depth]; 12679 ++curi[depth]; 12680 c = byts[n]; 12681 if (c == 0) 12682 { 12683 /* End of word, deal with the word. 12684 * Don't use keep-case words in the fold-case tree, 12685 * they will appear in the keep-case tree. 12686 * Only use the word when the region matches. */ 12687 flags = (int)idxs[n]; 12688 if ((round == 2 || (flags & WF_KEEPCAP) == 0) 12689 && (flags & WF_NEEDCOMP) == 0 12690 && (do_region 12691 || (flags & WF_REGION) == 0 12692 || (((unsigned)flags >> 16) 12693 & lp->lp_region) != 0)) 12694 { 12695 word[depth] = NUL; 12696 if (!do_region) 12697 flags &= ~WF_REGION; 12698 12699 /* Dump the basic word if there is no prefix or 12700 * when it's the first one. */ 12701 c = (unsigned)flags >> 24; 12702 if (c == 0 || curi[depth] == 2) 12703 dump_word(word, round, flags, lnum++); 12704 12705 /* Apply the prefix, if there is one. */ 12706 if (c != 0) 12707 lnum = dump_prefixes(slang, word, round, 12708 flags, lnum); 12709 } 12710 } 12711 else 12712 { 12713 /* Normal char, go one level deeper. */ 12714 word[depth++] = c; 12715 arridx[depth] = idxs[n]; 12716 curi[depth] = 1; 12717 } 12718 } 12719 } 12720 } 12721 } 12722 12723 /* Delete the empty line that we started with. */ 12724 if (curbuf->b_ml.ml_line_count > 1) 12725 ml_delete(curbuf->b_ml.ml_line_count, FALSE); 12726 12727 redraw_later(NOT_VALID); 12728 } 12729 12730 /* 12731 * Dump one word: apply case modifications and append a line to the buffer. 12732 */ 12733 static void 12734 dump_word(word, round, flags, lnum) 12735 char_u *word; 12736 int round; 12737 int flags; 12738 linenr_T lnum; 12739 { 12740 int keepcap = FALSE; 12741 char_u *p; 12742 char_u cword[MAXWLEN]; 12743 char_u badword[MAXWLEN + 10]; 12744 int i; 12745 12746 if (round == 1 && (flags & WF_CAPMASK) != 0) 12747 { 12748 /* Need to fix case according to "flags". */ 12749 make_case_word(word, cword, flags); 12750 p = cword; 12751 } 12752 else 12753 { 12754 p = word; 12755 if (round == 2 && ((captype(word, NULL) & WF_KEEPCAP) == 0 12756 || (flags & WF_FIXCAP) != 0)) 12757 keepcap = TRUE; 12758 } 12759 12760 /* Add flags and regions after a slash. */ 12761 if ((flags & (WF_BANNED | WF_RARE | WF_REGION)) || keepcap) 12762 { 12763 STRCPY(badword, p); 12764 STRCAT(badword, "/"); 12765 if (keepcap) 12766 STRCAT(badword, "="); 12767 if (flags & WF_BANNED) 12768 STRCAT(badword, "!"); 12769 else if (flags & WF_RARE) 12770 STRCAT(badword, "?"); 12771 if (flags & WF_REGION) 12772 for (i = 0; i < 7; ++i) 12773 if (flags & (0x10000 << i)) 12774 sprintf((char *)badword + STRLEN(badword), "%d", i + 1); 12775 p = badword; 12776 } 12777 12778 ml_append(lnum, p, (colnr_T)0, FALSE); 12779 } 12780 12781 /* 12782 * For ":spelldump": Find matching prefixes for "word". Prepend each to 12783 * "word" and append a line to the buffer. 12784 * Return the updated line number. 12785 */ 12786 static linenr_T 12787 dump_prefixes(slang, word, round, flags, startlnum) 12788 slang_T *slang; 12789 char_u *word; /* case-folded word */ 12790 int round; 12791 int flags; /* flags with prefix ID */ 12792 linenr_T startlnum; 12793 { 12794 idx_T arridx[MAXWLEN]; 12795 int curi[MAXWLEN]; 12796 char_u prefix[MAXWLEN]; 12797 char_u word_up[MAXWLEN]; 12798 int has_word_up = FALSE; 12799 int c; 12800 char_u *byts; 12801 idx_T *idxs; 12802 linenr_T lnum = startlnum; 12803 int depth; 12804 int n; 12805 int len; 12806 int i; 12807 12808 /* if the word starts with a lower-case letter make the word with an 12809 * upper-case letter in word_up[]. */ 12810 c = PTR2CHAR(word); 12811 if (SPELL_TOUPPER(c) != c) 12812 { 12813 onecap_copy(word, word_up, TRUE); 12814 has_word_up = TRUE; 12815 } 12816 12817 byts = slang->sl_pbyts; 12818 idxs = slang->sl_pidxs; 12819 if (byts != NULL) /* array not is empty */ 12820 { 12821 /* 12822 * Loop over all prefixes, building them byte-by-byte in prefix[]. 12823 * When at the end of a prefix check that it supports "flags". 12824 */ 12825 depth = 0; 12826 arridx[0] = 0; 12827 curi[0] = 1; 12828 while (depth >= 0 && !got_int) 12829 { 12830 n = arridx[depth]; 12831 len = byts[n]; 12832 if (curi[depth] > len) 12833 { 12834 /* Done all bytes at this node, go up one level. */ 12835 --depth; 12836 line_breakcheck(); 12837 } 12838 else 12839 { 12840 /* Do one more byte at this node. */ 12841 n += curi[depth]; 12842 ++curi[depth]; 12843 c = byts[n]; 12844 if (c == 0) 12845 { 12846 /* End of prefix, find out how many IDs there are. */ 12847 for (i = 1; i < len; ++i) 12848 if (byts[n + i] != 0) 12849 break; 12850 curi[depth] += i - 1; 12851 12852 c = valid_word_prefix(i, n, flags, word, slang, FALSE); 12853 if (c != 0) 12854 { 12855 vim_strncpy(prefix + depth, word, MAXWLEN - depth - 1); 12856 dump_word(prefix, round, 12857 (c & WF_RAREPFX) ? (flags | WF_RARE) 12858 : flags, lnum++); 12859 } 12860 12861 /* Check for prefix that matches the word when the 12862 * first letter is upper-case, but only if the prefix has 12863 * a condition. */ 12864 if (has_word_up) 12865 { 12866 c = valid_word_prefix(i, n, flags, word_up, slang, 12867 TRUE); 12868 if (c != 0) 12869 { 12870 vim_strncpy(prefix + depth, word_up, 12871 MAXWLEN - depth - 1); 12872 dump_word(prefix, round, 12873 (c & WF_RAREPFX) ? (flags | WF_RARE) 12874 : flags, lnum++); 12875 } 12876 } 12877 } 12878 else 12879 { 12880 /* Normal char, go one level deeper. */ 12881 prefix[depth++] = c; 12882 arridx[depth] = idxs[n]; 12883 curi[depth] = 1; 12884 } 12885 } 12886 } 12887 } 12888 12889 return lnum; 12890 } 12891 12892 /* 12893 * Move "p" to end of word. 12894 */ 12895 char_u * 12896 spell_to_word_end(start, buf) 12897 char_u *start; 12898 buf_T *buf; 12899 { 12900 char_u *p = start; 12901 12902 while (*p != NUL && spell_iswordp(p, buf)) 12903 mb_ptr_adv(p); 12904 return p; 12905 } 12906 12907 #if defined(FEAT_INS_EXPAND) || defined(PROTO) 12908 /* 12909 * Find start of the word in front of the cursor. We don't check if it is 12910 * badly spelled, with completion we can only change the word in front of the 12911 * cursor. 12912 * Used for Insert mode completion CTRL-X ?. 12913 * Returns the column number of the word. 12914 */ 12915 int 12916 spell_word_start(startcol) 12917 int startcol; 12918 { 12919 char_u *line; 12920 char_u *p; 12921 int col = 0; 12922 12923 if (no_spell_checking(curwin)) 12924 return startcol; 12925 12926 /* Find a word character before "startcol". */ 12927 line = ml_get_curline(); 12928 for (p = line + startcol; p > line; ) 12929 { 12930 mb_ptr_back(line, p); 12931 if (spell_iswordp_nmw(p)) 12932 break; 12933 } 12934 12935 /* Go back to start of the word. */ 12936 while (p > line) 12937 { 12938 col = p - line; 12939 mb_ptr_back(line, p); 12940 if (!spell_iswordp(p, curbuf)) 12941 break; 12942 col = 0; 12943 } 12944 12945 return col; 12946 } 12947 12948 /* 12949 * Need to check for 'spellcapcheck' now, the word is removed before 12950 * expand_spelling() is called. Therefore the ugly global variable. 12951 */ 12952 static int spell_expand_need_cap; 12953 12954 void 12955 spell_expand_check_cap(col) 12956 colnr_T col; 12957 { 12958 spell_expand_need_cap = check_need_cap(curwin->w_cursor.lnum, col); 12959 } 12960 12961 /* 12962 * Get list of spelling suggestions. 12963 * Used for Insert mode completion CTRL-X ?. 12964 * Returns the number of matches. The matches are in "matchp[]", array of 12965 * allocated strings. 12966 */ 12967 /*ARGSUSED*/ 12968 int 12969 expand_spelling(lnum, col, pat, matchp) 12970 linenr_T lnum; 12971 int col; 12972 char_u *pat; 12973 char_u ***matchp; 12974 { 12975 garray_T ga; 12976 12977 spell_suggest_list(&ga, pat, 100, spell_expand_need_cap); 12978 *matchp = ga.ga_data; 12979 return ga.ga_len; 12980 } 12981 #endif 12982 12983 #endif /* FEAT_SYN_HL */ 12984