1 #include "license.hunspell"
2 #include "license.myspell"
11 #include "affixmgr.hxx"
12 #include "affentry.hxx"
13 #include "langnum.hxx"
17 AffixMgr::AffixMgr(const char * affpath, HashMgr** ptr, int * md, const char * key)
19 // register hash manager and load affix data from aff file
38 // allow simplified compound forms (see 3rd field of CHECKCOMPOUNDPATTERN)
44 compoundflag = FLAG_NULL; // permits word in compound forms
45 compoundbegin = FLAG_NULL; // may be first word in compound forms
46 compoundmiddle = FLAG_NULL; // may be middle word in compound forms
47 compoundend = FLAG_NULL; // may be last word in compound forms
48 compoundroot = FLAG_NULL; // compound word signing flag
49 compoundpermitflag = FLAG_NULL; // compound permitting flag for suffixed word
50 compoundforbidflag = FLAG_NULL; // compound fordidden flag for suffixed word
51 checkcompounddup = 0; // forbid double words in compounds
52 checkcompoundrep = 0; // forbid bad compounds (may be non compound word with a REP substitution)
53 checkcompoundcase = 0; // forbid upper and lowercase combinations at word bounds
54 checkcompoundtriple = 0; // forbid compounds with triple letters
55 simplifiedtriple = 0; // allow simplified triple letters in compounds (Schiff+fahrt -> Schiffahrt)
56 forbiddenword = FORBIDDENWORD; // forbidden word signing flag
57 nosuggest = FLAG_NULL; // don't suggest words signed with NOSUGGEST flag
58 nongramsuggest = FLAG_NULL;
59 lang = NULL; // language
60 langnum = 0; // language code (see http://l10n.openoffice.org/languages.html)
61 needaffix = 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 maxngramsugs = -1; // undefined
86 maxdiff = -1; // undefined
88 maxcpdsugs = -1; // undefined
96 substandard = FLAG_NULL;
102 for (int i=0; i < SETSIZE; i++) {
109 for (int j=0; j < CONTSIZE; j++) {
113 if (parse_file(affpath, key)) {
114 HUNSPELL_WARNING(stderr, "Failure loading aff file %s\n",affpath);
117 if (cpdmin == -1) cpdmin = MINCPDLEN;
122 AffixMgr::~AffixMgr()
124 // pass through linked prefix entries and clean up
125 for (int i=0; i < SETSIZE ;i++) {
127 PfxEntry * ptr = pStart[i];
128 PfxEntry * nptr = NULL;
130 nptr = ptr->getNext();
137 // pass through linked suffix entries and clean up
138 for (int j=0; j < SETSIZE ; j++) {
140 SfxEntry * ptr = sStart[j];
141 SfxEntry * nptr = NULL;
143 nptr = ptr->getNext();
151 if (keystring) free(keystring);
153 if (trystring) free(trystring);
155 if (encoding) free(encoding);
158 for (int j=0; j < nummap; j++) {
159 for (int k=0; k < maptable[j].len; k++) {
160 if (maptable[j].set[k]) free(maptable[j].set[k]);
162 free(maptable[j].set);
163 maptable[j].set = NULL;
171 for (int j=0; j < numbreak; j++) {
172 if (breaktable[j]) free(breaktable[j]);
173 breaktable[j] = NULL;
180 for (int j=0; j < numrep; j++) {
181 free(reptable[j].pattern);
182 free(reptable[j].pattern2);
187 if (iconvtable) delete iconvtable;
188 if (oconvtable) delete oconvtable;
189 if (phone && phone->rules) {
190 for (int j=0; j < phone->num + 1; j++) {
191 free(phone->rules[j * 2]);
192 free(phone->rules[j * 2 + 1]);
200 for (int j=0; j < numdefcpd; j++) {
201 free(defcpdtable[j].def);
202 defcpdtable[j].def = NULL;
209 for (int j=0; j < numcheckcpd; j++) {
210 free(checkcpdtable[j].pattern);
211 free(checkcpdtable[j].pattern2);
212 free(checkcpdtable[j].pattern3);
213 checkcpdtable[j].pattern = NULL;
214 checkcpdtable[j].pattern2 = NULL;
215 checkcpdtable[j].pattern3 = NULL;
218 checkcpdtable = NULL;
221 FREE_FLAG(compoundflag);
222 FREE_FLAG(compoundbegin);
223 FREE_FLAG(compoundmiddle);
224 FREE_FLAG(compoundend);
225 FREE_FLAG(compoundpermitflag);
226 FREE_FLAG(compoundforbidflag);
227 FREE_FLAG(compoundroot);
228 FREE_FLAG(forbiddenword);
229 FREE_FLAG(nosuggest);
230 FREE_FLAG(nongramsuggest);
231 FREE_FLAG(needaffix);
232 FREE_FLAG(lemma_present);
233 FREE_FLAG(circumfix);
234 FREE_FLAG(onlyincompound);
240 if (cpdvowels) free(cpdvowels);
241 if (cpdvowels_utf16) free(cpdvowels_utf16);
242 if (cpdsyllablenum) free(cpdsyllablenum);
244 if (lang) free(lang);
245 if (wordchars) free(wordchars);
246 if (wordchars_utf16) free(wordchars_utf16);
247 if (ignorechars) free(ignorechars);
248 if (ignorechars_utf16) free(ignorechars_utf16);
249 if (version) free(version);
251 #ifdef MOZILLA_CLIENT
257 // read in aff file and build up prefix and suffix entry objects
258 int AffixMgr::parse_file(const char * affpath, const char * key)
260 char * line; // io buffers
261 char ft; // affix type
263 // checking flag duplication
264 char dupflags[CONTSIZE];
265 char dupflags_ini = 1;
267 // first line indicator for removing byte order mark
270 // open the affix file
271 FileMgr * afflst = new FileMgr(affpath, key);
273 HUNSPELL_WARNING(stderr, "error: could not open affix description file %s\n",affpath);
277 // step one is to parse the affix file building up the internal
278 // affix data structures
280 // read in each line ignoring any that do not
281 // start with a known line type indicator
282 while ((line = afflst->getline())) {
285 /* remove byte order mark */
288 // Affix file begins with byte order mark: possible incompatibility with old Hunspell versions
289 if (strncmp(line,"\xEF\xBB\xBF",3) == 0) {
290 memmove(line, line+3, strlen(line+3)+1);
294 /* parse in the keyboard string */
295 if (strncmp(line,"KEY",3) == 0) {
296 if (parse_string(line, &keystring, afflst->getlinenum())) {
302 /* parse in the try string */
303 if (strncmp(line,"TRY",3) == 0) {
304 if (parse_string(line, &trystring, afflst->getlinenum())) {
310 /* parse in the name of the character set used by the .dict and .aff */
311 if (strncmp(line,"SET",3) == 0) {
312 if (parse_string(line, &encoding, afflst->getlinenum())) {
316 if (strcmp(encoding, "UTF-8") == 0) {
318 #ifndef OPENOFFICEORG
319 #ifndef MOZILLA_CLIENT
320 if (initialize_utf_tbl()) return 1;
326 /* parse COMPLEXPREFIXES for agglutinative languages with right-to-left writing system */
327 if (strncmp(line,"COMPLEXPREFIXES",15) == 0)
330 /* parse in the flag used by the controlled compound words */
331 if (strncmp(line,"COMPOUNDFLAG",12) == 0) {
332 if (parse_flag(line, &compoundflag, afflst)) {
338 /* parse in the flag used by compound words */
339 if (strncmp(line,"COMPOUNDBEGIN",13) == 0) {
340 if (complexprefixes) {
341 if (parse_flag(line, &compoundend, afflst)) {
346 if (parse_flag(line, &compoundbegin, afflst)) {
353 /* parse in the flag used by compound words */
354 if (strncmp(line,"COMPOUNDMIDDLE",14) == 0) {
355 if (parse_flag(line, &compoundmiddle, afflst)) {
360 /* parse in the flag used by compound words */
361 if (strncmp(line,"COMPOUNDEND",11) == 0) {
362 if (complexprefixes) {
363 if (parse_flag(line, &compoundbegin, afflst)) {
368 if (parse_flag(line, &compoundend, afflst)) {
375 /* parse in the data used by compound_check() method */
376 if (strncmp(line,"COMPOUNDWORDMAX",15) == 0) {
377 if (parse_num(line, &cpdwordmax, afflst)) {
383 /* parse in the flag sign compounds in dictionary */
384 if (strncmp(line,"COMPOUNDROOT",12) == 0) {
385 if (parse_flag(line, &compoundroot, afflst)) {
391 /* parse in the flag used by compound_check() method */
392 if (strncmp(line,"COMPOUNDPERMITFLAG",18) == 0) {
393 if (parse_flag(line, &compoundpermitflag, afflst)) {
399 /* parse in the flag used by compound_check() method */
400 if (strncmp(line,"COMPOUNDFORBIDFLAG",18) == 0) {
401 if (parse_flag(line, &compoundforbidflag, afflst)) {
407 if (strncmp(line,"CHECKCOMPOUNDDUP",16) == 0) {
408 checkcompounddup = 1;
411 if (strncmp(line,"CHECKCOMPOUNDREP",16) == 0) {
412 checkcompoundrep = 1;
415 if (strncmp(line,"CHECKCOMPOUNDTRIPLE",19) == 0) {
416 checkcompoundtriple = 1;
419 if (strncmp(line,"SIMPLIFIEDTRIPLE",16) == 0) {
420 simplifiedtriple = 1;
423 if (strncmp(line,"CHECKCOMPOUNDCASE",17) == 0) {
424 checkcompoundcase = 1;
427 if (strncmp(line,"NOSUGGEST",9) == 0) {
428 if (parse_flag(line, &nosuggest, afflst)) {
434 if (strncmp(line,"NONGRAMSUGGEST",14) == 0) {
435 if (parse_flag(line, &nongramsuggest, afflst)) {
441 /* parse in the flag used by forbidden words */
442 if (strncmp(line,"FORBIDDENWORD",13) == 0) {
443 if (parse_flag(line, &forbiddenword, afflst)) {
449 /* parse in the flag used by forbidden words */
450 if (strncmp(line,"LEMMA_PRESENT",13) == 0) {
451 if (parse_flag(line, &lemma_present, afflst)) {
457 /* parse in the flag used by circumfixes */
458 if (strncmp(line,"CIRCUMFIX",9) == 0) {
459 if (parse_flag(line, &circumfix, afflst)) {
465 /* parse in the flag used by fogemorphemes */
466 if (strncmp(line,"ONLYINCOMPOUND",14) == 0) {
467 if (parse_flag(line, &onlyincompound, afflst)) {
473 /* parse in the flag used by `needaffixs' */
474 if (strncmp(line,"PSEUDOROOT",10) == 0) {
475 if (parse_flag(line, &needaffix, afflst)) {
481 /* parse in the flag used by `needaffixs' */
482 if (strncmp(line,"NEEDAFFIX",9) == 0) {
483 if (parse_flag(line, &needaffix, afflst)) {
489 /* parse in the minimal length for words in compounds */
490 if (strncmp(line,"COMPOUNDMIN",11) == 0) {
491 if (parse_num(line, &cpdmin, afflst)) {
495 if (cpdmin < 1) cpdmin = 1;
498 /* parse in the max. words and syllables in compounds */
499 if (strncmp(line,"COMPOUNDSYLLABLE",16) == 0) {
500 if (parse_cpdsyllable(line, afflst)) {
506 /* parse in the flag used by compound_check() method */
507 if (strncmp(line,"SYLLABLENUM",11) == 0) {
508 if (parse_string(line, &cpdsyllablenum, afflst->getlinenum())) {
514 /* parse in the flag used by the controlled compound words */
515 if (strncmp(line,"CHECKNUM",8) == 0) {
519 /* parse in the extra word characters */
520 if (strncmp(line,"WORDCHARS",9) == 0) {
521 if (parse_array(line, &wordchars, &wordchars_utf16, &wordchars_utf16_len, utf8, afflst->getlinenum())) {
527 /* parse in the ignored characters (for example, Arabic optional diacretics charachters */
528 if (strncmp(line,"IGNORE",6) == 0) {
529 if (parse_array(line, &ignorechars, &ignorechars_utf16, &ignorechars_utf16_len, utf8, afflst->getlinenum())) {
535 /* parse in the typical fault correcting table */
536 if (strncmp(line,"REP",3) == 0) {
537 if (parse_reptable(line, afflst)) {
543 /* parse in the input conversion table */
544 if (strncmp(line,"ICONV",5) == 0) {
545 if (parse_convtable(line, afflst, &iconvtable, "ICONV")) {
551 /* parse in the input conversion table */
552 if (strncmp(line,"OCONV",5) == 0) {
553 if (parse_convtable(line, afflst, &oconvtable, "OCONV")) {
559 /* parse in the phonetic translation table */
560 if (strncmp(line,"PHONE",5) == 0) {
561 if (parse_phonetable(line, afflst)) {
567 /* parse in the checkcompoundpattern table */
568 if (strncmp(line,"CHECKCOMPOUNDPATTERN",20) == 0) {
569 if (parse_checkcpdtable(line, afflst)) {
575 /* parse in the defcompound table */
576 if (strncmp(line,"COMPOUNDRULE",12) == 0) {
577 if (parse_defcpdtable(line, afflst)) {
583 /* parse in the related character map table */
584 if (strncmp(line,"MAP",3) == 0) {
585 if (parse_maptable(line, afflst)) {
591 /* parse in the word breakpoints table */
592 if (strncmp(line,"BREAK",5) == 0) {
593 if (parse_breaktable(line, afflst)) {
599 /* parse in the language for language specific codes */
600 if (strncmp(line,"LANG",4) == 0) {
601 if (parse_string(line, &lang, afflst->getlinenum())) {
605 langnum = get_lang_num(lang);
608 if (strncmp(line,"VERSION",7) == 0) {
609 for(line = line + 7; *line == ' ' || *line == '\t'; line++);
610 version = mystrdup(line);
613 if (strncmp(line,"MAXNGRAMSUGS",12) == 0) {
614 if (parse_num(line, &maxngramsugs, afflst)) {
620 if (strncmp(line,"ONLYMAXDIFF", 11) == 0)
623 if (strncmp(line,"MAXDIFF",7) == 0) {
624 if (parse_num(line, &maxdiff, afflst)) {
630 if (strncmp(line,"MAXCPDSUGS",10) == 0) {
631 if (parse_num(line, &maxcpdsugs, afflst)) {
637 if (strncmp(line,"NOSPLITSUGS",11) == 0) {
641 if (strncmp(line,"FULLSTRIP",9) == 0) {
645 if (strncmp(line,"SUGSWITHDOTS",12) == 0) {
649 /* parse in the flag used by forbidden words */
650 if (strncmp(line,"KEEPCASE",8) == 0) {
651 if (parse_flag(line, &keepcase, afflst)) {
657 /* parse in the flag used by `forceucase' */
658 if (strncmp(line,"FORCEUCASE",10) == 0) {
659 if (parse_flag(line, &forceucase, afflst)) {
665 /* parse in the flag used by `warn' */
666 if (strncmp(line,"WARN",4) == 0) {
667 if (parse_flag(line, &warn, afflst)) {
673 if (strncmp(line,"FORBIDWARN",10) == 0) {
677 /* parse in the flag used by the affix generator */
678 if (strncmp(line,"SUBSTANDARD",11) == 0) {
679 if (parse_flag(line, &substandard, afflst)) {
685 if (strncmp(line,"CHECKSHARPS",11) == 0) {
689 /* parse this affix: P - prefix, S - suffix */
691 if (strncmp(line,"PFX",3) == 0) ft = complexprefixes ? 'S' : 'P';
692 if (strncmp(line,"SFX",3) == 0) ft = complexprefixes ? 'P' : 'S';
695 memset(dupflags, 0, sizeof(dupflags));
698 if (parse_affix(line, ft, afflst, dupflags)) {
700 process_pfx_tree_to_list();
701 process_sfx_tree_to_list();
709 // convert affix trees to sorted list
710 process_pfx_tree_to_list();
711 process_sfx_tree_to_list();
713 // now we can speed up performance greatly taking advantage of the
714 // relationship between the affixes and the idea of "subsets".
716 // View each prefix as a potential leading subset of another and view
717 // each suffix (reversed) as a potential trailing subset of another.
719 // To illustrate this relationship if we know the prefix "ab" is found in the
720 // word to examine, only prefixes that "ab" is a leading subset of need be examined.
721 // Furthermore is "ab" is not present then none of the prefixes that "ab" is
722 // is a subset need be examined.
723 // The same argument goes for suffix string that are reversed.
725 // Then to top this off why not examine the first char of the word to quickly
726 // limit the set of prefixes to examine (i.e. the prefixes to examine must
727 // be leading supersets of the first character of the word (if they exist)
729 // To take advantage of this "subset" relationship, we need to add two links
730 // from entry. One to take next if the current prefix is found (call it nexteq)
731 // and one to take next if the current prefix is not found (call it nextne).
733 // Since we have built ordered lists, all that remains is to properly initialize
734 // the nextne and nexteq pointers that relate them
739 /* get encoding for CHECKCOMPOUNDCASE */
741 char * enc = get_encoding();
742 csconv = get_current_cs(enc);
748 strcpy(expw, wordchars);
752 for (int i = 0; i <= 255; i++) {
753 if ( (csconv[i].cupper != csconv[i].clower) &&
754 (! strchr(expw, (char) i))) {
755 *(expw + strlen(expw) + 1) = '\0';
756 *(expw + strlen(expw)) = (char) i;
760 wordchars = mystrdup(expw);
763 // default BREAK definition
764 if (numbreak == -1) {
765 breaktable = (char **) malloc(sizeof(char *) * 3);
766 if (!breaktable) return 1;
767 breaktable[0] = mystrdup("-");
768 breaktable[1] = mystrdup("^-");
769 breaktable[2] = mystrdup("-$");
770 if (breaktable[0] && breaktable[1] && breaktable[2]) numbreak = 3;
776 // we want to be able to quickly access prefix information
777 // both by prefix flag, and sorted by prefix string itself
778 // so we need to set up two indexes
780 int AffixMgr::build_pfxtree(PfxEntry* pfxptr)
784 PfxEntry * ep = pfxptr;
786 // get the right starting points
787 const char * key = ep->getKey();
788 const unsigned char flg = (unsigned char) (ep->getFlag() & 0x00FF);
790 // first index by flag which must exist
796 // handle the special case of null affix string
797 if (strlen(key) == 0) {
798 // always inset them at head of list at element 0
805 // now handle the normal case
809 unsigned char sp = *((const unsigned char *)key);
812 // handle the first insert
819 // otherwise use binary tree insertion so that a sorted
820 // list can easily be generated later
824 if (strcmp(ep->getKey(), ptr->getKey() ) <= 0) {
825 ptr = ptr->getNextEQ();
831 ptr = ptr->getNextNE();
841 // we want to be able to quickly access suffix information
842 // both by suffix flag, and sorted by the reverse of the
843 // suffix string itself; so we need to set up two indexes
844 int AffixMgr::build_sfxtree(SfxEntry* sfxptr)
848 SfxEntry * ep = sfxptr;
850 /* get the right starting point */
851 const char * key = ep->getKey();
852 const unsigned char flg = (unsigned char) (ep->getFlag() & 0x00FF);
854 // first index by flag which must exist
859 // next index by affix string
861 // handle the special case of null affix string
862 if (strlen(key) == 0) {
863 // always inset them at head of list at element 0
870 // now handle the normal case
874 unsigned char sp = *((const unsigned char *)key);
877 // handle the first insert
883 // otherwise use binary tree insertion so that a sorted
884 // list can easily be generated later
888 if (strcmp(ep->getKey(), ptr->getKey() ) <= 0) {
889 ptr = ptr->getNextEQ();
895 ptr = ptr->getNextNE();
905 // convert from binary tree to sorted list
906 int AffixMgr::process_pfx_tree_to_list()
908 for (int i=1; i< SETSIZE; i++) {
909 pStart[i] = process_pfx_in_order(pStart[i],NULL);
915 PfxEntry* AffixMgr::process_pfx_in_order(PfxEntry* ptr, PfxEntry* nptr)
918 nptr = process_pfx_in_order(ptr->getNextNE(), nptr);
920 nptr = process_pfx_in_order(ptr->getNextEQ(), ptr);
926 // convert from binary tree to sorted list
927 int AffixMgr:: process_sfx_tree_to_list()
929 for (int i=1; i< SETSIZE; i++) {
930 sStart[i] = process_sfx_in_order(sStart[i],NULL);
935 SfxEntry* AffixMgr::process_sfx_in_order(SfxEntry* ptr, SfxEntry* nptr)
938 nptr = process_sfx_in_order(ptr->getNextNE(), nptr);
940 nptr = process_sfx_in_order(ptr->getNextEQ(), ptr);
946 // reinitialize the PfxEntry links NextEQ and NextNE to speed searching
947 // using the idea of leading subsets this time
948 int AffixMgr::process_pfx_order()
952 // loop through each prefix list starting point
953 for (int i=1; i < SETSIZE; i++) {
957 // look through the remainder of the list
958 // and find next entry with affix that
959 // the current one is not a subset of
960 // mark that as destination for NextNE
961 // use next in list that you are a subset
964 for (; ptr != NULL; ptr = ptr->getNext()) {
966 PfxEntry * nptr = ptr->getNext();
967 for (; nptr != NULL; nptr = nptr->getNext()) {
968 if (! isSubset( ptr->getKey() , nptr->getKey() )) break;
970 ptr->setNextNE(nptr);
971 ptr->setNextEQ(NULL);
972 if ((ptr->getNext()) && isSubset(ptr->getKey() , (ptr->getNext())->getKey()))
973 ptr->setNextEQ(ptr->getNext());
976 // now clean up by adding smart search termination strings:
977 // if you are already a superset of the previous prefix
978 // but not a subset of the next, search can end here
979 // so set NextNE properly
982 for (; ptr != NULL; ptr = ptr->getNext()) {
983 PfxEntry * nptr = ptr->getNext();
984 PfxEntry * mptr = NULL;
985 for (; nptr != NULL; nptr = nptr->getNext()) {
986 if (! isSubset(ptr->getKey(),nptr->getKey())) break;
989 if (mptr) mptr->setNextNE(NULL);
995 // initialize the SfxEntry links NextEQ and NextNE to speed searching
996 // using the idea of leading subsets this time
997 int AffixMgr::process_sfx_order()
1001 // loop through each prefix list starting point
1002 for (int i=1; i < SETSIZE; i++) {
1006 // look through the remainder of the list
1007 // and find next entry with affix that
1008 // the current one is not a subset of
1009 // mark that as destination for NextNE
1010 // use next in list that you are a subset
1013 for (; ptr != NULL; ptr = ptr->getNext()) {
1014 SfxEntry * nptr = ptr->getNext();
1015 for (; nptr != NULL; nptr = nptr->getNext()) {
1016 if (! isSubset(ptr->getKey(),nptr->getKey())) break;
1018 ptr->setNextNE(nptr);
1019 ptr->setNextEQ(NULL);
1020 if ((ptr->getNext()) && isSubset(ptr->getKey(),(ptr->getNext())->getKey()))
1021 ptr->setNextEQ(ptr->getNext());
1025 // now clean up by adding smart search termination strings:
1026 // if you are already a superset of the previous suffix
1027 // but not a subset of the next, search can end here
1028 // so set NextNE properly
1031 for (; ptr != NULL; ptr = ptr->getNext()) {
1032 SfxEntry * nptr = ptr->getNext();
1033 SfxEntry * mptr = NULL;
1034 for (; nptr != NULL; nptr = nptr->getNext()) {
1035 if (! isSubset(ptr->getKey(),nptr->getKey())) break;
1038 if (mptr) mptr->setNextNE(NULL);
1044 // add flags to the result for dictionary debugging
1045 void AffixMgr::debugflag(char * result, unsigned short flag) {
1046 char * st = encode_flag(flag);
1047 mystrcat(result, " ", MAXLNLEN);
1048 mystrcat(result, MORPH_FLAG, MAXLNLEN);
1050 mystrcat(result, st, MAXLNLEN);
1055 // calculate the character length of the condition
1056 int AffixMgr::condlen(char * st)
1064 } else if (*st == ']') group = false;
1065 else if (!group && (!utf8 ||
1066 (!(*st & 0x80) || ((*st & 0xc0) == 0x80)))) l++;
1071 int AffixMgr::encodeit(affentry &entry, char * cs)
1073 if (strcmp(cs,".") != 0) {
1074 entry.numconds = (char) condlen(cs);
1075 strncpy(entry.c.conds, cs, MAXCONDLEN);
1076 // long condition (end of conds padded by strncpy)
1077 if (entry.c.conds[MAXCONDLEN - 1] && cs[MAXCONDLEN]) {
1078 entry.opts += aeLONGCOND;
1079 entry.c.l.conds2 = mystrdup(cs + MAXCONDLEN_1);
1080 if (!entry.c.l.conds2) return 1;
1084 entry.c.conds[0] = '\0';
1089 // return 1 if s1 is a leading subset of s2 (dots are for infixes)
1090 inline int AffixMgr::isSubset(const char * s1, const char * s2)
1092 while (((*s1 == *s2) || (*s1 == '.')) && (*s1 != '\0')) {
1096 return (*s1 == '\0');
1100 // check word for prefixes
1101 struct hentry * AffixMgr::prefix_check(const char * word, int len, char in_compound,
1102 const FLAG needflag)
1104 struct hentry * rv= NULL;
1110 // first handle the special case of 0 length prefixes
1111 PfxEntry * pe = pStart[0];
1115 ((in_compound != IN_CPD_NOT) || !(pe->getCont() &&
1116 (TESTAFF(pe->getCont(), onlyincompound, pe->getContLen())))) &&
1117 // permit prefixes in compounds
1118 ((in_compound != IN_CPD_END) || (pe->getCont() &&
1119 (TESTAFF(pe->getCont(), compoundpermitflag, pe->getContLen()))))
1122 rv = pe->checkword(word, len, in_compound, needflag);
1124 pfx=pe; // BUG: pfx not stateless
1131 // now handle the general case
1132 unsigned char sp = *((const unsigned char *)word);
1133 PfxEntry * pptr = pStart[sp];
1136 if (isSubset(pptr->getKey(),word)) {
1139 ((in_compound != IN_CPD_NOT) || !(pptr->getCont() &&
1140 (TESTAFF(pptr->getCont(), onlyincompound, pptr->getContLen())))) &&
1141 // permit prefixes in compounds
1142 ((in_compound != IN_CPD_END) || (pptr->getCont() &&
1143 (TESTAFF(pptr->getCont(), compoundpermitflag, pptr->getContLen()))))
1146 rv = pptr->checkword(word, len, in_compound, needflag);
1148 pfx=pptr; // BUG: pfx not stateless
1152 pptr = pptr->getNextEQ();
1154 pptr = pptr->getNextNE();
1161 // check word for prefixes
1162 struct hentry * AffixMgr::prefix_check_twosfx(const char * word, int len,
1163 char in_compound, const FLAG needflag)
1165 struct hentry * rv= NULL;
1170 // first handle the special case of 0 length prefixes
1171 PfxEntry * pe = pStart[0];
1174 rv = pe->check_twosfx(word, len, in_compound, needflag);
1179 // now handle the general case
1180 unsigned char sp = *((const unsigned char *)word);
1181 PfxEntry * pptr = pStart[sp];
1184 if (isSubset(pptr->getKey(),word)) {
1185 rv = pptr->check_twosfx(word, len, in_compound, needflag);
1190 pptr = pptr->getNextEQ();
1192 pptr = pptr->getNextNE();
1199 // check word for prefixes
1200 char * AffixMgr::prefix_check_morph(const char * word, int len, char in_compound,
1201 const FLAG needflag)
1205 char result[MAXLNLEN];
1211 // first handle the special case of 0 length prefixes
1212 PfxEntry * pe = pStart[0];
1214 st = pe->check_morph(word,len,in_compound, needflag);
1216 mystrcat(result, st, MAXLNLEN);
1219 // if (rv) return rv;
1223 // now handle the general case
1224 unsigned char sp = *((const unsigned char *)word);
1225 PfxEntry * pptr = pStart[sp];
1228 if (isSubset(pptr->getKey(),word)) {
1229 st = pptr->check_morph(word,len,in_compound, needflag);
1232 if ((in_compound != IN_CPD_NOT) || !((pptr->getCont() &&
1233 (TESTAFF(pptr->getCont(), onlyincompound, pptr->getContLen()))))) {
1234 mystrcat(result, st, MAXLNLEN);
1239 pptr = pptr->getNextEQ();
1241 pptr = pptr->getNextNE();
1245 if (*result) return mystrdup(result);
1250 // check word for prefixes
1251 char * AffixMgr::prefix_check_twosfx_morph(const char * word, int len,
1252 char in_compound, const FLAG needflag)
1256 char result[MAXLNLEN];
1262 // first handle the special case of 0 length prefixes
1263 PfxEntry * pe = pStart[0];
1265 st = pe->check_twosfx_morph(word,len,in_compound, needflag);
1267 mystrcat(result, st, MAXLNLEN);
1273 // now handle the general case
1274 unsigned char sp = *((const unsigned char *)word);
1275 PfxEntry * pptr = pStart[sp];
1278 if (isSubset(pptr->getKey(),word)) {
1279 st = pptr->check_twosfx_morph(word, len, in_compound, needflag);
1281 mystrcat(result, st, MAXLNLEN);
1285 pptr = pptr->getNextEQ();
1287 pptr = pptr->getNextNE();
1291 if (*result) return mystrdup(result);
1295 // Is word a non compound with a REP substitution (see checkcompoundrep)?
1296 int AffixMgr::cpdrep_check(const char * word, int wl)
1298 char candidate[MAXLNLEN];
1302 if ((wl < 2) || !numrep) return 0;
1304 for (int i=0; i < numrep; i++ ) {
1306 lenr = strlen(reptable[i].pattern2);
1307 lenp = strlen(reptable[i].pattern);
1308 // search every occurence of the pattern in the word
1309 while ((r=strstr(r, reptable[i].pattern)) != NULL) {
1310 strcpy(candidate, word);
1311 if (r-word + lenr + strlen(r+lenp) >= MAXLNLEN) break;
1312 strcpy(candidate+(r-word),reptable[i].pattern2);
1313 strcpy(candidate+(r-word)+lenr, r+lenp);
1314 if (candidate_check(candidate,strlen(candidate))) return 1;
1315 r++; // search for the next letter
1321 // forbid compoundings when there are special patterns at word bound
1322 int AffixMgr::cpdpat_check(const char * word, int pos, hentry * r1, hentry * r2, const char affixed)
1325 for (int i = 0; i < numcheckcpd; i++) {
1326 if (isSubset(checkcpdtable[i].pattern2, word + pos) &&
1327 (!r1 || !checkcpdtable[i].cond ||
1328 (r1->astr && TESTAFF(r1->astr, checkcpdtable[i].cond, r1->alen))) &&
1329 (!r2 || !checkcpdtable[i].cond2 ||
1330 (r2->astr && TESTAFF(r2->astr, checkcpdtable[i].cond2, r2->alen))) &&
1331 // zero length pattern => only TESTAFF
1332 // zero pattern (0/flag) => unmodified stem (zero affixes allowed)
1333 (!*(checkcpdtable[i].pattern) || (
1334 (*(checkcpdtable[i].pattern)=='0' && r1->blen <= pos && strncmp(word + pos - r1->blen, r1->word, r1->blen) == 0) ||
1335 (*(checkcpdtable[i].pattern)!='0' && (len = strlen(checkcpdtable[i].pattern)) &&
1336 strncmp(word + pos - len, checkcpdtable[i].pattern, len) == 0)))) {
1343 // forbid compounding with neighbouring upper and lower case characters at word bounds
1344 int AffixMgr::cpdcase_check(const char * word, int pos)
1349 u8_u16(&u, 1, word + pos);
1350 for (p = word + pos - 1; (*p & 0xc0) == 0x80; p--);
1352 unsigned short a = (u.h << 8) + u.l;
1353 unsigned short b = (w.h << 8) + w.l;
1354 if (((unicodetoupper(a, langnum) == a) || (unicodetoupper(b, langnum) == b)) &&
1355 (a != '-') && (b != '-')) return 1;
1357 unsigned char a = *(word + pos - 1);
1358 unsigned char b = *(word + pos);
1359 if ((csconv[a].ccase || csconv[b].ccase) && (a != '-') && (b != '-')) return 1;
1364 // check compound patterns
1365 int AffixMgr::defcpd_check(hentry *** words, short wnum, hentry * rv, hentry ** def, char all)
1367 signed short btpp[MAXWORDLEN]; // metacharacter (*, ?) positions for backtracking
1368 signed short btwp[MAXWORDLEN]; // word positions for metacharacters
1369 int btnum[MAXWORDLEN]; // number of matched characters in metacharacter positions
1384 (*words)[wnum] = rv;
1386 // has the last word COMPOUNDRULE flag?
1387 if (rv->alen == 0) {
1388 (*words)[wnum] = NULL;
1389 if (w) *words = NULL;
1393 for (i = 0; i < numdefcpd; i++) {
1394 for (j = 0; j < defcpdtable[i].len; j++) {
1395 if (defcpdtable[i].def[j] != '*' && defcpdtable[i].def[j] != '?' &&
1396 TESTAFF(rv->astr, defcpdtable[i].def[j], rv->alen)) ok = 1;
1400 (*words)[wnum] = NULL;
1401 if (w) *words = NULL;
1405 for (i = 0; i < numdefcpd; i++) {
1406 signed short pp = 0; // pattern position
1407 signed short wp = 0; // "words" position
1412 while ((pp < defcpdtable[i].len) && (wp <= wnum)) {
1413 if (((pp+1) < defcpdtable[i].len) &&
1414 ((defcpdtable[i].def[pp+1] == '*') || (defcpdtable[i].def[pp+1] == '?'))) {
1415 int wend = (defcpdtable[i].def[pp+1] == '?') ? wp : wnum;
1420 while (wp <= wend) {
1421 if (!(*words)[wp]->alen ||
1422 !TESTAFF((*words)[wp]->astr, defcpdtable[i].def[pp-2], (*words)[wp]->alen)) {
1428 if (wp <= wnum) ok2 = 0;
1429 btnum[bt] = wp - btwp[bt];
1430 if (btnum[bt] > 0) bt++;
1434 if (!(*words)[wp] || !(*words)[wp]->alen ||
1435 !TESTAFF((*words)[wp]->astr, defcpdtable[i].def[pp], (*words)[wp]->alen)) {
1441 if ((defcpdtable[i].len == pp) && !(wp > wnum)) ok = 0;
1446 while ((defcpdtable[i].len > r) && ((r+1) < defcpdtable[i].len) &&
1447 ((defcpdtable[i].def[r+1] == '*') || (defcpdtable[i].def[r+1] == '?'))) r+=2;
1448 if (defcpdtable[i].len <= r) return 1;
1455 wp = btwp[bt - 1] + (signed short) btnum[bt - 1];
1456 } while ((btnum[bt - 1] < 0) && --bt);
1459 if (ok && ok2 && (!all || (defcpdtable[i].len <= pp))) return 1;
1461 // check zero ending
1462 while (ok && ok2 && (defcpdtable[i].len > pp) && ((pp+1) < defcpdtable[i].len) &&
1463 ((defcpdtable[i].def[pp+1] == '*') || (defcpdtable[i].def[pp+1] == '?'))) pp+=2;
1464 if (ok && ok2 && (defcpdtable[i].len <= pp)) return 1;
1466 (*words)[wnum] = NULL;
1467 if (w) *words = NULL;
1471 inline int AffixMgr::candidate_check(const char * word, int len)
1473 struct hentry * rv=NULL;
1478 // rv = prefix_check(word,len,1);
1479 // if (rv) return 1;
1481 rv = affix_check(word,len);
1486 // calculate number of syllable for compound-checking
1487 short AffixMgr::get_syllable(const char * word, int wlen)
1489 if (cpdmaxsyllable==0) return 0;
1494 for (int i=0; i<wlen; i++) {
1495 if (strchr(cpdvowels, word[i])) num++;
1497 } else if (cpdvowels_utf16) {
1498 w_char w[MAXWORDUTF8LEN];
1499 int i = u8_u16(w, MAXWORDUTF8LEN, word);
1500 for (; i > 0; i--) {
1501 if (flag_bsearch((unsigned short *) cpdvowels_utf16,
1502 ((unsigned short *) w)[i - 1], cpdvowels_utf16_len)) num++;
1508 void AffixMgr::setcminmax(int * cmin, int * cmax, const char * word, int len) {
1511 for (*cmin = 0, i = 0; (i < cpdmin) && word[*cmin]; i++) {
1512 for ((*cmin)++; (word[*cmin] & 0xc0) == 0x80; (*cmin)++);
1514 for (*cmax = len, i = 0; (i < (cpdmin - 1)) && *cmax; i++) {
1515 for ((*cmax)--; (word[*cmax] & 0xc0) == 0x80; (*cmax)--);
1519 *cmax = len - cpdmin + 1;
1524 // check if compound word is correctly spelled
1525 // hu_mov_rule = spec. Hungarian rule (XXX)
1526 struct hentry * AffixMgr::compound_check(const char * word, int len,
1527 short wordnum, short numsyllable, short maxwordnum, short wnum, hentry ** words = NULL,
1528 char hu_mov_rule = 0, char is_sug = 0, int info = 0)
1531 short oldnumsyllable, oldnumsyllable2, oldwordnum, oldwordnum2;
1532 struct hentry * rv = NULL;
1533 struct hentry * rv_first;
1534 struct hentry * rwords[MAXWORDLEN]; // buffer for COMPOUND pattern checking
1535 char st [MAXWORDUTF8LEN + 4];
1545 int checkedstriple = 0;
1551 setcminmax(&cmin, &cmax, word, len);
1555 for (i = cmin; i < cmax; i++) {
1556 // go to end of the UTF-8 character
1558 for (; (st[i] & 0xc0) == 0x80; i++);
1559 if (i >= cmax) return NULL;
1564 do { // onlycpdrule loop
1566 oldnumsyllable = numsyllable;
1567 oldwordnum = wordnum;
1570 do { // simplified checkcompoundpattern loop
1573 for (; scpd <= numcheckcpd && (!checkcpdtable[scpd-1].pattern3 ||
1574 strncmp(word + i, checkcpdtable[scpd-1].pattern3, strlen(checkcpdtable[scpd-1].pattern3)) != 0); scpd++);
1576 if (scpd > numcheckcpd) break; // break simplified checkcompoundpattern loop
1577 strcpy(st + i, checkcpdtable[scpd-1].pattern);
1579 i += strlen(checkcpdtable[scpd-1].pattern);
1580 strcpy(st + i, checkcpdtable[scpd-1].pattern2);
1581 strcpy(st + i + strlen(checkcpdtable[scpd-1].pattern2), word + soldi + strlen(checkcpdtable[scpd-1].pattern3));
1584 len += strlen(checkcpdtable[scpd-1].pattern) + strlen(checkcpdtable[scpd-1].pattern2) - strlen(checkcpdtable[scpd-1].pattern3);
1587 setcminmax(&cmin, &cmax, st, len);
1589 cmax = len - cpdmin + 1;
1601 rv = lookup(st); // perhaps without prefix
1603 // search homonym with compound flag
1604 while ((rv) && !hu_mov_rule &&
1605 ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
1606 !((compoundflag && !words && !onlycpdrule && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
1607 (compoundbegin && !wordnum && !onlycpdrule &&
1608 TESTAFF(rv->astr, compoundbegin, rv->alen)) ||
1609 (compoundmiddle && wordnum && !words && !onlycpdrule &&
1610 TESTAFF(rv->astr, compoundmiddle, rv->alen)) ||
1612 ((!words && !wordnum && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0)) ||
1613 (words && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0))) && (onlycpdrule = 1))) || // switch onlycpdrule on
1614 (scpd != 0 && checkcpdtable[scpd-1].cond != FLAG_NULL &&
1615 !TESTAFF(rv->astr, checkcpdtable[scpd-1].cond, rv->alen)))
1617 rv = rv->next_homonym;
1620 if (rv) affixed = 0;
1623 if (onlycpdrule) break;
1624 if (compoundflag && !words &&
1625 !(rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundflag))) {
1626 if ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL,
1627 FLAG_NULL, compoundflag, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) && !hu_mov_rule &&
1629 ((compoundforbidflag && TESTAFF(sfx->getCont(), compoundforbidflag,
1630 sfx->getContLen())) || (compoundend &&
1631 TESTAFF(sfx->getCont(), compoundend,
1632 sfx->getContLen())))) {
1638 (((wordnum == 0) && compoundbegin &&
1639 ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundbegin, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
1640 (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundbegin)))) ||
1641 ((wordnum > 0) && compoundmiddle &&
1642 ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundmiddle, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
1643 (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundmiddle)))))
1644 ) checked_prefix = 1;
1645 // else check forbiddenwords and needaffix
1646 } else if (rv->astr && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
1647 TESTAFF(rv->astr, needaffix, rv->alen) ||
1648 TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
1649 (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen))
1656 // check non_compound flag in suffix and prefix
1657 if ((rv) && !hu_mov_rule &&
1658 ((pfx && pfx->getCont() &&
1659 TESTAFF(pfx->getCont(), compoundforbidflag,
1660 pfx->getContLen())) ||
1661 (sfx && sfx->getCont() &&
1662 TESTAFF(sfx->getCont(), compoundforbidflag,
1663 sfx->getContLen())))) {
1667 // check compoundend flag in suffix and prefix
1668 if ((rv) && !checked_prefix && compoundend && !hu_mov_rule &&
1669 ((pfx && pfx->getCont() &&
1670 TESTAFF(pfx->getCont(), compoundend,
1671 pfx->getContLen())) ||
1672 (sfx && sfx->getCont() &&
1673 TESTAFF(sfx->getCont(), compoundend,
1674 sfx->getContLen())))) {
1678 // check compoundmiddle flag in suffix and prefix
1679 if ((rv) && !checked_prefix && (wordnum==0) && compoundmiddle && !hu_mov_rule &&
1680 ((pfx && pfx->getCont() &&
1681 TESTAFF(pfx->getCont(), compoundmiddle,
1682 pfx->getContLen())) ||
1683 (sfx && sfx->getCont() &&
1684 TESTAFF(sfx->getCont(), compoundmiddle,
1685 sfx->getContLen())))) {
1689 // check forbiddenwords
1690 if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
1691 TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
1692 (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) {
1696 // increment word number, if the second root has a compoundroot flag
1697 if ((rv) && compoundroot &&
1698 (TESTAFF(rv->astr, compoundroot, rv->alen))) {
1702 // first word is acceptable in compound words?
1704 ( checked_prefix || (words && words[wnum]) ||
1705 (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
1706 ((oldwordnum == 0) && compoundbegin && TESTAFF(rv->astr, compoundbegin, rv->alen)) ||
1707 ((oldwordnum > 0) && compoundmiddle && TESTAFF(rv->astr, compoundmiddle, rv->alen))// ||
1710 // LANG_hu section: spec. Hungarian rule
1711 || ((langnum == LANG_hu) && hu_mov_rule && (
1712 TESTAFF(rv->astr, 'F', rv->alen) || // XXX hardwired Hungarian dictionary codes
1713 TESTAFF(rv->astr, 'G', rv->alen) ||
1714 TESTAFF(rv->astr, 'H', rv->alen)
1717 // END of LANG_hu section
1720 // test CHECKCOMPOUNDPATTERN conditions
1721 scpd == 0 || checkcpdtable[scpd-1].cond == FLAG_NULL ||
1722 TESTAFF(rv->astr, checkcpdtable[scpd-1].cond, rv->alen)
1724 && ! (( checkcompoundtriple && scpd == 0 && !words && // test triple letters
1725 (word[i-1]==word[i]) && (
1726 ((i>1) && (word[i-1]==word[i-2])) ||
1727 ((word[i-1]==word[i+1])) // may be word[i+1] == '\0'
1731 checkcompoundcase && scpd == 0 && !words && cpdcase_check(word, i)
1734 // LANG_hu section: spec. Hungarian rule
1735 || ((!rv) && (langnum == LANG_hu) && hu_mov_rule && (rv = affix_check(st,i)) &&
1736 (sfx && sfx->getCont() && ( // XXX hardwired Hungarian dic. codes
1737 TESTAFF(sfx->getCont(), (unsigned short) 'x', sfx->getContLen()) ||
1738 TESTAFF(sfx->getCont(), (unsigned short) '%', sfx->getContLen())
1742 ) { // first word is ok condition
1744 // LANG_hu section: spec. Hungarian rule
1745 if (langnum == LANG_hu) {
1746 // calculate syllable number of the word
1747 numsyllable += get_syllable(st, i);
1749 // + 1 word, if syllable number of the prefix > 1 (hungarian convention)
1750 if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++;
1752 // END of LANG_hu section
1758 do { // striple loop
1760 // check simplifiedtriple
1761 if (simplifiedtriple) {
1764 i--; // check "fahrt" instead of "ahrt" in "Schiffahrt"
1765 } else if (i > 2 && *(word+i - 1) == *(word + i - 2)) striple = 1;
1768 rv = lookup((st+i)); // perhaps without prefix
1770 // search homonym with compound flag
1771 while ((rv) && ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
1772 !((compoundflag && !words && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
1773 (compoundend && !words && TESTAFF(rv->astr, compoundend, rv->alen)) ||
1774 (numdefcpd && words && defcpd_check(&words, wnum + 1, rv, NULL,1))) ||
1775 (scpd != 0 && checkcpdtable[scpd-1].cond2 != FLAG_NULL &&
1776 !TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen))
1778 rv = rv->next_homonym;
1782 if (rv && forceucase && (rv) &&
1783 (TESTAFF(rv->astr, forceucase, rv->alen)) && !(info & SPELL_ORIGCAP)) rv = NULL;
1785 if (rv && words && words[wnum + 1]) return rv_first;
1787 oldnumsyllable2 = numsyllable;
1788 oldwordnum2 = wordnum;
1791 // LANG_hu section: spec. Hungarian rule, XXX hardwired dictionary code
1792 if ((rv) && (langnum == LANG_hu) && (TESTAFF(rv->astr, 'I', rv->alen)) && !(TESTAFF(rv->astr, 'J', rv->alen))) {
1795 // END of LANG_hu section
1797 // increment word number, if the second root has a compoundroot flag
1798 if ((rv) && (compoundroot) &&
1799 (TESTAFF(rv->astr, compoundroot, rv->alen))) {
1803 // check forbiddenwords
1804 if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
1805 TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
1806 (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) return NULL;
1808 // second word is acceptable, as a root?
1809 // hungarian conventions: compounding is acceptable,
1810 // when compound forms consist of 2 words, or if more,
1811 // then the syllable number of root words must be 6, or lesser.
1814 (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
1815 (compoundend && TESTAFF(rv->astr, compoundend, rv->alen))
1818 ((cpdwordmax==-1) || (wordnum+1<cpdwordmax)) ||
1819 ((cpdmaxsyllable!=0) &&
1820 (numsyllable + get_syllable(HENTRY_WORD(rv), rv->clen)<=cpdmaxsyllable))
1823 // test CHECKCOMPOUNDPATTERN
1824 !numcheckcpd || scpd != 0 || !cpdpat_check(word, i, rv_first, rv, 0)
1827 (!checkcompounddup || (rv != rv_first))
1829 // test CHECKCOMPOUNDPATTERN conditions
1830 && (scpd == 0 || checkcpdtable[scpd-1].cond2 == FLAG_NULL ||
1831 TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen))
1834 // forbid compound word, if it is a non compound word with typical fault
1835 if (checkcompoundrep && cpdrep_check(word,len)) return NULL;
1839 numsyllable = oldnumsyllable2;
1840 wordnum = oldwordnum2;
1842 // perhaps second word has prefix or/and suffix
1844 sfxflag = FLAG_NULL;
1845 rv = (compoundflag && !onlycpdrule) ? affix_check((word+i),strlen(word+i), compoundflag, IN_CPD_END) : NULL;
1846 if (!rv && compoundend && !onlycpdrule) {
1849 rv = affix_check((word+i),strlen(word+i), compoundend, IN_CPD_END);
1852 if (!rv && numdefcpd && words) {
1853 rv = affix_check((word+i),strlen(word+i), 0, IN_CPD_END);
1854 if (rv && defcpd_check(&words, wnum + 1, rv, NULL, 1)) return rv_first;
1858 // test CHECKCOMPOUNDPATTERN conditions (allowed forms)
1859 if (rv && !(scpd == 0 || checkcpdtable[scpd-1].cond2 == FLAG_NULL ||
1860 TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen))) rv = NULL;
1862 // test CHECKCOMPOUNDPATTERN conditions (forbidden compounds)
1863 if (rv && numcheckcpd && scpd == 0 && cpdpat_check(word, i, rv_first, rv, affixed)) rv = NULL;
1865 // check non_compound flag in suffix and prefix
1867 ((pfx && pfx->getCont() &&
1868 TESTAFF(pfx->getCont(), compoundforbidflag,
1869 pfx->getContLen())) ||
1870 (sfx && sfx->getCont() &&
1871 TESTAFF(sfx->getCont(), compoundforbidflag,
1872 sfx->getContLen())))) {
1877 if (rv && forceucase && (rv) &&
1878 (TESTAFF(rv->astr, forceucase, rv->alen)) && !(info & SPELL_ORIGCAP)) rv = NULL;
1880 // check forbiddenwords
1881 if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
1882 TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
1883 (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) return NULL;
1885 // pfxappnd = prefix of word+i, or NULL
1886 // calculate syllable number of prefix.
1887 // hungarian convention: when syllable number of prefix is more,
1888 // than 1, the prefix+word counts as two words.
1890 if (langnum == LANG_hu) {
1891 // calculate syllable number of the word
1892 numsyllable += get_syllable(word + i, strlen(word + i));
1894 // - affix syllable num.
1895 // XXX only second suffix (inflections, not derivations)
1897 char * tmp = myrevstrdup(sfxappnd);
1898 numsyllable -= get_syllable(tmp, strlen(tmp));
1902 // + 1 word, if syllable number of the prefix > 1 (hungarian convention)
1903 if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++;
1905 // increment syllable num, if last word has a SYLLABLENUM flag
1906 // and the suffix is beginning `s'
1908 if (cpdsyllablenum) {
1910 case 'c': { numsyllable+=2; break; }
1911 case 'J': { numsyllable += 1; break; }
1912 case 'I': { if (rv && TESTAFF(rv->astr, 'J', rv->alen)) numsyllable += 1; break; }
1917 // increment word number, if the second word has a compoundroot flag
1918 if ((rv) && (compoundroot) &&
1919 (TESTAFF(rv->astr, compoundroot, rv->alen))) {
1923 // second word is acceptable, as a word with prefix or/and suffix?
1924 // hungarian conventions: compounding is acceptable,
1925 // when compound forms consist 2 word, otherwise
1926 // the syllable number of root words is 6, or lesser.
1929 ((cpdwordmax == -1) || (wordnum + 1 < cpdwordmax)) ||
1930 ((cpdmaxsyllable != 0) &&
1931 (numsyllable <= cpdmaxsyllable))
1934 (!checkcompounddup || (rv != rv_first))
1936 // forbid compound word, if it is a non compound word with typical fault
1937 if (checkcompoundrep && cpdrep_check(word, len)) return NULL;
1941 numsyllable = oldnumsyllable2;
1942 wordnum = oldwordnum2;
1944 // perhaps second word is a compound word (recursive call)
1945 if (wordnum < maxwordnum) {
1946 rv = compound_check((st+i),strlen(st+i), wordnum+1,
1947 numsyllable, maxwordnum, wnum + 1, words, 0, is_sug, info);
1948 if (rv && numcheckcpd && ((scpd == 0 && cpdpat_check(word, i, rv_first, rv, affixed)) ||
1949 (scpd != 0 && !cpdpat_check(word, i, rv_first, rv, affixed)))) rv = NULL;
1954 // forbid compound word, if it is a non compound word with typical fault
1955 if (checkcompoundrep || forbiddenword) {
1956 struct hentry * rv2 = NULL;
1957 if (checkcompoundrep && cpdrep_check(word, len)) return NULL;
1959 if (strncmp(rv->word, word + i, rv->blen) == 0) {
1960 char r = *(st + i + rv->blen);
1961 *(st + i + rv->blen) = '\0';
1962 if (checkcompoundrep && cpdrep_check(st, i + rv->blen)) return NULL;
1963 if (forbiddenword) {
1965 if (!rv2) rv2 = affix_check(word, len);
1966 if (rv2 && rv2->astr && TESTAFF(rv2->astr, forbiddenword, rv2->alen) &&
1967 (strncmp(rv2->word, st, i + rv->blen) == 0)) {
1971 *(st + i + rv->blen) = r;
1977 } while (striple && !checkedstriple); // end of striple loop
1979 if (checkedstriple) {
1985 } // first word is ok condition
1996 } while (!onlycpdrule && simplifiedcpd && scpd <= numcheckcpd); // end of simplifiedcpd loop
1999 wordnum = oldwordnum;
2000 numsyllable = oldnumsyllable;
2004 strcpy(st, word); // XXX add more optim.
2008 } while (numdefcpd && oldwordnum == 0 && !onlycpdrule && (onlycpdrule = 1)); // end of onlycpd loop
2015 // check if compound word is correctly spelled
2016 // hu_mov_rule = spec. Hungarian rule (XXX)
2017 int AffixMgr::compound_check_morph(const char * word, int len,
2018 short wordnum, short numsyllable, short maxwordnum, short wnum, hentry ** words,
2019 char hu_mov_rule = 0, char ** result = NULL, char * partresult = NULL)
2022 short oldnumsyllable, oldnumsyllable2, oldwordnum, oldwordnum2;
2025 struct hentry * rv = NULL;
2026 struct hentry * rv_first;
2027 struct hentry * rwords[MAXWORDLEN]; // buffer for COMPOUND pattern checking
2028 char st [MAXWORDUTF8LEN + 4];
2032 char presult[MAXLNLEN];
2037 setcminmax(&cmin, &cmax, word, len);
2041 for (i = cmin; i < cmax; i++) {
2042 oldnumsyllable = numsyllable;
2043 oldwordnum = wordnum;
2046 // go to end of the UTF-8 character
2048 for (; (st[i] & 0xc0) == 0x80; i++);
2049 if (i >= cmax) return 0;
2058 if (partresult) mystrcat(presult, partresult, MAXLNLEN);
2060 rv = lookup(st); // perhaps without prefix
2062 // search homonym with compound flag
2063 while ((rv) && !hu_mov_rule &&
2064 ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
2065 !((compoundflag && !words && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
2066 (compoundbegin && !wordnum &&
2067 TESTAFF(rv->astr, compoundbegin, rv->alen)) ||
2068 (compoundmiddle && wordnum && !words &&
2069 TESTAFF(rv->astr, compoundmiddle, rv->alen)) ||
2071 ((!words && !wordnum && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0)) ||
2072 (words && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0))))
2074 rv = rv->next_homonym;
2078 sprintf(presult + strlen(presult), "%c%s%s", MSEP_FLD, MORPH_PART, st);
2079 if (!HENTRY_FIND(rv, MORPH_STEM)) {
2080 sprintf(presult + strlen(presult), "%c%s%s", MSEP_FLD, MORPH_STEM, st);
2082 // store the pointer of the hash entry
2083 // sprintf(presult + strlen(presult), "%c%s%p", MSEP_FLD, MORPH_HENTRY, rv);
2084 if (HENTRY_DATA(rv)) {
2085 sprintf(presult + strlen(presult), "%c%s", MSEP_FLD, HENTRY_DATA2(rv));
2089 if (compoundflag && !words &&
2090 !(rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundflag))) {
2091 if ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL,
2092 FLAG_NULL, compoundflag, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) && !hu_mov_rule &&
2094 ((compoundforbidflag && TESTAFF(sfx->getCont(), compoundforbidflag,
2095 sfx->getContLen())) || (compoundend &&
2096 TESTAFF(sfx->getCont(), compoundend,
2097 sfx->getContLen())))) {
2103 (((wordnum == 0) && compoundbegin &&
2104 ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundbegin, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
2105 (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundbegin)))) ||
2106 ((wordnum > 0) && compoundmiddle &&
2107 ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundmiddle, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
2108 (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundmiddle)))))
2110 // char * p = prefix_check_morph(st, i, 0, compound);
2112 if (compoundflag) p = affix_check_morph(st, i, compoundflag);
2113 if (!p || (*p == '\0')) {
2116 if ((wordnum == 0) && compoundbegin) {
2117 p = affix_check_morph(st, i, compoundbegin);
2118 } else if ((wordnum > 0) && compoundmiddle) {
2119 p = affix_check_morph(st, i, compoundmiddle);
2122 if (p && (*p != '\0')) {
2123 sprintf(presult + strlen(presult), "%c%s%s%s", MSEP_FLD,
2124 MORPH_PART, st, line_uniq_app(&p, MSEP_REC));
2129 // else check forbiddenwords
2130 } else if (rv->astr && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
2131 TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
2132 TESTAFF(rv->astr, needaffix, rv->alen))) {
2137 // check non_compound flag in suffix and prefix
2138 if ((rv) && !hu_mov_rule &&
2139 ((pfx && pfx->getCont() &&
2140 TESTAFF(pfx->getCont(), compoundforbidflag,
2141 pfx->getContLen())) ||
2142 (sfx && sfx->getCont() &&
2143 TESTAFF(sfx->getCont(), compoundforbidflag,
2144 sfx->getContLen())))) {
2148 // check compoundend flag in suffix and prefix
2149 if ((rv) && !checked_prefix && compoundend && !hu_mov_rule &&
2150 ((pfx && pfx->getCont() &&
2151 TESTAFF(pfx->getCont(), compoundend,
2152 pfx->getContLen())) ||
2153 (sfx && sfx->getCont() &&
2154 TESTAFF(sfx->getCont(), compoundend,
2155 sfx->getContLen())))) {
2159 // check compoundmiddle flag in suffix and prefix
2160 if ((rv) && !checked_prefix && (wordnum==0) && compoundmiddle && !hu_mov_rule &&
2161 ((pfx && pfx->getCont() &&
2162 TESTAFF(pfx->getCont(), compoundmiddle,
2163 pfx->getContLen())) ||
2164 (sfx && sfx->getCont() &&
2165 TESTAFF(sfx->getCont(), compoundmiddle,
2166 sfx->getContLen())))) {
2170 // check forbiddenwords
2171 if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen)
2172 || TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen))) continue;
2174 // increment word number, if the second root has a compoundroot flag
2175 if ((rv) && (compoundroot) &&
2176 (TESTAFF(rv->astr, compoundroot, rv->alen))) {
2180 // first word is acceptable in compound words?
2182 ( checked_prefix || (words && words[wnum]) ||
2183 (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
2184 ((oldwordnum == 0) && compoundbegin && TESTAFF(rv->astr, compoundbegin, rv->alen)) ||
2185 ((oldwordnum > 0) && compoundmiddle && TESTAFF(rv->astr, compoundmiddle, rv->alen))
2186 // LANG_hu section: spec. Hungarian rule
2187 || ((langnum == LANG_hu) && // hu_mov_rule
2189 TESTAFF(rv->astr, 'F', rv->alen) ||
2190 TESTAFF(rv->astr, 'G', rv->alen) ||
2191 TESTAFF(rv->astr, 'H', rv->alen)
2194 // END of LANG_hu section
2196 && ! (( checkcompoundtriple && !words && // test triple letters
2197 (word[i-1]==word[i]) && (
2198 ((i>1) && (word[i-1]==word[i-2])) ||
2199 ((word[i-1]==word[i+1])) // may be word[i+1] == '\0'
2203 // test CHECKCOMPOUNDPATTERN
2204 numcheckcpd && !words && cpdpat_check(word, i, rv, NULL, 0)
2207 checkcompoundcase && !words && cpdcase_check(word, i)
2210 // LANG_hu section: spec. Hungarian rule
2211 || ((!rv) && (langnum == LANG_hu) && hu_mov_rule && (rv = affix_check(st,i)) &&
2212 (sfx && sfx->getCont() && (
2213 TESTAFF(sfx->getCont(), (unsigned short) 'x', sfx->getContLen()) ||
2214 TESTAFF(sfx->getCont(), (unsigned short) '%', sfx->getContLen())
2218 // END of LANG_hu section
2221 // LANG_hu section: spec. Hungarian rule
2222 if (langnum == LANG_hu) {
2223 // calculate syllable number of the word
2224 numsyllable += get_syllable(st, i);
2226 // + 1 word, if syllable number of the prefix > 1 (hungarian convention)
2227 if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++;
2229 // END of LANG_hu section
2233 rv = lookup((word+i)); // perhaps without prefix
2235 // search homonym with compound flag
2236 while ((rv) && ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
2237 !((compoundflag && !words && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
2238 (compoundend && !words && TESTAFF(rv->astr, compoundend, rv->alen)) ||
2239 (numdefcpd && words && defcpd_check(&words, wnum + 1, rv, NULL,1))))) {
2240 rv = rv->next_homonym;
2243 if (rv && words && words[wnum + 1]) {
2244 mystrcat(*result, presult, MAXLNLEN);
2245 mystrcat(*result, " ", MAXLNLEN);
2246 mystrcat(*result, MORPH_PART, MAXLNLEN);
2247 mystrcat(*result, word+i, MAXLNLEN);
2248 if (complexprefixes && HENTRY_DATA(rv)) mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN);
2249 if (!HENTRY_FIND(rv, MORPH_STEM)) {
2250 mystrcat(*result, " ", MAXLNLEN);
2251 mystrcat(*result, MORPH_STEM, MAXLNLEN);
2252 mystrcat(*result, HENTRY_WORD(rv), MAXLNLEN);
2254 // store the pointer of the hash entry
2255 // sprintf(*result + strlen(*result), " %s%p", MORPH_HENTRY, rv);
2256 if (!complexprefixes && HENTRY_DATA(rv)) {
2257 mystrcat(*result, " ", MAXLNLEN);
2258 mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN);
2260 mystrcat(*result, "\n", MAXLNLEN);
2265 oldnumsyllable2 = numsyllable;
2266 oldwordnum2 = wordnum;
2268 // LANG_hu section: spec. Hungarian rule
2269 if ((rv) && (langnum == LANG_hu) && (TESTAFF(rv->astr, 'I', rv->alen)) && !(TESTAFF(rv->astr, 'J', rv->alen))) {
2272 // END of LANG_hu section
2273 // increment word number, if the second root has a compoundroot flag
2274 if ((rv) && (compoundroot) &&
2275 (TESTAFF(rv->astr, compoundroot, rv->alen))) {
2279 // check forbiddenwords
2280 if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
2281 TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen))) {
2286 // second word is acceptable, as a root?
2287 // hungarian conventions: compounding is acceptable,
2288 // when compound forms consist of 2 words, or if more,
2289 // then the syllable number of root words must be 6, or lesser.
2291 (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
2292 (compoundend && TESTAFF(rv->astr, compoundend, rv->alen))
2295 ((cpdwordmax==-1) || (wordnum+1<cpdwordmax)) ||
2296 ((cpdmaxsyllable!=0) &&
2297 (numsyllable+get_syllable(HENTRY_WORD(rv),rv->blen)<=cpdmaxsyllable))
2300 (!checkcompounddup || (rv != rv_first))
2304 // bad compound word
2305 mystrcat(*result, presult, MAXLNLEN);
2306 mystrcat(*result, " ", MAXLNLEN);
2307 mystrcat(*result, MORPH_PART, MAXLNLEN);
2308 mystrcat(*result, word+i, MAXLNLEN);
2310 if (HENTRY_DATA(rv)) {
2311 if (complexprefixes) mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN);
2312 if (! HENTRY_FIND(rv, MORPH_STEM)) {
2313 mystrcat(*result, " ", MAXLNLEN);
2314 mystrcat(*result, MORPH_STEM, MAXLNLEN);
2315 mystrcat(*result, HENTRY_WORD(rv), MAXLNLEN);
2317 // store the pointer of the hash entry
2318 // sprintf(*result + strlen(*result), " %s%p", MORPH_HENTRY, rv);
2319 if (!complexprefixes) {
2320 mystrcat(*result, " ", MAXLNLEN);
2321 mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN);
2324 mystrcat(*result, "\n", MAXLNLEN);
2328 numsyllable = oldnumsyllable2 ;
2329 wordnum = oldwordnum2;
2331 // perhaps second word has prefix or/and suffix
2333 sfxflag = FLAG_NULL;
2335 if (compoundflag) rv = affix_check((word+i),strlen(word+i), compoundflag); else rv = NULL;
2337 if (!rv && compoundend) {
2340 rv = affix_check((word+i),strlen(word+i), compoundend);
2343 if (!rv && numdefcpd && words) {
2344 rv = affix_check((word+i),strlen(word+i), 0, IN_CPD_END);
2345 if (rv && words && defcpd_check(&words, wnum + 1, rv, NULL, 1)) {
2347 if (compoundflag) m = affix_check_morph((word+i),strlen(word+i), compoundflag);
2348 if ((!m || *m == '\0') && compoundend) {
2350 m = affix_check_morph((word+i),strlen(word+i), compoundend);
2352 mystrcat(*result, presult, MAXLNLEN);
2353 if (m || (*m != '\0')) {
2354 sprintf(*result + strlen(*result), "%c%s%s%s", MSEP_FLD,
2355 MORPH_PART, word + i, line_uniq_app(&m, MSEP_REC));
2358 mystrcat(*result, "\n", MAXLNLEN);
2363 // check non_compound flag in suffix and prefix
2365 ((pfx && pfx->getCont() &&
2366 TESTAFF(pfx->getCont(), compoundforbidflag,
2367 pfx->getContLen())) ||
2368 (sfx && sfx->getCont() &&
2369 TESTAFF(sfx->getCont(), compoundforbidflag,
2370 sfx->getContLen())))) {
2374 // check forbiddenwords
2375 if ((rv) && (rv->astr) && (TESTAFF(rv->astr,forbiddenword,rv->alen) ||
2376 TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen))
2377 && (! TESTAFF(rv->astr, needaffix, rv->alen))) {
2382 if (langnum == LANG_hu) {
2383 // calculate syllable number of the word
2384 numsyllable += get_syllable(word + i, strlen(word + i));
2386 // - affix syllable num.
2387 // XXX only second suffix (inflections, not derivations)
2389 char * tmp = myrevstrdup(sfxappnd);
2390 numsyllable -= get_syllable(tmp, strlen(tmp));
2394 // + 1 word, if syllable number of the prefix > 1 (hungarian convention)
2395 if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++;
2397 // increment syllable num, if last word has a SYLLABLENUM flag
2398 // and the suffix is beginning `s'
2400 if (cpdsyllablenum) {
2402 case 'c': { numsyllable+=2; break; }
2403 case 'J': { numsyllable += 1; break; }
2404 case 'I': { if (rv && TESTAFF(rv->astr, 'J', rv->alen)) numsyllable += 1; break; }
2409 // increment word number, if the second word has a compoundroot flag
2410 if ((rv) && (compoundroot) &&
2411 (TESTAFF(rv->astr, compoundroot, rv->alen))) {
2414 // second word is acceptable, as a word with prefix or/and suffix?
2415 // hungarian conventions: compounding is acceptable,
2416 // when compound forms consist 2 word, otherwise
2417 // the syllable number of root words is 6, or lesser.
2420 ((cpdwordmax==-1) || (wordnum+1<cpdwordmax)) ||
2421 ((cpdmaxsyllable!=0) &&
2422 (numsyllable <= cpdmaxsyllable))
2425 (!checkcompounddup || (rv != rv_first))
2428 if (compoundflag) m = affix_check_morph((word+i),strlen(word+i), compoundflag);
2429 if ((!m || *m == '\0') && compoundend) {
2431 m = affix_check_morph((word+i),strlen(word+i), compoundend);
2433 mystrcat(*result, presult, MAXLNLEN);
2434 if (m && (*m != '\0')) {
2435 sprintf(*result + strlen(*result), "%c%s%s%s", MSEP_FLD,
2436 MORPH_PART, word + i, line_uniq_app(&m, MSEP_REC));
2439 sprintf(*result + strlen(*result), "%c", MSEP_REC);
2443 numsyllable = oldnumsyllable2;
2444 wordnum = oldwordnum2;
2446 // perhaps second word is a compound word (recursive call)
2447 if ((wordnum < maxwordnum) && (ok == 0)) {
2448 compound_check_morph((word+i),strlen(word+i), wordnum+1,
2449 numsyllable, maxwordnum, wnum + 1, words, 0, result, presult);
2455 wordnum = oldwordnum;
2456 numsyllable = oldnumsyllable;
2461 // return 1 if s1 (reversed) is a leading subset of end of s2
2462 /* inline int AffixMgr::isRevSubset(const char * s1, const char * end_of_s2, int len)
2464 while ((len > 0) && *s1 && (*s1 == *end_of_s2)) {
2469 return (*s1 == '\0');
2473 inline int AffixMgr::isRevSubset(const char * s1, const char * end_of_s2, int len)
2475 while ((len > 0) && (*s1 != '\0') && ((*s1 == *end_of_s2) || (*s1 == '.'))) {
2480 return (*s1 == '\0');
2483 // check word for suffixes
2485 struct hentry * AffixMgr::suffix_check (const char * word, int len,
2486 int sfxopts, PfxEntry * ppfx, char ** wlst, int maxSug, int * ns,
2487 const FLAG cclass, const FLAG needflag, char in_compound)
2489 struct hentry * rv = NULL;
2490 PfxEntry* ep = ppfx;
2492 // first handle the special case of 0 length suffixes
2493 SfxEntry * se = sStart[0];
2496 if (!cclass || se->getCont()) {
2497 // suffixes are not allowed in beginning of compounds
2498 if ((((in_compound != IN_CPD_BEGIN)) || // && !cclass
2499 // except when signed with compoundpermitflag flag
2500 (se->getCont() && compoundpermitflag &&
2501 TESTAFF(se->getCont(),compoundpermitflag,se->getContLen()))) && (!circumfix ||
2502 // no circumfix flag in prefix and suffix
2503 ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(),
2504 circumfix, ep->getContLen())) &&
2505 (!se->getCont() || !(TESTAFF(se->getCont(),circumfix,se->getContLen())))) ||
2506 // circumfix flag in prefix AND suffix
2507 ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(),
2508 circumfix, ep->getContLen())) &&
2509 (se->getCont() && (TESTAFF(se->getCont(),circumfix,se->getContLen()))))) &&
2512 !(se->getCont() && (TESTAFF(se->getCont(), onlyincompound, se->getContLen())))) &&
2513 // needaffix on prefix or first suffix
2515 !(se->getCont() && TESTAFF(se->getCont(), needaffix, se->getContLen())) ||
2516 (ppfx && !((ep->getCont()) &&
2517 TESTAFF(ep->getCont(), needaffix,
2520 rv = se->checkword(word,len, sfxopts, ppfx, wlst, maxSug, ns, (FLAG) cclass,
2521 needflag, (in_compound ? 0 : onlyincompound));
2523 sfx=se; // BUG: sfx not stateless
2531 // now handle the general case
2532 if (len == 0) return NULL; // FULLSTRIP
2533 unsigned char sp= *((const unsigned char *)(word + len - 1));
2534 SfxEntry * sptr = sStart[sp];
2537 if (isRevSubset(sptr->getKey(), word + len - 1, len)
2539 // suffixes are not allowed in beginning of compounds
2540 if ((((in_compound != IN_CPD_BEGIN)) || // && !cclass
2541 // except when signed with compoundpermitflag flag
2542 (sptr->getCont() && compoundpermitflag &&
2543 TESTAFF(sptr->getCont(),compoundpermitflag,sptr->getContLen()))) && (!circumfix ||
2544 // no circumfix flag in prefix and suffix
2545 ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(),
2546 circumfix, ep->getContLen())) &&
2547 (!sptr->getCont() || !(TESTAFF(sptr->getCont(),circumfix,sptr->getContLen())))) ||
2548 // circumfix flag in prefix AND suffix
2549 ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(),
2550 circumfix, ep->getContLen())) &&
2551 (sptr->getCont() && (TESTAFF(sptr->getCont(),circumfix,sptr->getContLen()))))) &&
2554 !((sptr->getCont() && (TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))))) &&
2555 // needaffix on prefix or first suffix
2557 !(sptr->getCont() && TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())) ||
2558 (ppfx && !((ep->getCont()) &&
2559 TESTAFF(ep->getCont(), needaffix,
2562 ) if (in_compound != IN_CPD_END || ppfx || !(sptr->getCont() && TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))) {
2563 rv = sptr->checkword(word,len, sfxopts, ppfx, wlst,
2564 maxSug, ns, cclass, needflag, (in_compound ? 0 : onlyincompound));
2566 sfx=sptr; // BUG: sfx not stateless
2567 sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless
2568 if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless
2572 sptr = sptr->getNextEQ();
2574 sptr = sptr->getNextNE();
2581 // check word for two-level suffixes
2583 struct hentry * AffixMgr::suffix_check_twosfx(const char * word, int len,
2584 int sfxopts, PfxEntry * ppfx, const FLAG needflag)
2586 struct hentry * rv = NULL;
2588 // first handle the special case of 0 length suffixes
2589 SfxEntry * se = sStart[0];
2591 if (contclasses[se->getFlag()])
2593 rv = se->check_twosfx(word,len, sfxopts, ppfx, needflag);
2599 // now handle the general case
2600 if (len == 0) return NULL; // FULLSTRIP
2601 unsigned char sp = *((const unsigned char *)(word + len - 1));
2602 SfxEntry * sptr = sStart[sp];
2605 if (isRevSubset(sptr->getKey(), word + len - 1, len)) {
2606 if (contclasses[sptr->getFlag()])
2608 rv = sptr->check_twosfx(word,len, sfxopts, ppfx, needflag);
2610 sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless
2611 if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless
2615 sptr = sptr->getNextEQ();
2617 sptr = sptr->getNextNE();
2624 char * AffixMgr::suffix_check_twosfx_morph(const char * word, int len,
2625 int sfxopts, PfxEntry * ppfx, const FLAG needflag)
2627 char result[MAXLNLEN];
2628 char result2[MAXLNLEN];
2629 char result3[MAXLNLEN];
2637 // first handle the special case of 0 length suffixes
2638 SfxEntry * se = sStart[0];
2640 if (contclasses[se->getFlag()])
2642 st = se->check_twosfx_morph(word,len, sfxopts, ppfx, needflag);
2645 if (ppfx->getMorph()) {
2646 mystrcat(result, ppfx->getMorph(), MAXLNLEN);
2647 mystrcat(result, " ", MAXLNLEN);
2648 } else debugflag(result, ppfx->getFlag());
2650 mystrcat(result, st, MAXLNLEN);
2652 if (se->getMorph()) {
2653 mystrcat(result, " ", MAXLNLEN);
2654 mystrcat(result, se->getMorph(), MAXLNLEN);
2655 } else debugflag(result, se->getFlag());
2656 mystrcat(result, "\n", MAXLNLEN);
2662 // now handle the general case
2663 if (len == 0) return NULL; // FULLSTRIP
2664 unsigned char sp = *((const unsigned char *)(word + len - 1));
2665 SfxEntry * sptr = sStart[sp];
2668 if (isRevSubset(sptr->getKey(), word + len - 1, len)) {
2669 if (contclasses[sptr->getFlag()])
2671 st = sptr->check_twosfx_morph(word,len, sfxopts, ppfx, needflag);
2673 sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless
2674 if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless
2675 strcpy(result2, st);
2680 if (sptr->getMorph()) {
2681 mystrcat(result3, " ", MAXLNLEN);
2682 mystrcat(result3, sptr->getMorph(), MAXLNLEN);
2683 } else debugflag(result3, sptr->getFlag());
2684 strlinecat(result2, result3);
2685 mystrcat(result2, "\n", MAXLNLEN);
2686 mystrcat(result, result2, MAXLNLEN);
2689 sptr = sptr->getNextEQ();
2691 sptr = sptr->getNextNE();
2694 if (*result) return mystrdup(result);
2698 char * AffixMgr::suffix_check_morph(const char * word, int len,
2699 int sfxopts, PfxEntry * ppfx, const FLAG cclass, const FLAG needflag, char in_compound)
2701 char result[MAXLNLEN];
2703 struct hentry * rv = NULL;
2707 PfxEntry* ep = ppfx;
2709 // first handle the special case of 0 length suffixes
2710 SfxEntry * se = sStart[0];
2712 if (!cclass || se->getCont()) {
2713 // suffixes are not allowed in beginning of compounds
2714 if (((((in_compound != IN_CPD_BEGIN)) || // && !cclass
2715 // except when signed with compoundpermitflag flag
2716 (se->getCont() && compoundpermitflag &&
2717 TESTAFF(se->getCont(),compoundpermitflag,se->getContLen()))) && (!circumfix ||
2718 // no circumfix flag in prefix and suffix
2719 ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(),
2720 circumfix, ep->getContLen())) &&
2721 (!se->getCont() || !(TESTAFF(se->getCont(),circumfix,se->getContLen())))) ||
2722 // circumfix flag in prefix AND suffix
2723 ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(),
2724 circumfix, ep->getContLen())) &&
2725 (se->getCont() && (TESTAFF(se->getCont(),circumfix,se->getContLen()))))) &&
2728 !((se->getCont() && (TESTAFF(se->getCont(), onlyincompound, se->getContLen()))))) &&
2729 // needaffix on prefix or first suffix
2731 !(se->getCont() && TESTAFF(se->getCont(), needaffix, se->getContLen())) ||
2732 (ppfx && !((ep->getCont()) &&
2733 TESTAFF(ep->getCont(), needaffix,
2737 rv = se->checkword(word, len, sfxopts, ppfx, NULL, 0, 0, cclass, needflag);
2740 if (ppfx->getMorph()) {
2741 mystrcat(result, ppfx->getMorph(), MAXLNLEN);
2742 mystrcat(result, " ", MAXLNLEN);
2743 } else debugflag(result, ppfx->getFlag());
2745 if (complexprefixes && HENTRY_DATA(rv)) mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN);
2746 if (! HENTRY_FIND(rv, MORPH_STEM)) {
2747 mystrcat(result, " ", MAXLNLEN);
2748 mystrcat(result, MORPH_STEM, MAXLNLEN);
2749 mystrcat(result, HENTRY_WORD(rv), MAXLNLEN);
2751 // store the pointer of the hash entry
2752 // sprintf(result + strlen(result), " %s%p", MORPH_HENTRY, rv);
2754 if (!complexprefixes && HENTRY_DATA(rv)) {
2755 mystrcat(result, " ", MAXLNLEN);
2756 mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN);
2758 if (se->getMorph()) {
2759 mystrcat(result, " ", MAXLNLEN);
2760 mystrcat(result, se->getMorph(), MAXLNLEN);
2761 } else debugflag(result, se->getFlag());
2762 mystrcat(result, "\n", MAXLNLEN);
2763 rv = se->get_next_homonym(rv, sfxopts, ppfx, cclass, needflag);
2769 // now handle the general case
2770 if (len == 0) return NULL; // FULLSTRIP
2771 unsigned char sp = *((const unsigned char *)(word + len - 1));
2772 SfxEntry * sptr = sStart[sp];
2775 if (isRevSubset(sptr->getKey(), word + len - 1, len)
2777 // suffixes are not allowed in beginning of compounds
2778 if (((((in_compound != IN_CPD_BEGIN)) || // && !cclass
2779 // except when signed with compoundpermitflag flag
2780 (sptr->getCont() && compoundpermitflag &&
2781 TESTAFF(sptr->getCont(),compoundpermitflag,sptr->getContLen()))) && (!circumfix ||
2782 // no circumfix flag in prefix and suffix
2783 ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(),
2784 circumfix, ep->getContLen())) &&
2785 (!sptr->getCont() || !(TESTAFF(sptr->getCont(),circumfix,sptr->getContLen())))) ||
2786 // circumfix flag in prefix AND suffix
2787 ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(),
2788 circumfix, ep->getContLen())) &&
2789 (sptr->getCont() && (TESTAFF(sptr->getCont(),circumfix,sptr->getContLen()))))) &&
2792 !((sptr->getCont() && (TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))))) &&
2793 // needaffix on first suffix
2794 (cclass || !(sptr->getCont() &&
2795 TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())))
2796 )) rv = sptr->checkword(word,len, sfxopts, ppfx, NULL, 0, 0, cclass, needflag);
2799 if (ppfx->getMorph()) {
2800 mystrcat(result, ppfx->getMorph(), MAXLNLEN);
2801 mystrcat(result, " ", MAXLNLEN);
2802 } else debugflag(result, ppfx->getFlag());
2804 if (complexprefixes && HENTRY_DATA(rv)) mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN);
2805 if (! HENTRY_FIND(rv, MORPH_STEM)) {
2806 mystrcat(result, " ", MAXLNLEN);
2807 mystrcat(result, MORPH_STEM, MAXLNLEN);
2808 mystrcat(result, HENTRY_WORD(rv), MAXLNLEN);
2810 // store the pointer of the hash entry
2811 // sprintf(result + strlen(result), " %s%p", MORPH_HENTRY, rv);
2813 if (!complexprefixes && HENTRY_DATA(rv)) {
2814 mystrcat(result, " ", MAXLNLEN);
2815 mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN);
2818 if (sptr->getMorph()) {
2819 mystrcat(result, " ", MAXLNLEN);
2820 mystrcat(result, sptr->getMorph(), MAXLNLEN);
2821 } else debugflag(result, sptr->getFlag());
2822 mystrcat(result, "\n", MAXLNLEN);
2823 rv = sptr->get_next_homonym(rv, sfxopts, ppfx, cclass, needflag);
2825 sptr = sptr->getNextEQ();
2827 sptr = sptr->getNextNE();
2831 if (*result) return mystrdup(result);
2835 // check if word with affixes is correctly spelled
2836 struct hentry * AffixMgr::affix_check (const char * word, int len, const FLAG needflag, char in_compound)
2838 struct hentry * rv= NULL;
2840 // check all prefixes (also crossed with suffixes if allowed)
2841 rv = prefix_check(word, len, in_compound, needflag);
2844 // if still not found check all suffixes
2845 rv = suffix_check(word, len, 0, NULL, NULL, 0, NULL, FLAG_NULL, needflag, in_compound);
2847 if (havecontclass) {
2852 // if still not found check all two-level suffixes
2853 rv = suffix_check_twosfx(word, len, 0, NULL, needflag);
2856 // if still not found check all two-level suffixes
2857 rv = prefix_check_twosfx(word, len, IN_CPD_NOT, needflag);
2863 // check if word with affixes is correctly spelled
2864 char * AffixMgr::affix_check_morph(const char * word, int len, const FLAG needflag, char in_compound)
2866 char result[MAXLNLEN];
2871 // check all prefixes (also crossed with suffixes if allowed)
2872 st = prefix_check_morph(word, len, in_compound);
2874 mystrcat(result, st, MAXLNLEN);
2878 // if still not found check all suffixes
2879 st = suffix_check_morph(word, len, 0, NULL, '\0', needflag, in_compound);
2881 mystrcat(result, st, MAXLNLEN);
2885 if (havecontclass) {
2888 // if still not found check all two-level suffixes
2889 st = suffix_check_twosfx_morph(word, len, 0, NULL, needflag);
2891 mystrcat(result, st, MAXLNLEN);
2895 // if still not found check all two-level suffixes
2896 st = prefix_check_twosfx_morph(word, len, IN_CPD_NOT, needflag);
2898 mystrcat(result, st, MAXLNLEN);
2903 return mystrdup(result);
2906 char * AffixMgr::morphgen(char * ts, int wl, const unsigned short * ap,
2907 unsigned short al, char * morph, char * targetmorph, int level)
2911 char * stemmorphcatpos;
2912 char mymorph[MAXLNLEN];
2914 if (!morph) return NULL;
2916 // check substandard flag
2917 if (TESTAFF(ap, substandard, al)) return NULL;
2919 if (morphcmp(morph, targetmorph) == 0) return mystrdup(ts);
2921 // int targetcount = get_sfxcount(targetmorph);
2923 // use input suffix fields, if exist
2924 if (strstr(morph, MORPH_INFL_SFX) || strstr(morph, MORPH_DERI_SFX)) {
2925 stemmorph = mymorph;
2926 strcpy(stemmorph, morph);
2927 mystrcat(stemmorph, " ", MAXLNLEN);
2928 stemmorphcatpos = stemmorph + strlen(stemmorph);
2931 stemmorphcatpos = NULL;
2934 for (int i = 0; i < al; i++) {
2935 const unsigned char c = (unsigned char) (ap[i] & 0x00FF);
2936 SfxEntry * sptr = sFlag[c];
2938 if (sptr->getFlag() == ap[i] && sptr->getMorph() && ((sptr->getContLen() == 0) ||
2939 // don't generate forms with substandard affixes
2940 !TESTAFF(sptr->getCont(), substandard, sptr->getContLen()))) {
2942 if (stemmorphcatpos) strcpy(stemmorphcatpos, sptr->getMorph());
2943 else stemmorph = (char *) sptr->getMorph();
2945 int cmp = morphcmp(stemmorph, targetmorph);
2948 char * newword = sptr->add(ts, wl);
2950 hentry * check = pHMgr->lookup(newword); // XXX extra dic
2951 if (!check || !check->astr ||
2952 !(TESTAFF(check->astr, forbiddenword, check->alen) ||
2953 TESTAFF(check->astr, ONLYUPCASEFLAG, check->alen))) {
2960 // recursive call for secondary suffixes
2961 if ((level == 0) && (cmp == 1) && (sptr->getContLen() > 0) &&
2962 // (get_sfxcount(stemmorph) < targetcount) &&
2963 !TESTAFF(sptr->getCont(), substandard, sptr->getContLen())) {
2964 char * newword = sptr->add(ts, wl);
2966 char * newword2 = morphgen(newword, strlen(newword), sptr->getCont(),
2967 sptr->getContLen(), stemmorph, targetmorph, 1);
2978 sptr = sptr->getFlgNxt();
2985 int AffixMgr::expand_rootword(struct guessword * wlst, int maxn, const char * ts,
2986 int wl, const unsigned short * ap, unsigned short al, char * bad, int badl,
2990 // first add root word to list
2991 if ((nh < maxn) && !(al && ((needaffix && TESTAFF(ap, needaffix, al)) ||
2992 (onlyincompound && TESTAFF(ap, onlyincompound, al))))) {
2993 wlst[nh].word = mystrdup(ts);
2994 if (!wlst[nh].word) return 0;
2995 wlst[nh].allow = (1 == 0);
2996 wlst[nh].orig = NULL;
2998 // add special phonetic version
2999 if (phon && (nh < maxn)) {
3000 wlst[nh].word = mystrdup(phon);
3001 if (!wlst[nh].word) return nh - 1;
3002 wlst[nh].allow = (1 == 0);
3003 wlst[nh].orig = mystrdup(ts);
3004 if (!wlst[nh].orig) return nh - 1;
3010 for (int i = 0; i < al; i++) {
3011 const unsigned char c = (unsigned char) (ap[i] & 0x00FF);
3012 SfxEntry * sptr = sFlag[c];
3014 if ((sptr->getFlag() == ap[i]) && (!sptr->getKeyLen() || ((badl > sptr->getKeyLen()) &&
3015 (strcmp(sptr->getAffix(), bad + badl - sptr->getKeyLen()) == 0))) &&
3016 // check needaffix flag
3017 !(sptr->getCont() && ((needaffix &&
3018 TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())) ||
3020 TESTAFF(sptr->getCont(), circumfix, sptr->getContLen())) ||
3022 TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))))
3024 char * newword = sptr->add(ts, wl);
3027 wlst[nh].word = newword;
3028 wlst[nh].allow = sptr->allowCross();
3029 wlst[nh].orig = NULL;
3031 // add special phonetic version
3032 if (phon && (nh < maxn)) {
3033 char st[MAXWORDUTF8LEN];
3035 strcat(st, sptr->getKey());
3036 reverseword(st + strlen(phon));
3037 wlst[nh].word = mystrdup(st);
3038 if (!wlst[nh].word) return nh - 1;
3039 wlst[nh].allow = (1 == 0);
3040 wlst[nh].orig = mystrdup(newword);
3041 if (!wlst[nh].orig) return nh - 1;
3049 sptr = sptr->getFlgNxt();
3055 // handle cross products of prefixes and suffixes
3056 for (int j=1;j<n ;j++)
3057 if (wlst[j].allow) {
3058 for (int k = 0; k < al; k++) {
3059 const unsigned char c = (unsigned char) (ap[k] & 0x00FF);
3060 PfxEntry * cptr = pFlag[c];
3062 if ((cptr->getFlag() == ap[k]) && cptr->allowCross() && (!cptr->getKeyLen() || ((badl > cptr->getKeyLen()) &&
3063 (strncmp(cptr->getKey(), bad, cptr->getKeyLen()) == 0)))) {
3064 int l1 = strlen(wlst[j].word);
3065 char * newword = cptr->add(wlst[j].word, l1);
3068 wlst[nh].word = newword;
3069 wlst[nh].allow = cptr->allowCross();
3070 wlst[nh].orig = NULL;
3077 cptr = cptr->getFlgNxt();
3083 // now handle pure prefixes
3084 for (int m = 0; m < al; m ++) {
3085 const unsigned char c = (unsigned char) (ap[m] & 0x00FF);
3086 PfxEntry * ptr = pFlag[c];
3088 if ((ptr->getFlag() == ap[m]) && (!ptr->getKeyLen() || ((badl > ptr->getKeyLen()) &&
3089 (strncmp(ptr->getKey(), bad, ptr->getKeyLen()) == 0))) &&
3090 // check needaffix flag
3091 !(ptr->getCont() && ((needaffix &&
3092 TESTAFF(ptr->getCont(), needaffix, ptr->getContLen())) ||
3094 TESTAFF(ptr->getCont(), circumfix, ptr->getContLen())) ||
3096 TESTAFF(ptr->getCont(), onlyincompound, ptr->getContLen()))))
3098 char * newword = ptr->add(ts, wl);
3101 wlst[nh].word = newword;
3102 wlst[nh].allow = ptr->allowCross();
3103 wlst[nh].orig = NULL;
3110 ptr = ptr->getFlgNxt();
3117 // return length of replacing table
3118 int AffixMgr::get_numrep() const
3123 // return replacing table
3124 struct replentry * AffixMgr::get_reptable() const
3126 if (! reptable ) return NULL;
3130 // return iconv table
3131 RepList * AffixMgr::get_iconvtable() const
3133 if (! iconvtable ) return NULL;
3137 // return oconv table
3138 RepList * AffixMgr::get_oconvtable() const
3140 if (! oconvtable ) return NULL;
3144 // return replacing table
3145 struct phonetable * AffixMgr::get_phonetable() const
3147 if (! phone ) return NULL;
3151 // return length of character map table
3152 int AffixMgr::get_nummap() const
3157 // return character map table
3158 struct mapentry * AffixMgr::get_maptable() const
3160 if (! maptable ) return NULL;
3164 // return length of word break table
3165 int AffixMgr::get_numbreak() const
3170 // return character map table
3171 char ** AffixMgr::get_breaktable() const
3173 if (! breaktable ) return NULL;
3177 // return text encoding of dictionary
3178 char * AffixMgr::get_encoding()
3180 if (! encoding ) encoding = mystrdup(SPELL_ENCODING);
3181 return mystrdup(encoding);
3184 // return text encoding of dictionary
3185 int AffixMgr::get_langnum() const
3190 // return double prefix option
3191 int AffixMgr::get_complexprefixes() const
3193 return complexprefixes;
3196 // return FULLSTRIP option
3197 int AffixMgr::get_fullstrip() const
3202 FLAG AffixMgr::get_keepcase() const
3207 FLAG AffixMgr::get_forceucase() const
3212 FLAG AffixMgr::get_warn() const
3217 int AffixMgr::get_forbidwarn() const
3222 int AffixMgr::get_checksharps() const
3227 char * AffixMgr::encode_flag(unsigned short aflag) const
3229 return pHMgr->encode_flag(aflag);
3233 // return the preferred ignore string for suggestions
3234 char * AffixMgr::get_ignore() const
3236 if (!ignorechars) return NULL;
3240 // return the preferred ignore string for suggestions
3241 unsigned short * AffixMgr::get_ignore_utf16(int * len) const
3243 *len = ignorechars_utf16_len;
3244 return ignorechars_utf16;
3247 // return the keyboard string for suggestions
3248 char * AffixMgr::get_key_string()
3250 if (! keystring ) keystring = mystrdup(SPELL_KEYSTRING);
3251 return mystrdup(keystring);
3254 // return the preferred try string for suggestions
3255 char * AffixMgr::get_try_string() const
3257 if (! trystring ) return NULL;
3258 return mystrdup(trystring);
3261 // return the preferred try string for suggestions
3262 const char * AffixMgr::get_wordchars() const
3267 unsigned short * AffixMgr::get_wordchars_utf16(int * len) const
3269 *len = wordchars_utf16_len;
3270 return wordchars_utf16;
3273 // is there compounding?
3274 int AffixMgr::get_compound() const
3276 return compoundflag || compoundbegin || numdefcpd;
3279 // return the compound words control flag
3280 FLAG AffixMgr::get_compoundflag() const
3282 return compoundflag;
3285 // return the forbidden words control flag
3286 FLAG AffixMgr::get_forbiddenword() const
3288 return forbiddenword;
3291 // return the forbidden words control flag
3292 FLAG AffixMgr::get_nosuggest() const
3297 // return the forbidden words control flag
3298 FLAG AffixMgr::get_nongramsuggest() const
3300 return nongramsuggest;
3303 // return the forbidden words flag modify flag
3304 FLAG AffixMgr::get_needaffix() const
3309 // return the onlyincompound flag
3310 FLAG AffixMgr::get_onlyincompound() const
3312 return onlyincompound;
3315 // return the compound word signal flag
3316 FLAG AffixMgr::get_compoundroot() const
3318 return compoundroot;
3321 // return the compound begin signal flag
3322 FLAG AffixMgr::get_compoundbegin() const
3324 return compoundbegin;
3327 // return the value of checknum
3328 int AffixMgr::get_checknum() const
3333 // return the value of prefix
3334 const char * AffixMgr::get_prefix() const
3336 if (pfx) return pfx->getKey();
3340 // return the value of suffix
3341 const char * AffixMgr::get_suffix() const
3346 // return the value of suffix
3347 const char * AffixMgr::get_version() const
3352 // return lemma_present flag
3353 FLAG AffixMgr::get_lemma_present() const
3355 return lemma_present;
3358 // utility method to look up root words in hash table
3359 struct hentry * AffixMgr::lookup(const char * word)
3362 struct hentry * he = NULL;
3363 for (i = 0; i < *maxdic && !he; i++) {
3364 he = (alldic[i])->lookup(word);
3369 // return the value of suffix
3370 int AffixMgr::have_contclass() const
3372 return havecontclass;
3376 int AffixMgr::get_utf8() const
3381 int AffixMgr::get_maxngramsugs(void) const
3383 return maxngramsugs;
3386 int AffixMgr::get_maxcpdsugs(void) const
3391 int AffixMgr::get_maxdiff(void) const
3396 int AffixMgr::get_onlymaxdiff(void) const
3401 // return nosplitsugs
3402 int AffixMgr::get_nosplitsugs(void) const
3407 // return sugswithdots
3408 int AffixMgr::get_sugswithdots(void) const
3410 return sugswithdots;
3414 int AffixMgr::parse_flag(char * line, unsigned short * out, FileMgr * af) {
3416 if (*out != FLAG_NULL && !(*out >= DEFAULTFLAGS)) {
3417 HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix file parameter\n", af->getlinenum());
3420 if (parse_string(line, &s, af->getlinenum())) return 1;
3421 *out = pHMgr->decode_flag(s);
3427 int AffixMgr::parse_num(char * line, int * out, FileMgr * af) {
3430 HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix file parameter\n", af->getlinenum());
3433 if (parse_string(line, &s, af->getlinenum())) return 1;
3439 /* parse in the max syllablecount of compound words and */
3440 int AffixMgr::parse_cpdsyllable(char * line, FileMgr * af)
3446 w_char w[MAXWORDLEN];
3447 piece = mystrsep(&tp, 0);
3449 if (*piece != '\0') {
3451 case 0: { np++; break; }
3452 case 1: { cpdmaxsyllable = atoi(piece); np++; break; }
3455 cpdvowels = mystrdup(piece);
3457 int n = u8_u16(w, MAXWORDLEN, piece);
3459 flag_qsort((unsigned short *) w, 0, n);
3460 cpdvowels_utf16 = (w_char *) malloc(n * sizeof(w_char));
3461 if (!cpdvowels_utf16) return 1;
3462 memcpy(cpdvowels_utf16, w, n * sizeof(w_char));
3464 cpdvowels_utf16_len = n;
3473 piece = mystrsep(&tp, 0);
3476 HUNSPELL_WARNING(stderr, "error: line %d: missing compoundsyllable information\n", af->getlinenum());
3479 if (np == 2) cpdvowels = mystrdup("aeiouAEIOU");
3483 /* parse in the typical fault correcting table */
3484 int AffixMgr::parse_reptable(char * line, FileMgr * af)
3487 HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
3494 piece = mystrsep(&tp, 0);
3496 if (*piece != '\0') {
3498 case 0: { np++; break; }
3500 numrep = atoi(piece);
3502 HUNSPELL_WARNING(stderr, "error: line %d: incorrect entry number\n", af->getlinenum());
3505 reptable = (replentry *) malloc(numrep * sizeof(struct replentry));
3506 if (!reptable) return 1;
3514 piece = mystrsep(&tp, 0);
3517 HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
3521 /* now parse the numrep lines to read in the remainder of the table */
3523 for (int j=0; j < numrep; j++) {
3524 if (!(nl = af->getline())) return 1;
3528 reptable[j].pattern = NULL;
3529 reptable[j].pattern2 = NULL;
3530 piece = mystrsep(&tp, 0);
3532 if (*piece != '\0') {
3535 if (strncmp(piece,"REP",3) != 0) {
3536 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3543 if (*piece == '^') reptable[j].start = true; else reptable[j].start = false;
3544 reptable[j].pattern = mystrrep(mystrdup(piece + int(reptable[j].start)),"_"," ");
3545 int lr = strlen(reptable[j].pattern) - 1;
3546 if (reptable[j].pattern[lr] == '$') {
3547 reptable[j].end = true;
3548 reptable[j].pattern[lr] = '\0';
3549 } else reptable[j].end = false;
3552 case 2: { reptable[j].pattern2 = mystrrep(mystrdup(piece),"_"," "); break; }
3557 piece = mystrsep(&tp, 0);
3559 if ((!(reptable[j].pattern)) || (!(reptable[j].pattern2))) {
3560 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3568 /* parse in the typical fault correcting table */
3569 int AffixMgr::parse_convtable(char * line, FileMgr * af, RepList ** rl, const char * keyword)
3572 HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
3580 piece = mystrsep(&tp, 0);
3582 if (*piece != '\0') {
3584 case 0: { np++; break; }
3586 numrl = atoi(piece);
3588 HUNSPELL_WARNING(stderr, "error: line %d: incorrect entry number\n", af->getlinenum());
3591 *rl = new RepList(numrl);
3600 piece = mystrsep(&tp, 0);
3603 HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
3607 /* now parse the num lines to read in the remainder of the table */
3609 for (int j=0; j < numrl; j++) {
3610 if (!(nl = af->getline())) return 1;
3614 char * pattern = NULL;
3615 char * pattern2 = NULL;
3616 piece = mystrsep(&tp, 0);
3618 if (*piece != '\0') {
3621 if (strncmp(piece, keyword, sizeof(keyword)) != 0) {
3622 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3629 case 1: { pattern = mystrrep(mystrdup(piece),"_"," "); break; }
3631 pattern2 = mystrrep(mystrdup(piece),"_"," ");
3638 piece = mystrsep(&tp, 0);
3640 if (!pattern || !pattern2) {
3645 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3648 (*rl)->add(pattern, pattern2);
3654 /* parse in the typical fault correcting table */
3655 int AffixMgr::parse_phonetable(char * line, FileMgr * af)
3658 HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
3665 piece = mystrsep(&tp, 0);
3667 if (*piece != '\0') {
3669 case 0: { np++; break; }
3671 phone = (phonetable *) malloc(sizeof(struct phonetable));
3672 if (!phone) return 1;
3673 phone->num = atoi(piece);
3674 phone->rules = NULL;
3675 phone->utf8 = (char) utf8;
3676 if (phone->num < 1) {
3677 HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
3680 phone->rules = (char * *) malloc(2 * (phone->num + 1) * sizeof(char *));
3681 if (!phone->rules) {
3693 piece = mystrsep(&tp, 0);
3696 HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
3700 /* now parse the phone->num lines to read in the remainder of the table */
3702 for (int j=0; j < phone->num; j++) {
3703 if (!(nl = af->getline())) return 1;
3707 phone->rules[j * 2] = NULL;
3708 phone->rules[j * 2 + 1] = NULL;
3709 piece = mystrsep(&tp, 0);
3711 if (*piece != '\0') {
3714 if (strncmp(piece,"PHONE",5) != 0) {
3715 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3721 case 1: { phone->rules[j * 2] = mystrrep(mystrdup(piece),"_",""); break; }
3722 case 2: { phone->rules[j * 2 + 1] = mystrrep(mystrdup(piece),"_",""); break; }
3727 piece = mystrsep(&tp, 0);
3729 if ((!(phone->rules[j * 2])) || (!(phone->rules[j * 2 + 1]))) {
3730 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3735 phone->rules[phone->num * 2] = mystrdup("");
3736 phone->rules[phone->num * 2 + 1] = mystrdup("");
3737 init_phonet_hash(*phone);
3741 /* parse in the checkcompoundpattern table */
3742 int AffixMgr::parse_checkcpdtable(char * line, FileMgr * af)
3744 if (numcheckcpd != 0) {
3745 HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
3752 piece = mystrsep(&tp, 0);
3754 if (*piece != '\0') {
3756 case 0: { np++; break; }
3758 numcheckcpd = atoi(piece);
3759 if (numcheckcpd < 1) {
3760 HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
3763 checkcpdtable = (patentry *) malloc(numcheckcpd * sizeof(struct patentry));
3764 if (!checkcpdtable) return 1;
3772 piece = mystrsep(&tp, 0);
3775 HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
3779 /* now parse the numcheckcpd lines to read in the remainder of the table */
3781 for (int j=0; j < numcheckcpd; j++) {
3782 if (!(nl = af->getline())) return 1;
3786 checkcpdtable[j].pattern = NULL;
3787 checkcpdtable[j].pattern2 = NULL;
3788 checkcpdtable[j].pattern3 = NULL;
3789 checkcpdtable[j].cond = FLAG_NULL;
3790 checkcpdtable[j].cond2 = FLAG_NULL;
3791 piece = mystrsep(&tp, 0);
3793 if (*piece != '\0') {
3796 if (strncmp(piece,"CHECKCOMPOUNDPATTERN",20) != 0) {
3797 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3804 checkcpdtable[j].pattern = mystrdup(piece);
3805 char * p = strchr(checkcpdtable[j].pattern, '/');
3808 checkcpdtable[j].cond = pHMgr->decode_flag(p + 1);
3812 checkcpdtable[j].pattern2 = mystrdup(piece);
3813 char * p = strchr(checkcpdtable[j].pattern2, '/');
3816 checkcpdtable[j].cond2 = pHMgr->decode_flag(p + 1);
3820 case 3: { checkcpdtable[j].pattern3 = mystrdup(piece); simplifiedcpd = 1; break; }
3825 piece = mystrsep(&tp, 0);
3827 if ((!(checkcpdtable[j].pattern)) || (!(checkcpdtable[j].pattern2))) {
3828 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3836 /* parse in the compound rule table */
3837 int AffixMgr::parse_defcpdtable(char * line, FileMgr * af)
3839 if (numdefcpd != 0) {
3840 HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
3847 piece = mystrsep(&tp, 0);
3849 if (*piece != '\0') {
3851 case 0: { np++; break; }
3853 numdefcpd = atoi(piece);
3854 if (numdefcpd < 1) {
3855 HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
3858 defcpdtable = (flagentry *) malloc(numdefcpd * sizeof(flagentry));
3859 if (!defcpdtable) return 1;
3867 piece = mystrsep(&tp, 0);
3870 HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
3874 /* now parse the numdefcpd lines to read in the remainder of the table */
3876 for (int j=0; j < numdefcpd; j++) {
3877 if (!(nl = af->getline())) return 1;
3881 defcpdtable[j].def = NULL;
3882 piece = mystrsep(&tp, 0);
3884 if (*piece != '\0') {
3887 if (strncmp(piece, "COMPOUNDRULE", 12) != 0) {
3888 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3894 case 1: { // handle parenthesized flags
3895 if (strchr(piece, '(')) {
3896 defcpdtable[j].def = (FLAG *) malloc(strlen(piece) * sizeof(FLAG));
3897 defcpdtable[j].len = 0;
3901 char * par = piece + 1;
3902 while (*par != '(' && *par != ')' && *par != '\0') par++;
3903 if (*par == '\0') end = 1; else *par = '\0';
3904 if (*piece == '(') piece++;
3905 if (*piece == '*' || *piece == '?') {
3906 defcpdtable[j].def[defcpdtable[j].len++] = (FLAG) *piece;
3907 } else if (*piece != '\0') {
3908 int l = pHMgr->decode_flags(&conv, piece, af);
3909 for (int k = 0; k < l; k++) defcpdtable[j].def[defcpdtable[j].len++] = conv[k];
3915 defcpdtable[j].len = pHMgr->decode_flags(&(defcpdtable[j].def), piece, af);
3923 piece = mystrsep(&tp, 0);
3925 if (!defcpdtable[j].len) {
3926 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3935 /* parse in the character map table */
3936 int AffixMgr::parse_maptable(char * line, FileMgr * af)
3939 HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
3946 piece = mystrsep(&tp, 0);
3948 if (*piece != '\0') {
3950 case 0: { np++; break; }
3952 nummap = atoi(piece);
3954 HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
3957 maptable = (mapentry *) malloc(nummap * sizeof(struct mapentry));
3958 if (!maptable) return 1;
3966 piece = mystrsep(&tp, 0);
3969 HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
3973 /* now parse the nummap lines to read in the remainder of the table */
3975 for (int j=0; j < nummap; j++) {
3976 if (!(nl = af->getline())) return 1;
3980 maptable[j].set = NULL;
3981 maptable[j].len = 0;
3982 piece = mystrsep(&tp, 0);
3984 if (*piece != '\0') {
3987 if (strncmp(piece,"MAP",3) != 0) {
3988 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3996 maptable[j].len = strlen(piece);
3997 maptable[j].set = (char **) malloc(maptable[j].len * sizeof(char*));
3998 if (!maptable[j].set) return 1;
3999 for (int k = 0; k < maptable[j].len; k++) {
4002 if (piece[k] == '(') {
4003 char * parpos = strchr(piece + k, ')');
4004 if (parpos != NULL) {
4006 chl = (int)(parpos - piece) - k - 1;
4010 if (utf8 && (piece[k] & 0xc0) == 0xc0) {
4011 for (k++; utf8 && (piece[k] & 0xc0) == 0x80; k++);
4016 maptable[j].set[setn] = (char *) malloc(chl + 1);
4017 if (!maptable[j].set[setn]) return 1;
4018 strncpy(maptable[j].set[setn], piece + chb, chl);
4019 maptable[j].set[setn][chl] = '\0';
4022 maptable[j].len = setn;
4028 piece = mystrsep(&tp, 0);
4030 if (!maptable[j].set || !maptable[j].len) {
4031 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
4039 /* parse in the word breakpoint table */
4040 int AffixMgr::parse_breaktable(char * line, FileMgr * af)
4042 if (numbreak > -1) {
4043 HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
4050 piece = mystrsep(&tp, 0);
4052 if (*piece != '\0') {
4054 case 0: { np++; break; }
4056 numbreak = atoi(piece);
4058 HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
4061 if (numbreak == 0) return 0;
4062 breaktable = (char **) malloc(numbreak * sizeof(char *));
4063 if (!breaktable) return 1;
4071 piece = mystrsep(&tp, 0);
4074 HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
4078 /* now parse the numbreak lines to read in the remainder of the table */
4080 for (int j=0; j < numbreak; j++) {
4081 if (!(nl = af->getline())) return 1;
4085 piece = mystrsep(&tp, 0);
4087 if (*piece != '\0') {
4090 if (strncmp(piece,"BREAK",5) != 0) {
4091 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
4098 breaktable[j] = mystrdup(piece);
4105 piece = mystrsep(&tp, 0);
4108 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
4116 void AffixMgr::reverse_condition(char * piece) {
4118 for (char * k = piece + strlen(piece) - 1; k >= piece; k--) {
4121 if (neg) *(k+1) = '['; else *k = ']';
4126 if (neg) *(k+1) = '^';
4131 if (*(k+1) == ']') neg = 1; else *(k+1) = *k;
4135 if (neg) *(k+1) = *k;
4141 int AffixMgr::parse_affix(char * line, const char at, FileMgr * af, char * dupflags)
4143 int numents = 0; // number of affentry structures to parse
4145 unsigned short aflag = 0; // affix char identifier
4148 std::vector<affentry> affentries;
4155 // checking lines with bad syntax
4157 int basefieldnum = 0;
4160 // split affix header line into pieces
4164 piece = mystrsep(&tp, 0);
4166 if (*piece != '\0') {
4168 // piece 1 - is type of affix
4169 case 0: { np++; break; }
4171 // piece 2 - is affix char
4174 aflag = pHMgr->decode_flag(piece);
4175 if (((at == 'S') && (dupflags[aflag] & dupSFX)) ||
4176 ((at == 'P') && (dupflags[aflag] & dupPFX))) {
4177 HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix flag\n",
4179 // return 1; XXX permissive mode for bad dictionaries
4181 dupflags[aflag] += (char) ((at == 'S') ? dupSFX : dupPFX);
4184 // piece 3 - is cross product indicator
4185 case 2: { np++; if (*piece == 'Y') ff = aeXPRODUCT; break; }
4187 // piece 4 - is number of affentries
4190 numents = atoi(piece);
4192 char * err = pHMgr->encode_flag(aflag);
4194 HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n",
4200 affentries.resize(numents);
4201 affentries[0].opts = ff;
4202 if (utf8) affentries[0].opts += aeUTF8;
4203 if (pHMgr->is_aliasf()) affentries[0].opts += aeALIASF;
4204 if (pHMgr->is_aliasm()) affentries[0].opts += aeALIASM;
4205 affentries[0].aflag = aflag;
4212 piece = mystrsep(&tp, 0);
4214 // check to make sure we parsed enough pieces
4216 char * err = pHMgr->encode_flag(aflag);
4218 HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
4224 // now parse numents affentries for this affix
4225 std::vector<affentry>::iterator start = affentries.begin();
4226 std::vector<affentry>::iterator end = affentries.end();
4227 for (std::vector<affentry>::iterator entry = start; entry != end; ++entry) {
4228 if (!(nl = af->getline())) return 1;
4234 // split line into pieces
4235 piece = mystrsep(&tp, 0);
4237 if (*piece != '\0') {
4239 // piece 1 - is type
4242 if (entry != start) entry->opts = start->opts &
4243 (char) (aeXPRODUCT + aeUTF8 + aeALIASF + aeALIASM);
4247 // piece 2 - is affix char
4250 if (pHMgr->decode_flag(piece) != aflag) {
4251 char * err = pHMgr->encode_flag(aflag);
4253 HUNSPELL_WARNING(stderr, "error: line %d: affix %s is corrupt\n",
4254 af->getlinenum(), err);
4260 if (entry != start) entry->aflag = start->aflag;
4264 // piece 3 - is string to strip or 0 for null
4267 if (complexprefixes) {
4268 if (utf8) reverseword_utf(piece); else reverseword(piece);
4270 entry->strip = mystrdup(piece);
4271 entry->stripl = (unsigned char) strlen(entry->strip);
4272 if (strcmp(entry->strip,"0") == 0) {
4274 entry->strip=mystrdup("");
4280 // piece 4 - is affix string or 0 for null
4283 entry->morphcode = NULL;
4284 entry->contclass = NULL;
4285 entry->contclasslen = 0;
4287 dash = strchr(piece, '/');
4293 remove_ignored_chars_utf(piece, ignorechars_utf16, ignorechars_utf16_len);
4295 remove_ignored_chars(piece,ignorechars);
4299 if (complexprefixes) {
4300 if (utf8) reverseword_utf(piece); else reverseword(piece);
4302 entry->appnd = mystrdup(piece);
4304 if (pHMgr->is_aliasf()) {
4305 int index = atoi(dash + 1);
4306 entry->contclasslen = (unsigned short) pHMgr->get_aliasf(index, &(entry->contclass), af);
4307 if (!entry->contclasslen) HUNSPELL_WARNING(stderr, "error: bad affix flag alias: \"%s\"\n", dash+1);
4309 entry->contclasslen = (unsigned short) pHMgr->decode_flags(&(entry->contclass), dash + 1, af);
4310 flag_qsort(entry->contclass, 0, entry->contclasslen);
4315 for (unsigned short _i = 0; _i < entry->contclasslen; _i++) {
4316 contclasses[(entry->contclass)[_i]] = 1;
4321 remove_ignored_chars_utf(piece, ignorechars_utf16, ignorechars_utf16_len);
4323 remove_ignored_chars(piece,ignorechars);
4327 if (complexprefixes) {
4328 if (utf8) reverseword_utf(piece); else reverseword(piece);
4330 entry->appnd = mystrdup(piece);
4333 entry->appndl = (unsigned char) strlen(entry->appnd);
4334 if (strcmp(entry->appnd,"0") == 0) {
4336 entry->appnd=mystrdup("");
4342 // piece 5 - is the conditions descriptions
4345 if (complexprefixes) {
4346 if (utf8) reverseword_utf(piece); else reverseword(piece);
4347 reverse_condition(piece);
4349 if (entry->stripl && (strcmp(piece, ".") != 0) &&
4350 redundant_condition(at, entry->strip, entry->stripl, piece, af->getlinenum()))
4354 reverse_condition(piece);
4356 if (encodeit(*entry, piece)) return 1;
4362 if (pHMgr->is_aliasm()) {
4363 int index = atoi(piece);
4364 entry->morphcode = pHMgr->get_aliasm(index);
4366 if (complexprefixes) { // XXX - fix me for morph. gen.
4367 if (utf8) reverseword_utf(piece); else reverseword(piece);
4369 // add the remaining of the line
4372 tp = tp + strlen(tp);
4374 entry->morphcode = mystrdup(piece);
4375 if (!entry->morphcode) return 1;
4383 piece = mystrsep(&tp, 0);
4385 // check to make sure we parsed enough pieces
4387 char * err = pHMgr->encode_flag(aflag);
4389 HUNSPELL_WARNING(stderr, "error: line %d: affix %s is corrupt\n",
4390 af->getlinenum(), err);
4397 // detect unnecessary fields, excepting comments
4399 int fieldnum = !(entry->morphcode) ? 5 : ((*(entry->morphcode)=='#') ? 5 : 6);
4400 if (fieldnum != basefieldnum)
4401 HUNSPELL_WARNING(stderr, "warning: line %d: bad field number\n", af->getlinenum());
4403 basefieldnum = !(entry->morphcode) ? 5 : ((*(entry->morphcode)=='#') ? 5 : 6);
4408 // now create SfxEntry or PfxEntry objects and use links to
4409 // build an ordered (sorted by affix string) list
4410 for (std::vector<affentry>::iterator entry = start; entry != end; ++entry) {
4412 PfxEntry * pfxptr = new PfxEntry(this,&(*entry));
4413 build_pfxtree(pfxptr);
4415 SfxEntry * sfxptr = new SfxEntry(this,&(*entry));
4416 build_sfxtree(sfxptr);
4422 int AffixMgr::redundant_condition(char ft, char * strip, int stripl, const char * cond, int linenum) {
4423 int condl = strlen(cond);
4428 if (ft == 'P') { // prefix
4429 if (strncmp(strip, cond, condl) == 0) return 1;
4432 for (i = 0, j = 0; (i < stripl) && (j < condl); i++, j++) {
4433 if (cond[j] != '[') {
4434 if (cond[j] != strip[i]) {
4435 HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum);
4439 neg = (cond[j+1] == '^') ? 1 : 0;
4443 if (strip[i] == cond[j]) in = 1;
4444 } while ((j < (condl - 1)) && (cond[j] != ']'));
4445 if (j == (condl - 1) && (cond[j] != ']')) {
4446 HUNSPELL_WARNING(stderr, "error: line %d: missing ] in condition:\n%s\n", linenum, cond);
4449 if ((!neg && !in) || (neg && in)) {
4450 HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum);
4455 if (j >= condl) return 1;
4458 if ((stripl >= condl) && strcmp(strip + stripl - condl, cond) == 0) return 1;
4461 for (i = stripl - 1, j = condl - 1; (i >= 0) && (j >= 0); i--, j--) {
4462 if (cond[j] != ']') {
4463 if (cond[j] != strip[i]) {
4464 HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum);
4471 if (strip[i] == cond[j]) in = 1;
4472 } while ((j > 0) && (cond[j] != '['));
4473 if ((j == 0) && (cond[j] != '[')) {
4474 HUNSPELL_WARNING(stderr, "error: line: %d: missing ] in condition:\n%s\n", linenum, cond);
4477 neg = (cond[j+1] == '^') ? 1 : 0;
4478 if ((!neg && !in) || (neg && in)) {
4479 HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum);
4484 if (j < 0) return 1;