1 #include "license.hunspell"
2 #include "license.myspell"
16 #include "affixmgr.hxx"
17 #include "affentry.hxx"
18 #include "langnum.hxx"
22 #ifndef MOZILLA_CLIENT
28 AffixMgr::AffixMgr(const char * affpath
, HashMgr
* ptr
)
30 // register hash manager and load affix data from aff file
46 compoundflag
= FLAG_NULL
; // permits word in compound forms
47 compoundbegin
= FLAG_NULL
; // may be first word in compound forms
48 compoundmiddle
= FLAG_NULL
; // may be middle word in compound forms
49 compoundend
= FLAG_NULL
; // may be last word in compound forms
50 compoundroot
= FLAG_NULL
; // compound word signing flag
51 compoundpermitflag
= FLAG_NULL
; // compound permitting flag for suffixed word
52 compoundforbidflag
= FLAG_NULL
; // compound fordidden flag for suffixed word
53 checkcompounddup
= 0; // forbid double words in compounds
54 checkcompoundrep
= 0; // forbid bad compounds (may be non compound word with a REP substitution)
55 checkcompoundcase
= 0; // forbid upper and lowercase combinations at word bounds
56 checkcompoundtriple
= 0; // forbid compounds with triple letters
57 forbiddenword
= FLAG_NULL
; // forbidden word signing flag
58 nosuggest
= FLAG_NULL
; // don't suggest words signed with NOSUGGEST flag
59 lang
= NULL
; // language
60 langnum
= 0; // language code (see http://l10n.openoffice.org/languages.html)
61 pseudoroot
= FLAG_NULL
; // forbidden root, allowed only with suffixes
62 cpdwordmax
= -1; // default: unlimited wordcount in compound words
63 cpdmin
= -1; // undefined
64 cpdmaxsyllable
= 0; // default: unlimited syllablecount in compound words
65 cpdvowels
=NULL
; // vowels (for calculating of Hungarian compounding limit, O(n) search! XXX)
66 cpdvowels_utf16
=NULL
; // vowels for UTF-8 encoding (bsearch instead of O(n) search)
67 cpdvowels_utf16_len
=0; // vowels
68 pfxappnd
=NULL
; // previous prefix for counting the syllables of prefix BUG
69 sfxappnd
=NULL
; // previous suffix for counting a special syllables BUG
70 cpdsyllablenum
=NULL
; // syllable count incrementing flag
71 checknum
=0; // checking numbers, and word with numbers
72 wordchars
=NULL
; // letters + spec. word characters
73 wordchars_utf16
=NULL
; // letters + spec. word characters
74 wordchars_utf16_len
=0; // letters + spec. word characters
75 ignorechars
=NULL
; // letters + spec. word characters
76 ignorechars_utf16
=NULL
; // letters + spec. word characters
77 ignorechars_utf16_len
=0; // letters + spec. word characters
78 version
=NULL
; // affix and dictionary file version string
79 havecontclass
=0; // flags of possible continuing classes (double affix)
80 // LEMMA_PRESENT: not put root into the morphological output. Lemma presents
81 // in morhological description in dictionary file. It's often combined with PSEUDOROOT.
82 lemma_present
= FLAG_NULL
;
83 circumfix
= FLAG_NULL
;
84 onlyincompound
= FLAG_NULL
;
85 flag_mode
= FLAG_CHAR
; // default one-character flags in affix and dic file
86 maxngramsugs
= -1; // undefined
92 derived
= NULL
; // XXX not threadsafe variable for experimental stemming
96 for (int i
=0; i
< SETSIZE
; i
++) {
103 for (int j
=0; j
< CONTSIZE
; j
++) {
107 if (parse_file(affpath
)) {
108 HUNSPELL_WARNING(stderr
, "Failure loading aff file %s\n",affpath
);
109 wordchars
= mystrdup("qwertzuiopasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM");
112 if (cpdmin
== -1) cpdmin
= MINCPDLEN
;
117 AffixMgr::~AffixMgr()
120 // pass through linked prefix entries and clean up
121 for (int i
=0; i
< SETSIZE
;i
++) {
123 PfxEntry
* ptr
= (PfxEntry
*)pStart
[i
];
124 PfxEntry
* nptr
= NULL
;
126 nptr
= ptr
->getNext();
133 // pass through linked suffix entries and clean up
134 for (int j
=0; j
< SETSIZE
; j
++) {
136 SfxEntry
* ptr
= (SfxEntry
*)sStart
[j
];
137 SfxEntry
* nptr
= NULL
;
139 nptr
= ptr
->getNext();
147 if (trystring
) free(trystring
);
149 if (encoding
) free(encoding
);
152 for (int j
=0; j
< nummap
; j
++) {
153 if (maptable
[j
].set
) free(maptable
[j
].set
);
154 if (maptable
[j
].set_utf16
) free(maptable
[j
].set_utf16
);
155 maptable
[j
].set
= NULL
;
163 for (int j
=0; j
< numbreak
; j
++) {
164 if (breaktable
[j
]) free(breaktable
[j
]);
165 breaktable
[j
] = NULL
;
172 for (int j
=0; j
< numrep
; j
++) {
173 free(reptable
[j
].pattern
);
174 free(reptable
[j
].pattern2
);
175 reptable
[j
].pattern
= NULL
;
176 reptable
[j
].pattern2
= NULL
;
182 for (int j
=0; j
< numdefcpd
; j
++) {
183 free(defcpdtable
[j
].def
);
184 defcpdtable
[j
].def
= NULL
;
191 for (int j
=0; j
< numcheckcpd
; j
++) {
192 free(checkcpdtable
[j
].pattern
);
193 free(checkcpdtable
[j
].pattern2
);
194 checkcpdtable
[j
].pattern
= NULL
;
195 checkcpdtable
[j
].pattern2
= NULL
;
198 checkcpdtable
= NULL
;
201 FREE_FLAG(compoundflag
);
202 FREE_FLAG(compoundbegin
);
203 FREE_FLAG(compoundmiddle
);
204 FREE_FLAG(compoundend
);
205 FREE_FLAG(compoundpermitflag
);
206 FREE_FLAG(compoundforbidflag
);
207 FREE_FLAG(compoundroot
);
208 FREE_FLAG(forbiddenword
);
209 FREE_FLAG(nosuggest
);
210 FREE_FLAG(pseudoroot
);
211 FREE_FLAG(lemma_present
);
212 FREE_FLAG(circumfix
);
213 FREE_FLAG(onlyincompound
);
219 if (cpdvowels
) free(cpdvowels
);
220 if (cpdvowels_utf16
) free(cpdvowels_utf16
);
221 if (cpdsyllablenum
) free(cpdsyllablenum
);
223 if (lang
) free(lang
);
224 if (wordchars
) free(wordchars
);
225 if (wordchars_utf16
) free(wordchars_utf16
);
226 if (ignorechars
) free(ignorechars
);
227 if (ignorechars_utf16
) free(ignorechars_utf16
);
228 if (version
) free(version
);
229 if (derived
) free(derived
);
234 // read in aff file and build up prefix and suffix entry objects
235 int AffixMgr::parse_file(const char * affpath
)
239 char line
[MAXLNLEN
+1];
244 // checking flag duplication
245 char dupflags
[CONTSIZE
];
246 char dupflags_ini
= 1;
248 // first line indicator for removing byte order mark
251 // open the affix file
253 afflst
= fopen(affpath
,"r");
255 HUNSPELL_WARNING(stderr
, "error: could not open affix description file %s\n",affpath
);
259 // step one is to parse the affix file building up the internal
260 // affix data structures
263 // read in each line ignoring any that do not
264 // start with a known line type indicator
265 while (fgets(line
,MAXLNLEN
,afflst
)) {
268 /* remove byte order mark */
271 if (strncmp(line
,"",3) == 0) {
272 memmove(line
, line
+3, strlen(line
+3)+1);
273 HUNSPELL_WARNING(stderr
, "warning: affix file begins with byte order mark: possible incompatibility with old Hunspell versions\n");
277 /* parse in the try string */
278 if (strncmp(line
,"TRY",3) == 0) {
279 if (parse_string(line
, &trystring
, "TRY")) {
285 /* parse in the name of the character set used by the .dict and .aff */
286 if (strncmp(line
,"SET",3) == 0) {
287 if (parse_string(line
, &encoding
, "SET")) {
291 if (strcmp(encoding
, "UTF-8") == 0) {
293 #ifndef OPENOFFICEORG
294 #ifndef MOZILLA_CLIENT
295 if (initialize_utf_tbl()) {
304 /* parse COMPLEXPREFIXES for agglutinative languages with right-to-left writing system */
305 if (strncmp(line
,"COMPLEXPREFIXES",15) == 0)
308 /* parse in the flag used by the controlled compound words */
309 if (strncmp(line
,"COMPOUNDFLAG",12) == 0) {
310 if (parse_flag(line
, &compoundflag
, "COMPOUNDFLAG")) {
316 /* parse in the flag used by compound words */
317 if (strncmp(line
,"COMPOUNDBEGIN",13) == 0) {
318 if (complexprefixes
) {
319 if (parse_flag(line
, &compoundend
, "COMPOUNDBEGIN")) {
324 if (parse_flag(line
, &compoundbegin
, "COMPOUNDBEGIN")) {
331 /* parse in the flag used by compound words */
332 if (strncmp(line
,"COMPOUNDMIDDLE",14) == 0) {
333 if (parse_flag(line
, &compoundmiddle
, "COMPOUNDMIDDLE")) {
338 /* parse in the flag used by compound words */
339 if (strncmp(line
,"COMPOUNDEND",11) == 0) {
340 if (complexprefixes
) {
341 if (parse_flag(line
, &compoundbegin
, "COMPOUNDEND")) {
346 if (parse_flag(line
, &compoundend
, "COMPOUNDEND")) {
353 /* parse in the data used by compound_check() method */
354 if (strncmp(line
,"COMPOUNDWORDMAX",15) == 0) {
355 if (parse_num(line
, &cpdwordmax
, "COMPOUNDWORDMAX")) {
361 /* parse in the flag sign compounds in dictionary */
362 if (strncmp(line
,"COMPOUNDROOT",12) == 0) {
363 if (parse_flag(line
, &compoundroot
, "COMPOUNDROOT")) {
369 /* parse in the flag used by compound_check() method */
370 if (strncmp(line
,"COMPOUNDPERMITFLAG",18) == 0) {
371 if (parse_flag(line
, &compoundpermitflag
, "COMPOUNDPERMITFLAG")) {
377 /* parse in the flag used by compound_check() method */
378 if (strncmp(line
,"COMPOUNDFORBIDFLAG",18) == 0) {
379 if (parse_flag(line
, &compoundforbidflag
, "COMPOUNDFORBIDFLAG")) {
385 if (strncmp(line
,"CHECKCOMPOUNDDUP",16) == 0) {
386 checkcompounddup
= 1;
389 if (strncmp(line
,"CHECKCOMPOUNDREP",16) == 0) {
390 checkcompoundrep
= 1;
393 if (strncmp(line
,"CHECKCOMPOUNDTRIPLE",19) == 0) {
394 checkcompoundtriple
= 1;
397 if (strncmp(line
,"CHECKCOMPOUNDCASE",17) == 0) {
398 checkcompoundcase
= 1;
401 if (strncmp(line
,"NOSUGGEST",9) == 0) {
402 if (parse_flag(line
, &nosuggest
, "NOSUGGEST")) {
408 /* parse in the flag used by forbidden words */
409 if (strncmp(line
,"FORBIDDENWORD",13) == 0) {
410 if (parse_flag(line
, &forbiddenword
, "FORBIDDENWORD")) {
416 /* parse in the flag used by forbidden words */
417 if (strncmp(line
,"LEMMA_PRESENT",13) == 0) {
418 if (parse_flag(line
, &lemma_present
, "LEMMA_PRESENT")) {
424 /* parse in the flag used by circumfixes */
425 if (strncmp(line
,"CIRCUMFIX",9) == 0) {
426 if (parse_flag(line
, &circumfix
, "CIRCUMFIX")) {
432 /* parse in the flag used by fogemorphemes */
433 if (strncmp(line
,"ONLYINCOMPOUND",14) == 0) {
434 if (parse_flag(line
, &onlyincompound
, "ONLYINCOMPOUND")) {
440 /* parse in the flag used by `pseudoroots' */
441 if (strncmp(line
,"PSEUDOROOT",10) == 0) {
442 if (parse_flag(line
, &pseudoroot
, "PSEUDOROOT")) {
448 /* parse in the flag used by `pseudoroots' */
449 if (strncmp(line
,"NEEDAFFIX",9) == 0) {
450 if (parse_flag(line
, &pseudoroot
, "NEEDAFFIX")) {
456 /* parse in the minimal length for words in compounds */
457 if (strncmp(line
,"COMPOUNDMIN",11) == 0) {
458 if (parse_num(line
, &cpdmin
, "COMPOUNDMIN")) {
462 if (cpdmin
< 1) cpdmin
= 1;
465 /* parse in the max. words and syllables in compounds */
466 if (strncmp(line
,"COMPOUNDSYLLABLE",16) == 0) {
467 if (parse_cpdsyllable(line
)) {
473 /* parse in the flag used by compound_check() method */
474 if (strncmp(line
,"SYLLABLENUM",11) == 0) {
475 if (parse_string(line
, &cpdsyllablenum
, "SYLLABLENUM")) {
481 /* parse in the flag used by the controlled compound words */
482 if (strncmp(line
,"CHECKNUM",8) == 0) {
486 /* parse in the extra word characters */
487 if (strncmp(line
,"WORDCHARS",9) == 0) {
488 if (parse_array(line
, &wordchars
, &wordchars_utf16
, &wordchars_utf16_len
, "WORDCHARS", utf8
)) {
494 /* parse in the ignored characters (for example, Arabic optional diacretics charachters */
495 if (strncmp(line
,"IGNORE",6) == 0) {
496 if (parse_array(line
, &ignorechars
, &ignorechars_utf16
, &ignorechars_utf16_len
, "IGNORE", utf8
)) {
502 /* parse in the typical fault correcting table */
503 if (strncmp(line
,"REP",3) == 0) {
504 if (parse_reptable(line
, afflst
)) {
510 /* parse in the checkcompoundpattern table */
511 if (strncmp(line
,"CHECKCOMPOUNDPATTERN",20) == 0) {
512 if (parse_checkcpdtable(line
, afflst
)) {
518 /* parse in the defcompound table */
519 if (strncmp(line
,"COMPOUNDRULE",12) == 0) {
520 if (parse_defcpdtable(line
, afflst
)) {
526 /* parse in the related character map table */
527 if (strncmp(line
,"MAP",3) == 0) {
528 if (parse_maptable(line
, afflst
)) {
534 /* parse in the word breakpoints table */
535 if (strncmp(line
,"BREAK",5) == 0) {
536 if (parse_breaktable(line
, afflst
)) {
542 /* parse in the language for language specific codes */
543 if (strncmp(line
,"LANG",4) == 0) {
544 if (parse_string(line
, &lang
, "LANG")) {
548 langnum
= get_lang_num(lang
);
551 if (strncmp(line
,"VERSION",7) == 0) {
552 if (parse_string(line
, &version
, "VERSION")) {
558 if (strncmp(line
,"MAXNGRAMSUGS",12) == 0) {
559 if (parse_num(line
, &maxngramsugs
, "MAXNGRAMSUGS")) {
565 if (strncmp(line
,"NOSPLITSUGS",11) == 0) {
569 if (strncmp(line
,"SUGSWITHDOTS",12) == 0) {
573 /* parse in the flag used by forbidden words */
574 if (strncmp(line
,"KEEPCASE",8) == 0) {
575 if (parse_flag(line
, &keepcase
, "KEEPCASE")) {
581 if (strncmp(line
,"CHECKSHARPS",11) == 0) {
585 /* parse this affix: P - prefix, S - suffix */
587 if (strncmp(line
,"PFX",3) == 0) ft
= complexprefixes
? 'S' : 'P';
588 if (strncmp(line
,"SFX",3) == 0) ft
= complexprefixes
? 'P' : 'S';
591 for (int i
= 0; i
< CONTSIZE
; i
++) dupflags
[i
] = 0;
594 if (parse_affix(line
, ft
, afflst
, dupflags
)) {
596 process_pfx_tree_to_list();
597 process_sfx_tree_to_list();
605 // convert affix trees to sorted list
606 process_pfx_tree_to_list();
607 process_sfx_tree_to_list();
609 // now we can speed up performance greatly taking advantage of the
610 // relationship between the affixes and the idea of "subsets".
612 // View each prefix as a potential leading subset of another and view
613 // each suffix (reversed) as a potential trailing subset of another.
615 // To illustrate this relationship if we know the prefix "ab" is found in the
616 // word to examine, only prefixes that "ab" is a leading subset of need be examined.
617 // Furthermore is "ab" is not present then none of the prefixes that "ab" is
618 // is a subset need be examined.
619 // The same argument goes for suffix string that are reversed.
621 // Then to top this off why not examine the first char of the word to quickly
622 // limit the set of prefixes to examine (i.e. the prefixes to examine must
623 // be leading supersets of the first character of the word (if they exist)
625 // To take advantage of this "subset" relationship, we need to add two links
626 // from entry. One to take next if the current prefix is found (call it nexteq)
627 // and one to take next if the current prefix is not found (call it nextne).
629 // Since we have built ordered lists, all that remains is to properly intialize
630 // the nextne and nexteq pointers that relate them
635 // expand wordchars string, based on csutil (for external tokenization)
637 char * enc
= get_encoding();
638 csconv
= get_current_cs(enc
);
644 strcpy(expw
, wordchars
);
648 for (int i
= 0; i
<= 255; i
++) {
649 if ( (csconv
[i
].cupper
!= csconv
[i
].clower
) &&
650 (! strchr(expw
, (char) i
))) {
651 *(expw
+ strlen(expw
) + 1) = '\0';
652 *(expw
+ strlen(expw
)) = (char) i
;
656 wordchars
= mystrdup(expw
);
658 // temporary BREAK definition for German dash handling (OOo issue 64400)
659 if ((langnum
== LANG_de
) && (!breaktable
)) {
660 breaktable
= (char **) malloc(sizeof(char *));
661 if (!breaktable
) return 1;
662 breaktable
[0] = mystrdup("-");
669 // we want to be able to quickly access prefix information
670 // both by prefix flag, and sorted by prefix string itself
671 // so we need to set up two indexes
673 int AffixMgr::build_pfxtree(AffEntry
* pfxptr
)
677 PfxEntry
* ep
= (PfxEntry
*) pfxptr
;
679 // get the right starting points
680 const char * key
= ep
->getKey();
681 const unsigned char flg
= (unsigned char) (ep
->getFlag() & 0x00FF);
683 // first index by flag which must exist
684 ptr
= (PfxEntry
*)pFlag
[flg
];
686 pFlag
[flg
] = (AffEntry
*) ep
;
689 // handle the special case of null affix string
690 if (strlen(key
) == 0) {
691 // always inset them at head of list at element 0
692 ptr
= (PfxEntry
*)pStart
[0];
694 pStart
[0] = (AffEntry
*)ep
;
698 // now handle the normal case
702 unsigned char sp
= *((const unsigned char *)key
);
703 ptr
= (PfxEntry
*)pStart
[sp
];
705 // handle the first insert
707 pStart
[sp
] = (AffEntry
*)ep
;
712 // otherwise use binary tree insertion so that a sorted
713 // list can easily be generated later
717 if (strcmp(ep
->getKey(), ptr
->getKey() ) <= 0) {
718 ptr
= ptr
->getNextEQ();
724 ptr
= ptr
->getNextNE();
734 // we want to be able to quickly access suffix information
735 // both by suffix flag, and sorted by the reverse of the
736 // suffix string itself; so we need to set up two indexes
737 int AffixMgr::build_sfxtree(AffEntry
* sfxptr
)
741 SfxEntry
* ep
= (SfxEntry
*) sfxptr
;
743 /* get the right starting point */
744 const char * key
= ep
->getKey();
745 const unsigned char flg
= (unsigned char) (ep
->getFlag() & 0x00FF);
747 // first index by flag which must exist
748 ptr
= (SfxEntry
*)sFlag
[flg
];
750 sFlag
[flg
] = (AffEntry
*) ep
;
752 // next index by affix string
754 // handle the special case of null affix string
755 if (strlen(key
) == 0) {
756 // always inset them at head of list at element 0
757 ptr
= (SfxEntry
*)sStart
[0];
759 sStart
[0] = (AffEntry
*)ep
;
763 // now handle the normal case
767 unsigned char sp
= *((const unsigned char *)key
);
768 ptr
= (SfxEntry
*)sStart
[sp
];
770 // handle the first insert
772 sStart
[sp
] = (AffEntry
*)ep
;
776 // otherwise use binary tree insertion so that a sorted
777 // list can easily be generated later
781 if (strcmp(ep
->getKey(), ptr
->getKey() ) <= 0) {
782 ptr
= ptr
->getNextEQ();
788 ptr
= ptr
->getNextNE();
798 // convert from binary tree to sorted list
799 int AffixMgr::process_pfx_tree_to_list()
801 for (int i
=1; i
< SETSIZE
; i
++) {
802 pStart
[i
] = process_pfx_in_order(pStart
[i
],NULL
);
808 AffEntry
* AffixMgr::process_pfx_in_order(AffEntry
* ptr
, AffEntry
* nptr
)
811 nptr
= process_pfx_in_order(((PfxEntry
*) ptr
)->getNextNE(), nptr
);
812 ((PfxEntry
*) ptr
)->setNext((PfxEntry
*) nptr
);
813 nptr
= process_pfx_in_order(((PfxEntry
*) ptr
)->getNextEQ(), ptr
);
819 // convert from binary tree to sorted list
820 int AffixMgr:: process_sfx_tree_to_list()
822 for (int i
=1; i
< SETSIZE
; i
++) {
823 sStart
[i
] = process_sfx_in_order(sStart
[i
],NULL
);
828 AffEntry
* AffixMgr::process_sfx_in_order(AffEntry
* ptr
, AffEntry
* nptr
)
831 nptr
= process_sfx_in_order(((SfxEntry
*) ptr
)->getNextNE(), nptr
);
832 ((SfxEntry
*) ptr
)->setNext((SfxEntry
*) nptr
);
833 nptr
= process_sfx_in_order(((SfxEntry
*) ptr
)->getNextEQ(), ptr
);
839 // reinitialize the PfxEntry links NextEQ and NextNE to speed searching
840 // using the idea of leading subsets this time
841 int AffixMgr::process_pfx_order()
845 // loop through each prefix list starting point
846 for (int i
=1; i
< SETSIZE
; i
++) {
848 ptr
= (PfxEntry
*)pStart
[i
];
850 // look through the remainder of the list
851 // and find next entry with affix that
852 // the current one is not a subset of
853 // mark that as destination for NextNE
854 // use next in list that you are a subset
857 for (; ptr
!= NULL
; ptr
= ptr
->getNext()) {
859 PfxEntry
* nptr
= ptr
->getNext();
860 for (; nptr
!= NULL
; nptr
= nptr
->getNext()) {
861 if (! isSubset( ptr
->getKey() , nptr
->getKey() )) break;
863 ptr
->setNextNE(nptr
);
864 ptr
->setNextEQ(NULL
);
865 if ((ptr
->getNext()) && isSubset(ptr
->getKey() , (ptr
->getNext())->getKey()))
866 ptr
->setNextEQ(ptr
->getNext());
869 // now clean up by adding smart search termination strings:
870 // if you are already a superset of the previous prefix
871 // but not a subset of the next, search can end here
872 // so set NextNE properly
874 ptr
= (PfxEntry
*) pStart
[i
];
875 for (; ptr
!= NULL
; ptr
= ptr
->getNext()) {
876 PfxEntry
* nptr
= ptr
->getNext();
877 PfxEntry
* mptr
= NULL
;
878 for (; nptr
!= NULL
; nptr
= nptr
->getNext()) {
879 if (! isSubset(ptr
->getKey(),nptr
->getKey())) break;
882 if (mptr
) mptr
->setNextNE(NULL
);
888 // initialize the SfxEntry links NextEQ and NextNE to speed searching
889 // using the idea of leading subsets this time
890 int AffixMgr::process_sfx_order()
894 // loop through each prefix list starting point
895 for (int i
=1; i
< SETSIZE
; i
++) {
897 ptr
= (SfxEntry
*) sStart
[i
];
899 // look through the remainder of the list
900 // and find next entry with affix that
901 // the current one is not a subset of
902 // mark that as destination for NextNE
903 // use next in list that you are a subset
906 for (; ptr
!= NULL
; ptr
= ptr
->getNext()) {
907 SfxEntry
* nptr
= ptr
->getNext();
908 for (; nptr
!= NULL
; nptr
= nptr
->getNext()) {
909 if (! isSubset(ptr
->getKey(),nptr
->getKey())) break;
911 ptr
->setNextNE(nptr
);
912 ptr
->setNextEQ(NULL
);
913 if ((ptr
->getNext()) && isSubset(ptr
->getKey(),(ptr
->getNext())->getKey()))
914 ptr
->setNextEQ(ptr
->getNext());
918 // now clean up by adding smart search termination strings:
919 // if you are already a superset of the previous suffix
920 // but not a subset of the next, search can end here
921 // so set NextNE properly
923 ptr
= (SfxEntry
*) sStart
[i
];
924 for (; ptr
!= NULL
; ptr
= ptr
->getNext()) {
925 SfxEntry
* nptr
= ptr
->getNext();
926 SfxEntry
* mptr
= NULL
;
927 for (; nptr
!= NULL
; nptr
= nptr
->getNext()) {
928 if (! isSubset(ptr
->getKey(),nptr
->getKey())) break;
931 if (mptr
) mptr
->setNextNE(NULL
);
939 // takes aff file condition string and creates the
940 // conds array - please see the appendix at the end of the
941 // file affentry.cxx which describes what is going on here
942 // in much more detail
944 int AffixMgr::encodeit(struct affentry
* ptr
, char * cs
)
948 unsigned char mbr
[MAXLNLEN
];
949 w_char wmbr
[MAXLNLEN
];
950 w_char
* wpos
= wmbr
;
952 // now clear the conditions array */
953 for (i
=0;i
<SETSIZE
;i
++) ptr
->conds
.base
[i
] = (unsigned char) 0;
955 // now parse the string to create the conds array */
957 unsigned char neg
= 0; // complement indicator
958 int grp
= 0; // group indicator
959 unsigned char n
= 0; // number of conditions
960 int ec
= 0; // end condition indicator
961 int nm
= 0; // number of member in group
963 // if no condition just return
964 if (strcmp(cs
,".")==0) {
971 c
= *((unsigned char *)(cs
+ i
));
973 // start group indicator
980 if ((grp
== 1) && (c
== '^')) {
985 // end goup indicator
991 // add character of group to list
992 if ((grp
== 1) && (c
!= 0)) {
1007 // set the proper bits in the condition array vals for those chars
1008 for (j
=0;j
<nm
;j
++) {
1009 k
= (unsigned int) mbr
[j
];
1010 ptr
->conds
.base
[k
] = ptr
->conds
.base
[k
] | ((unsigned char)1 << n
);
1013 // complement so set all of them and then unset indicated ones
1014 for (j
=0;j
<SETSIZE
;j
++) ptr
->conds
.base
[j
] = ptr
->conds
.base
[j
] | ((unsigned char)1 << n
);
1015 for (j
=0;j
<nm
;j
++) {
1016 k
= (unsigned int) mbr
[j
];
1017 ptr
->conds
.base
[k
] = ptr
->conds
.base
[k
] & ~((unsigned char)1 << n
);
1024 // not a group so just set the proper bit for this char
1025 // but first handle special case of . inside condition
1027 // wild card character so set them all
1028 for (j
=0;j
<SETSIZE
;j
++) ptr
->conds
.base
[j
] = ptr
->conds
.base
[j
] | ((unsigned char)1 << n
);
1030 ptr
->conds
.base
[(unsigned int) c
] = ptr
->conds
.base
[(unsigned int)c
] | ((unsigned char)1 << n
);
1035 } else { // UTF-8 character set
1037 ptr
->conds
.utf8
.neg
[n
] = neg
;
1039 // set the proper bits in the condition array vals for those chars
1040 for (j
=0;j
<nm
;j
++) {
1041 k
= (unsigned int) mbr
[j
];
1043 u8_u16(wpos
, 1, (char *) mbr
+ j
);
1045 if ((k
& 0xe0) == 0xe0) j
+=2; else j
++; // 3-byte UTF-8 character
1047 ptr
->conds
.utf8
.ascii
[k
] = ptr
->conds
.utf8
.ascii
[k
] | ((unsigned char)1 << n
);
1050 } else { // neg == 1
1051 // complement so set all of them and then unset indicated ones
1052 for (j
=0;j
<(SETSIZE
/2);j
++) ptr
->conds
.utf8
.ascii
[j
] = ptr
->conds
.utf8
.ascii
[j
] | ((unsigned char)1 << n
);
1053 for (j
=0;j
<nm
;j
++) {
1054 k
= (unsigned int) mbr
[j
];
1056 u8_u16(wpos
, 1, (char *) mbr
+ j
);
1058 if ((k
& 0xe0) == 0xe0) j
+=2; else j
++; // 3-byte UTF-8 character
1060 ptr
->conds
.utf8
.ascii
[k
] = ptr
->conds
.utf8
.ascii
[k
] & ~((unsigned char)1 << n
);
1067 ptr
->conds
.utf8
.wlen
[n
] = wpos
- wmbr
;
1068 if ((wpos
- wmbr
) != 0) {
1069 ptr
->conds
.utf8
.wchars
[n
] = (w_char
*) malloc(sizeof(w_char
) * (wpos
- wmbr
));
1070 if (!ptr
->conds
.utf8
.wchars
[n
]) return 1;
1071 memcpy(ptr
->conds
.utf8
.wchars
[n
], wmbr
, sizeof(w_char
) * (wpos
- wmbr
));
1072 flag_qsort((unsigned short *) ptr
->conds
.utf8
.wchars
[n
], 0, ptr
->conds
.utf8
.wlen
[n
]);
1075 } else { // grp == 0
1076 // is UTF-8 character?
1078 ptr
->conds
.utf8
.wchars
[n
] = (w_char
*) malloc(sizeof(w_char
));
1079 if (!ptr
->conds
.utf8
.wchars
[n
]) return 1;
1080 ptr
->conds
.utf8
.wlen
[n
] = 1;
1081 u8_u16(ptr
->conds
.utf8
.wchars
[n
], 1, cs
+ i
);
1082 if ((c
& 0xe0) == 0xe0) i
+=2; else i
++; // 3-byte UFT-8 character
1084 ptr
->conds
.utf8
.wchars
[n
] = NULL
;
1085 // not a group so just set the proper bit for this char
1086 // but first handle special case of . inside condition
1088 ptr
->conds
.utf8
.all
[n
] = 1;
1089 // wild card character so set them all
1090 for (j
=0;j
<(SETSIZE
/2);j
++) ptr
->conds
.utf8
.ascii
[j
] = ptr
->conds
.utf8
.ascii
[j
] | ((unsigned char)1 << n
);
1092 ptr
->conds
.utf8
.all
[n
] = 0;
1093 ptr
->conds
.utf8
.ascii
[(unsigned int) c
] = ptr
->conds
.utf8
.ascii
[(unsigned int)c
] | ((unsigned char)1 << n
);
1110 // return 1 if s1 is a leading subset of s2
1111 /* inline int AffixMgr::isSubset(const char * s1, const char * s2)
1113 while ((*s1 == *s2) && *s1) {
1117 return (*s1 == '\0');
1121 // return 1 if s1 is a leading subset of s2 (dots are for infixes)
1122 inline int AffixMgr::isSubset(const char * s1
, const char * s2
)
1124 while (((*s1
== *s2
) || (*s1
== '.')) && (*s1
!= '\0')) {
1128 return (*s1
== '\0');
1132 // check word for prefixes
1133 struct hentry
* AffixMgr::prefix_check(const char * word
, int len
, char in_compound
,
1134 const FLAG needflag
)
1136 struct hentry
* rv
= NULL
;
1142 // first handle the special case of 0 length prefixes
1143 PfxEntry
* pe
= (PfxEntry
*) pStart
[0];
1147 ((in_compound
!= IN_CPD_NOT
) || !(pe
->getCont() &&
1148 (TESTAFF(pe
->getCont(), onlyincompound
, pe
->getContLen())))) &&
1149 // permit prefixes in compounds
1150 ((in_compound
!= IN_CPD_END
) || (pe
->getCont() &&
1151 (TESTAFF(pe
->getCont(), compoundpermitflag
, pe
->getContLen()))))
1154 rv
= pe
->checkword(word
, len
, in_compound
, needflag
);
1156 pfx
=(AffEntry
*)pe
; // BUG: pfx not stateless
1163 // now handle the general case
1164 unsigned char sp
= *((const unsigned char *)word
);
1165 PfxEntry
* pptr
= (PfxEntry
*)pStart
[sp
];
1168 if (isSubset(pptr
->getKey(),word
)) {
1171 ((in_compound
!= IN_CPD_NOT
) || !(pptr
->getCont() &&
1172 (TESTAFF(pptr
->getCont(), onlyincompound
, pptr
->getContLen())))) &&
1173 // permit prefixes in compounds
1174 ((in_compound
!= IN_CPD_END
) || (pptr
->getCont() &&
1175 (TESTAFF(pptr
->getCont(), compoundpermitflag
, pptr
->getContLen()))))
1178 rv
= pptr
->checkword(word
, len
, in_compound
, needflag
);
1180 pfx
=(AffEntry
*)pptr
; // BUG: pfx not stateless
1184 pptr
= pptr
->getNextEQ();
1186 pptr
= pptr
->getNextNE();
1193 // check word for prefixes
1194 struct hentry
* AffixMgr::prefix_check_twosfx(const char * word
, int len
,
1195 char in_compound
, const FLAG needflag
)
1197 struct hentry
* rv
= NULL
;
1202 // first handle the special case of 0 length prefixes
1203 PfxEntry
* pe
= (PfxEntry
*) pStart
[0];
1206 rv
= pe
->check_twosfx(word
, len
, in_compound
, needflag
);
1211 // now handle the general case
1212 unsigned char sp
= *((const unsigned char *)word
);
1213 PfxEntry
* pptr
= (PfxEntry
*)pStart
[sp
];
1216 if (isSubset(pptr
->getKey(),word
)) {
1217 rv
= pptr
->check_twosfx(word
, len
, in_compound
, needflag
);
1219 pfx
= (AffEntry
*)pptr
;
1222 pptr
= pptr
->getNextEQ();
1224 pptr
= pptr
->getNextNE();
1231 #ifdef HUNSPELL_EXPERIMENTAL
1232 // check word for prefixes
1233 char * AffixMgr::prefix_check_morph(const char * word
, int len
, char in_compound
,
1234 const FLAG needflag
)
1238 char result
[MAXLNLEN
];
1244 // first handle the special case of 0 length prefixes
1245 PfxEntry
* pe
= (PfxEntry
*) pStart
[0];
1247 st
= pe
->check_morph(word
,len
,in_compound
, needflag
);
1252 // if (rv) return rv;
1256 // now handle the general case
1257 unsigned char sp
= *((const unsigned char *)word
);
1258 PfxEntry
* pptr
= (PfxEntry
*)pStart
[sp
];
1261 if (isSubset(pptr
->getKey(),word
)) {
1262 st
= pptr
->check_morph(word
,len
,in_compound
, needflag
);
1265 if ((in_compound
!= IN_CPD_NOT
) || !((pptr
->getCont() &&
1266 (TESTAFF(pptr
->getCont(), onlyincompound
, pptr
->getContLen()))))) {
1268 pfx
= (AffEntry
*)pptr
;
1272 pptr
= pptr
->getNextEQ();
1274 pptr
= pptr
->getNextNE();
1278 if (*result
) return mystrdup(result
);
1283 // check word for prefixes
1284 char * AffixMgr::prefix_check_twosfx_morph(const char * word
, int len
,
1285 char in_compound
, const FLAG needflag
)
1289 char result
[MAXLNLEN
];
1295 // first handle the special case of 0 length prefixes
1296 PfxEntry
* pe
= (PfxEntry
*) pStart
[0];
1298 st
= pe
->check_twosfx_morph(word
,len
,in_compound
, needflag
);
1306 // now handle the general case
1307 unsigned char sp
= *((const unsigned char *)word
);
1308 PfxEntry
* pptr
= (PfxEntry
*)pStart
[sp
];
1311 if (isSubset(pptr
->getKey(),word
)) {
1312 st
= pptr
->check_twosfx_morph(word
, len
, in_compound
, needflag
);
1316 pfx
= (AffEntry
*)pptr
;
1318 pptr
= pptr
->getNextEQ();
1320 pptr
= pptr
->getNextNE();
1324 if (*result
) return mystrdup(result
);
1327 #endif // END OF HUNSPELL_EXPERIMENTAL CODE
1330 // Is word a non compound with a REP substitution (see checkcompoundrep)?
1331 int AffixMgr::cpdrep_check(const char * word
, int wl
)
1333 char candidate
[MAXLNLEN
];
1337 if ((wl
< 2) || !numrep
) return 0;
1339 for (int i
=0; i
< numrep
; i
++ ) {
1341 lenr
= strlen(reptable
[i
].pattern2
);
1342 lenp
= strlen(reptable
[i
].pattern
);
1343 // search every occurence of the pattern in the word
1344 while ((r
=strstr(r
, reptable
[i
].pattern
)) != NULL
) {
1345 strcpy(candidate
, word
);
1346 if (r
-word
+ lenr
+ strlen(r
+lenp
) >= MAXLNLEN
) break;
1347 strcpy(candidate
+(r
-word
),reptable
[i
].pattern2
);
1348 strcpy(candidate
+(r
-word
)+lenr
, r
+lenp
);
1349 if (candidate_check(candidate
,strlen(candidate
))) return 1;
1350 r
++; // search for the next letter
1356 // forbid compoundings when there are special patterns at word bound
1357 int AffixMgr::cpdpat_check(const char * word
, int pos
)
1360 for (int i
= 0; i
< numcheckcpd
; i
++) {
1361 if (isSubset(checkcpdtable
[i
].pattern2
, word
+ pos
) &&
1362 (len
= strlen(checkcpdtable
[i
].pattern
)) && (pos
> len
) &&
1363 (strncmp(word
+ pos
- len
, checkcpdtable
[i
].pattern
, len
) == 0)) return 1;
1368 // forbid compounding with neighbouring upper and lower case characters at word bounds
1369 int AffixMgr::cpdcase_check(const char * word
, int pos
)
1374 u8_u16(&u
, 1, word
+ pos
);
1375 for (p
= word
+ pos
- 1; (*p
& 0xc0) == 0x80; p
--);
1377 unsigned short a
= (u
.h
<< 8) + u
.l
;
1378 unsigned short b
= (w
.h
<< 8) + w
.l
;
1379 if (((unicodetoupper(a
, langnum
) == a
) || (unicodetoupper(b
, langnum
) == b
))) return 1;
1381 unsigned char a
= *(word
+ pos
- 1);
1382 unsigned char b
= *(word
+ pos
);
1383 if ((csconv
[a
].ccase
|| csconv
[b
].ccase
) && (a
!= '-') && (b
!= '-')) return 1;
1388 // check compound patterns
1389 int AffixMgr::defcpd_check(hentry
*** words
, short wnum
, hentry
* rv
, hentry
** def
, char all
)
1391 signed short btpp
[MAXWORDLEN
]; // metacharacter (*, ?) positions for backtracking
1392 signed short btwp
[MAXWORDLEN
]; // word positions for metacharacters
1393 int btnum
[MAXWORDLEN
]; // number of matched characters in metacharacter positions
1402 (*words
)[wnum
] = rv
;
1404 for (i
= 0; i
< numdefcpd
; i
++) {
1405 signed short pp
= 0; // pattern position
1406 signed short wp
= 0; // "words" position
1411 while ((pp
< defcpdtable
[i
].len
) && (wp
<= wnum
)) {
1412 if (((pp
+1) < defcpdtable
[i
].len
) &&
1413 ((defcpdtable
[i
].def
[pp
+1] == '*') || (defcpdtable
[i
].def
[pp
+1] == '?'))) {
1414 int wend
= (defcpdtable
[i
].def
[pp
+1] == '?') ? wp
: wnum
;
1419 while (wp
<= wend
) {
1420 if (!(*words
)[wp
]->alen
||
1421 !TESTAFF((*words
)[wp
]->astr
, defcpdtable
[i
].def
[pp
-2], (*words
)[wp
]->alen
)) {
1427 if (wp
<= wnum
) ok2
= 0;
1428 btnum
[bt
] = wp
- btwp
[bt
];
1429 if (btnum
[bt
] > 0) bt
++;
1433 if (!(*words
)[wp
] || !(*words
)[wp
]->alen
||
1434 !TESTAFF((*words
)[wp
]->astr
, defcpdtable
[i
].def
[pp
], (*words
)[wp
]->alen
)) {
1440 if ((defcpdtable
[i
].len
== pp
) && !(wp
> wnum
)) ok
= 0;
1445 while ((defcpdtable
[i
].len
> r
) && ((r
+1) < defcpdtable
[i
].len
) &&
1446 ((defcpdtable
[i
].def
[r
+1] == '*') || (defcpdtable
[i
].def
[r
+1] == '?'))) r
+=2;
1447 if (defcpdtable
[i
].len
<= r
) return 1;
1454 wp
= btwp
[bt
- 1] + btnum
[bt
- 1];
1455 } while ((btnum
[bt
- 1] < 0) && --bt
);
1458 if (ok
&& ok2
&& (!all
|| (defcpdtable
[i
].len
<= pp
))) return 1;
1459 // check zero ending
1460 while (ok
&& ok2
&& (defcpdtable
[i
].len
> pp
) && ((pp
+1) < defcpdtable
[i
].len
) &&
1461 ((defcpdtable
[i
].def
[pp
+1] == '*') || (defcpdtable
[i
].def
[pp
+1] == '?'))) pp
+=2;
1462 if (ok
&& ok2
&& (defcpdtable
[i
].len
<= pp
)) return 1;
1464 (*words
)[wnum
] = NULL
;
1465 if (w
) *words
= NULL
;
1469 inline int AffixMgr::candidate_check(const char * word
, int len
)
1471 struct hentry
* rv
=NULL
;
1476 // rv = prefix_check(word,len,1);
1477 // if (rv) return 1;
1479 rv
= affix_check(word
,len
);
1484 // calculate number of syllable for compound-checking
1485 short AffixMgr::get_syllable(const char * word
, int wlen
)
1487 if (cpdmaxsyllable
==0) return 0;
1492 for (int i
=0; i
<wlen
; i
++) {
1493 if (strchr(cpdvowels
, word
[i
])) num
++;
1495 } else if (cpdvowels_utf16
) {
1496 w_char w
[MAXWORDUTF8LEN
];
1497 int i
= u8_u16(w
, MAXWORDUTF8LEN
, word
);
1499 if (flag_bsearch((unsigned short *) cpdvowels_utf16
,
1500 ((unsigned short *) w
)[i
- 1], cpdvowels_utf16_len
)) num
++;
1506 // check if compound word is correctly spelled
1507 // hu_mov_rule = spec. Hungarian rule (XXX)
1508 struct hentry
* AffixMgr::compound_check(const char * word
, int len
,
1509 short wordnum
, short numsyllable
, short maxwordnum
, short wnum
, hentry
** words
= NULL
,
1510 char hu_mov_rule
= 0, int * cmpdstemnum
= NULL
, int * cmpdstem
= NULL
, char is_sug
= 0)
1513 short oldnumsyllable
, oldnumsyllable2
, oldwordnum
, oldwordnum2
;
1514 int oldcmpdstemnum
= 0;
1515 struct hentry
* rv
= NULL
;
1516 struct hentry
* rv_first
;
1517 struct hentry
* rwords
[MAXWORDLEN
]; // buffer for COMPOUND pattern checking
1518 char st
[MAXWORDUTF8LEN
+ 4];
1535 for (cmin
= 0, i
= 0; (i
< cpdmin
) && word
[cmin
]; i
++) {
1537 for (; (word
[cmin
] & 0xc0) == 0x80; cmin
++);
1539 for (cmax
= len
, i
= 0; (i
< (cpdmin
- 1)) && cmax
; i
++) {
1541 for (; (word
[cmax
] & 0xc0) == 0x80; cmax
--);
1545 cmax
= len
- cpdmin
+ 1;
1550 for (i
= cmin
; i
< cmax
; i
++) {
1552 oldnumsyllable
= numsyllable
;
1553 oldwordnum
= wordnum
;
1556 // go to end of the UTF-8 character
1558 for (; (st
[i
] & 0xc0) == 0x80; i
++);
1559 if (i
>= cmax
) return NULL
;
1571 rv
= lookup(st
); // perhaps without prefix
1573 // search homonym with compound flag
1574 while ((rv
) && !hu_mov_rule
&&
1575 ((pseudoroot
&& TESTAFF(rv
->astr
, pseudoroot
, rv
->alen
)) ||
1576 !((compoundflag
&& !words
&& TESTAFF(rv
->astr
, compoundflag
, rv
->alen
)) ||
1577 (compoundbegin
&& !wordnum
&&
1578 TESTAFF(rv
->astr
, compoundbegin
, rv
->alen
)) ||
1579 (compoundmiddle
&& wordnum
&& !words
&&
1580 TESTAFF(rv
->astr
, compoundmiddle
, rv
->alen
)) ||
1582 ((!words
&& !wordnum
&& defcpd_check(&words
, wnum
, rv
, (hentry
**) &rwords
, 0)) ||
1583 (words
&& defcpd_check(&words
, wnum
, rv
, (hentry
**) &rwords
, 0))))
1585 rv
= rv
->next_homonym
;
1590 !(rv
= prefix_check(st
, i
, hu_mov_rule
? IN_CPD_OTHER
: IN_CPD_BEGIN
, compoundflag
))) {
1591 if ((rv
= suffix_check(st
, i
, 0, NULL
, NULL
, 0, NULL
,
1592 FLAG_NULL
, compoundflag
, hu_mov_rule
? IN_CPD_OTHER
: IN_CPD_BEGIN
)) && !hu_mov_rule
&&
1593 ((SfxEntry
*)sfx
)->getCont() &&
1594 ((compoundforbidflag
&& TESTAFF(((SfxEntry
*)sfx
)->getCont(), compoundforbidflag
,
1595 ((SfxEntry
*)sfx
)->getContLen())) || (compoundend
&&
1596 TESTAFF(((SfxEntry
*)sfx
)->getCont(), compoundend
,
1597 ((SfxEntry
*)sfx
)->getContLen())))) {
1602 (((wordnum
== 0) && compoundbegin
&&
1603 ((rv
= suffix_check(st
, i
, 0, NULL
, NULL
, 0, NULL
, FLAG_NULL
, compoundbegin
, hu_mov_rule
? IN_CPD_OTHER
: IN_CPD_BEGIN
)) ||
1604 (rv
= prefix_check(st
, i
, hu_mov_rule
? IN_CPD_OTHER
: IN_CPD_BEGIN
, compoundbegin
)))) ||
1605 ((wordnum
> 0) && compoundmiddle
&&
1606 ((rv
= suffix_check(st
, i
, 0, NULL
, NULL
, 0, NULL
, FLAG_NULL
, compoundmiddle
, hu_mov_rule
? IN_CPD_OTHER
: IN_CPD_BEGIN
)) ||
1607 (rv
= prefix_check(st
, i
, hu_mov_rule
? IN_CPD_OTHER
: IN_CPD_BEGIN
, compoundmiddle
)))))
1608 ) checked_prefix
= 1;
1609 // else check forbiddenwords and pseudoroot
1610 } else if (rv
->astr
&& (TESTAFF(rv
->astr
, forbiddenword
, rv
->alen
) ||
1611 TESTAFF(rv
->astr
, pseudoroot
, rv
->alen
) ||
1612 (is_sug
&& nosuggest
&& TESTAFF(rv
->astr
, nosuggest
, rv
->alen
))
1618 // check non_compound flag in suffix and prefix
1619 if ((rv
) && !hu_mov_rule
&&
1620 ((pfx
&& ((PfxEntry
*)pfx
)->getCont() &&
1621 TESTAFF(((PfxEntry
*)pfx
)->getCont(), compoundforbidflag
,
1622 ((PfxEntry
*)pfx
)->getContLen())) ||
1623 (sfx
&& ((SfxEntry
*)sfx
)->getCont() &&
1624 TESTAFF(((SfxEntry
*)sfx
)->getCont(), compoundforbidflag
,
1625 ((SfxEntry
*)sfx
)->getContLen())))) {
1629 // check compoundend flag in suffix and prefix
1630 if ((rv
) && !checked_prefix
&& compoundend
&& !hu_mov_rule
&&
1631 ((pfx
&& ((PfxEntry
*)pfx
)->getCont() &&
1632 TESTAFF(((PfxEntry
*)pfx
)->getCont(), compoundend
,
1633 ((PfxEntry
*)pfx
)->getContLen())) ||
1634 (sfx
&& ((SfxEntry
*)sfx
)->getCont() &&
1635 TESTAFF(((SfxEntry
*)sfx
)->getCont(), compoundend
,
1636 ((SfxEntry
*)sfx
)->getContLen())))) {
1640 // check compoundmiddle flag in suffix and prefix
1641 if ((rv
) && !checked_prefix
&& (wordnum
==0) && compoundmiddle
&& !hu_mov_rule
&&
1642 ((pfx
&& ((PfxEntry
*)pfx
)->getCont() &&
1643 TESTAFF(((PfxEntry
*)pfx
)->getCont(), compoundmiddle
,
1644 ((PfxEntry
*)pfx
)->getContLen())) ||
1645 (sfx
&& ((SfxEntry
*)sfx
)->getCont() &&
1646 TESTAFF(((SfxEntry
*)sfx
)->getCont(), compoundmiddle
,
1647 ((SfxEntry
*)sfx
)->getContLen())))) {
1651 // check forbiddenwords
1652 if ((rv
) && (rv
->astr
) && (TESTAFF(rv
->astr
, forbiddenword
, rv
->alen
) ||
1653 (is_sug
&& nosuggest
&& TESTAFF(rv
->astr
, nosuggest
, rv
->alen
)))) {
1657 // increment word number, if the second root has a compoundroot flag
1658 if ((rv
) && compoundroot
&&
1659 (TESTAFF(rv
->astr
, compoundroot
, rv
->alen
))) {
1663 // first word is acceptable in compound words?
1665 ( checked_prefix
|| (words
&& words
[wnum
]) ||
1666 (compoundflag
&& TESTAFF(rv
->astr
, compoundflag
, rv
->alen
)) ||
1667 ((oldwordnum
== 0) && compoundbegin
&& TESTAFF(rv
->astr
, compoundbegin
, rv
->alen
)) ||
1668 ((oldwordnum
> 0) && compoundmiddle
&& TESTAFF(rv
->astr
, compoundmiddle
, rv
->alen
))// ||
1671 // LANG_hu section: spec. Hungarian rule
1672 || ((langnum
== LANG_hu
) && hu_mov_rule
&& (
1673 TESTAFF(rv
->astr
, 'F', rv
->alen
) || // XXX hardwired Hungarian dictionary codes
1674 TESTAFF(rv
->astr
, 'G', rv
->alen
) ||
1675 TESTAFF(rv
->astr
, 'H', rv
->alen
)
1678 // END of LANG_hu section
1680 && ! (( checkcompoundtriple
&& // test triple letters
1681 (word
[i
-1]==word
[i
]) && (
1682 ((i
>1) && (word
[i
-1]==word
[i
-2])) ||
1683 ((word
[i
-1]==word
[i
+1])) // may be word[i+1] == '\0'
1687 // test CHECKCOMPOUNDPATTERN
1688 numcheckcpd
&& cpdpat_check(word
, i
)
1691 checkcompoundcase
&& cpdcase_check(word
, i
)
1694 // LANG_hu section: spec. Hungarian rule
1695 || ((!rv
) && (langnum
== LANG_hu
) && hu_mov_rule
&& (rv
= affix_check(st
,i
)) &&
1696 (sfx
&& ((SfxEntry
*)sfx
)->getCont() && ( // XXX hardwired Hungarian dic. codes
1697 TESTAFF(((SfxEntry
*)sfx
)->getCont(), (unsigned short) 'x', ((SfxEntry
*)sfx
)->getContLen()) ||
1698 TESTAFF(((SfxEntry
*)sfx
)->getCont(), (unsigned short) '%', ((SfxEntry
*)sfx
)->getContLen())
1702 // END of LANG_hu section
1705 // LANG_hu section: spec. Hungarian rule
1706 if (langnum
== LANG_hu
) {
1707 // calculate syllable number of the word
1708 numsyllable
+= get_syllable(st
, i
);
1710 // + 1 word, if syllable number of the prefix > 1 (hungarian convention)
1711 if (pfx
&& (get_syllable(((PfxEntry
*)pfx
)->getKey(),strlen(((PfxEntry
*)pfx
)->getKey())) > 1)) wordnum
++;
1713 // END of LANG_hu section
1716 if (cmpdstem
) cmpdstem
[*cmpdstemnum
- 1] = i
;
1721 rv
= lookup((word
+i
)); // perhaps without prefix
1723 // search homonym with compound flag
1724 while ((rv
) && ((pseudoroot
&& TESTAFF(rv
->astr
, pseudoroot
, rv
->alen
)) ||
1725 !((compoundflag
&& !words
&& TESTAFF(rv
->astr
, compoundflag
, rv
->alen
)) ||
1726 (compoundend
&& !words
&& TESTAFF(rv
->astr
, compoundend
, rv
->alen
)) ||
1727 (numdefcpd
&& words
&& defcpd_check(&words
, wnum
+ 1, rv
, NULL
,1))))) {
1728 rv
= rv
->next_homonym
;
1731 if (rv
&& words
&& words
[wnum
+ 1]) return rv
;
1733 oldnumsyllable2
= numsyllable
;
1734 oldwordnum2
= wordnum
;
1736 // LANG_hu section: spec. Hungarian rule, XXX hardwired dictionary code
1737 if ((rv
) && (langnum
== LANG_hu
) && (TESTAFF(rv
->astr
, 'I', rv
->alen
)) && !(TESTAFF(rv
->astr
, 'J', rv
->alen
))) {
1740 // END of LANG_hu section
1742 // increment word number, if the second root has a compoundroot flag
1743 if ((rv
) && (compoundroot
) &&
1744 (TESTAFF(rv
->astr
, compoundroot
, rv
->alen
))) {
1748 // check forbiddenwords
1749 if ((rv
) && (rv
->astr
) && (TESTAFF(rv
->astr
, forbiddenword
, rv
->alen
) ||
1750 (is_sug
&& nosuggest
&& TESTAFF(rv
->astr
, nosuggest
, rv
->alen
)))) return NULL
;
1752 // second word is acceptable, as a root?
1753 // hungarian conventions: compounding is acceptable,
1754 // when compound forms consist of 2 words, or if more,
1755 // then the syllable number of root words must be 6, or lesser.
1758 (compoundflag
&& TESTAFF(rv
->astr
, compoundflag
, rv
->alen
)) ||
1759 (compoundend
&& TESTAFF(rv
->astr
, compoundend
, rv
->alen
))
1762 ((cpdwordmax
==-1) || (wordnum
+1<cpdwordmax
)) ||
1763 ((cpdmaxsyllable
==0) ||
1764 (numsyllable
+ get_syllable(rv
->word
,rv
->wlen
)<=cpdmaxsyllable
))
1767 (!checkcompounddup
|| (rv
!= rv_first
))
1771 // forbid compound word, if it is a non compound word with typical fault
1772 if (checkcompoundrep
&& cpdrep_check(word
,len
)) return NULL
;
1776 numsyllable
= oldnumsyllable2
;
1777 wordnum
= oldwordnum2
;
1779 // perhaps second word has prefix or/and suffix
1781 sfxflag
= FLAG_NULL
;
1782 rv
= (compoundflag
) ? affix_check((word
+i
),strlen(word
+i
), compoundflag
, IN_CPD_END
) : NULL
;
1783 if (!rv
&& compoundend
) {
1786 rv
= affix_check((word
+i
),strlen(word
+i
), compoundend
, IN_CPD_END
);
1789 if (!rv
&& numdefcpd
&& words
) {
1790 rv
= affix_check((word
+i
),strlen(word
+i
), 0, IN_CPD_END
);
1791 if (rv
&& defcpd_check(&words
, wnum
+ 1, rv
, NULL
, 1)) return rv
;
1794 // check non_compound flag in suffix and prefix
1796 ((pfx
&& ((PfxEntry
*)pfx
)->getCont() &&
1797 TESTAFF(((PfxEntry
*)pfx
)->getCont(), compoundforbidflag
,
1798 ((PfxEntry
*)pfx
)->getContLen())) ||
1799 (sfx
&& ((SfxEntry
*)sfx
)->getCont() &&
1800 TESTAFF(((SfxEntry
*)sfx
)->getCont(), compoundforbidflag
,
1801 ((SfxEntry
*)sfx
)->getContLen())))) {
1805 // check forbiddenwords
1806 if ((rv
) && (rv
->astr
) && (TESTAFF(rv
->astr
, forbiddenword
, rv
->alen
) ||
1807 (is_sug
&& nosuggest
&& TESTAFF(rv
->astr
, nosuggest
, rv
->alen
)))) return NULL
;
1809 // pfxappnd = prefix of word+i, or NULL
1810 // calculate syllable number of prefix.
1811 // hungarian convention: when syllable number of prefix is more,
1812 // than 1, the prefix+word counts as two words.
1814 if (langnum
== LANG_hu
) {
1815 // calculate syllable number of the word
1816 numsyllable
+= get_syllable(word
+ i
, strlen(word
+ i
));
1818 // - affix syllable num.
1819 // XXX only second suffix (inflections, not derivations)
1821 char * tmp
= myrevstrdup(sfxappnd
);
1822 numsyllable
-= get_syllable(tmp
, strlen(tmp
));
1826 // + 1 word, if syllable number of the prefix > 1 (hungarian convention)
1827 if (pfx
&& (get_syllable(((PfxEntry
*)pfx
)->getKey(),strlen(((PfxEntry
*)pfx
)->getKey())) > 1)) wordnum
++;
1829 // increment syllable num, if last word has a SYLLABLENUM flag
1830 // and the suffix is beginning `s'
1832 if (cpdsyllablenum
) {
1834 case 'c': { numsyllable
+=2; break; }
1835 case 'J': { numsyllable
+= 1; break; }
1836 case 'I': { if (TESTAFF(rv
->astr
, 'J', rv
->alen
)) numsyllable
+= 1; break; }
1841 // increment word number, if the second word has a compoundroot flag
1842 if ((rv
) && (compoundroot
) &&
1843 (TESTAFF(rv
->astr
, compoundroot
, rv
->alen
))) {
1847 // second word is acceptable, as a word with prefix or/and suffix?
1848 // hungarian conventions: compounding is acceptable,
1849 // when compound forms consist 2 word, otherwise
1850 // the syllable number of root words is 6, or lesser.
1853 ((cpdwordmax
== -1) || (wordnum
+ 1 < cpdwordmax
)) ||
1854 ((cpdmaxsyllable
== 0) ||
1855 (numsyllable
<= cpdmaxsyllable
))
1858 (!checkcompounddup
|| (rv
!= rv_first
))
1860 // forbid compound word, if it is a non compound word with typical fault
1861 if (checkcompoundrep
&& cpdrep_check(word
, len
)) return NULL
;
1865 numsyllable
= oldnumsyllable2
;
1866 wordnum
= oldwordnum2
;
1868 if (cmpdstemnum
) oldcmpdstemnum
= *cmpdstemnum
;
1870 // perhaps second word is a compound word (recursive call)
1871 if (wordnum
< maxwordnum
) {
1872 rv
= compound_check((word
+i
),strlen(word
+i
), wordnum
+1,
1873 numsyllable
, maxwordnum
, wnum
+ 1, words
,
1874 0, cmpdstemnum
, cmpdstem
, is_sug
);
1879 // forbid compound word, if it is a non compound word with typical fault
1880 if (checkcompoundrep
&& cpdrep_check(word
, len
)) return NULL
;
1884 if (cmpdstemnum
) *cmpdstemnum
= oldcmpdstemnum
;
1889 wordnum
= oldwordnum
;
1890 numsyllable
= oldnumsyllable
;
1896 #ifdef HUNSPELL_EXPERIMENTAL
1897 // check if compound word is correctly spelled
1898 // hu_mov_rule = spec. Hungarian rule (XXX)
1899 int AffixMgr::compound_check_morph(const char * word
, int len
,
1900 short wordnum
, short numsyllable
, short maxwordnum
, short wnum
, hentry
** words
,
1901 char hu_mov_rule
= 0, char ** result
= NULL
, char * partresult
= NULL
)
1904 short oldnumsyllable
, oldnumsyllable2
, oldwordnum
, oldwordnum2
;
1907 struct hentry
* rv
= NULL
;
1908 struct hentry
* rv_first
;
1909 struct hentry
* rwords
[MAXWORDLEN
]; // buffer for COMPOUND pattern checking
1910 char st
[MAXWORDUTF8LEN
+ 4];
1914 char presult
[MAXLNLEN
];
1920 for (cmin
= 0, i
= 0; (i
< cpdmin
) && word
[cmin
]; i
++) {
1922 for (; (word
[cmin
] & 0xc0) == 0x80; cmin
++);
1924 for (cmax
= len
, i
= 0; (i
< (cpdmin
- 1)) && cmax
; i
++) {
1926 for (; (word
[cmax
] & 0xc0) == 0x80; cmax
--);
1930 cmax
= len
- cpdmin
+ 1;
1935 for (i
= cmin
; i
< cmax
; i
++) {
1936 oldnumsyllable
= numsyllable
;
1937 oldwordnum
= wordnum
;
1940 // go to end of the UTF-8 character
1942 for (; (st
[i
] & 0xc0) == 0x80; i
++);
1943 if (i
>= cmax
) return 0;
1952 if (partresult
) strcat(presult
, partresult
);
1954 rv
= lookup(st
); // perhaps without prefix
1956 // search homonym with compound flag
1957 while ((rv
) && !hu_mov_rule
&&
1958 ((pseudoroot
&& TESTAFF(rv
->astr
, pseudoroot
, rv
->alen
)) ||
1959 !((compoundflag
&& !words
&& TESTAFF(rv
->astr
, compoundflag
, rv
->alen
)) ||
1960 (compoundbegin
&& !wordnum
&&
1961 TESTAFF(rv
->astr
, compoundbegin
, rv
->alen
)) ||
1962 (compoundmiddle
&& wordnum
&& !words
&&
1963 TESTAFF(rv
->astr
, compoundmiddle
, rv
->alen
)) ||
1965 ((!words
&& !wordnum
&& defcpd_check(&words
, wnum
, rv
, (hentry
**) &rwords
, 0)) ||
1966 (words
&& defcpd_check(&words
, wnum
, rv
, (hentry
**) &rwords
, 0))))
1968 rv
= rv
->next_homonym
;
1972 if (rv
->description
) {
1973 if ((!rv
->astr
) || !TESTAFF(rv
->astr
, lemma_present
, rv
->alen
))
1974 strcat(presult
, st
);
1975 strcat(presult
, rv
->description
);
1981 !(rv
= prefix_check(st
, i
, hu_mov_rule
? IN_CPD_OTHER
: IN_CPD_BEGIN
, compoundflag
))) {
1982 if ((rv
= suffix_check(st
, i
, 0, NULL
, NULL
, 0, NULL
,
1983 FLAG_NULL
, compoundflag
, hu_mov_rule
? IN_CPD_OTHER
: IN_CPD_BEGIN
)) && !hu_mov_rule
&&
1984 ((SfxEntry
*)sfx
)->getCont() &&
1985 ((compoundforbidflag
&& TESTAFF(((SfxEntry
*)sfx
)->getCont(), compoundforbidflag
,
1986 ((SfxEntry
*)sfx
)->getContLen())) || (compoundend
&&
1987 TESTAFF(((SfxEntry
*)sfx
)->getCont(), compoundend
,
1988 ((SfxEntry
*)sfx
)->getContLen())))) {
1994 (((wordnum
== 0) && compoundbegin
&&
1995 ((rv
= suffix_check(st
, i
, 0, NULL
, NULL
, 0, NULL
, FLAG_NULL
, compoundbegin
, hu_mov_rule
? IN_CPD_OTHER
: IN_CPD_BEGIN
)) ||
1996 (rv
= prefix_check(st
, i
, hu_mov_rule
? IN_CPD_OTHER
: IN_CPD_BEGIN
, compoundbegin
)))) ||
1997 ((wordnum
> 0) && compoundmiddle
&&
1998 ((rv
= suffix_check(st
, i
, 0, NULL
, NULL
, 0, NULL
, FLAG_NULL
, compoundmiddle
, hu_mov_rule
? IN_CPD_OTHER
: IN_CPD_BEGIN
)) ||
1999 (rv
= prefix_check(st
, i
, hu_mov_rule
? IN_CPD_OTHER
: IN_CPD_BEGIN
, compoundmiddle
)))))
2001 //char * p = prefix_check_morph(st, i, 0, compound);
2003 if (compoundflag
) p
= affix_check_morph(st
, i
, compoundflag
);
2004 if (!p
|| (*p
== '\0')) {
2005 if ((wordnum
== 0) && compoundbegin
) {
2006 p
= affix_check_morph(st
, i
, compoundbegin
);
2007 } else if ((wordnum
> 0) && compoundmiddle
) {
2008 p
= affix_check_morph(st
, i
, compoundmiddle
);
2013 if (strchr(p
, '\n')) {
2014 strcat(presult
, "(");
2015 strcat(presult
, line_join(p
, '|'));
2016 strcat(presult
, ")");
2021 if (presult
[strlen(presult
) - 1] == '\n') {
2022 presult
[strlen(presult
) - 1] = '\0';
2025 //strcat(presult, "+");
2027 // else check forbiddenwords
2028 } else if (rv
->astr
&& (TESTAFF(rv
->astr
, forbiddenword
, rv
->alen
) ||
2029 TESTAFF(rv
->astr
, pseudoroot
, rv
->alen
))) {
2034 // check non_compound flag in suffix and prefix
2035 if ((rv
) && !hu_mov_rule
&&
2036 ((pfx
&& ((PfxEntry
*)pfx
)->getCont() &&
2037 TESTAFF(((PfxEntry
*)pfx
)->getCont(), compoundforbidflag
,
2038 ((PfxEntry
*)pfx
)->getContLen())) ||
2039 (sfx
&& ((SfxEntry
*)sfx
)->getCont() &&
2040 TESTAFF(((SfxEntry
*)sfx
)->getCont(), compoundforbidflag
,
2041 ((SfxEntry
*)sfx
)->getContLen())))) {
2045 // check compoundend flag in suffix and prefix
2046 if ((rv
) && !checked_prefix
&& compoundend
&& !hu_mov_rule
&&
2047 ((pfx
&& ((PfxEntry
*)pfx
)->getCont() &&
2048 TESTAFF(((PfxEntry
*)pfx
)->getCont(), compoundend
,
2049 ((PfxEntry
*)pfx
)->getContLen())) ||
2050 (sfx
&& ((SfxEntry
*)sfx
)->getCont() &&
2051 TESTAFF(((SfxEntry
*)sfx
)->getCont(), compoundend
,
2052 ((SfxEntry
*)sfx
)->getContLen())))) {
2056 // check compoundmiddle flag in suffix and prefix
2057 if ((rv
) && !checked_prefix
&& (wordnum
==0) && compoundmiddle
&& !hu_mov_rule
&&
2058 ((pfx
&& ((PfxEntry
*)pfx
)->getCont() &&
2059 TESTAFF(((PfxEntry
*)pfx
)->getCont(), compoundmiddle
,
2060 ((PfxEntry
*)pfx
)->getContLen())) ||
2061 (sfx
&& ((SfxEntry
*)sfx
)->getCont() &&
2062 TESTAFF(((SfxEntry
*)sfx
)->getCont(), compoundmiddle
,
2063 ((SfxEntry
*)sfx
)->getContLen())))) {
2067 // check forbiddenwords
2068 if ((rv
) && (rv
->astr
) && TESTAFF(rv
->astr
, forbiddenword
, rv
->alen
)) continue;
2070 // increment word number, if the second root has a compoundroot flag
2071 if ((rv
) && (compoundroot
) &&
2072 (TESTAFF(rv
->astr
, compoundroot
, rv
->alen
))) {
2076 // first word is acceptable in compound words?
2078 ( checked_prefix
|| (words
&& words
[wnum
]) ||
2079 (compoundflag
&& TESTAFF(rv
->astr
, compoundflag
, rv
->alen
)) ||
2080 ((oldwordnum
== 0) && compoundbegin
&& TESTAFF(rv
->astr
, compoundbegin
, rv
->alen
)) ||
2081 ((oldwordnum
> 0) && compoundmiddle
&& TESTAFF(rv
->astr
, compoundmiddle
, rv
->alen
))
2082 // LANG_hu section: spec. Hungarian rule
2083 || ((langnum
== LANG_hu
) && // hu_mov_rule
2085 TESTAFF(rv
->astr
, 'F', rv
->alen
) ||
2086 TESTAFF(rv
->astr
, 'G', rv
->alen
) ||
2087 TESTAFF(rv
->astr
, 'H', rv
->alen
)
2090 // END of LANG_hu section
2092 && ! (( checkcompoundtriple
&& // test triple letters
2093 (word
[i
-1]==word
[i
]) && (
2094 ((i
>1) && (word
[i
-1]==word
[i
-2])) ||
2095 ((word
[i
-1]==word
[i
+1])) // may be word[i+1] == '\0'
2099 // test CHECKCOMPOUNDPATTERN
2100 numcheckcpd
&& cpdpat_check(word
, i
)
2103 checkcompoundcase
&& cpdcase_check(word
, i
)
2106 // LANG_hu section: spec. Hungarian rule
2107 || ((!rv
) && (langnum
== LANG_hu
) && hu_mov_rule
&& (rv
= affix_check(st
,i
)) &&
2108 (sfx
&& ((SfxEntry
*)sfx
)->getCont() && (
2109 TESTAFF(((SfxEntry
*)sfx
)->getCont(), (unsigned short) 'x', ((SfxEntry
*)sfx
)->getContLen()) ||
2110 TESTAFF(((SfxEntry
*)sfx
)->getCont(), (unsigned short) '%', ((SfxEntry
*)sfx
)->getContLen())
2114 // END of LANG_hu section
2117 // LANG_hu section: spec. Hungarian rule
2118 if (langnum
== LANG_hu
) {
2119 // calculate syllable number of the word
2120 numsyllable
+= get_syllable(st
, i
);
2122 // + 1 word, if syllable number of the prefix > 1 (hungarian convention)
2123 if (pfx
&& (get_syllable(((PfxEntry
*)pfx
)->getKey(),strlen(((PfxEntry
*)pfx
)->getKey())) > 1)) wordnum
++;
2125 // END of LANG_hu section
2129 rv
= lookup((word
+i
)); // perhaps without prefix
2131 // search homonym with compound flag
2132 while ((rv
) && ((pseudoroot
&& TESTAFF(rv
->astr
, pseudoroot
, rv
->alen
)) ||
2133 !((compoundflag
&& !words
&& TESTAFF(rv
->astr
, compoundflag
, rv
->alen
)) ||
2134 (compoundend
&& !words
&& TESTAFF(rv
->astr
, compoundend
, rv
->alen
)) ||
2135 (numdefcpd
&& defcpd_check(&words
, wnum
+ 1, rv
, NULL
,1))))) {
2136 rv
= rv
->next_homonym
;
2139 if (rv
&& words
&& words
[wnum
+ 1]) {
2140 strcat(*result
, presult
);
2141 if (complexprefixes
&& rv
->description
) strcat(*result
, rv
->description
);
2142 if (rv
->description
&& ((!rv
->astr
) ||
2143 !TESTAFF(rv
->astr
, lemma_present
, rv
->alen
)))
2144 strcat(*result
, rv
->word
);
2145 if (!complexprefixes
&& rv
->description
) strcat(*result
, rv
->description
);
2146 strcat(*result
, "\n");
2151 oldnumsyllable2
= numsyllable
;
2152 oldwordnum2
= wordnum
;
2154 // LANG_hu section: spec. Hungarian rule
2155 if ((rv
) && (langnum
== LANG_hu
) && (TESTAFF(rv
->astr
, 'I', rv
->alen
)) && !(TESTAFF(rv
->astr
, 'J', rv
->alen
))) {
2158 // END of LANG_hu section
2159 // increment word number, if the second root has a compoundroot flag
2160 if ((rv
) && (compoundroot
) &&
2161 (TESTAFF(rv
->astr
, compoundroot
, rv
->alen
))) {
2165 // check forbiddenwords
2166 if ((rv
) && (rv
->astr
) && TESTAFF(rv
->astr
, forbiddenword
, rv
->alen
)) {
2171 // second word is acceptable, as a root?
2172 // hungarian conventions: compounding is acceptable,
2173 // when compound forms consist of 2 words, or if more,
2174 // then the syllable number of root words must be 6, or lesser.
2176 (compoundflag
&& TESTAFF(rv
->astr
, compoundflag
, rv
->alen
)) ||
2177 (compoundend
&& TESTAFF(rv
->astr
, compoundend
, rv
->alen
))
2180 ((cpdwordmax
==-1) || (wordnum
+1<cpdwordmax
)) ||
2181 ((cpdmaxsyllable
==0) ||
2182 (numsyllable
+get_syllable(rv
->word
,rv
->wlen
)<=cpdmaxsyllable
))
2185 (!checkcompounddup
|| (rv
!= rv_first
))
2189 // bad compound word
2190 strcat(*result
, presult
);
2192 if (rv
->description
) {
2193 if (complexprefixes
) strcat(*result
, rv
->description
);
2194 if ((!rv
->astr
) || !TESTAFF(rv
->astr
, lemma_present
, rv
->alen
))
2195 strcat(*result
, rv
->word
);
2196 if (!complexprefixes
) strcat(*result
, rv
->description
);
2198 strcat(*result
, "\n");
2202 numsyllable
= oldnumsyllable2
;
2203 wordnum
= oldwordnum2
;
2205 // perhaps second word has prefix or/and suffix
2207 sfxflag
= FLAG_NULL
;
2209 if (compoundflag
) rv
= affix_check((word
+i
),strlen(word
+i
), compoundflag
); else rv
= NULL
;
2211 if (!rv
&& compoundend
) {
2214 rv
= affix_check((word
+i
),strlen(word
+i
), compoundend
);
2217 if (!rv
&& numdefcpd
&& words
) {
2218 rv
= affix_check((word
+i
),strlen(word
+i
), 0, IN_CPD_END
);
2219 if (rv
&& words
&& defcpd_check(&words
, wnum
+ 1, rv
, NULL
, 1)) {
2221 if (compoundflag
) m
= affix_check_morph((word
+i
),strlen(word
+i
), compoundflag
);
2222 if ((!m
|| *m
== '\0') && compoundend
)
2223 m
= affix_check_morph((word
+i
),strlen(word
+i
), compoundend
);
2224 strcat(*result
, presult
);
2227 if (strchr(m
, '\n')) {
2228 strcat(*result
, "(");
2229 strcat(*result
, line_join(m
, '|'));
2230 strcat(*result
, ")");
2236 strcat(*result
, "\n");
2241 // check non_compound flag in suffix and prefix
2243 ((pfx
&& ((PfxEntry
*)pfx
)->getCont() &&
2244 TESTAFF(((PfxEntry
*)pfx
)->getCont(), compoundforbidflag
,
2245 ((PfxEntry
*)pfx
)->getContLen())) ||
2246 (sfx
&& ((SfxEntry
*)sfx
)->getCont() &&
2247 TESTAFF(((SfxEntry
*)sfx
)->getCont(), compoundforbidflag
,
2248 ((SfxEntry
*)sfx
)->getContLen())))) {
2252 // check forbiddenwords
2253 if ((rv
) && (rv
->astr
) && (TESTAFF(rv
->astr
,forbiddenword
,rv
->alen
))
2254 && (! TESTAFF(rv
->astr
, pseudoroot
, rv
->alen
))) {
2259 if (langnum
== LANG_hu
) {
2260 // calculate syllable number of the word
2261 numsyllable
+= get_syllable(word
+ i
, strlen(word
+ i
));
2263 // - affix syllable num.
2264 // XXX only second suffix (inflections, not derivations)
2266 char * tmp
= myrevstrdup(sfxappnd
);
2267 numsyllable
-= get_syllable(tmp
, strlen(tmp
));
2271 // + 1 word, if syllable number of the prefix > 1 (hungarian convention)
2272 if (pfx
&& (get_syllable(((PfxEntry
*)pfx
)->getKey(),strlen(((PfxEntry
*)pfx
)->getKey())) > 1)) wordnum
++;
2274 // increment syllable num, if last word has a SYLLABLENUM flag
2275 // and the suffix is beginning `s'
2277 if (cpdsyllablenum
) {
2279 case 'c': { numsyllable
+=2; break; }
2280 case 'J': { numsyllable
+= 1; break; }
2281 case 'I': { if (rv
&& TESTAFF(rv
->astr
, 'J', rv
->alen
)) numsyllable
+= 1; break; }
2286 // increment word number, if the second word has a compoundroot flag
2287 if ((rv
) && (compoundroot
) &&
2288 (TESTAFF(rv
->astr
, compoundroot
, rv
->alen
))) {
2291 // second word is acceptable, as a word with prefix or/and suffix?
2292 // hungarian conventions: compounding is acceptable,
2293 // when compound forms consist 2 word, otherwise
2294 // the syllable number of root words is 6, or lesser.
2297 ((cpdwordmax
==-1) || (wordnum
+1<cpdwordmax
)) ||
2298 ((cpdmaxsyllable
==0) ||
2299 (numsyllable
<= cpdmaxsyllable
))
2302 (!checkcompounddup
|| (rv
!= rv_first
))
2305 if (compoundflag
) m
= affix_check_morph((word
+i
),strlen(word
+i
), compoundflag
);
2306 if ((!m
|| *m
== '\0') && compoundend
)
2307 m
= affix_check_morph((word
+i
),strlen(word
+i
), compoundend
);
2308 strcat(*result
, presult
);
2311 if (strchr(m
, '\n')) {
2312 strcat(*result
, "(");
2313 strcat(*result
, line_join(m
, '|'));
2314 strcat(*result
, ")");
2320 strcat(*result
, "\n");
2324 numsyllable
= oldnumsyllable2
;
2325 wordnum
= oldwordnum2
;
2327 // perhaps second word is a compound word (recursive call)
2328 if ((wordnum
< maxwordnum
) && (ok
== 0)) {
2329 compound_check_morph((word
+i
),strlen(word
+i
), wordnum
+1,
2330 numsyllable
, maxwordnum
, wnum
+ 1, words
, 0, result
, presult
);
2336 wordnum
= oldwordnum
;
2337 numsyllable
= oldnumsyllable
;
2341 #endif // END OF HUNSPELL_EXPERIMENTAL CODE
2343 // return 1 if s1 (reversed) is a leading subset of end of s2
2344 /* inline int AffixMgr::isRevSubset(const char * s1, const char * end_of_s2, int len)
2346 while ((len > 0) && *s1 && (*s1 == *end_of_s2)) {
2351 return (*s1 == '\0');
2355 inline int AffixMgr::isRevSubset(const char * s1
, const char * end_of_s2
, int len
)
2357 while ((len
> 0) && (*s1
!= '\0') && ((*s1
== *end_of_s2
) || (*s1
== '.'))) {
2362 return (*s1
== '\0');
2365 // check word for suffixes
2367 struct hentry
* AffixMgr::suffix_check (const char * word
, int len
,
2368 int sfxopts
, AffEntry
* ppfx
, char ** wlst
, int maxSug
, int * ns
,
2369 const FLAG cclass
, const FLAG needflag
, char in_compound
)
2371 struct hentry
* rv
= NULL
;
2372 char result
[MAXLNLEN
];
2374 PfxEntry
* ep
= (PfxEntry
*) ppfx
;
2376 // first handle the special case of 0 length suffixes
2377 SfxEntry
* se
= (SfxEntry
*) sStart
[0];
2380 if (!cclass
|| se
->getCont()) {
2381 // suffixes are not allowed in beginning of compounds
2382 if ((((in_compound
!= IN_CPD_BEGIN
)) || // && !cclass
2383 // except when signed with compoundpermitflag flag
2384 (se
->getCont() && compoundpermitflag
&&
2385 TESTAFF(se
->getCont(),compoundpermitflag
,se
->getContLen()))) && (!circumfix
||
2386 // no circumfix flag in prefix and suffix
2387 ((!ppfx
|| !(ep
->getCont()) || !TESTAFF(ep
->getCont(),
2388 circumfix
, ep
->getContLen())) &&
2389 (!se
->getCont() || !(TESTAFF(se
->getCont(),circumfix
,se
->getContLen())))) ||
2390 // circumfix flag in prefix AND suffix
2391 ((ppfx
&& (ep
->getCont()) && TESTAFF(ep
->getCont(),
2392 circumfix
, ep
->getContLen())) &&
2393 (se
->getCont() && (TESTAFF(se
->getCont(),circumfix
,se
->getContLen()))))) &&
2396 !((se
->getCont() && (TESTAFF(se
->getCont(), onlyincompound
, se
->getContLen()))))) &&
2397 // pseudoroot on prefix or first suffix
2399 !(se
->getCont() && TESTAFF(se
->getCont(), pseudoroot
, se
->getContLen())) ||
2400 (ppfx
&& !((ep
->getCont()) &&
2401 TESTAFF(ep
->getCont(), pseudoroot
,
2405 rv
= se
->checkword(word
,len
, sfxopts
, ppfx
, wlst
, maxSug
, ns
, (FLAG
) cclass
,
2406 needflag
, (in_compound
? 0 : onlyincompound
));
2408 sfx
=(AffEntry
*)se
; // BUG: sfx not stateless
2416 // now handle the general case
2417 unsigned char sp
= *((const unsigned char *)(word
+ len
- 1));
2418 SfxEntry
* sptr
= (SfxEntry
*) sStart
[sp
];
2421 if (isRevSubset(sptr
->getKey(), word
+ len
- 1, len
)
2423 // suffixes are not allowed in beginning of compounds
2424 if ((((in_compound
!= IN_CPD_BEGIN
)) || // && !cclass
2425 // except when signed with compoundpermitflag flag
2426 (sptr
->getCont() && compoundpermitflag
&&
2427 TESTAFF(sptr
->getCont(),compoundpermitflag
,sptr
->getContLen()))) && (!circumfix
||
2428 // no circumfix flag in prefix and suffix
2429 ((!ppfx
|| !(ep
->getCont()) || !TESTAFF(ep
->getCont(),
2430 circumfix
, ep
->getContLen())) &&
2431 (!sptr
->getCont() || !(TESTAFF(sptr
->getCont(),circumfix
,sptr
->getContLen())))) ||
2432 // circumfix flag in prefix AND suffix
2433 ((ppfx
&& (ep
->getCont()) && TESTAFF(ep
->getCont(),
2434 circumfix
, ep
->getContLen())) &&
2435 (sptr
->getCont() && (TESTAFF(sptr
->getCont(),circumfix
,sptr
->getContLen()))))) &&
2438 !((sptr
->getCont() && (TESTAFF(sptr
->getCont(), onlyincompound
, sptr
->getContLen()))))) &&
2439 // pseudoroot on prefix or first suffix
2441 !(sptr
->getCont() && TESTAFF(sptr
->getCont(), pseudoroot
, sptr
->getContLen())) ||
2442 (ppfx
&& !((ep
->getCont()) &&
2443 TESTAFF(ep
->getCont(), pseudoroot
,
2447 rv
= sptr
->checkword(word
,len
, sfxopts
, ppfx
, wlst
,
2448 maxSug
, ns
, cclass
, needflag
, (in_compound
? 0 : onlyincompound
));
2450 sfx
=(AffEntry
*)sptr
; // BUG: sfx not stateless
2451 sfxflag
= sptr
->getFlag(); // BUG: sfxflag not stateless
2452 if (!sptr
->getCont()) sfxappnd
=sptr
->getKey(); // BUG: sfxappnd not stateless
2453 if (cclass
|| sptr
->getCont()) {
2455 derived
= mystrdup(word
);
2457 strcpy(result
, derived
); // XXX check size
2458 strcat(result
, "\n");
2459 strcat(result
, word
);
2461 derived
= mystrdup(result
);
2467 sptr
= sptr
->getNextEQ();
2469 sptr
= sptr
->getNextNE();
2476 // check word for two-level suffixes
2478 struct hentry
* AffixMgr::suffix_check_twosfx(const char * word
, int len
,
2479 int sfxopts
, AffEntry
* ppfx
, const FLAG needflag
)
2481 struct hentry
* rv
= NULL
;
2483 // first handle the special case of 0 length suffixes
2484 SfxEntry
* se
= (SfxEntry
*) sStart
[0];
2486 if (contclasses
[se
->getFlag()])
2488 rv
= se
->check_twosfx(word
,len
, sfxopts
, ppfx
, needflag
);
2494 // now handle the general case
2495 unsigned char sp
= *((const unsigned char *)(word
+ len
- 1));
2496 SfxEntry
* sptr
= (SfxEntry
*) sStart
[sp
];
2499 if (isRevSubset(sptr
->getKey(), word
+ len
- 1, len
)) {
2500 if (contclasses
[sptr
->getFlag()])
2502 rv
= sptr
->check_twosfx(word
,len
, sfxopts
, ppfx
, needflag
);
2504 sfxflag
= sptr
->getFlag(); // BUG: sfxflag not stateless
2505 if (!sptr
->getCont()) sfxappnd
=sptr
->getKey(); // BUG: sfxappnd not stateless
2509 sptr
= sptr
->getNextEQ();
2511 sptr
= sptr
->getNextNE();
2518 #ifdef HUNSPELL_EXPERIMENTAL
2519 char * AffixMgr::suffix_check_twosfx_morph(const char * word
, int len
,
2520 int sfxopts
, AffEntry
* ppfx
, const FLAG needflag
)
2522 char result
[MAXLNLEN
];
2523 char result2
[MAXLNLEN
];
2524 char result3
[MAXLNLEN
];
2532 // first handle the special case of 0 length suffixes
2533 SfxEntry
* se
= (SfxEntry
*) sStart
[0];
2535 if (contclasses
[se
->getFlag()])
2537 st
= se
->check_twosfx_morph(word
,len
, sfxopts
, ppfx
, needflag
);
2540 if (((PfxEntry
*) ppfx
)->getMorph()) strcat(result
, ((PfxEntry
*) ppfx
)->getMorph());
2544 if (se
->getMorph()) strcat(result
, se
->getMorph());
2545 strcat(result
, "\n");
2551 // now handle the general case
2552 unsigned char sp
= *((const unsigned char *)(word
+ len
- 1));
2553 SfxEntry
* sptr
= (SfxEntry
*) sStart
[sp
];
2556 if (isRevSubset(sptr
->getKey(), word
+ len
- 1, len
)) {
2557 if (contclasses
[sptr
->getFlag()])
2559 st
= sptr
->check_twosfx_morph(word
,len
, sfxopts
, ppfx
, needflag
);
2561 sfxflag
= sptr
->getFlag(); // BUG: sfxflag not stateless
2562 if (!sptr
->getCont()) sfxappnd
=sptr
->getKey(); // BUG: sfxappnd not stateless
2563 strcpy(result2
, st
);
2568 unsigned short flag
= sptr
->getFlag();
2569 if (flag_mode
== FLAG_NUM
) {
2570 sprintf(result3
, "<%d>", sptr
->getKey());
2571 } else if (flag_mode
== FLAG_LONG
) {
2572 sprintf(result3
, "<%c%c>", flag
>> 8, (flag
<< 8) >>8);
2573 } else sprintf(result3
, "<%c>", flag
);
2574 strcat(result3
, ":");
2576 if (sptr
->getMorph()) strcat(result3
, sptr
->getMorph());
2577 strlinecat(result2
, result3
);
2578 strcat(result2
, "\n");
2579 strcat(result
, result2
);
2582 sptr
= sptr
->getNextEQ();
2584 sptr
= sptr
->getNextNE();
2587 if (result
) return mystrdup(result
);
2591 char * AffixMgr::suffix_check_morph(const char * word
, int len
,
2592 int sfxopts
, AffEntry
* ppfx
, const FLAG cclass
, const FLAG needflag
, char in_compound
)
2594 char result
[MAXLNLEN
];
2596 struct hentry
* rv
= NULL
;
2600 PfxEntry
* ep
= (PfxEntry
*) ppfx
;
2602 // first handle the special case of 0 length suffixes
2603 SfxEntry
* se
= (SfxEntry
*) sStart
[0];
2605 if (!cclass
|| se
->getCont()) {
2606 // suffixes are not allowed in beginning of compounds
2607 if (((((in_compound
!= IN_CPD_BEGIN
)) || // && !cclass
2608 // except when signed with compoundpermitflag flag
2609 (se
->getCont() && compoundpermitflag
&&
2610 TESTAFF(se
->getCont(),compoundpermitflag
,se
->getContLen()))) && (!circumfix
||
2611 // no circumfix flag in prefix and suffix
2612 ((!ppfx
|| !(ep
->getCont()) || !TESTAFF(ep
->getCont(),
2613 circumfix
, ep
->getContLen())) &&
2614 (!se
->getCont() || !(TESTAFF(se
->getCont(),circumfix
,se
->getContLen())))) ||
2615 // circumfix flag in prefix AND suffix
2616 ((ppfx
&& (ep
->getCont()) && TESTAFF(ep
->getCont(),
2617 circumfix
, ep
->getContLen())) &&
2618 (se
->getCont() && (TESTAFF(se
->getCont(),circumfix
,se
->getContLen()))))) &&
2621 !((se
->getCont() && (TESTAFF(se
->getCont(), onlyincompound
, se
->getContLen()))))) &&
2622 // pseudoroot on prefix or first suffix
2624 !(se
->getCont() && TESTAFF(se
->getCont(), pseudoroot
, se
->getContLen())) ||
2625 (ppfx
&& !((ep
->getCont()) &&
2626 TESTAFF(ep
->getCont(), pseudoroot
,
2630 rv
= se
->checkword(word
,len
, sfxopts
, ppfx
, NULL
, 0, 0, cclass
, needflag
);
2633 if (((PfxEntry
*) ppfx
)->getMorph()) strcat(result
, ((PfxEntry
*) ppfx
)->getMorph());
2635 if (complexprefixes
&& rv
->description
) strcat(result
, rv
->description
);
2636 if (rv
->description
&& ((!rv
->astr
) ||
2637 !TESTAFF(rv
->astr
, lemma_present
, rv
->alen
)))
2638 strcat(result
, rv
->word
);
2639 if (!complexprefixes
&& rv
->description
) strcat(result
, rv
->description
);
2640 if (se
->getMorph()) strcat(result
, se
->getMorph());
2641 strcat(result
, "\n");
2642 rv
= se
->get_next_homonym(rv
, sfxopts
, ppfx
, cclass
, needflag
);
2648 // now handle the general case
2649 unsigned char sp
= *((const unsigned char *)(word
+ len
- 1));
2650 SfxEntry
* sptr
= (SfxEntry
*) sStart
[sp
];
2653 if (isRevSubset(sptr
->getKey(), word
+ len
- 1, len
)
2655 // suffixes are not allowed in beginning of compounds
2656 if (((((in_compound
!= IN_CPD_BEGIN
)) || // && !cclass
2657 // except when signed with compoundpermitflag flag
2658 (sptr
->getCont() && compoundpermitflag
&&
2659 TESTAFF(sptr
->getCont(),compoundpermitflag
,sptr
->getContLen()))) && (!circumfix
||
2660 // no circumfix flag in prefix and suffix
2661 ((!ppfx
|| !(ep
->getCont()) || !TESTAFF(ep
->getCont(),
2662 circumfix
, ep
->getContLen())) &&
2663 (!sptr
->getCont() || !(TESTAFF(sptr
->getCont(),circumfix
,sptr
->getContLen())))) ||
2664 // circumfix flag in prefix AND suffix
2665 ((ppfx
&& (ep
->getCont()) && TESTAFF(ep
->getCont(),
2666 circumfix
, ep
->getContLen())) &&
2667 (sptr
->getCont() && (TESTAFF(sptr
->getCont(),circumfix
,sptr
->getContLen()))))) &&
2670 !((sptr
->getCont() && (TESTAFF(sptr
->getCont(), onlyincompound
, sptr
->getContLen()))))) &&
2671 // pseudoroot on first suffix
2672 (cclass
|| !(sptr
->getCont() &&
2673 TESTAFF(sptr
->getCont(), pseudoroot
, sptr
->getContLen())))
2674 )) rv
= sptr
->checkword(word
,len
, sfxopts
, ppfx
, NULL
, 0, 0, cclass
, needflag
);
2677 if (((PfxEntry
*) ppfx
)->getMorph()) strcat(result
, ((PfxEntry
*) ppfx
)->getMorph());
2679 if (complexprefixes
&& rv
->description
) strcat(result
, rv
->description
);
2680 if (rv
->description
&& ((!rv
->astr
) ||
2681 !TESTAFF(rv
->astr
, lemma_present
, rv
->alen
))) strcat(result
, rv
->word
);
2682 if (!complexprefixes
&& rv
->description
) strcat(result
, rv
->description
);
2684 unsigned short flag
= sptr
->getFlag();
2685 if (flag_mode
== FLAG_NUM
) {
2686 sprintf(result
, "<%d>", sptr
->getKey());
2687 } else if (flag_mode
== FLAG_LONG
) {
2688 sprintf(result
, "<%c%c>", flag
>> 8, (flag
<< 8) >>8);
2689 } else sprintf(result
, "<%c>", flag
);
2690 strcat(result
, ":");
2693 if (sptr
->getMorph()) strcat(result
, sptr
->getMorph());
2694 strcat(result
, "\n");
2695 rv
= sptr
->get_next_homonym(rv
, sfxopts
, ppfx
, cclass
, needflag
);
2697 sptr
= sptr
->getNextEQ();
2699 sptr
= sptr
->getNextNE();
2703 if (*result
) return mystrdup(result
);
2706 #endif // END OF HUNSPELL_EXPERIMENTAL CODE
2709 // check if word with affixes is correctly spelled
2710 struct hentry
* AffixMgr::affix_check (const char * word
, int len
, const FLAG needflag
, char in_compound
)
2712 struct hentry
* rv
= NULL
;
2713 if (derived
) free(derived
);
2716 // check all prefixes (also crossed with suffixes if allowed)
2717 rv
= prefix_check(word
, len
, in_compound
, needflag
);
2720 // if still not found check all suffixes
2721 rv
= suffix_check(word
, len
, 0, NULL
, NULL
, 0, NULL
, FLAG_NULL
, needflag
, in_compound
);
2723 if (havecontclass
) {
2727 // if still not found check all two-level suffixes
2728 rv
= suffix_check_twosfx(word
, len
, 0, NULL
, needflag
);
2730 // if still not found check all two-level suffixes
2731 rv
= prefix_check_twosfx(word
, len
, IN_CPD_NOT
, needflag
);
2736 #ifdef HUNSPELL_EXPERIMENTAL
2737 // check if word with affixes is correctly spelled
2738 char * AffixMgr::affix_check_morph(const char * word
, int len
, const FLAG needflag
, char in_compound
)
2740 char result
[MAXLNLEN
];
2745 // check all prefixes (also crossed with suffixes if allowed)
2746 st
= prefix_check_morph(word
, len
, in_compound
);
2752 // if still not found check all suffixes
2753 st
= suffix_check_morph(word
, len
, 0, NULL
, '\0', needflag
, in_compound
);
2759 if (havecontclass
) {
2762 // if still not found check all two-level suffixes
2763 st
= suffix_check_twosfx_morph(word
, len
, 0, NULL
, needflag
);
2769 // if still not found check all two-level suffixes
2770 st
= prefix_check_twosfx_morph(word
, len
, IN_CPD_NOT
, needflag
);
2777 return mystrdup(result
);
2779 #endif // END OF HUNSPELL_EXPERIMENTAL CODE
2782 int AffixMgr::expand_rootword(struct guessword
* wlst
, int maxn
, const char * ts
,
2783 int wl
, const unsigned short * ap
, unsigned short al
, char * bad
, int badl
)
2788 // first add root word to list
2789 if ((nh
< maxn
) && !(al
&& ((pseudoroot
&& TESTAFF(ap
, pseudoroot
, al
)) ||
2790 (onlyincompound
&& TESTAFF(ap
, onlyincompound
, al
))))) {
2791 wlst
[nh
].word
= mystrdup(ts
);
2792 wlst
[nh
].allow
= (1 == 0);
2797 for (int i
= 0; i
< al
; i
++) {
2798 unsigned short c
= (unsigned short) ap
[i
];
2799 SfxEntry
* sptr
= (SfxEntry
*)sFlag
[c
];
2801 if (!sptr
->getKeyLen() || ((badl
> sptr
->getKeyLen()) &&
2802 (strcmp(sptr
->getAffix(), bad
+ badl
- sptr
->getKeyLen()) == 0)) &&
2803 // check pseudoroot flag
2804 !(sptr
->getCont() && ((pseudoroot
&&
2805 TESTAFF(sptr
->getCont(), pseudoroot
, sptr
->getContLen())) ||
2807 TESTAFF(sptr
->getCont(), circumfix
, sptr
->getContLen())) ||
2809 TESTAFF(sptr
->getCont(), onlyincompound
, sptr
->getContLen()))))
2811 char * newword
= sptr
->add(ts
, wl
);
2814 wlst
[nh
].word
= newword
;
2815 wlst
[nh
].allow
= sptr
->allowCross();
2822 sptr
= (SfxEntry
*)sptr
->getFlgNxt();
2828 // handle cross products of prefixes and suffixes
2829 for (int j
=1;j
<n
;j
++)
2830 if (wlst
[j
].allow
) {
2831 for (int k
= 0; k
< al
; k
++) {
2832 unsigned short c
= (unsigned short) ap
[k
];
2833 PfxEntry
* cptr
= (PfxEntry
*) pFlag
[c
];
2835 if (cptr
->allowCross() && (!cptr
->getKeyLen() || ((badl
> cptr
->getKeyLen()) &&
2836 (strncmp(cptr
->getKey(), bad
, cptr
->getKeyLen()) == 0)))) {
2837 int l1
= strlen(wlst
[j
].word
);
2838 char * newword
= cptr
->add(wlst
[j
].word
, l1
);
2841 wlst
[nh
].word
= newword
;
2842 wlst
[nh
].allow
= cptr
->allowCross();
2849 cptr
= (PfxEntry
*)cptr
->getFlgNxt();
2855 // now handle pure prefixes
2856 for (int m
= 0; m
< al
; m
++) {
2857 unsigned short c
= (unsigned short) ap
[m
];
2858 PfxEntry
* ptr
= (PfxEntry
*) pFlag
[c
];
2860 if (!ptr
->getKeyLen() || ((badl
> ptr
->getKeyLen()) &&
2861 (strncmp(ptr
->getKey(), bad
, ptr
->getKeyLen()) == 0)) &&
2862 // check pseudoroot flag
2863 !(ptr
->getCont() && ((pseudoroot
&&
2864 TESTAFF(ptr
->getCont(), pseudoroot
, ptr
->getContLen())) ||
2866 TESTAFF(ptr
->getCont(), circumfix
, ptr
->getContLen())) ||
2868 TESTAFF(ptr
->getCont(), onlyincompound
, ptr
->getContLen()))))
2870 char * newword
= ptr
->add(ts
, wl
);
2873 wlst
[nh
].word
= newword
;
2874 wlst
[nh
].allow
= ptr
->allowCross();
2881 ptr
= (PfxEntry
*)ptr
->getFlgNxt();
2890 // return length of replacing table
2891 int AffixMgr::get_numrep()
2896 // return replacing table
2897 struct replentry
* AffixMgr::get_reptable()
2899 if (! reptable
) return NULL
;
2903 // return length of character map table
2904 int AffixMgr::get_nummap()
2909 // return character map table
2910 struct mapentry
* AffixMgr::get_maptable()
2912 if (! maptable
) return NULL
;
2916 // return length of word break table
2917 int AffixMgr::get_numbreak()
2922 // return character map table
2923 char ** AffixMgr::get_breaktable()
2925 if (! breaktable
) return NULL
;
2929 // return text encoding of dictionary
2930 char * AffixMgr::get_encoding()
2933 encoding
= mystrdup("ISO8859-1");
2935 return mystrdup(encoding
);
2938 // return text encoding of dictionary
2939 int AffixMgr::get_langnum()
2944 // return double prefix option
2945 int AffixMgr::get_complexprefixes()
2947 return complexprefixes
;
2950 FLAG
AffixMgr::get_keepcase()
2955 int AffixMgr::get_checksharps()
2960 // return the preferred ignore string for suggestions
2961 char * AffixMgr::get_ignore()
2963 if (!ignorechars
) return NULL
;
2964 return mystrdup(ignorechars
);
2967 // return the preferred ignore string for suggestions
2968 unsigned short * AffixMgr::get_ignore_utf16(int * len
)
2970 *len
= ignorechars_utf16_len
;
2971 return ignorechars_utf16
;
2974 // return the preferred try string for suggestions
2975 char * AffixMgr::get_try_string()
2977 if (! trystring
) return NULL
;
2978 return mystrdup(trystring
);
2981 // return the preferred try string for suggestions
2982 const char * AffixMgr::get_wordchars()
2987 unsigned short * AffixMgr::get_wordchars_utf16(int * len
)
2989 *len
= wordchars_utf16_len
;
2990 return wordchars_utf16
;
2993 // is there compounding?
2994 int AffixMgr::get_compound()
2996 return compoundflag
|| compoundbegin
|| numdefcpd
;
2999 // return the compound words control flag
3000 FLAG
AffixMgr::get_compoundflag()
3002 return compoundflag
;
3005 // return the forbidden words control flag
3006 FLAG
AffixMgr::get_forbiddenword()
3008 return forbiddenword
;
3011 // return the forbidden words control flag
3012 FLAG
AffixMgr::get_nosuggest()
3017 // return the forbidden words flag modify flag
3018 FLAG
AffixMgr::get_pseudoroot()
3023 // return the onlyincompound flag
3024 FLAG
AffixMgr::get_onlyincompound()
3026 return onlyincompound
;
3029 // return the compound word signal flag
3030 FLAG
AffixMgr::get_compoundroot()
3032 return compoundroot
;
3035 // return the compound begin signal flag
3036 FLAG
AffixMgr::get_compoundbegin()
3038 return compoundbegin
;
3041 // return the value of checknum
3042 int AffixMgr::get_checknum()
3047 // return the value of prefix
3048 const char * AffixMgr::get_prefix()
3050 if (pfx
) return ((PfxEntry
*)pfx
)->getKey();
3054 // return the value of suffix
3055 const char * AffixMgr::get_suffix()
3060 // return the value of derived form (base word with first suffix).
3061 const char * AffixMgr::get_derived()
3066 // return the value of suffix
3067 const char * AffixMgr::get_version()
3072 // return lemma_present flag
3073 FLAG
AffixMgr::get_lemma_present()
3075 return lemma_present
;
3078 // utility method to look up root words in hash table
3079 struct hentry
* AffixMgr::lookup(const char * word
)
3081 if (! pHMgr
) return NULL
;
3082 return pHMgr
->lookup(word
);
3085 // return the value of suffix
3086 const int AffixMgr::have_contclass()
3088 return havecontclass
;
3092 int AffixMgr::get_utf8()
3097 // return nosplitsugs
3098 int AffixMgr::get_maxngramsugs(void)
3100 return maxngramsugs
;
3103 // return nosplitsugs
3104 int AffixMgr::get_nosplitsugs(void)
3109 // return sugswithdots
3110 int AffixMgr::get_sugswithdots(void)
3112 return sugswithdots
;
3116 int AffixMgr::parse_flag(char * line
, unsigned short * out
, const char * name
) {
3118 if (*out
!= FLAG_NULL
) {
3119 HUNSPELL_WARNING(stderr
, "error: duplicate %s line\n", name
);
3122 if (parse_string(line
, &s
, name
)) return 1;
3123 *out
= pHMgr
->decode_flag(s
);
3129 int AffixMgr::parse_num(char * line
, int * out
, const char * name
) {
3132 HUNSPELL_WARNING(stderr
, "error: duplicate %s line\n", name
);
3135 if (parse_string(line
, &s
, name
)) return 1;
3141 /* parse in the max syllablecount of compound words and */
3142 int AffixMgr::parse_cpdsyllable(char * line
)
3148 w_char w
[MAXWORDLEN
];
3149 piece
= mystrsep(&tp
, 0);
3151 if (*piece
!= '\0') {
3153 case 0: { np
++; break; }
3154 case 1: { cpdmaxsyllable
= atoi(piece
); np
++; break; }
3157 cpdvowels
= mystrdup(piece
);
3159 int n
= u8_u16(w
, MAXWORDLEN
, piece
);
3161 flag_qsort((unsigned short *) w
, 0, n
);
3162 cpdvowels_utf16
= (w_char
*) malloc(n
* sizeof(w_char
));
3163 if (!cpdvowels_utf16
) return 1;
3164 memcpy(cpdvowels_utf16
, w
, n
* sizeof(w_char
));
3166 cpdvowels_utf16_len
= n
;
3176 piece
= mystrsep(&tp
, 0);
3179 HUNSPELL_WARNING(stderr
, "error: missing compoundsyllable information\n");
3182 if (np
== 2) cpdvowels
= mystrdup("aeiouAEIOU");
3186 /* parse in the typical fault correcting table */
3187 int AffixMgr::parse_reptable(char * line
, FILE * af
)
3190 HUNSPELL_WARNING(stderr
, "error: duplicate REP tables used\n");
3197 piece
= mystrsep(&tp
, 0);
3199 if (*piece
!= '\0') {
3201 case 0: { np
++; break; }
3203 numrep
= atoi(piece
);
3205 HUNSPELL_WARNING(stderr
, "incorrect number of entries in replacement table\n");
3209 reptable
= (replentry
*) malloc(numrep
* sizeof(struct replentry
));
3222 piece
= mystrsep(&tp
, 0);
3225 HUNSPELL_WARNING(stderr
, "error: missing replacement table information\n");
3229 /* now parse the numrep lines to read in the remainder of the table */
3231 for (int j
=0; j
< numrep
; j
++) {
3232 if (!fgets(nl
,MAXLNLEN
,af
)) return 1;
3236 reptable
[j
].pattern
= NULL
;
3237 reptable
[j
].pattern2
= NULL
;
3238 piece
= mystrsep(&tp
, 0);
3240 if (*piece
!= '\0') {
3243 if (strncmp(piece
,"REP",3) != 0) {
3244 HUNSPELL_WARNING(stderr
, "error: replacement table is corrupt\n");
3250 case 1: { reptable
[j
].pattern
= mystrrep(mystrdup(piece
),"_"," "); break; }
3251 case 2: { reptable
[j
].pattern2
= mystrrep(mystrdup(piece
),"_"," "); break; }
3257 piece
= mystrsep(&tp
, 0);
3259 if ((!(reptable
[j
].pattern
)) || (!(reptable
[j
].pattern2
))) {
3260 HUNSPELL_WARNING(stderr
, "error: replacement table is corrupt\n");
3267 /* parse in the checkcompoundpattern table */
3268 int AffixMgr::parse_checkcpdtable(char * line
, FILE * af
)
3270 if (numcheckcpd
!= 0) {
3271 HUNSPELL_WARNING(stderr
, "error: duplicate compound pattern tables used\n");
3278 piece
= mystrsep(&tp
, 0);
3280 if (*piece
!= '\0') {
3282 case 0: { np
++; break; }
3284 numcheckcpd
= atoi(piece
);
3285 if (numcheckcpd
< 1) {
3286 HUNSPELL_WARNING(stderr
, "incorrect number of entries in compound pattern table\n");
3290 checkcpdtable
= (replentry
*) malloc(numcheckcpd
* sizeof(struct replentry
));
3291 if (!checkcpdtable
) {
3303 piece
= mystrsep(&tp
, 0);
3306 HUNSPELL_WARNING(stderr
, "error: missing compound pattern table information\n");
3310 /* now parse the numcheckcpd lines to read in the remainder of the table */
3312 for (int j
=0; j
< numcheckcpd
; j
++) {
3313 if (!fgets(nl
,MAXLNLEN
,af
)) return 1;
3317 checkcpdtable
[j
].pattern
= NULL
;
3318 checkcpdtable
[j
].pattern2
= NULL
;
3319 piece
= mystrsep(&tp
, 0);
3321 if (*piece
!= '\0') {
3324 if (strncmp(piece
,"CHECKCOMPOUNDPATTERN",20) != 0) {
3325 HUNSPELL_WARNING(stderr
, "error: compound pattern table is corrupt\n");
3331 case 1: { checkcpdtable
[j
].pattern
= mystrdup(piece
); break; }
3332 case 2: { checkcpdtable
[j
].pattern2
= mystrdup(piece
); break; }
3338 piece
= mystrsep(&tp
, 0);
3340 if ((!(checkcpdtable
[j
].pattern
)) || (!(checkcpdtable
[j
].pattern2
))) {
3341 HUNSPELL_WARNING(stderr
, "error: compound pattern table is corrupt\n");
3348 /* parse in the compound rule table */
3349 int AffixMgr::parse_defcpdtable(char * line
, FILE * af
)
3351 if (numdefcpd
!= 0) {
3352 HUNSPELL_WARNING(stderr
, "error: duplicate compound rule tables used\n");
3359 piece
= mystrsep(&tp
, 0);
3361 if (*piece
!= '\0') {
3363 case 0: { np
++; break; }
3365 numdefcpd
= atoi(piece
);
3366 if (numdefcpd
< 1) {
3367 HUNSPELL_WARNING(stderr
, "incorrect number of entries in compound rule table\n");
3371 defcpdtable
= (flagentry
*) malloc(numdefcpd
* sizeof(flagentry
));
3384 piece
= mystrsep(&tp
, 0);
3387 HUNSPELL_WARNING(stderr
, "error: missing compound rule table information\n");
3391 /* now parse the numdefcpd lines to read in the remainder of the table */
3393 for (int j
=0; j
< numdefcpd
; j
++) {
3394 if (!fgets(nl
,MAXLNLEN
,af
)) return 1;
3398 defcpdtable
[j
].def
= NULL
;
3399 piece
= mystrsep(&tp
, 0);
3401 if (*piece
!= '\0') {
3404 if (strncmp(piece
, "COMPOUNDRULE", 12) != 0) {
3405 HUNSPELL_WARNING(stderr
, "error: compound rule table is corrupt\n");
3412 defcpdtable
[j
].len
=
3413 pHMgr
->decode_flags(&(defcpdtable
[j
].def
), piece
);
3421 piece
= mystrsep(&tp
, 0);
3423 if (!defcpdtable
[j
].len
) {
3424 HUNSPELL_WARNING(stderr
, "error: compound rule table is corrupt\n");
3432 /* parse in the character map table */
3433 int AffixMgr::parse_maptable(char * line
, FILE * af
)
3436 HUNSPELL_WARNING(stderr
, "error: duplicate MAP tables used\n");
3443 piece
= mystrsep(&tp
, 0);
3445 if (*piece
!= '\0') {
3447 case 0: { np
++; break; }
3449 nummap
= atoi(piece
);
3451 HUNSPELL_WARNING(stderr
, "incorrect number of entries in map table\n");
3455 maptable
= (mapentry
*) malloc(nummap
* sizeof(struct mapentry
));
3468 piece
= mystrsep(&tp
, 0);
3471 HUNSPELL_WARNING(stderr
, "error: missing map table information\n");
3475 /* now parse the nummap lines to read in the remainder of the table */
3477 for (int j
=0; j
< nummap
; j
++) {
3478 if (!fgets(nl
,MAXLNLEN
,af
)) return 1;
3482 maptable
[j
].set
= NULL
;
3483 maptable
[j
].len
= 0;
3484 piece
= mystrsep(&tp
, 0);
3486 if (*piece
!= '\0') {
3489 if (strncmp(piece
,"MAP",3) != 0) {
3490 HUNSPELL_WARNING(stderr
, "error: map table is corrupt\n");
3497 maptable
[j
].len
= 0;
3498 maptable
[j
].set
= NULL
;
3499 maptable
[j
].set_utf16
= NULL
;
3501 maptable
[j
].set
= mystrdup(piece
);
3502 maptable
[j
].len
= strlen(maptable
[j
].set
);
3504 w_char w
[MAXWORDLEN
];
3505 int n
= u8_u16(w
, MAXWORDLEN
, piece
);
3507 flag_qsort((unsigned short *) w
, 0, n
);
3508 maptable
[j
].set_utf16
= (w_char
*) malloc(n
* sizeof(w_char
));
3509 if (!maptable
[j
].set_utf16
) return 1;
3510 memcpy(maptable
[j
].set_utf16
, w
, n
* sizeof(w_char
));
3512 maptable
[j
].len
= n
;
3520 piece
= mystrsep(&tp
, 0);
3522 if ((!(maptable
[j
].set
|| maptable
[j
].set_utf16
)) || (!(maptable
[j
].len
))) {
3523 HUNSPELL_WARNING(stderr
, "error: map table is corrupt\n");
3530 /* parse in the word breakpoint table */
3531 int AffixMgr::parse_breaktable(char * line
, FILE * af
)
3533 if (numbreak
!= 0) {
3534 HUNSPELL_WARNING(stderr
, "error: duplicate word breakpoint tables used\n");
3541 piece
= mystrsep(&tp
, 0);
3543 if (*piece
!= '\0') {
3545 case 0: { np
++; break; }
3547 numbreak
= atoi(piece
);
3549 HUNSPELL_WARNING(stderr
, "incorrect number of entries in BREAK table\n");
3553 breaktable
= (char **) malloc(numbreak
* sizeof(char *));
3566 piece
= mystrsep(&tp
, 0);
3569 HUNSPELL_WARNING(stderr
, "error: missing word breakpoint table information\n");
3573 /* now parse the numbreak lines to read in the remainder of the table */
3575 for (int j
=0; j
< numbreak
; j
++) {
3576 if (!fgets(nl
,MAXLNLEN
,af
)) return 1;
3580 piece
= mystrsep(&tp
, 0);
3582 if (*piece
!= '\0') {
3585 if (strncmp(piece
,"BREAK",5) != 0) {
3586 HUNSPELL_WARNING(stderr
, "error: BREAK table is corrupt\n");
3593 breaktable
[j
] = mystrdup(piece
);
3601 piece
= mystrsep(&tp
, 0);
3604 HUNSPELL_WARNING(stderr
, "error: BREAK table is corrupt\n");
3611 int AffixMgr::parse_affix(char * line
, const char at
, FILE * af
, char * dupflags
)
3613 int numents
= 0; // number of affentry structures to parse
3615 unsigned short aflag
= 0; // affix char identifier
3618 struct affentry
* ptr
= NULL
;
3619 struct affentry
* nptr
= NULL
;
3626 // checking lines with bad syntax
3628 int basefieldnum
= 0;
3631 // split affix header line into pieces
3634 piece
= mystrsep(&tp
, 0);
3636 if (*piece
!= '\0') {
3638 // piece 1 - is type of affix
3639 case 0: { np
++; break; }
3641 // piece 2 - is affix char
3644 aflag
= pHMgr
->decode_flag(piece
);
3645 if (((at
== 'S') && (dupflags
[aflag
] & dupSFX
)) ||
3646 ((at
== 'P') && (dupflags
[aflag
] & dupPFX
))) {
3647 HUNSPELL_WARNING(stderr
, "error: duplicate affix flag %s in line %s\n", piece
, nl
);
3648 // return 1; XXX permissive mode for bad dictionaries
3650 dupflags
[aflag
] += ((at
== 'S') ? dupSFX
: dupPFX
);
3653 // piece 3 - is cross product indicator
3654 case 2: { np
++; if (*piece
== 'Y') ff
= aeXPRODUCT
; break; }
3656 // piece 4 - is number of affentries
3659 numents
= atoi(piece
);
3661 char * err
= pHMgr
->encode_flag(aflag
);
3662 HUNSPELL_WARNING(stderr
, "error: affix %s header has incorrect entry count in line %s\n",
3667 ptr
= (struct affentry
*) malloc(numents
* sizeof(struct affentry
));
3670 if (utf8
) ptr
->opts
+= aeUTF8
;
3671 if (pHMgr
->is_aliasf()) ptr
->opts
+= aeALIASF
;
3672 #ifdef HUNSPELL_EXPERIMENTAL
3673 if (pHMgr
->is_aliasm()) ptr
->opts
+= aeALIASM
;
3683 piece
= mystrsep(&tp
, 0);
3685 // check to make sure we parsed enough pieces
3687 char * err
= pHMgr
->encode_flag(aflag
);
3688 HUNSPELL_WARNING(stderr
, "error: affix %s header has insufficient data in line %s\n", err
, nl
);
3694 // store away ptr to first affentry
3697 // now parse numents affentries for this affix
3698 for (int j
=0; j
< numents
; j
++) {
3699 if (!fgets(nl
,MAXLNLEN
,af
)) return 1;
3705 // split line into pieces
3706 piece
= mystrsep(&tp
, 0);
3708 if (*piece
!= '\0') {
3710 // piece 1 - is type
3713 if (nptr
!= ptr
) nptr
->opts
= ptr
->opts
;
3717 // piece 2 - is affix char
3720 if (pHMgr
->decode_flag(piece
) != aflag
) {
3721 char * err
= pHMgr
->encode_flag(aflag
);
3722 HUNSPELL_WARNING(stderr
, "error: affix %s is corrupt near line %s\n", err
, nl
);
3723 HUNSPELL_WARNING(stderr
, "error: possible incorrect count\n");
3729 if (nptr
!= ptr
) nptr
->aflag
= ptr
->aflag
;
3733 // piece 3 - is string to strip or 0 for null
3736 if (complexprefixes
) {
3737 if (utf8
) reverseword_utf(piece
); else reverseword(piece
);
3739 nptr
->strip
= mystrdup(piece
);
3740 nptr
->stripl
= (unsigned char) strlen(nptr
->strip
);
3741 if (strcmp(nptr
->strip
,"0") == 0) {
3743 nptr
->strip
=mystrdup("");
3749 // piece 4 - is affix string or 0 for null
3752 #ifdef HUNSPELL_EXPERIMENTAL
3753 nptr
->morphcode
= NULL
;
3755 nptr
->contclass
= NULL
;
3756 nptr
->contclasslen
= 0;
3758 dash
= strchr(piece
, '/');
3764 remove_ignored_chars_utf(piece
, ignorechars_utf16
, ignorechars_utf16_len
);
3766 remove_ignored_chars(piece
,ignorechars
);
3770 if (complexprefixes
) {
3771 if (utf8
) reverseword_utf(piece
); else reverseword(piece
);
3773 nptr
->appnd
= mystrdup(piece
);
3775 if (pHMgr
->is_aliasf()) {
3776 int index
= atoi(dash
+ 1);
3777 nptr
->contclasslen
= (unsigned short) pHMgr
->get_aliasf(index
, &(nptr
->contclass
));
3779 nptr
->contclasslen
= (unsigned short) pHMgr
->decode_flags(&(nptr
->contclass
), dash
+ 1);
3780 flag_qsort(nptr
->contclass
, 0, nptr
->contclasslen
);
3785 for (unsigned short _i
= 0; _i
< nptr
->contclasslen
; _i
++) {
3786 contclasses
[(nptr
->contclass
)[_i
]] = 1;
3791 remove_ignored_chars_utf(piece
, ignorechars_utf16
, ignorechars_utf16_len
);
3793 remove_ignored_chars(piece
,ignorechars
);
3797 if (complexprefixes
) {
3798 if (utf8
) reverseword_utf(piece
); else reverseword(piece
);
3800 nptr
->appnd
= mystrdup(piece
);
3803 nptr
->appndl
= (unsigned char) strlen(nptr
->appnd
);
3804 if (strcmp(nptr
->appnd
,"0") == 0) {
3806 nptr
->appnd
=mystrdup("");
3812 // piece 5 - is the conditions descriptions
3815 if (complexprefixes
) {
3817 if (utf8
) reverseword_utf(piece
); else reverseword(piece
);
3818 // reverse condition
3819 for (char * k
= piece
+ strlen(piece
) - 1; k
>= piece
; k
--) {
3822 if (neg
) *(k
+1) = '['; else *k
= ']';
3827 if (neg
) *(k
+1) = '^';
3832 if (*(k
+1) == ']') neg
= 1; else *(k
+1) = *k
;
3836 if (neg
) *(k
+1) = *k
;
3841 if (nptr
->stripl
&& (strcmp(piece
, ".") != 0) &&
3842 redundant_condition(at
, nptr
->strip
, nptr
->stripl
, piece
, nl
))
3844 if (encodeit(nptr
,piece
)) return 1;
3848 #ifdef HUNSPELL_EXPERIMENTAL
3851 if (pHMgr
->is_aliasm()) {
3852 int index
= atoi(piece
);
3853 nptr
->morphcode
= pHMgr
->get_aliasm(index
);
3855 if (complexprefixes
) {
3856 if (utf8
) reverseword_utf(piece
); else reverseword(piece
);
3858 nptr
->morphcode
= mystrdup(piece
);
3869 piece
= mystrsep(&tp
, 0);
3871 // check to make sure we parsed enough pieces
3873 char * err
= pHMgr
->encode_flag(aflag
);
3874 HUNSPELL_WARNING(stderr
, "error: affix %s is corrupt near line %s\n", err
, nl
);
3881 #ifdef HUNSPELL_EXPERIMENTAL
3882 // detect unnecessary fields, excepting comments
3884 int fieldnum
= !(nptr
->morphcode
) ? 5 : ((*(nptr
->morphcode
)=='#') ? 5 : 6);
3885 if (fieldnum
!= basefieldnum
)
3886 HUNSPELL_WARNING(stderr
, "warning: bad field number:\n%s\n", nl
);
3888 basefieldnum
= !(nptr
->morphcode
) ? 5 : ((*(nptr
->morphcode
)=='#') ? 5 : 6);
3895 // now create SfxEntry or PfxEntry objects and use links to
3896 // build an ordered (sorted by affix string) list
3898 for (int k
= 0; k
< numents
; k
++) {
3900 PfxEntry
* pfxptr
= new PfxEntry(this,nptr
);
3901 build_pfxtree((AffEntry
*)pfxptr
);
3903 SfxEntry
* sfxptr
= new SfxEntry(this,nptr
);
3904 build_sfxtree((AffEntry
*)sfxptr
);
3912 int AffixMgr::redundant_condition(char ft
, char * strip
, int stripl
, const char * cond
, char * line
) {
3913 int condl
= strlen(cond
);
3918 if (ft
== 'P') { // prefix
3919 if (strncmp(strip
, cond
, condl
) == 0) return 1;
3922 for (i
= 0, j
= 0; (i
< stripl
) && (j
< condl
); i
++, j
++) {
3923 if (cond
[j
] != '[') {
3924 if (cond
[j
] != strip
[i
]) {
3925 HUNSPELL_WARNING(stderr
, "warning: incompatible stripping characters and condition:\n%s\n", line
);
3928 neg
= (cond
[j
+1] == '^') ? 1 : 0;
3932 if (strip
[i
] == cond
[j
]) in
= 1;
3933 } while ((j
< (condl
- 1)) && (cond
[j
] != ']'));
3934 if (j
== (condl
- 1) && (cond
[j
] != ']')) {
3935 HUNSPELL_WARNING(stderr
, "error: missing ] in condition:\n%s\n", line
);
3938 if ((!neg
&& !in
) || (neg
&& in
)) {
3939 HUNSPELL_WARNING(stderr
, "warning: incompatible stripping characters and condition:\n%s\n", line
);
3944 if (j
>= condl
) return 1;
3947 if ((stripl
>= condl
) && strcmp(strip
+ stripl
- condl
, cond
) == 0) return 1;
3950 for (i
= stripl
- 1, j
= condl
- 1; (i
>= 0) && (j
>= 0); i
--, j
--) {
3951 if (cond
[j
] != ']') {
3952 if (cond
[j
] != strip
[i
]) {
3953 HUNSPELL_WARNING(stderr
, "warning: incompatible stripping characters and condition:\n%s\n", line
);
3959 if (strip
[i
] == cond
[j
]) in
= 1;
3960 } while ((j
> 0) && (cond
[j
] != '['));
3961 if ((j
== 0) && (cond
[j
] != '[')) {
3962 HUNSPELL_WARNING(stderr
, "error: missing ] in condition:\n%s\n", line
);
3965 neg
= (cond
[j
+1] == '^') ? 1 : 0;
3966 if ((!neg
&& !in
) || (neg
&& in
)) {
3967 HUNSPELL_WARNING(stderr
, "warning: incompatible stripping characters and condition:\n%s\n", line
);
3972 if (j
< 0) return 1;